Skip to content

docs(agents): a negative --is-ancestor reading is void without a positive control - #15355

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-15087-is-ancestor-shallow-control
Sep 4, 2026
Merged

docs(agents): a negative --is-ancestor reading is void without a positive control#15355
os-zhuang merged 2 commits into
mainfrom
claude/issue-15087-is-ancestor-shallow-control

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #15087

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 — "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-by premise checks are all hoping
for 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-repository spelling rather than introducing a second spelling for the same
question.

What the clause says

Both halves, as triaged:

  • The failure is one-directional. Missing objects can only withhold an ancestry path,
    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.
  • A negative reading is a two-legged measurement, incomplete with one leg. In the same
    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~5 and
the 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-repository is a cheap trigger rather than the verdict:
after git fetch --unshallow it can still read true when another graft remains, while
the 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 — bbbac0fc is an ancestor of origin/main:

git merge-base --is-ancestor bbbac0fc origin/main   → exit 0

Same question in a scratch --depth 50 clone, with the object present but its path
truncated (git fetch --depth 1 origin bbbac0fc):

git rev-parse --is-shallow-repository               → true
git rev-list --count HEAD                           → 50
git merge-base --is-ancestor bbbac0fc HEAD          → exit 1   ← silently wrong
git merge-base --is-ancestor HEAD~5   HEAD          → exit 0   ← near control: passes, proves nothing
git merge-base --is-ancestor BOUNDARY HEAD          → exit 0   ← boundary control: also blind
git fetch --unshallow                               → depth 700
git merge-base --is-ancestor bbbac0fc HEAD          → exit 0   ← the true answer

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.

  • All 15 derived gates green, including 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-declared and
    pnpm --filter @objectstack/lint run check:doc-formula-expressions (which first reported
    PREREQUISITE NOT MET, exit 3 — nothing measured — until its closure was built).
  • Whole-repo pnpm lint green through the shared verification lock, exit 0. No narrowing
    was claimed: the full repo scan ran.
  • node scripts/pm/check-governed-merges.mjs --test reports 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, enqueues
it, or arms auto-merge; a human merges it. Labelled skip-changeset: the diff publishes
nothing from any released package.


Generated by Claude Code

…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
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026
@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation labels Sep 4, 2026
@claude
claude Bot requested review from hotlong and os-zhuang September 4, 2026 12:46
@os-zhuang
os-zhuang marked this pull request as ready for review September 4, 2026 12:59
@os-zhuang
os-zhuang enabled auto-merge September 4, 2026 13:00
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 638ea04 Sep 4, 2026
31 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-15087-is-ancestor-shallow-control branch September 4, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants