From 0d46da5c6c01fa832b17268d2d3f1b14bbc35508 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 07:12:55 +0000 Subject: [PATCH 1/4] tooling(scripts): assertion floor for check-declaration-mirrors' self-test `cases.filter((c) => !c.cond)` was this self-test's only success condition, so "every case held" and "the cases never ran" printed the same line (#13489). Class-1 sink repair, the PR #15156 shape: the concise-arrow `ok` sink gains a block body that calls `registerCase()` before the unchanged `cases.push`. No assertion condition is inverted or rewritten. Class-2 roster, the PR #15217 shape: ONE battery hoisted to the top of the self-test body, floor at the measured 23, `SELF_TEST_BATTERIES` size pinned at 1. No comment was promoted to a section head. Case count before == after, measured by pinning the roster to an unreachable value and reading the breach line: 23. `--self-test` stdout and stderr are byte-identical to the base tree's, exit 0 on both. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-declaration-mirrors.mjs | 103 +++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/scripts/check-declaration-mirrors.mjs b/scripts/check-declaration-mirrors.mjs index 226142a865..016e2efd3d 100644 --- a/scripts/check-declaration-mirrors.mjs +++ b/scripts/check-declaration-mirrors.mjs @@ -406,9 +406,63 @@ async function main() { // as one that passed (#13798). const SELF_TEST_VERDICT = 'check-declaration-mirrors self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `cases.filter((c) => !c.cond)` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-declaration-mirrors self-test': 23, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + async function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-declaration-mirrors self-test'); const cases = []; - const ok = (label, cond) => cases.push({ label, cond }); + // The concise arrow gains a BLOCK body so the case can be registered before + // it is recorded; `cases.push` is unchanged, so no assertion is rewritten. + const ok = (label, cond) => { + registerCase(); + cases.push({ label, cond }); + }; const dir = mkdtempSync(join(tmpdir(), 'os-decl-mirror-')); let seq = 0; @@ -549,6 +603,53 @@ async function selfTest() { mirrorFiles().every((f) => f.endsWith('.d.mts')), ); + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink IS the `cases` ledger, so the floor speaks its idiom: a + // breach is recorded as a failing case and reds through the existing verdict + // below. It bypasses `ok()` deliberately — a floor message is not a case. + const floorFailure = (message) => { cases.push({ label: message, cond: false }); }; + 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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', + ); + } + const failed = cases.filter((c) => !c.cond); for (const c of cases) console.log(`${c.cond ? 'ok ' : 'FAIL'} ${c.label}`); if (failed.length) { From 6cddfb79c7c7a817ed4e2faf74c4330f489e7ea0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 07:14:18 +0000 Subject: [PATCH 2/4] tooling(scripts): assertion floor for check-docs-single-h1' self-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `cases.filter((c) => !c.ok)` was this self-test's only success condition, so "every case held" and "the cases never ran" printed the same line (#13489). Class-1 sink repair, the PR #15156 shape: the concise-arrow `t` sink gains a block body that calls `registerCase()` before the unchanged `cases.push`. No assertion condition is inverted or rewritten. Class-2 roster, the PR #15217 shape: ONE battery hoisted to the top of the self-test body, floor at the measured 20, `SELF_TEST_BATTERIES` size pinned at 1. The file's single named section banner is NOT split on, and no comment was promoted to a section head. Case count before == after, measured by pinning the roster to an unreachable value and reading the breach line: 20 — which agrees with the count the existing verdict line prints. `--self-test` stdout and stderr are byte-identical to the base tree's, exit 0 on both. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-docs-single-h1.mjs | 104 ++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/scripts/check-docs-single-h1.mjs b/scripts/check-docs-single-h1.mjs index 776e08985e..6a3c207682 100644 --- a/scripts/check-docs-single-h1.mjs +++ b/scripts/check-docs-single-h1.mjs @@ -378,9 +378,64 @@ function main(argv) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `cases.filter((c) => !c.ok)` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. It +// carries exactly ONE named section banner (`The carve-out machinery, driven by +// a SYNTHETIC exclusion`), which is fewer than the two the sectioning criterion +// needs, and ⛔ a single banner is NOT split on — sectioning on it would leave +// every case above it in an unnamed remainder battery this transplant would +// have to invent a name for. The hoisted single battery is the shape PR #14896, +// PR #15003 and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-docs-single-h1 self-test': 20, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + export function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-docs-single-h1 self-test'); const cases = []; - const t = (name, ok, detail) => cases.push({ name, ok: Boolean(ok), detail }); + // The concise arrow gains a BLOCK body so the case can be registered before + // it is recorded; `cases.push` is unchanged, so no assertion is rewritten. + const t = (name, ok, detail) => { + registerCase(); + cases.push({ name, ok: Boolean(ok), detail }); + }; const dir = mkdtempSync(join(tmpdir(), 'docs-single-h1-')); try { @@ -493,6 +548,53 @@ export function selfTest() { rmSync(dir, { recursive: true, force: true }); } + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink IS the `cases` ledger, so the floor speaks its idiom: a + // breach is recorded as a failing case and reds through the existing verdict + // below. It bypasses `t()` deliberately — a floor message is not a case. + const floorFailure = (message) => { cases.push({ name: message, ok: false }); }; + 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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', + ); + } + const failed = cases.filter((c) => !c.ok); for (const c of failed) console.error(` ✗ ${c.name}${c.detail ? ` — ${c.detail}` : ''}`); if (failed.length) { From b790f7b972ac527f8f7384a498cfb756c327c256 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 07:16:17 +0000 Subject: [PATCH 3/4] tooling(scripts): assertion floors for three counter-sink self-tests `problems.length` / `failed` were these self-tests' only success condition, so "every case held" and "the cases never ran" printed the same line (#13489). Class-1 sink repair via the THUNK route PR #15198 measured, because each sink write is failure-only: routing failure-only writes through a helper would floor zero cases on a green run, which is no floor at all. `check(() => { ... })` registers the case and then runs the existing site VERBATIM, so registration happens whether or not the site fires and no assertion condition is inverted or rewritten. Per-iteration registration through a block-bodied helper is the landed shape (check-doc-frontmatter, check-test-source-alias both call their helper from inside a `for`). Class-2 roster, the PR #15217 shape: ONE battery hoisted to the top of each self-test body, floor at the measured count, roster size pinned at 1. No comment was promoted to a section head; a single banner was not split on. Floors, each measured by pinning the roster to an unreachable value and reading the breach line -- never transcribed: check-meta-type-normalized 4 (4 thunked sites) check-org-identifier 31 (agrees with its printed `cases.length + 1`) check-error-code-casing 51 (agrees with its printed 46 + 5) `--self-test` stdout and stderr byte-identical to the base tree's on all three, exit 0 both sides; normal mode also byte-identical where the gate has one. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-error-code-casing.mjs | 136 ++++++++++++++++++++++--- scripts/check-meta-type-normalized.mjs | 120 +++++++++++++++++++++- scripts/check-org-identifier.mjs | 124 ++++++++++++++++++++-- 3 files changed, 355 insertions(+), 25 deletions(-) diff --git a/scripts/check-error-code-casing.mjs b/scripts/check-error-code-casing.mjs index b903b61f60..041e712b9a 100644 --- a/scripts/check-error-code-casing.mjs +++ b/scripts/check-error-code-casing.mjs @@ -306,7 +306,64 @@ export function findViolations(src, file, stats = null) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-error-code-casing self-test': 51, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-error-code-casing self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const cases = [ // [source, expectedHitCount, label] [`return c.json({ error: { code: 'not_found', message: 'x' } }, 404);`, 1, 'emission'], @@ -385,10 +442,12 @@ function selfTest() { let failed = 0; for (const [src, want, label] of cases) { const got = findViolations(src, 'self-test.ts').length; - if (got !== want) { - console.error(` ✗ self-test "${label}": expected ${want} hit(s), got ${got}`); - failed++; - } +check(() => { + if (got !== want) { + console.error(` ✗ self-test "${label}": expected ${want} hit(s), got ${got}`); + failed++; + } +}); } // [#10658] The shrink-only registry, in both directions. The second one is // the load-bearing half: when the owning card's rename lands, a stale line @@ -419,22 +478,73 @@ function selfTest() { for (const [input, want, label] of partitionCases) { const got = partitionKnown(input, fixtureRegistry); const shape = { known: got.known.length, fresh: got.fresh.length, stale: got.stale.length }; - if (shape.known !== want.known || shape.fresh !== want.fresh || shape.stale !== want.stale) { - console.error(` ✗ self-test "${label}": expected ${JSON.stringify(want)}, got ${JSON.stringify(shape)}`); - failed++; - } +check(() => { + if (shape.known !== want.known || shape.fresh !== want.fresh || shape.stale !== want.stale) { + console.error(` ✗ self-test "${label}": expected ${JSON.stringify(want)}, got ${JSON.stringify(shape)}`); + failed++; + } +}); } // [#10716] The live registry, at zero. It is closed to new entries by the rule // above, so "closed" is checked rather than merely written down: a wire-visible // code that genuinely needs deferring is a call for the ADR-0112 owner to make // in the open, not a line someone adds back here on the way past. - if (KNOWN_LOWERCASE_CODES.size !== 0) { - console.error( - ` ✗ self-test "the live registry stays empty": KNOWN_LOWERCASE_CODES holds ${KNOWN_LOWERCASE_CODES.size} entry/entries — ` + - `this list is closed (#10658/#10716); a new deferral is an ADR-0112 decision, not a line here.`, +check(() => { + if (KNOWN_LOWERCASE_CODES.size !== 0) { + console.error( + ` ✗ self-test "the live registry stays empty": KNOWN_LOWERCASE_CODES holds ${KNOWN_LOWERCASE_CODES.size} entry/entries — ` + + `this list is closed (#10658/#10716); a new deferral is an ADR-0112 decision, not a line here.`, + ); + failed++; + } +}); + + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink is the `failed` counter, so the floor speaks its idiom: a + // breach prints like any other case failure and reds through the existing + // verdict below. + const floorFailure = (message) => { console.error(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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', ); - failed++; } if (failed) { diff --git a/scripts/check-meta-type-normalized.mjs b/scripts/check-meta-type-normalized.mjs index 08cd48173d..9fdee8a1ec 100644 --- a/scripts/check-meta-type-normalized.mjs +++ b/scripts/check-meta-type-normalized.mjs @@ -194,7 +194,65 @@ function findViolations(file) { // as one that passed (#13798). const SELF_TEST_VERDICT = 'check-meta-type-normalized self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `problems.length` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. It +// carries exactly ONE named section banner, which is fewer than the two the +// sectioning criterion needs, and ⛔ a single banner is NOT split on — nor is +// any comment promoted to a section head, which is a judgement per comment this +// transplant does not make. The hoisted single battery is the shape PR #14896, +// PR #15003 and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-meta-type-normalized self-test': 4, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-meta-type-normalized self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const fixture = ` // if (req.params.type === 'doc') {} -- quoted in a line comment /** JSDoc quoting req.params.type !== 'book' for the post-mortem. */ @@ -224,10 +282,64 @@ function selfTest() { const problems = []; // Three comparisons: a, b, c. NOT ok2 — its left side is the normalizer's // return value, not the raw param, which is the whole distinction. - if (comparisons !== 3) problems.push(`expected 3 comparisons, saw ${comparisons}`); - if (!hits.includes('switch')) problems.push('missed the switch discriminant'); - if (!hits.includes('membership')) problems.push('missed the membership test'); - if (hits.length !== 5) problems.push(`expected 5 findings total, saw ${hits.length} — a pass-through or a comment was flagged`); + check(() => { + if (comparisons !== 3) problems.push(`expected 3 comparisons, saw ${comparisons}`); + }); + check(() => { + if (!hits.includes('switch')) problems.push('missed the switch discriminant'); + }); + check(() => { + if (!hits.includes('membership')) problems.push('missed the membership test'); + }); + check(() => { + if (hits.length !== 5) problems.push(`expected 5 findings total, saw ${hits.length} — a pass-through or a comment was flagged`); + }); + + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink IS the `problems` ledger, so the floor speaks its idiom: a + // breach is recorded as a problem and reds through the existing verdict below. + const floorFailure = (message) => { problems.push(message); }; + 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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', + ); + } if (problems.length) { console.error('check:meta-type-normalized --self-test FAILED'); diff --git a/scripts/check-org-identifier.mjs b/scripts/check-org-identifier.mjs index 50237d50af..36df46c993 100644 --- a/scripts/check-org-identifier.mjs +++ b/scripts/check-org-identifier.mjs @@ -591,7 +591,64 @@ export function countSessionBindings(text, file) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-org-identifier self-test': 31, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-org-identifier self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const BT = String.fromCharCode(96); // backtick, kept out of the literals below const cases = [ // [source, expected offender count, label] @@ -670,10 +727,12 @@ function selfTest() { let failed = 0; for (const [src, want, label] of cases) { const got = findOffenders(src, 'self-test.ts').length; - if (got !== want) { - console.error(` ✗ self-test "${label}": expected ${want} offender(s), got ${got}`); - failed++; - } +check(() => { + if (got !== want) { + console.error(` ✗ self-test "${label}": expected ${want} offender(s), got ${got}`); + failed++; + } +}); } // The population invariant is itself a contract: a resolver that discovers @@ -682,11 +741,60 @@ function selfTest() { 'function h(ctx) {\n const sess = ctx.session ?? {};\n return sess.organizationId;\n}', 'self-test.ts', ); - if (populated.length !== 1) { - console.error( - ` ✗ self-test "the binding population is discovered at all": expected 1 binding, got ${populated.length}`, +check(() => { + if (populated.length !== 1) { + console.error( + ` ✗ self-test "the binding population is discovered at all": expected 1 binding, got ${populated.length}`, + ); + failed++; + } +}); + + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink is the `failed` counter, so the floor speaks its idiom: a + // breach prints like any other case failure and reds through the existing + // verdict below. + const floorFailure = (message) => { console.error(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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', ); - failed++; } if (failed) { From 3263b69559719cdfe76a1aea4c80faf25e8ad153 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 07:18:22 +0000 Subject: [PATCH 4/4] tooling(scripts): assertion floors for three more counter-sink self-tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same repair as the previous commit: the THUNK route PR #15198 measured, because every sink write in these three is failure-only, plus PR #15217's single hoisted battery with the roster size pinned at 1. Floors, each measured by pinning the roster to an unreachable value and reading the breach line -- never transcribed: check-console-intercept-disarm 11 (its printed 10 cases + the real-tree floor) check-examples-live-imports 36 (agrees with its printed `cases.length`) check-optional-error-sink-contract 66 (its printed 19 + 9x4 spellings + the reject side + 9 derivation + the `run` reference pin) check-examples-live-imports' one sink site is an if/ELSE, so it is wrapped by hand rather than by a brace matcher that would stop at the `if` block's own closing brace; the branches are reindented, not rewritten. ⛔ check-optional-error-sink-contract's two assertions over `baseline.entries` are deliberately left UNREGISTERED and say so in place: they run one-per-row of a shrink-only ledger, and a floor moving with that list would red every legitimate removal (#13797's ruling, carried forward by PR #15217's check-whole-set-label-write). `LOG_CHANNELS` is not that -- it is the contract's own vocabulary, not a list meant to shrink -- so its loops do register. `--self-test` stdout and stderr byte-identical to the base tree's on all three, exit 0 both sides; normal mode byte-identical where the gate has one. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --- scripts/check-console-intercept-disarm.mjs | 130 ++++++++++++-- scripts/check-examples-live-imports.mjs | 119 +++++++++++- .../check-optional-error-sink-contract.mjs | 169 +++++++++++++++--- 3 files changed, 377 insertions(+), 41 deletions(-) diff --git a/scripts/check-console-intercept-disarm.mjs b/scripts/check-console-intercept-disarm.mjs index 631b0f8a60..8ca99a84a8 100644 --- a/scripts/check-console-intercept-disarm.mjs +++ b/scripts/check-console-intercept-disarm.mjs @@ -367,7 +367,64 @@ export default defineConfig({ test: { disableConsoleIntercept: true } }); // as one that passed (#13798). const SELF_TEST_VERDICT = 'check-console-intercept-disarm self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failures` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-console-intercept-disarm self-test': 11, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-console-intercept-disarm self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const cases = [ { name: 'disarmed package passes', @@ -499,11 +556,13 @@ function selfTest() { ) { problems.push(`expected ${testCase.expectVitestPackages} vitest package(s), got ${vitestPackages}`); } - if (problems.length > 0) { - failures += 1; - console.error(`self-test FAIL: ${testCase.name}\n ${problems.join('\n ')}`); - for (const f of findings) console.error(` finding: ${f.split('\n')[0]}`); - } +check(() => { + if (problems.length > 0) { + failures += 1; + console.error(`self-test FAIL: ${testCase.name}\n ${problems.join('\n ')}`); + for (const f of findings) console.error(` finding: ${f.split('\n')[0]}`); + } +}); } finally { rmSync(caseDir, { recursive: true, force: true }); } @@ -511,12 +570,61 @@ function selfTest() { // Anti-vacuity over the REAL tree: the scan must see the real population. const real = scan(REPO_ROOT); - if (real.vitestPackages < 60) { - failures += 1; - console.error( - `self-test FAIL: real-tree scan sees only ${real.vitestPackages} vitest-running ` + - `package(s); the population this gate was written against had 72. The ` + - `workspace expansion has gone blind, which would pass every future arrival.`, +check(() => { + if (real.vitestPackages < 60) { + failures += 1; + console.error( + `self-test FAIL: real-tree scan sees only ${real.vitestPackages} vitest-running ` + + `package(s); the population this gate was written against had 72. The ` + + `workspace expansion has gone blind, which would pass every future arrival.`, + ); + } +}); + + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink is the `failures` counter, so the floor speaks its idiom: a + // breach prints like any other case failure and reds through the existing + // verdict below. + const floorFailure = (message) => { console.error(message); failures += 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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', ); } diff --git a/scripts/check-examples-live-imports.mjs b/scripts/check-examples-live-imports.mjs index 95ae0f25ee..41333736df 100644 --- a/scripts/check-examples-live-imports.mjs +++ b/scripts/check-examples-live-imports.mjs @@ -757,7 +757,64 @@ function verify() { // as one that passed (#13798). const SELF_TEST_VERDICT = 'check-examples-live-imports self-test reached its verdict'; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failed` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-examples-live-imports self-test': 36, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-examples-live-imports self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const apps = [ { dir: 'examples/app-showcase', name: '@objectstack/example-showcase' }, { dir: 'examples/app-crm', name: '@objectstack/example-crm' }, @@ -902,13 +959,63 @@ function selfTest() { let failed = 0; for (const [name, ok] of cases) { - if (!ok) { - failed++; - console.error(` FAIL ${name}`); - } else { - console.log(` ok ${name}`); - } + check(() => { + if (!ok) { + failed++; + console.error(` FAIL ${name}`); + } else { + console.log(` ok ${name}`); + } + }); + } + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink is the `failed` counter, so the floor speaks its idiom: a + // breach prints like any other case failure and reds through the existing + // verdict below. It is evaluated AFTER the verdict loop, because that loop is + // where the cases register. + const floorFailure = (message) => { console.error(` FAIL ${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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', + ); + } + if (failed) { console.error(`\n${failed}/${cases.length} self-test case(s) failed.`); process.exit(1); diff --git a/scripts/check-optional-error-sink-contract.mjs b/scripts/check-optional-error-sink-contract.mjs index 315d9e5a8b..1f7eac8713 100644 --- a/scripts/check-optional-error-sink-contract.mjs +++ b/scripts/check-optional-error-sink-contract.mjs @@ -802,7 +802,65 @@ function run({ list = false } = {}) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; +// ── The self-test's own battery roster and floor (#13489) ────────────────── +// +// `failures` 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 way +// PR #13487 validated on check-doc-authoring: what is pinned is the registered +// NAMES, not a number. The floor requires the OPENED set to equal the DECLARED +// set with each battery at or above its own count. +// +// This file declares ONE battery, opened at the top of the self-test body. Its +// blocks are headed by unmarked prose comments, so it carries fewer than the two +// named section banners the sectioning criterion needs, and ⛔ a comment is NOT +// promoted to a section head — that is a judgement per comment this transplant +// does not make. The hoisted single battery is the shape PR #14896, PR #15003 +// and PR #15217 landed for exactly this case. +// +// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3 +// keeps a total "right" the moment a sibling grows. +// +// The count is a FLOOR, not an equality — adding cases is ordinary work and must +// not red. A battery BELOW its floor means cases stopped running; the remedy is +// to find what stopped registering. +const SELF_TEST_BATTERIES = Object.freeze({ + 'check-optional-error-sink-contract self-test': 66, +}); + +// DELETING an entry silences that battery's floor exactly as effectively as +// zeroing it, so the roster's own size is pinned too. +const SELF_TEST_BATTERY_FLOOR = 1; + +// The key an assertion is filed under when no battery is open. It is not a +// declared battery, so it reds by the same set difference rather than silently +// inflating whichever battery happened to run last. +const UNATTRIBUTED_BATTERY = '(no battery open)'; + function selfTest() { + // The battery ledger this self-test's floor is evaluated against (#13489). + // `battery()` opens a battery; every assertion below is attributed to the one + // most recently opened, so a section that stops running stops registering and + // names ITSELF at the floor rather than going quiet. + const batterySeen = new Map(); + let openBattery = null; + const battery = (name) => { + openBattery = name; + }; + const registerCase = () => { + const b = openBattery ?? UNATTRIBUTED_BATTERY; + batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1); + }; + battery('check-optional-error-sink-contract self-test'); + // The thunk PR #15198 measured: it registers the case and then runs the + // existing site VERBATIM, so no assertion condition is inverted or rewritten + // and the sink keeps its own semantics. Registration happens whether or not + // the site fires, which is what makes the count a floor on cases RUN rather + // than a count of failures. + const check = (fn) => { + registerCase(); + fn(); + }; const cases = [ { name: 'no-fallback: an optional `error` alone — nothing to reach for', @@ -982,10 +1040,12 @@ function selfTest() { ) { problems.push('unreadable-with-optional-error count mismatch'); } - if (problems.length > 0) { - failures++; - console.error(` ✗ ${c.name}\n ${problems.join('\n ')}`); - } +check(() => { + if (problems.length > 0) { + failures++; + console.error(` ✗ ${c.name}\n ${problems.join('\n ')}`); + } +}); } // ⭐ The file PREFILTER, pinned separately because the cases above call @@ -1001,27 +1061,33 @@ function selfTest() { `interface L { ${channel}?(m: string): void }`, // optional method ]; for (const code of spellings) { - if (!CHANNEL_PREFILTER.test(code)) { - failures++; - console.error(` ✗ prefilter skips a file declaring \`${channel}\`: ${code}`); - } +check(() => { + if (!CHANNEL_PREFILTER.test(code)) { + failures++; + console.error(` ✗ prefilter skips a file declaring \`${channel}\`: ${code}`); + } +}); } } // The reject side of the prefilter, so "always true" is distinguishable from // "correctly derived". A module with no channel member is skipped, and that // is what keeps the scan cheap. - if (CHANNEL_PREFILTER.test('export const answer: number = 42;')) { - failures++; - console.error(' ✗ prefilter matches a file with no log-channel member — it is not filtering at all'); - } +check(() => { + if (CHANNEL_PREFILTER.test('export const answer: number = 42;')) { + failures++; + console.error(' ✗ prefilter matches a file with no log-channel member — it is not filtering at all'); + } +}); // The derivation itself: a channel added to the vocabulary must widen the // prefilter automatically. A hand-written second copy of the vocabulary is // how the previous narrowing outlived its own reason. for (const channel of LOG_CHANNELS) { - if (!CHANNEL_PREFILTER.source.includes(channel)) { - failures++; - console.error(` ✗ prefilter is not derived from LOG_CHANNELS — \`${channel}\` is missing from it`); - } +check(() => { + if (!CHANNEL_PREFILTER.source.includes(channel)) { + failures++; + console.error(` ✗ prefilter is not derived from LOG_CHANNELS — \`${channel}\` is missing from it`); + } +}); } // ⭐ And that `run` actually GOES THROUGH it. Found by ablation while // writing this: replacing the call site with a literal `/\berror…/` — the @@ -1046,18 +1112,26 @@ function selfTest() { .toString() .replace(/\/\*[\s\S]*?\*\//g, ' ') .replace(/(^|[^:])\/\/[^\n]*/g, '$1 '); - if (!runBody.includes('CHANNEL_PREFILTER.test(')) { - failures++; - console.error( - ' ✗ `run` does not reference CHANNEL_PREFILTER — the scan is filtering files through some other\n' + - ' test. Every count this gate prints is bounded by that decision; route it through the\n' + - ' derived prefilter, or the `noErrorMember` tally silently stops being a count (#11069).', - ); - } +check(() => { + if (!runBody.includes('CHANNEL_PREFILTER.test(')) { + failures++; + console.error( + ' ✗ `run` does not reference CHANNEL_PREFILTER — the scan is filtering files through some other\n' + + ' test. Every count this gate prints is bounded by that decision; route it through the\n' + + ' derived prefilter, or the `noErrorMember` tally silently stops being a count (#11069).', + ); + } +}); // The ledger's own shape is part of the contract: a hand-edited entry that // cannot key against a finding would silently excuse nothing (or, worse, // everything). + // ⛔ The two assertions in the loop below are deliberately NOT registered. + // They run one-per-row of a SHRINK-ONLY ledger, and a floor that moves with + // that list would red every legitimate removal — training the next author to + // edit the floor, which is the one habit these floors exist to prevent + // (#13797's ruling, carried forward by PR #15217). The structural cases above + // are what this battery floors. const baseline = loadBaseline(); for (const e of baseline.entries ?? []) { for (const field of ['file', 'sink', 'verdict', 'note']) { @@ -1072,6 +1146,53 @@ function selfTest() { } } + // ── The floor: every declared battery RAN, and ran its cases (#13489) ──── + // + // Evaluated after every battery 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 + // batteries that registered assertions EQUALS the set declared. A set + // difference names WHICH battery stopped; a count says only that something did. + // This file's sink is the `failures` counter, so the floor speaks its idiom: a + // breach prints like any other case failure and reds through the existing + // verdict below. + const floorFailure = (message) => { console.error(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.`, + ); + } + 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 — an assertion 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 those cases hold.' + : `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 (an early return, a deleted block, a guard that now ' + + 'skips) and restore it.', + ); + } + if (failures > 0) { console.error(`✗ optional-error-sink-contract self-test: ${failures} case(s) failed`); return 1;