Skip to content

fix(scripts): make the ADR symbol-anchor self-tests prove they reached their verdict - #15132

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13798-batch4-verdict-handshake
Sep 4, 2026
Merged

fix(scripts): make the ADR symbol-anchor self-tests prove they reached their verdict#15132
baozhoutao merged 1 commit into
mainfrom
claude/issue-13798-batch4-verdict-handshake

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #13798

Batch 4 — the whole remaining measured population of hole 2 (verdict handshake): scripts/check-adr-symbol-anchors.mjs and scripts/symbol-anchors.mjs. Boundary-only, as batches 1–3.

Both dispatched their self-test as if (process.argv.includes('--self-test')) selfTest();, discarding the return value, so a return anywhere above the verdict line printed nothing and still exited 0 — a self-test that never finished, reported as one that passed.

Premise re-derived on this tree, ⛔ not inherited

Batch 1's exclusion list wrote both off as "carrying no --self-test dispatch at all". True of the tree batch 1 measured, false today: both arrived in 09cc6be43 (PR #14277). An exclusion list describes the tree it was measured on, so the population was re-derived here rather than inherited:

  • three-spelling marker census on 2cc461030: sentinel 0, flag 0, helper 0 in both files;
  • and then BEHAVIOURALLY, with the census's own probeEarlyReturn: both DEFEATED, mutated exit 0, 0 bytes printed. That silence-plus-zero is the defect.

Spelling, judged per file — ⛔ not defaulted, and no fourth spelling

SENTINEL for both. Each dispatch discards the self-test's return value (nothing in the tree imports either selfTest; both files' consumers import only the extractor/corpus exports), so the handshake can be the return value. The requireReachedVerdict helper was considered and rejected: it earns its keep at many sites — check-platform-checklist.mjs uses it at eleven — and each file here has exactly one.

The brace trap, inherited from batches 1, 2 and 3 rather than rediscovered

Both dispatches were unbraced with a trailing else:

  • check-adr-symbol-anchors.mjs: if (--self-test) selfTest(); else if (--list) list(); else runCheck();
  • symbol-anchors.mjs: if (--self-test) selfTest(); else { library usage }

The if body is braced so the trailing else/else if cannot re-bind to the inner refusal; the else arms are left unbraced, per the landed scripts/pm/check-label-desc-cap.mjs precedent. Both other arms were exercised after the change: --list exits 0 with its JSON (5512 bytes) and the bare library run still prints the grammar.

Nothing inside either self-test body changed; hole-1 floor classification reads NONE for both before and after, so no roster work leaked in and the two counts stay unmerged.

Evidence — the defect shown failing, not a green self-test

Instrument: the census's own probeEarlyReturn (scripts/measure-self-test-floor.mjs) — it mutates a copy, re-reads the marker from disk and refuses as NOT MEASURED unless the anchored count is exactly 1; its positive and negative controls run on every invocation.

file before after
check-adr-symbol-anchors.mjs DEFEATED · mutated exit 0 · 0 bytes HELD · mutated exit 1 · 210 bytes
symbol-anchors.mjs DEFEATED · mutated exit 0 · 0 bytes HELD · mutated exit 1 · 200 bytes

After, head line: selfTest() returned without reaching its verdict,.

Mutation proved on disk by the instrument's own marker count; restore proved by observed state, ⛔ never by an exit code — each tracked original's git hash-object was pinned before and after every probe and never moved (1b999bfd… and ecbcc454… before the edit; 44e5b7a5… and 8f7f1569… after), an empty hash coded as FAILURE rather than as nothing-to-compare, git status --porcelain clean, zero leftover .self-test-floor-probe-* copies. The ablation was run on the committed tree, so the restore leg had a real reference point. Rebuild leg: NOT APPLICABLE, stated rather than omitted — no dist/ is on the path, every gate here runs from source and the probe spawns the file itself.

Full census on this branch head (6e616eb72) — why Fixes, verified rather than assumed

node scripts/measure-self-test-floor.mjs --probe --json: 167 files — 161 HELD, 0 DEFEATED, 6 NOT MEASURED. Batch 3 read 167 / 159 / 2 / 6; the two it named are the two here.

The 6 NOT MEASURED were each read, because "not measured" is never a pass:

  • check-platform-checklist.mjs (helper spelling, 11 sites), check-workspace-manifest-cycles.mjs and scripts/pm/dispatch-gates.mjs (sentinel spelling) do carry the handshake — the one-function probe simply cannot express the mutation on them (multi-callee dispatch; ambiguous entry — the census gap for that one is open as The self-test-floor census cannot measure check-workspace-manifest-cycles.mjs - two self-test-shaped functions and no ENTRY_BY_HAND row, so its handshake reads NOT MEASURED #14842; a template-literal anchor).
  • check-regen-pending.mjs, git-merge-regen.mjs and setup-git-hooks.mjs dispatch an inline top-level block with no callee to leave early, so hole 2's mutation is not expressible on them. Each was read directly rather than waved through: all three derive their exit status from collected results (process.exit(ok ? 0 : 1), process.exitCode = 1 inside fail()), so none of them is silently green over unrun cases. They are the reshaping class batch 1's ACCEPT placed outside this card's batches, and they are not a defeated population.

⇒ zero DEFEATED remain, so Fixes is correct here where batch 3's Part of was.

Gates

Derived at the final head with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no hand-built path list): 2 paths vs merge base 2cc461030, 24 runnable families (16 by path + 2 by change KIND + 6 declared whole-tree), harvested with --commands. 24 of 24 RAN-PASS, 0 NOT MEASURED. Every exit code was captured by redirecting first and reading $? after — ⛔ never through a pipe.

⭐ And on top of the derivation, because a derived family is not a complete account of what CI runs (the always-runs tail): both files' own --self-test (already named, run explicitly anyway), pnpm check:pm-dispatch-gates (1338 cases pass), pnpm check:nul-bytes plus a direct control-byte sweep of both touched files (grep -naP over the C0 range plus DEL — no match), and pnpm lint whole repo, not narrowed: 5854 files, 0 errors, 0 warnings (counts read from --format json).

node scripts/check-adr-symbol-anchors.mjs production path: 1856 anchors across 134 records resolve, 0 line anchors survive.

Changeset

None — scripts/** only, nothing published from any released package. skip-changeset applied at PR-open time and read back.

Part of the #13489 survey; hole 1 (#13799) is ⛔ orthogonal and untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…d their verdict

`scripts/check-adr-symbol-anchors.mjs` and `scripts/symbol-anchors.mjs` both
dispatched their self-test as `if (process.argv.includes('--self-test'))
selfTest();` — the return value discarded — so a `return` anywhere above the
verdict line printed nothing and still exited 0: a self-test that never
finished, reported as one that passed.

Both take the SENTINEL spelling of the boundary-only handshake, because both
dispatches discard the return value, so the handshake can BE it. Nothing inside
either self-test body changed.

Both dispatches were unbraced with a trailing `else`/`else if`, so the `if`
body is braced and the `else` arms left unbraced, per the landed
`scripts/pm/check-label-desc-cap.mjs` precedent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

144 of 154 scripts/** self-tests exit 0 when an early return leaves them before their verdict — 137 printing nothing at all

2 participants