Skip to content

test(scripts): batch 7a — floor five table-driven self-tests on their own row labels - #15288

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13799-batch7a-row-batteries
Sep 4, 2026
Merged

test(scripts): batch 7a — floor five table-driven self-tests on their own row labels#15288
baozhoutao merged 1 commit into
mainfrom
claude/issue-13799-batch7a-row-batteries

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #13799

Batch 7a — five table-driven scripts/** self-tests, under recipe A exactly as the batch 7 pilot settled it (PR #15271, ACCEPT 5537827823). Dispatched by the PM claim 5537845785; dev claim 5537877110. Base origin/main = 4dd5041bd.

Each of these five had a zero failure count as its ONLY success condition, so "every case held" and "the cases never ran" printed the same line.

The shape, copied from the pilot

  1. Battery name = the row label, verbatim, no prefix. Duplicates are refused at declaration time by two halves: a duplicate in the ROSTER literal collapses to one key and trips the size pin; a duplicate in the TABLE is caught by a cross-check that names the colliding label.
  2. registerCase() is the FIRST statement of the driving loop body — the row is the battery, attribution is the loop variable, no battery() opener. It stays first in all five even though four of the loop bodies carry continue: the floor asserts REACH, and placing it after a guard reintroduces the inversion recipe A exists to avoid.
  3. The roster is a LITERAL the table is checked against, never derived from it; floor 1 per row, floors not equalities; the roster's own size pinned at the row count.
  4. The red-run line reads N failure(s) (cases and floor); the green line is byte-identical to main's in all five.

Row labels are not label-first in four of the five: the label sits in the row object's own label key, so the battery name is derived from that field (registerCase(c.label), cross-check cases.map((c) => c.label)). check-skills-token-ratchet is label-first ([name, actual, expected]) and reads registerCase(name) / cases.map(([name]) => name).

⛔ No assertion condition touched. ⛔ No control flow rewritten — no continue to return, no thunk around a loop body, no comment promoted to a section head. The verdict handshake is kept exactly as landed, and its spelling differs per file: four carry the module-level selfTestReachedVerdict flag, check-skills-token-ratchet carries the SELF_TEST_VERDICT returned-sentinel variant. Sink spelling likewise: failed += 1 in four, failed++ in check-skills-token-ratchet.

Per file: rows, batteries, floors

file rows measured on a run batteries declared floor per row roster size pin loop body carries continue label position
scripts/check-refd-timer-probe.mjs 11 11 1 11 yes row object label key
scripts/check-skill-compatibility-version.mjs 18 18 1 18 yes row object label key
scripts/check-skill-frame-sync.mjs 13 13 1 13 yes row object label key
scripts/check-skills-token-ratchet.mjs 64 64 1 64 no first column
scripts/check-widget-option-census.mjs 16 16 1 16 yes row object label key

No duplicate label in any of the five (measured: the symmetric difference of roster keys against row labels is empty in all five, and no label repeats within a table).

Deferrals

None. The dispatch's deferral trigger (a file with TWO tables, or no per-row label at all) is discussed for check-widget-option-census in the note below; it was floored rather than deferred, and the reasoning is recorded for review.

check-widget-option-census — the two-table question, decided and recorded

That file carries its cases table (16 rows, floored here) and a second literal array checks with its own driving loop — the four "instrument checks against the REAL tree". Two measurements decided it rather than a judgement call:

  • The checks rows carry no stable literal label: their second element is a template string interpolating live tree contents (real tree: ${SCHEMA_NAME} in ${SPEC_FILE} reads as ${JSON.stringify(realDeclared.keys)}). A LITERAL roster key cannot match a label that moves with the tree, so recipe A is structurally inapplicable to that array — it is not a second cases table.
  • Leaving the 16-row cases table unfloored to avoid a shape question about four unfloorable rows would have cost the whole file its floor. The pilot's own shape already floors the table and nothing else: check-refd-timer-probe (3 discovery assertions), check-skill-compatibility-version (6 decl cases) and check-skill-frame-sync (5 decl + 3 population cases) all keep non-table assertions outside the roster in this PR, exactly as the pilot left check-sdui-manifest.

Recorded as an open question in the os-dev-report rather than decided silently.

Verification record

Base worktree installed at the dispatch pin 4dd5041bd for the byte comparisons. Exit codes captured by redirect before any pipe; verdicts quoted from each gate's own line.

what reading
surface 5 files, all scripts/**, +637/-5 (three-dot vs origin/main); skip-changeset (a scripts/** gate publishes from no package)
both modes, both trees all 20 streams byte-identical (cmp silent): normal mode and --self-test, stdout and stderr, for each of the five, base vs head, every run exit 0
roster vs table symmetric difference empty in all five; every floor is 1; every size pin equals the measured row count
extractWatchHints identical on both trees for all five (no roster key or row label is admissible: admission is /^[\w.@][\w.@/*-]*$/ over the raw literal and every label contains spaces — checked directly against all 122 labels, all rejected); check:watch-hint-literal green
census node scripts/measure-self-test-floor.mjs --json: NONE 35 to 31, COUNT 2 to 1, ROSTER 132 to 137. The five movers are exactly this PR's five files, symmetric difference against the worklist empty. NONE drops by 4 rather than 5 because check-refd-timer-probe was the COUNT-class file at base, as the dispatch predicted
check-refd-timer-probe's printed count left as is — it is ${cases.length}, DERIVED, not a stale literal, so the #15231 class does not apply to it

Ablations. The implementation was committed before any leg, so every restore had a real reference point. Every mutation is proven on disk by an anchored before/after count plus the blob hash moving off the HEAD blob; every restore by git checkout HEAD -- <absolute path> then hash equality against the HEAD blob and an empty git diff HEAD; every leg carries a trap restore on EXIT/INT/TERM with absolute paths, and an empty git hash-object result is read as FAILURE rather than "nothing to compare". Legs: row deleted, row renamed, registerCase() removed, and the roster size pin made unreachable — on all five files; plus a duplicate label in the TABLE and a duplicate key in the ROSTER on check-refd-timer-probe. Every leg exit 1 with the expected line. One leg (dup-table-label) was refused by its own landing check on a miscounted anchor and re-run — a non-measurement, reported as such, not as a pass.

The per-leg lines, and the full gate record, are in the os-dev-report comment on #13799.

Gates

All run in the FOREGROUND at the final commit 124d27115, exit codes captured by redirect BEFORE any pipe. Derived family from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (which read the change set from git itself: 5 paths vs merge base 4dd5041bd) — 26 commands, all exit 0. Always-runs tail beyond that family: check-self-test-workflow-commands.mjs and its --self-test, check:declared-population-live — all exit 0 (check:pm-dispatch-gates, check:self-test-wired + --self-test, check:ratchet-remedy-authority, check:watch-hint-literal, check:nul-bytes are already in the derived family and ran there). Each edited script also owes its own suite regardless of the family: normal mode AND --self-test against the tree, run for all five, all exit 0.

Beyond the derived family: repo-wide pnpm lint (eslint . --no-inline-config) exit 0 — the whole farm, not a narrowed run. A control-byte scan of all five files and of every body posted to GitHub is clean. The only test suite in the repo naming any of these five is packages/sdui-parser/src/__tests__/dashboard-widget-options.test.ts (prose references only, no import) — run anyway through the shared verify lock: 21 passed.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code


Generated by Claude Code

…bels (#13799 batch 7a)

Recipe A, as settled by the batch 7 pilot: each row's own label in the literal
`cases` table becomes a declared battery with a floor of 1, `registerCase()` is
the FIRST statement of the driving loop body, and the roster is a LITERAL the
table is checked against with its own size pinned at the row count.

Files and batteries: check-refd-timer-probe 11, check-skill-compatibility-version
18, check-skill-frame-sync 13, check-skills-token-ratchet 64,
check-widget-option-census 16.

No assertion condition touched, no control flow rewritten, no comment promoted to
a section head, the verdict handshake kept as landed per file. The red-run
failure line now reads `N failure(s) (cases and floor)`; the green line is
unchanged.

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/l label Sep 4, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 4, 2026 09:02
@baozhoutao
baozhoutao enabled auto-merge September 4, 2026 09:02
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 9a3dfac Sep 4, 2026
39 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13799-batch7a-row-batteries branch September 4, 2026 09:43
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