Skip to content

test(scripts): assertion floors for nine self-tests — #13799 batch 8a (b5 hoisted single battery) - #15308

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-13799-batch8a-hoisted-batteries
Sep 4, 2026
Merged

test(scripts): assertion floors for nine self-tests — #13799 batch 8a (b5 hoisted single battery)#15308
baozhoutao merged 2 commits into
mainfrom
claude/issue-13799-batch8a-hoisted-batteries

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #13799

Fixes #15305

Batch 8a of the scripts/** assertion-floor card: the b5 hoisted-single-battery recipe (settled by PR #15217, ACCEPT 5536823391) transplanted into the nine census rows (5538556734) that already carry a block-bodied assertion helper and fewer than two named section banners.

Each file gets ONE battery, opened at the top of its self-test body and named BASENAME self-test; the floor is the case count measured on a run; SELF_TEST_BATTERIES's own size is pinned at 1; registerCase() is called from the block body of the helper the file already had; and the verdict refuses a below-floor, DID-NOT-RUN or undeclared battery through the file's own failure sink. No comment is promoted to a section head, no assertion condition is touched, and every verdict handshake is kept exactly as it landed.

Branch head for every run quoted below: 9ecea9ca2. Base for every byte comparison: an installed worktree detached at the dispatch pin 5b09356b7.

The nine

# file census row handshake helper (sink) floor red line
1 scripts/check-adr-symbol-anchors.mjs 1 sentinel module-level assert(), print + exit 1 17 unchanged (no count printed)
2 scripts/symbol-anchors.mjs 26 sentinel module-level assert(), print + exit 1 51 unchanged (no count printed)
3 scripts/check-i18n-walk-parity.mjs 5 flag in-body eq(what, got, want), failures list 23 N case(s) failed becomes N failure(s) (cases and floor)
4 scripts/check-test-completeness.mjs 9 sentinel in-body eq() that throws 67 unchanged (throws)
5 scripts/checklist-select.mjs 13 flag in-body eq() that exits 17 unchanged (no count printed)
6 scripts/pm/release-rehearsal-clone.mjs 21 flag in-body t(name, cond, extra), failures tally 32 N failure(s) becomes N failure(s) (cases and floor)
7 scripts/publish-smoke-pack.mjs 22 sentinel in-body check(name, fn) thunk 4 unchanged (SELF-TEST FAILED, no count)
8 scripts/render-release-coverage-anchor.mjs 23 flag module-level expect(what, ok) 10 N of M assertion(s) becomes N failure(s) (cases and floor) of M assertion(s)
9 scripts/run-with-stall-guard.mjs 24 flag in-body check(label, cond, detail), failures list 41 N self-test case(s) failed becomes N failure(s) (cases and floor)

Total floored: 262 cases. Nothing deferred — all nine matched their census row (block-bodied helper present, roster 0, handshake present).

Two floors are the census's own warning coming true, and both would have been WRONG if counted from the source. symbol-anchors has 40 static assert( sites but registers 51 (loops); render-release-coverage-anchor has 8 static expect( sites but registers 10 (a loop over three rendered bodies). The census flagged the second and predicted 40 for the first — so row 26's floor is a correction to the census table, measured the same way row 23's was.

Where the helper is module-level (rows 1, 2, 8) the case sites now call a thin in-body wrapper that registers and then defers to the existing assertion, whose semantics are untouched — the shape PR #15156 landed for exactly this case. Call sites moved: 17, 40 and 8 respectively; no condition, no message and no argument changed.

checklist-select also stops transcribing its count (#15305). The success line's hand-typed 17 is now read off a counter incremented in eq:

  • before: console.log('✓ checklist-select self-test: 17 cases pass.');
  • after: console.log(`✓ checklist-select self-test: ${cases} cases pass.`);

The rendered line is unchanged today, because the transcribed 17 was accurate — which is what lets the two-mode byte comparison below read as clean rather than as a wash. It stops being accurate the moment a case is added, which is the defect.

check-test-completeness scopes its floor to the loud run. selfTest({ quiet: true }) runs on every production invocation of that gate, where nothing claims a self-test verdict; the floor exists to stop a green SELF-TEST VERDICT being printed over cases that never ran, so it is evaluated exactly where that verdict is printed. Proven below, not asserted.

Proof

Floors measured, never counted. For each file the roster was first pinned at an unreachable 9999 and the breach line read off the run — e.g. self-test battery "symbol-anchors self-test" registered 51 case(s), below its pinned floor of 9999. The floor was then set to that number.

27 ablation legs, three per file, mutation and restore both proven on disk. Every leg proves the mutation landed by counting the removed text and the injected text and by the blob hash moving; restores are git checkout HEAD -- ABSOLUTE_PATH in a finally, proven by blob-hash equality with the HEAD blob and an empty git diff HEAD (an empty hash reads as failure, never as "nothing to compare"). These are plain .mjs scripts run from source, so no dist leg applies.

leg what it mutates measured on all nine
A battery opener renamed exit 1, naming both ... self-test DID NOT RUN — 0 cases registered, N pinned and ... self-tst registered N case(s) but is not declared
B the introduced registerCase() removed exit 1, DID NOT RUN — 0 cases registered, N pinned, with every original case still passing (the four files that print per-case lines show 4 / 32 / 10 / 41 ok lines and exactly the 2 floor failures; the five fail-fast files reach the floor block at all only because every case passed)
C the roster entry deleted exit 1, SELF_TEST_BATTERIES declares 0 batteries, below the pinned 1 and registered N case(s) but is not declared — the size-1 pin is load-bearing in both directions

A fourth leg for check-test-completeness: with registerCase() ablated, --self-test exits 1 on DID NOT RUN, while the production invocation still exits 3 with output byte-identical to the base tree's. The floor cannot fire on the quiet call.

Both modes vs the installed base worktree at 5b09356b7, stdout and stderr compared separately.

  • --self-test, 9/9: eight byte-identical on both streams; release-rehearsal-clone identical except one elapsed-milliseconds line (90 ms vs 97 ms) that also differs base-against-base on a repeat run — control shown, and identical once that number is masked.
  • normal mode, 9/9: seven byte-identical on both streams; render-release-coverage-anchor differs only in its Swept ISO-TIMESTAMP line, which also differs base-against-base; release-rehearsal-clone differs only in the worktree path it echoes on line 1, an artefact of comparing two directories.
  • check-i18n-walk-parity's production path refuses as PREREQUISITE NOT MET in an unbuilt tree (identical on both sides). It was therefore re-measured properly: with @objectstack/spec and @objectstack/cli built, the base file and the branch file were run against the same built tree — stdout and stderr byte-identical, both exit 0, 11 declared group(s), 8 walked, 3 exempted.

The instrument, before and after (node scripts/measure-self-test-floor.mjs --json, run in each tree): base ROSTER 133 / NONE 34 / COUNT 2 becomes branch ROSTER 142 / NONE 25 / COUNT 2. NONE drops by exactly 9, nothing enters NONE, and the symmetric difference between the dropped set and this batch's worklist is empty.

extractWatchHints over all nine, computed in both trees from scripts/pm/dispatch-gates.mjs: identical, 9/9. Battery names carry spaces, so they cannot pass its admission test. check:watch-hint-literal exits 0.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path list passed — the script takes its own change set), re-derived after the last commit with an unchanged family. Exit codes captured by redirect before any pipe. 30 commands, 30 exit 0, plus the always-runs tail: check:pm-dispatch-gates in the foreground (1381 cases pass, exit 0), check:self-test-wired and its --self-test, node scripts/check-self-test-workflow-commands.mjs and its --self-test, check:declared-population-live, check:ratchet-remedy-authority, check:watch-hint-literal, check:nul-bytes — all exit 0.

Whole-repo pnpm lint (eslint . --no-inline-config) exits 0; no narrowing claimed. A control-byte scan over the nine edited files and over every body posted for this batch returns no hits.

The derivation prints a STALE-TREE notice: the branch is 4 commits behind origin/main, and the 9 files it names as changed are batch 7a/7b's scripts. The intersection of origin/main's movement with this diff is empty and git merge-tree against origin/main reports no conflict, so the branch is deliberately not merged forward.

Scope

Exactly nine files under scripts/, no other path. skip-changeset applied — nothing here is published from any package.


Generated by Claude Code

…ngle battery

`SELF-TEST PASSED (n cases)` was printed on `process.exitCode !== 1` alone, so
a self-test whose four cases never registered printed the same line as one
where every case held.

Recipe b5 (PR #15217): ONE battery opened at the top of the self-test body,
named `publish-smoke-pack self-test`, floor = the count measured on a run (4),
`SELF_TEST_BATTERIES` size pinned at 1, `registerCase()` called from the
existing `check(name, fn)` helper's block body, and a verdict that refuses a
below-floor / DID-NOT-RUN / undeclared battery through the same sink the cases
use (a `FAIL` line plus the failing exit code). No comment is promoted to a
section head; no assertion condition is touched; the verdict handshake
(`SELF_TEST_VERDICT`) is unchanged.

Floor measured, not counted: the roster was first pinned at 9999 and the breach
line named `registered 4 case(s)`.

Part of #13799

Co-authored-by: Claude <noreply@anthropic.com>
@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 the size/m label Sep 4, 2026
The b5 recipe (PR #15217) applied to the remaining eight census rows of
#13799 batch 8a. Each file gets ONE battery opened at the top of its
self-test body, named `BASENAME self-test`, with the floor read off a run
(the roster pinned at 9999 first, the breach line naming N), the roster's own
size pinned at 1, `registerCase()` called from the block body of the helper
the file already has, and a verdict refusing below-floor / DID NOT RUN /
undeclared batteries through the file's own failure sink.

Floors measured on a run: check-adr-symbol-anchors 17, symbol-anchors 51,
check-i18n-walk-parity 23, check-test-completeness 67, checklist-select 17,
release-rehearsal-clone 32, render-release-coverage-anchor 10,
run-with-stall-guard 41.

Two of them are the census's own warning coming true: symbol-anchors has 40
static `assert(` sites but registers 51 (loops), and
render-release-coverage-anchor has 8 static `expect(` sites but registers 10.
A floor counted from the source would have been wrong in both.

Where the helper is module-level (check-adr-symbol-anchors, symbol-anchors,
render-release-coverage-anchor) the case sites call a thin in-body wrapper
that registers and then defers to the existing assertion, exactly as PR
#15156 landed for that shape; no assertion condition is touched.

checklist-select also stops transcribing its case count: the success line's
hand-typed `17` is now read off a counter (#15305). It renders the same text
today, which is what makes the byte comparison across the change readable.

check-test-completeness scopes its floor to the loud run, because
`selfTest({ quiet: true })` also runs on every production invocation of that
gate, where nothing claims a self-test verdict.

Part of #13799
Fixes #15305

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants