fix(core,metrics): renames follow the file — no phantom old => new path, lifecycle carried across the move - #104
Merged
Merged
Conversation
Contributor
|
evidtrail ✅ 1 commit — agent 1 — every commit in this change set carries provenance. Details — scope, provenance, limitsScope: Evidence: 100% coverage — declared 1 · inferred 0 · none 0.
Limits
|
…path, lifecycle carried across the move
Found by an external review and reproduced. `old.ts` → `git mv new.ts` →
edit `new.ts` produced THREE paths in the stream: `old.ts`,
`old.ts => new.ts` and `new.ts`, the rename filed as a modification of a
file that never existed. Every rename was one more eligible file nothing
could retouch, and the real file's edits landed under a name its first
touch was never recorded against. Rate biased down: it flatters.
- core: `git log --numstat` names a renamed file as `old => new` or
`dir/{old => new}/file` (1,911 brace-form and 45 plain-form on
react-router). `splitRenamePath` parses both; the stream records the
file under its new name with `previousPath` and status `renamed`
(additive field, no schema bump). `git show` in PR scope gets the same.
- metrics: persistence is one chronological pass; a lifecycle follows the
file across a rename. A pure rename (0/0) is not a touch — only the
address changed. A rename with edits is. Hotfix antecedents move with
the file the same way.
Fixture: 0/3 (0%) → 1/1 (100%).
AI-Mode: agent
ceccode
force-pushed
the
fix/renames-follow-the-file
branch
from
October 1, 2026 06:50
837453c to
6e82189
Compare
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.
Found by an external review (18 September) and reproduced before touching anything. It flatters, and by a lot.
old.ts→git mv new.ts→ editnew.tsproduced three paths in the stream —old.ts,old.ts => new.tsandnew.ts— with the rename filed as a modification of a file that never existed. Every rename in a repository was one more eligible file nothing could ever retouch, and the real file's later edits landed under a name its first touch had never been recorded against.Reproduction (fixture)
old.ts(1 Jan) →git mv new.ts(2 Jan) → editnew.ts(4 Jan), 7-day horizonWhat changed
git log --numstatnames a renamed file asold => newordir/{old => new}/file(on react-router: 1,911 brace-form, 45 plain-form).splitRenamePathparses both forms, including a brace group at the start of the path and one with an empty side. The stream records the file under its new name withpreviousPathand statusrenamed(additive field, no schema bump).git showin PR scope gets the same parsing.Dogfood: six repositories, same HEAD, before → after (30-day horizon, full history)
The direction is the same everywhere: eligible files fall (the phantom
old => newentries and the duplicate lifecycles under the new name disappear), retouches move slightly, and the rate rises — on rename-heavy repositories by eight to sixteen points. The phantoms had been padding the denominator with files nothing could touch. A repository with no renames does not move at all.Tests
paths.test.ts: plain form, brace form with prefix/suffix, brace group at the start, empty-side brace without a double slash, an arrow that is not a rename.collect.test.ts: a realgit mvyields one file under the new name withpreviousPath, and no phantom path anywhere in the stream.persistence.test.ts: the reviewer's case (0/3 → 1/1); a pure rename is not a touch and opens no lifecycle; a rename with edits is a touch.outcome-correlation.test.ts: a hotfix on the new name links to the last touch under the old one.pnpm build/typecheck/lintgreen; tests 307 → 317.