Skip to content

tooling(scripts): row batteries for four table-driven self-tests (#13799 batch 7b) - #15286

Merged
baozhoutao merged 4 commits into
mainfrom
claude/issue-13799-batch7b-row-batteries
Sep 4, 2026
Merged

tooling(scripts): row batteries for four table-driven self-tests (#13799 batch 7b)#15286
baozhoutao merged 4 commits into
mainfrom
claude/issue-13799-batch7b-row-batteries

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #13799

Batch 7b of the assertion-floor card: four table-driven scripts/** self-tests, under recipe A exactly as the batch 7 pilot (PR #15271, ACCEPT 5537827823) settled it. Every file had failures === 0 as its ONLY success condition, so "every case held" and "the cases never ran" printed the same line. Four files floored, nothing deferred.

All evidence below was produced at the final commit 5dfa45644, against an installed base worktree at the dispatch pin origin/main = 4dd5041bd.

Rows to batteries to floors

file rows batteries declared floor / row roster size pin assertions NOT floored (stated in place)
scripts/pm/check-skill-id-lint.mjs 14 14 1 14 none — every assertion is a table row
scripts/check-agent-model-declared.mjs 18 18 1 18 the 4 discovery-block assertions below the loop (verdict has always printed 18 rows, not the 22 assertions that run)
scripts/check-startup-registry-verdict.mjs 18 18 1 18 the 8 expectRoot(...) assertions of the dead-root reverse proof
scripts/js-comment-mask.mjs 23 23 1 23 the 12 interpolation-view assertions pushed onto extra at runtime

Recipe A, per file: battery name = the row label verbatim, no prefix; registerCase(label) is the FIRST statement of the driving loop body; the roster is a LITERAL the table is checked against, never derived from it; floors, not equalities; the roster's own size pinned at the row count; the red-run line becomes N failure(s) (cases and floor) and the green line is byte-identical to main's.

check-agent-model-declared's driving loop carries three continues (one in a catch), so registration sits ahead of the try — per Q1 of the pilot ACCEPT, the floor asserts REACH, and a registration behind a guard reintroduces the very inversion the recipe exists to avoid.

Two "not a row" scope calls, both written into the file. They are not deferrals — the tables themselves are fully floored:

  • check-agent-model-declared / check-startup-registry-verdict: those assertions are written inline one at a time below the loop, and each file's verdict line has always counted the table's rows separately from them.
  • js-comment-mask: the interpolation section is not a second literal table. Its rows exist only because an x(...) call pushed them onto extra at runtime, so a roster taken from that loop would be DERIVED — a deleted call would silently delete its own floor, the one defect this shape exists to prevent. Flooring it needs a literal roster of its own and is not a table-row question.

Roster ⇄ table cross-check

Roster keys printed against the labels the loop actually registered, per file: 14 / 18 / 18 / 23, symmetric difference EMPTY in all four. Both halves of the duplicate refusal are in place: a duplicate ROSTER key collapses and trips the size pin, a duplicate TABLE label is named by the cross-check.

Ablations — 16 legs, 4 per file, all from the committed implementation

leg result (all four files)
one row deleted exit 1, names that row DID NOT RUN (roster keeps the label; 2 to 1 occurrences proved on disk)
one row renamed in the table only exit 1, names both: the undeclared registration AND the declared name that DID NOT RUN
registerCase() call removed exit 1, every row DID NOT RUN — 14 / 18 / 18 / 23 lines — with all original assertions still green (14 / 22 / 26 / 35 ticks)
size pin set unreachable (999) exit 1, breach line names the count: declares 14 / 18 / 18 / 23 batteries

The third leg is where the row count is measured on runs rather than read off the source, and it agrees with the roster size in every file.

No rebuild leg is owed: these are plain .mjs scripts executed directly from source, with no dist on the resolution path. Every mutation was proved on disk before the run (occurrence counts of the removed and injected text, plus byte deltas — never an editor's exit code), and every restore is proved by HEAD-blob hash equality plus an empty git diff HEAD; each leg ran under a trap ... EXIT INT TERM with absolute paths.

Both modes vs the base worktree

For all four files, --self-test and normal mode: stdout and stderr byte-identical to the base tree's, exit code identical (0 on both; js-comment-mask's no-argument usage path is 2 on both).

js-comment-mask is a library — the importer proof

Six importers, --self-test on both trees: check-comment-mask-corpus, check-comment-mask-adoption, check-error-code-casing, check-entry-guard, check-declaration-mirrors, check-watch-hint-literalall exit 0, stdout and stderr byte-identical base vs branch. The same six also run green in normal mode on the edited tree, which is the real consumption path.

Census and hints

node scripts/measure-self-test-floor.mjs --json: NONE 35 to 31, ROSTER 132 to 136, COUNT unchanged at 2. The movers are exactly the four files of this batch — symmetric difference against the worklist EMPTY, and no file newly reads NONE.

extractWatchHints output is identical on both trees for all four files, and check:watch-hint-literal is green.

Gates

The derived family for this diff — node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, 30 commands, derived at 5dfa45644 — plus the always-runs tail. All green, exit codes captured by redirect before any pipe.

Two of the thirty first came back NOT MEASURED on a fresh worktree rather than red — check:doc-formula-expressions (exit 3) and check:browser-reachable-entries — both refusing because the packages they read compiled output from were not built. Neither is a finding and neither can be reached by this diff; both were re-run green after building @objectstack/spec, @objectstack/formula and @objectstack/lint through the shared verify lock. Also green: check:pm-dispatch-gates, check-self-test-wired (both modes), check-self-test-workflow-commands (both modes), check:declared-population-live, check:ratchet-remedy-authority, check:watch-hint-literal, check:nul-bytes.

Control-byte scan clean over every edited file and every posted body.

Invariants

⛔ No assertion condition touched. ⛔ No control flow rewritten. ⛔ No comment promoted to a section head. The verdict handshake is untouched in each file, in each file's own spelling (sentinel return in check-skill-id-lint and js-comment-mask, the selfTestReachedVerdict flag in check-agent-model-declared, flag plus exit code in check-startup-registry-verdict). The only four deleted lines in the whole diff are the four red-run verdict lines; everything else is additive.

skip-changeset: no package publishes anything from this diff.


Generated by Claude Code


Generated by Claude Code

`failed === 0` was this self-test's only success condition, so "every case
held" and "the cases never ran" printed the same line (#13489).

Recipe A, as the batch 7 pilot (PR #15271) settled it for the table-driven
class: each of the 14 rows of the literal `cases` table is a declared battery
under its own label verbatim, floor 1, and `registerCase(name)` is the first
statement of the driving loop body. The roster is a LITERAL the table is
checked against — never derived from it — and its own size is pinned at 14,
which is also half of the duplicate-label refusal.

No assertion condition touched, no control flow rewritten, no comment promoted
to a section head, verdict handshake unchanged. The red-run line becomes
`N failure(s) (cases and floor)` because floor breaches report into the same
sink; the green line is byte-identical to main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@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
…-test

`failed === 0` was this self-test's only success condition, so "every case
held" and "the cases never ran" printed the same line (#13489).

Recipe A, as the batch 7 pilot (PR #15271) settled it: each of the 18 rows of
the literal `cases` table is a declared battery under its own `label` verbatim,
floor 1, roster size pinned at 18. `registerCase(c.label)` is the FIRST
statement of the driving loop body — ahead of the `try` whose `catch` ends in
`continue` and ahead of the two guards that do the same, because the floor
asserts REACH and a registration behind a guard reintroduces the inversion.

Scope stated in place: the four discovery-block assertions below the loop are
not rows of the table (the verdict has always printed 18 rows, not the 22
assertions that run), so they are left exactly as they are.

No assertion condition touched, no control flow rewritten, no comment promoted
to a section head, verdict handshake unchanged. Red-run line becomes
`N failure(s) (cases and floor)`; the green line is byte-identical to main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…self-test

`failures === 0` was this self-test's only success condition, so "every case
held" and "the cases never ran" printed the same line (#13489).

Recipe A, as the batch 7 pilot (PR #15271) settled it: each of the 18 rows of
the literal `cases` table is a declared battery under its own `name` verbatim,
floor 1, roster size pinned at 18, and `registerCase(c.name)` is the first
statement of the driving loop body.

Scope stated in place: the eight `expectRoot(...)` assertions of the dead-root
reverse proof below the loop are not rows of the table — the verdict line has
always counted the 18 analysis rows and named that proof separately — so they
are left exactly as they are.

No assertion condition touched, no control flow rewritten, no comment promoted
to a section head, verdict handshake unchanged. Red-run line becomes
`N failure(s) (cases and floor)`; the green line is byte-identical to main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
`failed === 0` was this self-test's only success condition, so "every case
held" and "the cases never ran" printed the same line (#13489).

Recipe A, as the batch 7 pilot (PR #15271) settled it: each of the 23 rows of
the literal mask/strip `cases` table is a declared battery under its own label
verbatim, floor 1, roster size pinned at 23, and `registerCase(name)` is the
first statement of the driving loop body.

Scope stated in place: the twelve `interpolation`-view assertions below the
corpus loop are NOT a second literal table — their rows exist only because an
`x(...)` call pushed them onto `extra` at runtime, so a roster taken from that
loop would be DERIVED and a deleted call would delete its own floor. They are
left exactly as they are and the verdict keeps counting them separately.

This module is a LIBRARY the gates import, so the change is proved from the
consumer side too: six importers' `--self-test` runs (comment-mask-corpus,
comment-mask-adoption, error-code-casing, entry-guard, declaration-mirrors,
watch-hint-literal) are byte-identical to the base tree's on both streams and
green in normal mode on the edited tree.

No assertion condition touched, no control flow rewritten, no comment promoted
to a section head, verdict handshake unchanged. Red-run line becomes
`N failure(s) (cases and floor)`; the green line is byte-identical to main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Out-of-scope finding filed while landing this batch, unassigned and finding-labelled, not queued: #15296 — recipe A floors the table rows only, so 24 inline assertions in three of these four files (4 discovery + 8 expectRoot + 12 interpolation) still register nothing on a green run, while the census now reads those files as ROSTER. None of those 24 assertions was touched here; each scope call is written into the file it belongs to.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l 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.

2 participants