docs(agents): a negative --is-ancestor reading is void without a positive control - #15355
Merged
Merged
Conversation
…sitive control
`git merge-base --is-ancestor` is a one-bit answer with no error channel for "I
could not see far enough": in a shallow clone it answers exit 1 for a commit that
IS in the target's history, silently, at a normal exit code. The failure is
one-directional — missing objects can only withhold an ancestry path, never invent
one — so exit 0 stands in any clone and only exit 1 needs a control leg. That is
the direction the premise checks devs are ordered to run ("not released yet", "not
on main", `Blocked-by`) actually ask for.
Sits beside the existing shallow-clone guard for `log -S`/`--follow`/`blame` and
echoes its `--is-shallow-repository` spelling rather than adding a second one,
with the measured caveat that the depth read is a cheap trigger and the control
leg is the verdict.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
`check:pm-skill-ratchet` prices a per-session token read in BYTES, not display columns, so the CJK lines landed at 137-140B against a 120B budget. Re-wrapped at legal break points (spaces, and between two CJK characters) with the content byte-identical — 8 lines become 9; the file is 459 of a 469 ceiling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
os-zhuang
approved these changes
Sep 4, 2026
os-zhuang
marked this pull request as ready for review
September 4, 2026 12:59
os-zhuang
enabled auto-merge
September 4, 2026 13:00
This was referenced Sep 4, 2026
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 #15087
git merge-base --is-ancestoris a one-bit answer with no error channel for "I could notsee far enough." In a shallow clone it answers exit 1 — "not an ancestor" — for a commit
that IS in the target's history, silently, at a normal exit code. That is the reassuring
direction: "not released yet", "not on main" and
Blocked-bypremise checks are all hopingfor exit 1, so a control-free reading looks exactly like a pass.
This adds one clause to the dev contract's standard-clauses section, immediately beside the
existing shallow-clone guard for
log -S/--follow/blame— echoing that guard's--is-shallow-repositoryspelling rather than introducing a second spelling for the samequestion.
What the clause says
Both halves, as triaged:
never invent one, so exit 0 ("is an ancestor") is self-proving and stands in any clone.
Only exit 1 needs a control leg. Stating it this way keeps the guard off the safe
direction instead of taxing every call — a rule that costs more than it must is the kind
that quietly stops being run.
checkout, against the same target ref, a commit known to be in the target's history must
answer exit 0; otherwise the negative reading is void — deepen or unshallow until the
control passes, then re-read. Written in the shape this repo already uses for a zero-hit
search that needs a known-present control before it counts as a reading.
One refinement, measured while writing it
The control leg has to be chosen, not merely run. In a depth-50 clone,
HEAD~5andthe shallow boundary commit both answer exit 0 while the real reading is still truncated —
a near control is completely blind to the truncation it exists to detect. The clause
therefore requires the control commit to be at least as deep as the one under test.
The same run showed
--is-shallow-repositoryis a cheap trigger rather than the verdict:after
git fetch --unshallowit can still readtruewhen another graft remains, whilethe reading itself is already correct. The control leg is the verdict; the depth read only
tells you to go looking.
Reproduction
Ground truth in the full clone —
bbbac0fcis an ancestor oforigin/main:Same question in a scratch
--depth 50clone, with the object present but its pathtruncated (
git fetch --depth 1 origin bbbac0fc):The scratch clone was removed afterwards.
Verification
Gate family re-derived from the actual change set (not a hand-built path list) and
reconciled with what was run; the union below was run at
076ee33d, the final commit.pnpm check:pm-skill-ratchet(
.claude/agents/os-dev.md is 459 lines (ceiling 469; headroom 10)),pnpm check:nul-bytes,pnpm check:agent-model-declaredandpnpm --filter @objectstack/lint run check:doc-formula-expressions(which first reportedPREREQUISITE NOT MET, exit 3 — nothing measured — until its closure was built).
pnpm lintgreen through the shared verification lock, exit 0. No narrowingwas claimed: the full repo scan ran.
node scripts/pm/check-governed-merges.mjs --testreports GOVERNED, exit 3.The second commit is a re-wrap only: the ratchet prices lines in bytes, not display
columns, so the CJK lines first landed at 137-140B against a 120B budget. The re-wrap
breaks only at legal points and was proven byte-identical by round-trip before it was
applied.
Landing
Governed surface (
.claude/**) — this stays a draft. No seat flips it ready, enqueuesit, or arms auto-merge; a human merges it. Labelled
skip-changeset: the diff publishesnothing from any released package.
Generated by Claude Code