From a9acaa0759067ce588cbe2f8d5f3d5d0d3a390ed Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 08:36:08 +0000 Subject: [PATCH 1/4] tooling(scripts): row batteries for check-skill-id-lint's self-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/pm/check-skill-id-lint.mjs | 117 ++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/scripts/pm/check-skill-id-lint.mjs b/scripts/pm/check-skill-id-lint.mjs index 0dc7a3f4efb..802663b35a2 100644 --- a/scripts/pm/check-skill-id-lint.mjs +++ b/scripts/pm/check-skill-id-lint.mjs @@ -169,6 +169,57 @@ function run() { // as one that passed (#13798). const SELF_TEST_VERDICT = 'check-skill-id-lint self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed === 0` used to be this self-test's ONLY success condition, so "every +// case held" and "the cases never ran" printed the same line. Closed the PR +// #13487 way: what is pinned is the registered NAMES, not a number. +// +// This self-test is TABLE-DRIVEN — one literal `cases` table, one loop over it, +// and a sink (`failed++`) that writes only when a case FAILS. Routing THAT sink +// through `registerCase()` would register a case only when it fails: a fully +// green run would register 0 and every battery would read DID NOT RUN, the +// floor inverted rather than installed. So the roster is the table's own rows. +// Each row LABEL is a declared battery, verbatim, with a floor of 1, and +// `registerCase(name)` is the first statement of the driving loop body — so the +// case is attributed to the row actually being run, whatever that row asserts +// afterwards. There is no `battery()` opener: for a table-driven self-test the +// ROW is the battery, so attribution is the loop variable rather than a +// most-recently-opened section. +// +// ⛔ A pinned TOTAL is not the repair, and neither is a roster DERIVED from the +// table: `cases.length` moves with the table, so a deleted row would delete its +// own floor. The roster below is a LITERAL the table is checked against, which +// is what lets a deleted or renamed row name ITSELF in the refusal. +// +// The counts are a FLOOR, not an equality — a row that grows into several +// registrations must not red. 1 is the honest floor for a table row: the loop +// reaches it exactly once per run. +const SELF_TEST_BATTERIES = Object.freeze({ + 'clean file -> green': 1, + 'one ID -> red': 1, + 'un-waived scanned files carry no waiver': 1, + 'legacy file at exact count -> green': 1, + 'legacy file at zero -> green': 1, + 'legacy file grew -> red': 1, + 'legacy file partially cleaned -> red': 1, + 'red message names the standard': 1, + 'placeholders stay legal': 1, + 'real IDs match': 1, + 'every separator-less scanned file declares a root-file watch hint': 1, + 'and the declaration names no file this gate does not scan': 1, + 'AGENTS.md is the root file it declares': 1, + 'the declared form is NOT an EXTRA_FILES entry': 1, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. This pin is also half of +// the duplicate-label refusal: two rows sharing a label collapse to ONE key in +// the literal above, so the roster falls below this number; the table +// cross-check in the floor block is the other half, and names WHICH label +// collided. +const SELF_TEST_BATTERY_FLOOR = 14; + function selfTest() { // The waiver mechanism is tested with a synthetic entry so it stays covered // while LEGACY_EXACT is empty (see header). @@ -196,15 +247,79 @@ function selfTest() { // not exist — red on a missing input, for a file that is fine. ['the declared form is NOT an EXTRA_FILES entry', EXTRA_FILES.some((f) => ROOT_FILE_WATCH_HINTS.includes(f)), false], ]; + // The ledger this self-test's floor is evaluated against (#13489). + const batterySeen = new Map(); + const registerCase = (name) => { + batterySeen.set(name, (batterySeen.get(name) ?? 0) + 1); + }; + let failed = 0; for (const [name, actual, expected] of cases) { + registerCase(name); const ok = actual === expected; if (!ok) failed++; console.log(` ${ok ? '✓' : '✗'} ${name}`); } LEGACY_EXACT.delete('synthetic-legacy.md'); + + // ── The floor: every declared row RAN, and ran its case (#13489) ─────── + // + // Evaluated after every row has had its chance and BEFORE the verdict, so the + // success line below can only be printed by a run in which the set of rows + // that registered EQUALS the set declared. A set difference names WHICH row + // stopped; a count says only that something did. + const floorFailure = (message) => { + console.error(`✗ self-test floor: ${message}`); + failed++; + }; + const declaredBatteries = Object.keys(SELF_TEST_BATTERIES); + let floorBreached = false; + if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) { + floorBreached = true; + floorFailure( + `SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` + + `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`, + ); + } + const rowLabels = cases.map(([name]) => name); + const duplicated = [...new Set(rowLabels.filter((name, i) => rowLabels.indexOf(name) !== i))]; + if (duplicated.length > 0) { + floorBreached = true; + floorFailure( + `the cases table uses ${duplicated.map((n) => JSON.stringify(n)).join(', ')} as a row label more than once — ` + + 'two rows sharing a label are ONE battery, so the second can stop running while the first keeps the floor met.', + ); + } + for (const [name, count] of batterySeen) { + if (declaredBatteries.includes(name)) continue; + floorBreached = true; + floorFailure( + `self-test battery "${name}" registered ${count} case(s) but is not declared in ` + + 'SELF_TEST_BATTERIES — a case attributed to no declared battery is one nothing floors.', + ); + } + for (const name of declaredBatteries) { + const count = batterySeen.get(name) ?? 0; + if (count >= SELF_TEST_BATTERIES[name]) continue; + floorBreached = true; + floorFailure( + count === 0 + ? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` + + 'The verdict below would have claimed that case holds.' + : `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` + + `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`, + ); + } + if (floorBreached) { + floorFailure( + 'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' + + 'number. Find what stopped registering (a deleted row, a renamed label, a loop that no longer ' + + 'reaches it) and restore it.', + ); + } + if (failed) { - console.error(`✗ check-skill-id-lint self-test: ${failed} of ${cases.length} case(s) failed.`); + console.error(`✗ check-skill-id-lint self-test: ${failed} failure(s) (cases and floor).`); process.exit(1); } console.log(`✓ check-skill-id-lint self-test: ${cases.length} cases pass.`); From 104b4afca21a2709ba548662fd5c3cfe00e432db Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 08:37:10 +0000 Subject: [PATCH 2/4] tooling(scripts): row batteries for check-agent-model-declared's self-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-agent-model-declared.mjs | 131 ++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/scripts/check-agent-model-declared.mjs b/scripts/check-agent-model-declared.mjs index 7d05a043652..84e8e1e05b7 100644 --- a/scripts/check-agent-model-declared.mjs +++ b/scripts/check-agent-model-declared.mjs @@ -334,6 +334,72 @@ function report(problems) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed === 0` used to be this self-test's ONLY success condition, so "every +// case held" and "the cases never ran" printed the same line. Closed the PR +// #13487 way: what is pinned is the registered NAMES, not a number. +// +// This self-test is TABLE-DRIVEN — one literal `cases` table, one loop over it, +// and a sink (`failed += 1`) that writes only when a case FAILS. Routing THAT +// sink through `registerCase()` would register a case only when it fails: a +// fully green run would register 0 and every battery would read DID NOT RUN, +// the floor inverted rather than installed. So the roster is the table's own +// rows. Each row LABEL is a declared battery, verbatim, with a floor of 1, and +// `registerCase(c.label)` is the FIRST statement of the driving loop body — +// before the `try` whose `catch` ends in `continue`, and before the two guards +// that also end in `continue`. The floor asserts REACH, so a registration +// placed after a guard would reintroduce the very inversion it exists to avoid: +// a row whose guard always skips would read DID NOT RUN, indistinguishable from +// a deleted row. There is no `battery()` opener: for a table-driven self-test +// the ROW is the battery, so attribution is the loop variable rather than a +// most-recently-opened section. +// +// ⛔ A pinned TOTAL is not the repair, and neither is a roster DERIVED from the +// table: `cases.length` moves with the table, so a deleted row would delete its +// own floor. The roster below is a LITERAL the table is checked against, which +// is what lets a deleted or renamed row name ITSELF in the refusal. +// +// The counts are a FLOOR, not an equality — a row that grows into several +// registrations must not red. 1 is the honest floor for a table row: the loop +// reaches it exactly once per run. +// +// SCOPE, stated so the next reader does not mistake the number: the four +// discovery-block assertions below the loop (`*.md` only, a missing +// `.claude/agents/`, a directory holding no `.md`, and real-tree discovery) are +// NOT rows of this table — they are written inline, one at a time, and the +// verdict line has always counted the 18 ROWS rather than the 22 assertions +// that run. This roster floors the 18 rows; those four assertions are left +// exactly as they are. +const SELF_TEST_BATTERIES = Object.freeze({ + 'a definition pinning a tier alias → GREEN': 1, + 'a definition pinning a fully-qualified model id → GREEN': 1, + 'a quoted value → GREEN (both YAML spellings are legal)': 1, + 'a value with a trailing comment → GREEN': 1, + 'R1 — frontmatter with name+description and NO model: → RED': 1, + 'R2 — `model:` present but empty → RED': 1, + 'R3 — no frontmatter at all → RED': 1, + 'R4 — unterminated frontmatter → RED': 1, + 'R5 — a typo of a tier alias → RED (declared-looking, undeclared in effect)': 1, + 'R5b — an alias with a version suffix is not an alias → RED': 1, + 'R6 — `model: inherit` with nothing justifying it → RED': 1, + 'R6b — `model: inherit` WITH a justification entry whose reason is in the file → GREEN': 1, + 'R7 — a justified inherit whose written reason was edited away → RED (self-invalidating)': 1, + 'R8 — a justification entry naming an unscanned file → RED (anti-dormancy)': 1, + 'R9 — a justification entry on a file that now pins a tier → RED (dead config)': 1, + 'R10 — an empty scan → RED, never a green skip (#4690, the whole point)': 1, + 'one bad definition among several → RED, naming the bad one only': 1, + 'an indented `model:` inside description prose does NOT satisfy the rule → RED': 1, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. This pin is also half of +// the duplicate-label refusal: two rows sharing a label collapse to ONE key in +// the literal above, so the roster falls below this number; the table +// cross-check in the floor block is the other half, and names WHICH label +// collided. +const SELF_TEST_BATTERY_FLOOR = 18; + function selfTest() { const fm = (modelLine) => `---\nname: demo\ndescription: >\n A demo agent.\n${modelLine ? `${modelLine}\n` : ''}---\n\nBody.\n`; @@ -466,8 +532,15 @@ function selfTest() { }, ]; + // The ledger this self-test's floor is evaluated against (#13489). + const batterySeen = new Map(); + const registerCase = (name) => { + batterySeen.set(name, (batterySeen.get(name) ?? 0) + 1); + }; + let failed = 0; for (const c of cases) { + registerCase(c.label); let problems; try { ({ problems } = runAllChecks(c.files, c.justified ?? [])); @@ -542,8 +615,64 @@ function selfTest() { console.log(` ✓ real-tree discovery: ${disc.files.length} agent definition(s), ${disc.problems.length} layout problem(s)`); } + // ── The floor: every declared row RAN, and ran its case (#13489) ─────── + // + // Evaluated after every row has had its chance and BEFORE the verdict, so the + // success line below can only be printed by a run in which the set of rows + // that registered EQUALS the set declared. A set difference names WHICH row + // stopped; a count says only that something did. + const floorFailure = (message) => { + console.error(`✗ self-test floor: ${message}`); + failed += 1; + }; + const declaredBatteries = Object.keys(SELF_TEST_BATTERIES); + let floorBreached = false; + if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) { + floorBreached = true; + floorFailure( + `SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` + + `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`, + ); + } + const rowLabels = cases.map((c) => c.label); + const duplicated = [...new Set(rowLabels.filter((name, i) => rowLabels.indexOf(name) !== i))]; + if (duplicated.length > 0) { + floorBreached = true; + floorFailure( + `the cases table uses ${duplicated.map((n) => JSON.stringify(n)).join(', ')} as a row label more than once — ` + + 'two rows sharing a label are ONE battery, so the second can stop running while the first keeps the floor met.', + ); + } + for (const [name, count] of batterySeen) { + if (declaredBatteries.includes(name)) continue; + floorBreached = true; + floorFailure( + `self-test battery "${name}" registered ${count} case(s) but is not declared in ` + + 'SELF_TEST_BATTERIES — a case attributed to no declared battery is one nothing floors.', + ); + } + for (const name of declaredBatteries) { + const count = batterySeen.get(name) ?? 0; + if (count >= SELF_TEST_BATTERIES[name]) continue; + floorBreached = true; + floorFailure( + count === 0 + ? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` + + 'The verdict below would have claimed that case holds.' + : `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` + + `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`, + ); + } + if (floorBreached) { + floorFailure( + 'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' + + 'number. Find what stopped registering (a deleted row, a renamed label, a loop that no longer ' + + 'reaches it) and restore it.', + ); + } + if (failed > 0) { - console.error(`\n✗ check-agent-model-declared self-test failed (${failed} case(s)).`); + console.error(`\n✗ check-agent-model-declared self-test: ${failed} failure(s) (cases and floor).`); process.exit(1); } console.log(`\n✓ check-agent-model-declared self-test: ${cases.length} cases pass.`); From 2d6b0869e0b74fed4d6af25523975c471101e590 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 08:38:00 +0000 Subject: [PATCH 3/4] tooling(scripts): row batteries for check-startup-registry-verdict's self-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-startup-registry-verdict.mjs | 127 ++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/scripts/check-startup-registry-verdict.mjs b/scripts/check-startup-registry-verdict.mjs index 90e7b6143e3..2422b4e2dbe 100644 --- a/scripts/check-startup-registry-verdict.mjs +++ b/scripts/check-startup-registry-verdict.mjs @@ -1059,6 +1059,68 @@ function run({ list = false, packagesDir } = {}) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failures === 0` used to be this self-test's ONLY success condition, so +// "every case held" and "the cases never ran" printed the same line. Closed the +// PR #13487 way: what is pinned is the registered NAMES, not a number. +// +// This self-test is TABLE-DRIVEN — one literal `cases` table, one loop over it, +// and a sink (`failures++`) that writes only when a case FAILS. Routing THAT +// sink through `registerCase()` would register a case only when it fails: a +// fully green run would register 0 and every battery would read DID NOT RUN, +// the floor inverted rather than installed. So the roster is the table's own +// rows. Each row `name` is a declared battery, verbatim, with a floor of 1, and +// `registerCase(c.name)` is the first statement of the driving loop body — so +// the case is attributed to the row actually being run, whatever that row +// asserts afterwards. There is no `battery()` opener: for a table-driven +// self-test the ROW is the battery, so attribution is the loop variable rather +// than a most-recently-opened section. +// +// ⛔ A pinned TOTAL is not the repair, and neither is a roster DERIVED from the +// table: `cases.length` moves with the table, so a deleted row would delete its +// own floor. The roster below is a LITERAL the table is checked against, which +// is what lets a deleted or renamed row name ITSELF in the refusal. +// +// The counts are a FLOOR, not an equality — a row that grows into several +// registrations must not red. 1 is the honest floor for a table row: the loop +// reaches it exactly once per run. +// +// SCOPE, stated so the next reader does not mistake the number: the dead-root +// reverse proof below the loop drives `expectRoot(...)` eight times over a real +// temporary tree. Those are NOT rows of this table — they are written inline, +// one at a time — and the verdict line has always counted the 18 analysis rows +// and named the dead-root proof separately. This roster floors the 18 rows; +// the eight `expectRoot` assertions are left exactly as they are. +const SELF_TEST_BATTERIES = Object.freeze({ + '#4777/A flags: init() probes a service and ANNOUNCES the absence': 1, + '#4777/A flags: init() probes a service and CACHES the absence on the instance': 1, + '#4777/A flags: init() probe whose absence is PERSISTED': 1, + '#4777/A passes: probe DEFERRED into a lazily-resolved closure': 1, + '#4777/A passes: probe deferred into a kernel:ready hook': 1, + '#4777/A passes: read-only probe — used, never recorded': 1, + '#4777/A passes: the same shape in start() — every init() has completed by then': 1, + '#4777/A passes: non-literal service name is skipped, never guessed': 1, + '#4777/A flags: the probe hidden behind a same-file helper init() calls': 1, + '#4777/A passes: the probe is licensed by optionalDependencies (the kernel orders the provider ahead)': 1, + '#4777/A passes: the probe is licensed by requiresServices': 1, + '#4777/A passes: a plugin probing the service it provides itself': 1, + '#4777/B flags: open node-type registry judged and announced before any seal': 1, + '#4777/B passes: the verdict is drawn at the declared seal': 1, + '#4777/B passes: read-only audit — returns state, never announces': 1, + '#4777/B passes: keyed lookup at registration warning about a duplicate': 1, + '#4777/B passes: keyed descriptor lookup on a runtime path': 1, + '#4777/B flags: the read hidden behind one same-file indirection': 1, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. This pin is also half of +// the duplicate-label refusal: two rows sharing a label collapse to ONE key in +// the literal above, so the roster falls below this number; the table +// cross-check in the floor block is the other half, and names WHICH label +// collided. +const SELF_TEST_BATTERY_FLOOR = 18; + function selfTest() { // Every fixture is analysed on its own, so the provider index is built from // the fixture itself — which means each one must declare the provider it is @@ -1355,8 +1417,15 @@ function selfTest() { }, ]; + // The ledger this self-test's floor is evaluated against (#13489). + const batterySeen = new Map(); + const registerCase = (name) => { + batterySeen.set(name, (batterySeen.get(name) ?? 0) + 1); + }; + let failures = 0; for (const c of cases) { + registerCase(c.name); const sf = parseSourceFile('t.ts', `${PROVIDERS}\n${c.code}`, ts.ScriptKind.TS); const findings = []; const seams = []; @@ -1437,8 +1506,64 @@ function selfTest() { rmSync(dir, { recursive: true, force: true }); } + // ── The floor: every declared row RAN, and ran its case (#13489) ─────── + // + // Evaluated after every row has had its chance and BEFORE the verdict, so the + // success line below can only be printed by a run in which the set of rows + // that registered EQUALS the set declared. A set difference names WHICH row + // stopped; a count says only that something did. + const floorFailure = (message) => { + console.error(`✗ self-test floor: ${message}`); + failures++; + }; + const declaredBatteries = Object.keys(SELF_TEST_BATTERIES); + let floorBreached = false; + if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) { + floorBreached = true; + floorFailure( + `SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` + + `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`, + ); + } + const rowLabels = cases.map((c) => c.name); + const duplicated = [...new Set(rowLabels.filter((name, i) => rowLabels.indexOf(name) !== i))]; + if (duplicated.length > 0) { + floorBreached = true; + floorFailure( + `the cases table uses ${duplicated.map((n) => JSON.stringify(n)).join(', ')} as a row label more than once — ` + + 'two rows sharing a label are ONE battery, so the second can stop running while the first keeps the floor met.', + ); + } + for (const [name, count] of batterySeen) { + if (declaredBatteries.includes(name)) continue; + floorBreached = true; + floorFailure( + `self-test battery "${name}" registered ${count} case(s) but is not declared in ` + + 'SELF_TEST_BATTERIES — a case attributed to no declared battery is one nothing floors.', + ); + } + for (const name of declaredBatteries) { + const count = batterySeen.get(name) ?? 0; + if (count >= SELF_TEST_BATTERIES[name]) continue; + floorBreached = true; + floorFailure( + count === 0 + ? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` + + 'The verdict below would have claimed that case holds.' + : `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` + + `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`, + ); + } + if (floorBreached) { + floorFailure( + 'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' + + 'number. Find what stopped registering (a deleted row, a renamed label, a loop that no longer ' + + 'reaches it) and restore it.', + ); + } + if (failures > 0) { - console.error(`\n✗ self-test: ${failures} case(s) failed\n`); + console.error(`\n✗ self-test: ${failures} failure(s) (cases and floor)\n`); return 1; } console.log(`\n✓ self-test: ${cases.length} analysis case(s) + the dead-root hard error (red when the scan root is renamed, green when restored) all passed\n`); From 5dfa456442cb3083b0516c2e557b53161e99a378 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 08:40:57 +0000 Subject: [PATCH 4/4] tooling(scripts): row batteries for js-comment-mask's self-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/js-comment-mask.mjs | 135 +++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/scripts/js-comment-mask.mjs b/scripts/js-comment-mask.mjs index f12dd5bdcf6..605436136a1 100644 --- a/scripts/js-comment-mask.mjs +++ b/scripts/js-comment-mask.mjs @@ -418,6 +418,76 @@ export function maskComments(source) { // as one that passed (#13798). const SELF_TEST_VERDICT = 'js-comment-mask self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed === 0` used to be this self-test's ONLY success condition, so "every +// case held" and "the cases never ran" printed the same line. Closed the PR +// #13487 way: what is pinned is the registered NAMES, not a number. +// +// This self-test is TABLE-DRIVEN — one literal `cases` table, one loop over it, +// and a sink (`failed++`) that writes only when a case FAILS. Routing THAT sink +// through `registerCase()` would register a case only when it fails: a fully +// green run would register 0 and every battery would read DID NOT RUN, the +// floor inverted rather than installed. So the roster is the table's own rows. +// Each row LABEL is a declared battery, verbatim, with a floor of 1, and +// `registerCase(name)` is the first statement of the driving loop body — so the +// case is attributed to the row actually being run, whatever that row asserts +// afterwards. There is no `battery()` opener: for a table-driven self-test the +// ROW is the battery, so attribution is the loop variable rather than a +// most-recently-opened section. +// +// ⛔ A pinned TOTAL is not the repair, and neither is a roster DERIVED from the +// table: `cases.length` moves with the table, so a deleted row would delete its +// own floor. The roster below is a LITERAL the table is checked against, which +// is what lets a deleted or renamed row name ITSELF in the refusal. +// +// The counts are a FLOOR, not an equality — a row that grows into several +// registrations must not red. 1 is the honest floor for a table row: the loop +// reaches it exactly once per run. +// +// SCOPE, stated so the next reader does not mistake the number: the +// `interpolation` section below the corpus loop 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 `x` +// call would silently delete its own floor, which is the one defect this shape +// exists to prevent. Those twelve assertions are therefore left exactly as they +// are, and the verdict line keeps counting them separately, as it always has. +// Flooring them needs the literal-roster treatment of its own and is not a +// table-row question. +const SELF_TEST_BATTERIES = Object.freeze({ + 'string containing a block-comment opener': 1, + 'URL inside a string': 1, + 'bare // inside a string': 1, + 'block-comment opener inside a template literal': 1, + 'regex character class holding a double quote': 1, + 'regex character class holding a BACKTICK first': 1, + 'markdown regex carrying a backtick': 1, + 'regex literal containing an escaped //': 1, + 'line comment immediately after a colon': 1, + 'regex literal after the `return` keyword': 1, + 'regex literal after the `case` keyword': 1, + 'shebang is a comment': 1, + 'division after a paren, then a quote-bearing regex': 1, + 'nested template inside an interpolation': 1, + 'escaped backtick inside a template, without nesting': 1, + 'template nested inside a nested template': 1, + 'template spanning lines, carrying a nested template': 1, + 'a backtick inside a regex inside an interpolation': 1, + 'an object literal, then a nested template, in one interpolation': 1, + 'a brace and a backtick quoted inside an interpolation': 1, + 'a real comment inside an interpolation': 1, + 'a genuine docblock is still removed': 1, + 'a genuine line comment is still removed': 1, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. This pin is also half of +// the duplicate-label refusal: two rows sharing a label collapse to ONE key in +// the literal above, so the roster falls below this number; the table +// cross-check in the floor block is the other half, and names WHICH label +// collided. +const SELF_TEST_BATTERY_FLOOR = 23; + export function selfTest() { const BT = String.fromCharCode(96); // backtick, kept out of the literal below const cases = [ @@ -493,8 +563,15 @@ export function selfTest() { // Both projections are driven, on every shape: they share one scanner, so a // shape either family gets wrong is a scanner bug, and a disagreement between // them about what IS a comment is the thing that must never ship. + // The ledger this self-test's floor is evaluated against (#13489). + const batterySeen = new Map(); + const registerCase = (name) => { + batterySeen.set(name, (batterySeen.get(name) ?? 0) + 1); + }; + let failed = 0; for (const [name, src] of cases) { + registerCase(name); const masked = maskComments(src); const stripped = stripComments(src); const problems = []; @@ -571,8 +648,64 @@ export function selfTest() { } const total = cases.length + extra.length; + // ── The floor: every declared row RAN, and ran its case (#13489) ─────── + // + // Evaluated after every row has had its chance and BEFORE the verdict, so the + // success line below can only be printed by a run in which the set of rows + // that registered EQUALS the set declared. A set difference names WHICH row + // stopped; a count says only that something did. + const floorFailure = (message) => { + console.error(`✗ self-test floor: ${message}`); + failed++; + }; + const declaredBatteries = Object.keys(SELF_TEST_BATTERIES); + let floorBreached = false; + if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) { + floorBreached = true; + floorFailure( + `SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` + + `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`, + ); + } + const rowLabels = cases.map(([name]) => name); + const duplicated = [...new Set(rowLabels.filter((name, i) => rowLabels.indexOf(name) !== i))]; + if (duplicated.length > 0) { + floorBreached = true; + floorFailure( + `the cases table uses ${duplicated.map((n) => JSON.stringify(n)).join(', ')} as a row label more than once — ` + + 'two rows sharing a label are ONE battery, so the second can stop running while the first keeps the floor met.', + ); + } + for (const [name, count] of batterySeen) { + if (declaredBatteries.includes(name)) continue; + floorBreached = true; + floorFailure( + `self-test battery "${name}" registered ${count} case(s) but is not declared in ` + + 'SELF_TEST_BATTERIES — a case attributed to no declared battery is one nothing floors.', + ); + } + for (const name of declaredBatteries) { + const count = batterySeen.get(name) ?? 0; + if (count >= SELF_TEST_BATTERIES[name]) continue; + floorBreached = true; + floorFailure( + count === 0 + ? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` + + 'The verdict below would have claimed that case holds.' + : `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` + + `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`, + ); + } + if (floorBreached) { + floorFailure( + 'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' + + 'number. Find what stopped registering (a deleted row, a renamed label, a loop that no longer ' + + 'reaches it) and restore it.', + ); + } + if (failed) { - console.error(`\u2717 js-comment-mask self-test: ${failed} of ${total} case(s) failed.`); + console.error(`\u2717 js-comment-mask self-test: ${failed} failure(s) (cases and floor).`); process.exit(1); } console.log(`\u2713 js-comment-mask self-test: ${total} cases pass (${cases.length} mask/strip corpus, ${extra.length} interpolation view).`);