fix(pm): git-history's touch leg reads the graft, not just the parent's presence - #18398
Merged
os-elon-musk merged 3 commits intoSep 16, 2026
Merged
Conversation
…'s presence
`touchIsProvable` proved leg 1 by asking whether every parent the commit object
names is present locally. A later fetch — of another ref, or of a single sha —
brings that parent into the object store without moving the shallow boundary,
so the graft stays registered and git's traversal goes on treating the commit
as parentless. Leg 1 then passed, `diff-tree` diffed the commit against its
EMPTY grafted parent list and printed nothing, and the verdict came back
`{provable: false, boundary: false, reason: "<sha> has its parent(s) locally
but its diff does not touch <path>"}`: the refusal correct, the flag a false
negative, and the sentence telling an operator the file was untouched when a
grafted walk is what answered. Which sentence came out depended on when the
last fetch ran.
Leg 1 now reads the graft where git applies it, per sha: `git rev-parse
<sha>^@` lists the parents the TRAVERSAL uses — the `--max-parents=0` reading
asked of one commit — and an object naming a parent that list does not carry is
grafted whether or not the parent is in the store. Measured at 2.6 ms per call
against 15 ms for `rev-list --max-parents=0 <sha>` (20 runs each): the ancestry
walk costs more the more history it can see, `^@` costs the same everywhere. It
also needs no shallow-file path to resolve through a linked worktree's common
dir, and it catches a graft from any source.
A real root still passes (its object names no parent, so the two lists agree)
and an ordinary commit still answers — both pinned, beside a FIRING CONTROL on
the same grafted clone, so the new leg cannot become "refuse everything".
Eight self-test cases in the `touch` battery, floor 17 -> 25, on a fixture that
constructs the state rather than describing it: clone `--depth=5`, then a plain
fetch of the boundary's own parent sha with no `--deepen`, so nothing rewrites
the shallow list. The fixture's state is READ FIRST (shallow list still names
the boundary, parent object present, walk still roots there) — a case asserting
on a state that did not form proves nothing.
Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt
Co-authored-by: Claude <noreply@anthropic.com>
…ate it now tests The case asserts that a still-shallow clone answers once the named commit is usable; its title still said the predicate was "the parent's presence", which is the half this branch just replaced. Wording only — the assertion is byte-for -byte the same. Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt Co-authored-by: Claude <noreply@anthropic.com>
…printed for The refusal for the 「parent fetched, graft still registered」 state prints `fetch --deepen=N`. Measured on the fixture: with fetching allowed the same clone deepens once and answers the TRUE touch at exit 0, so the refusal is a step rather than a dead end. Pinned last on that clone, because it deepens it. `touch` battery 25 -> 26 cases, self-test 63. Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 16, 2026
os-elon-musk
marked this pull request as ready for review
September 16, 2026 09:24
os-elon-musk
deleted the
claude/issue-18355-git-history-touch-boundary-flag
branch
September 16, 2026 09:41
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.
Fixes #18355
touchIsProvableproved leg 1 by asking whether every parent the commit OBJECT names is present locally. A later fetch — of another ref, or of one sha — brings that parent into the object store without moving the shallow boundary, so the graft stays registered and git's traversal goes on treating the commit as parentless. Leg 1 then passed,diff-treediffed the commit against its EMPTY grafted parent list and printed nothing, and the verdict came back:The refusal is correct (no caller is handed the sha) and both the
boundaryflag and the sentence beside it were wrong: an operator was told the file was simply untouched, when a grafted walk is what answered. Which of the two sentences came out depended on when the last fetch ran.The discriminator, and why it is not the shallow file
Leg 1 now reads the graft where git applies it, per sha.
git rev-parse SHA^@lists the parents the TRAVERSAL uses — the--max-parents=0reading asked of ONE commit — and an object naming a parent that list does not carry is grafted, whether or not that parent is in the store. New exportisGraftBoundary(cwd, sha, parents?)compares the two lists;touchIsProvablefails leg 1 on it and returnsboundary: truewith a reason that names the outlived registration.The card named two candidate discriminators (shallow-file membership, or
rev-list --max-parents=0). Both were measured;SHA^@is the same traversal fact, asked per sha:git rev-parse SHA^@git rev-list --max-parents=0 SHA.gitfrom a repo root) and the sha normalised to 40 hex; misses a graft from any other sourceA real root still passes leg 1 (its object names no parent, so the two lists agree) and an ordinary commit still answers — the case a
--max-parents=0count on its own cannot tell apart, which is why this compares two lists instead of counting one.No behaviour change beyond the flag and the sentence
The refusal itself, the
--deependoubling to--unshallow, exit 2 and the empty stdout are untouched. A grafted commit'sdiff-treeprints nothing in every case, so no reading that used to beprovable: truecan become a refusal here.Tests
node scripts/pm/git-history.mjs --self-test— 62 cases, exit 0 (was 54). Eight new cases in thetouchbattery, floor 17 to 25.node scripts/pm/check-harness-current.mjs --self-test— the consumer, 14 cases, exit 0, unedited. It printsboundarywhere it used to printunprovablefor this state, which is the point of the card.--depth=5, then a plaingit fetch origin PARENT_SHA(no--deepen, so nothing rewrites the shallow list) withuploadpack.allowAnySHA1InWanton the upstream. The case READS THE STATE FIRST — shallow list still names the boundary, parent object present, walk still roots there, clone still shallow — because a case asserting on a state that did not form proves nothing.git checkout HEAD --under a trap, blob hash compared both ways): 3 cases red, then byte-identical restore and green again.Acceptance notes
graft boundaryand stayed GREEN under ablation: the refusal boilerplate prints that phrase on every refusal, so the case was pinning a constant. The ablation is what found it; the case now pins the reason clause and the absence of the misleading sentence, and its own history is written into the case name.skip-changeset:scripts/pm/**is not in any package'sfiles[]— nothing publishes.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt
Generated by Claude Code