fix(devx): the objectui pin guard tests walk completeness, not object presence - #9448
Merged
Merged
Conversation
… presence `bump-objectui.sh` decided whether the pin range could be walked with `git cat-file -e OLD_SHA` — "is the OLD endpoint present as an object". That is a different question from "is the walk between the endpoints complete", and the gap is measured rather than theoretical: on the bump that landed `.changeset/console-82a94170c405.md` the test PASSED against a history truncated at commit 110 of 191, so the degraded path never fired and the digest exited 0 having credited 36 of its 119 entries to one commit that adds exactly one changeset. A truncated history is worse than an absent endpoint precisely because it ANSWERS: git presents its oldest visible commit as parentless, diffs it against the empty tree, and reports every `.changeset/*.md` in that commit's tree as added by it. The dedup in `collectAddedChangesets` then hands it whatever no newer commit claimed, so one commit absorbs a batch. `findRangeTruncation` tests the harm directly — a parentless commit INSIDE the range — which catches a `--depth` clone, a hand-written `.git/shallow`, a graft, a `git replace` and unrelated histories without naming any of them, and which does not fire on a shallow clone whose boundary sits at or before `from` (that range walks completely, and refusing it would be a false positive). It lives in `classifyRange`, the single shared implementation, so `objectui-range.mjs` cannot derive from a truncated walk either. `bump-objectui.sh` now repairs the input before labelling a derivation of it: `git fetch --unshallow` costs ~6s and ~4MB on objectui and takes the walk from 110 commits to the true 191, while a degraded console changeset becomes permanent published CHANGELOG text. The fetch is additive, announced, opt-out-able with OBJECTUI_NO_DEEPEN=1, and — measured — can exit 0 without repairing anything, so the range is re-checked instead of the status trusted. The arithmetic check the card sketched is NOT implemented, because it was measured not to work: `git show --name-only` on the sink runs the same empty-tree diff, so the credited set is a subset of the attributed set by construction. It fires on neither known-bad run (36 credited vs 72 attributed; 12 vs 47) and cannot fire on any history. Pinned as a tautology in the self-test so it is not reintroduced as a cheap backstop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
os-steve
marked this pull request as ready for review
August 18, 2026 01:37
os-steve
enabled auto-merge
August 18, 2026 01:38
os-steve
disabled auto-merge
August 18, 2026 01:39
os-steve
enabled auto-merge
August 18, 2026 01:39
os-steve
disabled auto-merge
August 18, 2026 01:39
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 #9408
The card is right about the mechanism and I reproduced it byte-for-byte before changing anything. Two of its four suggested repairs are implemented, one is subsumed, and one is not implemented because I measured it and it does not work — details below.
Reproduced first, on two specimens
This container is still a live specimen — the PM triage note expected it not to be.
/home/user/objectuiis shallow right now, boundary75444e38a, inside the pin range:Running the unmodified digest against it credited 12 entries to
75444e38aand exited 0 — PR #9398's competing derivation, regenerated. Grafting a second copy at183d09b78(the card's own recipe) reproduced the landed record exactly: 36 entries credited to the sink,110 non-merge commits, and the entry list byte-identical to the pre-correction artifact at83fe945d6(119 lines,diffclean). The index arithmetic holds independently:183d09b78at index 110,75444e38aat index 137.Not implemented: the arithmetic check, because it cannot fire
The card's fourth suggestion — no commit may be credited with more added changesets than
git show --name-onlyattributes to it — is described as catching all three bad runs. Measured against both runs I can reproduce, it catches neither:git show --name-onlyattributes183d09b78(landed record)75444e38a(this container)It cannot fire on any history.
git show --name-onlyon the sink runs the same empty-tree diff that produced the fault, and the digest's credited set is that attributed set minus theseendedup — a subset by construction. The comparison is a tautology: no false positives because no positives at all. Pinned as such in the self-test (C12) so it is not reintroduced later as a "cheap backstop".What replaces it: one true check
findRangeTruncationtests a parentless commit inside the range. That is the harm itself — the sink is the parentless commit — so it catches a--depthclone, a hand-written.git/shallow, a graft, agit replace, and unrelated histories without naming any of them. It is also strictly more precise than asking whether the clone is shallow: a shallow clone whose boundary sits at or beforefromwalks the range completely, and refusing it would be a false positive. In an untruncated history the range cannot contain a parentless commit at all, so a hit is always real.It lives in
classifyRange, the single shared implementation, soobjectui-range.mjscannot derive from a truncated walk either (it already wraps that call in a louddie()).Deepen first — measured, not assumed
git fetch --unshallowon objectui costs 6 seconds and ~4 MB, and takes the walk from 110 commits to the true 191. A degraded console changeset becomes permanent published CHANGELOG text, so repairing the input beats labelling a truncated derivation of it. The fetch is additive (adds objects, drops.git/shallow; moves no branch, touches no working tree), announced before and after, and opt-out-able withOBJECTUI_NO_DEEPEN=1.One measured trap it handles:
git fetch --unshallowin a checkout with no remote configured exits 0 and changes nothing. The range is therefore re-checked afterwards rather than the fetch status trusted — otherwise this card's failure would simply reappear one layer further in. Pinned in C17/C18.The other two PM assumptions, tested
⚠️ **Degraded list**, "NOT a complete account of the range", and the tip subject only. I extended the reason only — a truncated range must also be distinguishable from an absent endpoint, because the remedies differ and only one is a fetch away.to-endpoint validation is missing. Partly falsified. The digest CLI already validated both endpoints (exit 2, verified). The gap was inbump-objectui.sh's ownRANGE_OK, which now goes through--check-walkableand covers both.Tests
18 new checks (C1–C18) in
objectui-changeset-digest.mjs --self-test. The truncation is synthesized by writing.git/shallow, so the complete and truncated fixtures are the same commits differing in exactly one file — each assertion is about the graft, not about two repos that differ somehow.Both directions are pinned, per the explicit ask that a refusal-only test would pass on a guard that refuses everything: C1/C2 keep the complete walk, and C7 is the sharper control — a tree that is shallow with its boundary outside the range must still derive identically (
stillWhole.body === whole.body). C7 is exactly the false positive an--is-shallow-repositoryguard would produce.Reverse verification, at commit
68615c550: ablating both refusal call sites turns 8 checks red (C8, C9, C10, C13, C14, C15, C16, C17) and the ablated build emits the defect in miniature —5 releasing of 5 changesets added across 3 non-merge commitswith entries 1, 2 and 3 all credited to one sink. C3/C4/C5/C12 stay green by design: they measure the defect mechanism, not the guard. Restored withgit checkout HEAD --, byte-identical, green.Gates run against
68615c550(working tree clean, equal to head):Gate family derived from the changed paths with
node scripts/pm/dispatch-gates.mjs, which namescheck:objectui-changesetalone;check:nul-bytesand the pin-fresh consumer were added because the diff touches them.Scope
scripts/bump-objectui.shandscripts/objectui-changeset-digest.mjsonly, as dispatched. No changeset:scripts/is repo tooling and this PR publishes nothing, so it carriesskip-changeset..github/workflows/cut-rc.ymlhas the same object-presence test at line 285 and prints "range is walkable, the changeset digest will be complete" on the strength of it. It is currently harmless — that job clones objectui full, deliberately — so I did not widen this PR into a third file; filed separately instead.Generated by Claude Code
Generated by Claude Code