vcs: follow a rename to the end, and never name a path that is not there - #8
Merged
Conversation
moved_to looked at the deleting commit and stopped, so a file renamed twice was reported at its first hop. Found on open-gsd/gsd-core, where docs/CONFIGURATION.md cites sdk/shared/model-catalog.json and the chain is sdk/ -> get-shit-done/ -> gsd-core/. We named the middle one, and get-shit-done/ has zero files at that repository's HEAD - so the advice pointed into a directory that does not exist anywhere in the tree. That is the worst thing this function can do: its whole purpose is to save the reader a search, and it was sending them somewhere empty. The invariant is now the one a reader assumes - a destination is named only if it is tracked at HEAD. A chain that dead-ends untracked returns None, so the finding says 'deleted and never restored', which is true. Saying less beats saying wrong. Bounded at ten hops; the worst real case is two. Verdicts do not move: 34 broken before and after on that repository. Four tests with their own two-commit history, because a rename is paired only within one commit. Confirmed failing beforehand for the right reasons - the two-hop case returned the middle path and the dead-end case returned a phantom - while the one-hop and never-renamed cases passed throughout.
melbinjp
added a commit
that referenced
this pull request
Aug 18, 2026
…wn (#9) pyproject carried version = "0.1.2" while src/docproof/__init__.py carried "0.1.0". Three releases had shipped and the README pinned v0.1.2, so anyone who followed the README ran a tool whose every report header read docproof 0.1.0 - their-repo, N document(s) and whose --version agreed. The packaging metadata was right; the number the tool says about itself was two releases stale. That is a documented claim contradicted by the repository, which is the exact thing this tool exists to find, inside this tool. Found while checking whether docproof was fit to offer into another project's CI, after a gsd-core maintainer had been pointed at it. He would have seen 0.1.0. Correcting the number would have left the second copy in place, so the second copy is gone instead: hatchling reads the package attribute and there is one literal. Bumped to 0.1.3, which is also the release that carries the rename-chain fix in #8. tests/test_version_has_one_source.py asserts the SHAPE rather than the value. A test that the two numbers are equal passes today and rots the moment somebody bumps one of them, which is how this arose. It asserts there is no static version under [project] at all, so the drift cannot be reintroduced by remembering to keep two things in sync - the class of rule this repository exists because people forget. Verified: full suite 167 passing, and `python -m build --wheel` produces docproof-0.1.3-py3-none-any.whl, so the build really does read the attribute.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
moved to Xshould mean X is there now. It did not.Git.moved_tolooked at the deleting commit and stopped, so a file renamed more than oncewas reported at its FIRST hop - a path that may itself be gone. That is the worst thing this
function can do, because its entire purpose is to save the reader a search, and instead it
sends them somewhere empty.
Found on a real repository
open-gsd/gsd-core,docs/CONFIGURATION.mdcitessdk/shared/model-catalog.json:We reported the middle one.
get-shit-done/has zero files at that repository's HEAD, sothe advice pointed into a directory that no longer exists anywhere in the tree.
The invariant, now enforced
A destination is named only if it is tracked at HEAD. The chain is followed, and if it
dead-ends somewhere untracked the answer is
None- so the finding reads "deleted ... andnever restored", which is true, rather than naming a phantom. Saying less beats saying wrong.
Bounded at ten hops, because a rename cycle is cheaper to survive than to prove impossible.
The worst real case in the corpus is two.
Verdicts do not move
This changes the advice, not the judgement. On the repository that surfaced it: 34 broken
before, 34 after. The docstring already said this function "changes no verdict" and that
stays true.
Proof
Four tests, building their own two-commit history because a rename is paired only within one
commit and
make_repocannot produce two.Confirmed failing before the fix, and for the right reasons rather than by luck:
NoneThe two that were already right still pass, which is the half that shows the fix did not
buy its result by breaking something else.
162 tests, ruff clean, docproof passes on itself.