Skip to content

Commit 9ecea9c

Browse files
committed
test(scripts): floor eight more self-tests with a hoisted single battery
The b5 recipe (PR #15217) applied to the remaining eight census rows of #13799 batch 8a. Each file gets ONE battery opened at the top of its self-test body, named `BASENAME self-test`, with the floor read off a run (the roster pinned at 9999 first, the breach line naming N), the roster's own size pinned at 1, `registerCase()` called from the block body of the helper the file already has, and a verdict refusing below-floor / DID NOT RUN / undeclared batteries through the file's own failure sink. Floors measured on a run: check-adr-symbol-anchors 17, symbol-anchors 51, check-i18n-walk-parity 23, check-test-completeness 67, checklist-select 17, release-rehearsal-clone 32, render-release-coverage-anchor 10, run-with-stall-guard 41. Two of them are the census's own warning coming true: symbol-anchors has 40 static `assert(` sites but registers 51 (loops), and render-release-coverage-anchor has 8 static `expect(` sites but registers 10. A floor counted from the source would have been wrong in both. Where the helper is module-level (check-adr-symbol-anchors, symbol-anchors, render-release-coverage-anchor) the case sites call a thin in-body wrapper that registers and then defers to the existing assertion, exactly as PR #15156 landed for that shape; no assertion condition is touched. checklist-select also stops transcribing its case count: the success line's hand-typed `17` is now read off a counter (#15305). It renders the same text today, which is what makes the byte comparison across the change readable. check-test-completeness scopes its floor to the loud run, because `selfTest({ quiet: true })` also runs on every production invocation of that gate, where nothing claims a self-test verdict. Part of #13799 Fixes #15305 Co-authored-by: Claude <noreply@anthropic.com>
1 parent d24610a commit 9ecea9c

8 files changed

Lines changed: 894 additions & 71 deletions

scripts/check-adr-symbol-anchors.mjs

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,69 @@ function list(root = process.cwd()) {
153153

154154
function assert(cond, msg) { if (!cond) { console.error(`❌ check-adr-symbol-anchors --self-test: ${msg}`); process.exit(1); } }
155155

156+
// ── The self-test's own battery roster and floor (#13489) ──────────────────
157+
//
158+
// A module-level `assert()` that exits on the first failure used to be this
159+
// self-test's ONLY success condition, so "every case held" and "the cases
160+
// never ran" printed the same line. Closed the way PR #13487 validated on
161+
// check-doc-authoring: what is pinned is the registered NAMES, not a
162+
// number. The floor requires the OPENED set to equal the DECLARED set with
163+
// each battery at or above its own count.
164+
//
165+
// This file declares ONE battery, opened at the top of the self-test body. It
166+
// carries fewer than the two named section banners the sectioning criterion
167+
// needs, and ⛔ a comment is NOT promoted to a section head — that is a
168+
// judgement per comment this transplant does not make. The hoisted single
169+
// battery is the shape PR #14896, PR #15003 and PR #15217 landed for exactly
170+
// this case.
171+
//
172+
// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3
173+
// keeps a total "right" the moment a sibling grows.
174+
//
175+
// The count is a FLOOR, not an equality — adding cases is ordinary work and must
176+
// not red. A battery BELOW its floor means cases stopped running; the remedy is
177+
// to find what stopped registering.
178+
const SELF_TEST_BATTERIES = Object.freeze({
179+
'check-adr-symbol-anchors self-test': 17,
180+
});
181+
182+
// DELETING an entry silences that battery's floor exactly as effectively as
183+
// zeroing it, so the roster's own size is pinned too.
184+
const SELF_TEST_BATTERY_FLOOR = 1;
185+
186+
// The key an assertion is filed under when no battery is open. It is not a
187+
// declared battery, so it reds by the same set difference rather than silently
188+
// inflating whichever battery happened to run last.
189+
const UNATTRIBUTED_BATTERY = '(no battery open)';
190+
156191
// Returned by `selfTest()` only after its verdict is printed. The dispatch
157192
// refuses anything else: a `return` that leaves the function above that line
158193
// prints nothing and still exits 0 — a self-test that never finished, reported
159194
// as one that passed (#13798).
160195
const SELF_TEST_VERDICT = 'check-adr-symbol-anchors self-test reached its verdict';
161196

162197
export function selfTest() {
198+
// The battery ledger this self-test's floor is evaluated against (#13489).
199+
// `battery()` opens a battery; every assertion below is attributed to the one
200+
// most recently opened, so a section that stops running stops registering and
201+
// names ITSELF at the floor rather than going quiet.
202+
const batterySeen = new Map();
203+
let openBattery = null;
204+
const battery = (name) => {
205+
openBattery = name;
206+
};
207+
const registerCase = () => {
208+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
209+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
210+
};
211+
battery('check-adr-symbol-anchors self-test');
212+
// A thin in-body wrapper over the module-level `assert`: it attributes the
213+
// case to the open battery and then defers to the existing assertion, whose
214+
// semantics (print and exit 1 on the first failure) are unchanged.
215+
const check = (cond, message) => {
216+
registerCase();
217+
assert(cond, message);
218+
};
163219
// 1. ⭐ The instrument this gate cannot hold about itself on a clean tree: a
164220
// synthetic corpus carrying one of EVERY finding class, plus the healthy
165221
// forms, so "no findings" is told apart from "the rule stopped matching".
@@ -191,18 +247,18 @@ export function selfTest() {
191247
const kinds = findings.map((f) => f.kind);
192248
const count = (k) => kinds.filter((x) => x === k).length;
193249

194-
assert(count('line-anchor') === 3, `3 line anchors (plain, hyphen range, EN DASH range) must be found, got ${count('line-anchor')}`);
250+
check(count('line-anchor') === 3, `3 line anchors (plain, hyphen range, EN DASH range) must be found, got ${count('line-anchor')}`);
195251
// `noSuchSymbol` (absent) and `commentOnlySymbol` (named only in a
196252
// comment — the census's permissiveness, refused here). The vanished FILE
197253
// is a different class and is asserted separately below.
198-
assert(count('unresolved-symbol') === 2, `2 unresolved symbols must be found, got ${count('unresolved-symbol')}`);
199-
assert(count('bad-exemption') === 1, `an invalid exemption class must be a finding, got ${count('bad-exemption')}`);
200-
assert(count('unresolved-path') === 1, `a vanished target must be a finding, got ${count('unresolved-path')}`);
201-
assert(counts.exempt === 1, 'a valid exemption must be honoured exactly once');
254+
check(count('unresolved-symbol') === 2, `2 unresolved symbols must be found, got ${count('unresolved-symbol')}`);
255+
check(count('bad-exemption') === 1, `an invalid exemption class must be a finding, got ${count('bad-exemption')}`);
256+
check(count('unresolved-path') === 1, `a vanished target must be a finding, got ${count('unresolved-path')}`);
257+
check(counts.exempt === 1, 'a valid exemption must be honoured exactly once');
202258
// ...and the healthy record must contribute NOTHING. A rule that fires on
203259
// good anchors is as broken as one that misses bad ones.
204-
assert(!findings.some((f) => f.doc.includes('0001-good')), 'the healthy record must produce no findings');
205-
assert(counts.declaration >= 1 && counts.literal >= 1, 'both resolution classes must be exercised by the fixture');
260+
check(!findings.some((f) => f.doc.includes('0001-good')), 'the healthy record must produce no findings');
261+
check(counts.declaration >= 1 && counts.literal >= 1, 'both resolution classes must be exercised by the fixture');
206262
} finally {
207263
rmSync(tmp, { recursive: true, force: true });
208264
}
@@ -211,10 +267,10 @@ export function selfTest() {
211267
// (dispatch-gates / check-declared-population-live), so a wrong entry runs
212268
// perfectly green here and shows up only as a dev who was never told this
213269
// gate reads their surface.
214-
assert(ROOT_DIR_WATCH_HINTS.every((h) => h.startsWith(ADR_DIR)), 'every watch hint must be under the declared ADR dir');
215-
assert(existsSync(ADR_DIR), `the declared population must reach the tree: ${ADR_DIR}`);
216-
assert(CORPUS.docRoots.includes(ADR_DIR), 'the corpus must sweep the population this gate declares');
217-
assert(
270+
check(ROOT_DIR_WATCH_HINTS.every((h) => h.startsWith(ADR_DIR)), 'every watch hint must be under the declared ADR dir');
271+
check(existsSync(ADR_DIR), `the declared population must reach the tree: ${ADR_DIR}`);
272+
check(CORPUS.docRoots.includes(ADR_DIR), 'the corpus must sweep the population this gate declares');
273+
check(
218274
ROOT_DIR_WATCH_HINTS.every((h) => CORPUS.docRoots.includes(h.replace(/\/\*+$/, ''))),
219275
`the declared hints must name the roots the corpus sweeps: ${ROOT_DIR_WATCH_HINTS.join(', ')} vs ${CORPUS.docRoots.join(', ')}`,
220276
);
@@ -223,19 +279,68 @@ export function selfTest() {
223279
// clean tree from an extractor that silently matches nothing — the exact
224280
// failure mode that let 243 rotted anchors sit unnoticed.
225281
const live = sweepCorpus(CORPUS);
226-
assert(live.counts.anchors > 300, `the live ADR corpus must yield its anchors, got ${live.counts.anchors}`);
227-
assert(live.counts.symbol > 0, 'the live corpus must contain resolved SYMBOL anchors');
282+
check(live.counts.anchors > 300, `the live ADR corpus must yield its anchors, got ${live.counts.anchors}`);
283+
check(live.counts.symbol > 0, 'the live corpus must contain resolved SYMBOL anchors');
228284

229285
// 4. The gate is wired to run. A gate nothing invokes is this repo's most
230286
// carded defect class, and renaming a step silently detaches it.
231287
const workflow = readFileSync('.github/workflows/lint.yml', 'utf8');
232-
assert(workflow.includes('node scripts/check-adr-symbol-anchors.mjs'), 'lint.yml must invoke this gate');
233-
assert(workflow.includes('node scripts/check-adr-symbol-anchors.mjs --self-test'), 'lint.yml must invoke this gate\'s --self-test');
288+
check(workflow.includes('node scripts/check-adr-symbol-anchors.mjs'), 'lint.yml must invoke this gate');
289+
check(workflow.includes('node scripts/check-adr-symbol-anchors.mjs --self-test'), 'lint.yml must invoke this gate\'s --self-test');
234290

235291
// 5. The census declaration is intact, INCLUDING the one-way error direction
236292
// the ruling ordered recorded (point 5).
237-
assert(CENSUS_13556.rotRateIsLowerBound === true, 'the 72.1% figure is a LOWER bound and must be declared as one');
238-
assert(CENSUS_13556.totalSurface === CENSUS_13556.distinctLineAnchors + CENSUS_13556.continuationAnchors, 'the declared surface must be the sum of its parts');
293+
check(CENSUS_13556.rotRateIsLowerBound === true, 'the 72.1% figure is a LOWER bound and must be declared as one');
294+
check(CENSUS_13556.totalSurface === CENSUS_13556.distinctLineAnchors + CENSUS_13556.continuationAnchors, 'the declared surface must be the sum of its parts');
295+
296+
// ── The floor: every declared battery RAN, and ran its cases (#13489) ────
297+
//
298+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
299+
// the success line below can only be printed by a run in which the set of
300+
// batteries that registered assertions EQUALS the set declared. A set
301+
// difference names WHICH battery stopped; a count says only that something did.
302+
// The floor's refusal joins the SAME sink the cases use — the module-level
303+
// `assert`, which prints and exits 1 — so a breached floor cannot be printed
304+
// over by the verdict below.
305+
const floorMessages = [];
306+
const floorFailure = (message) => { floorMessages.push(message); };
307+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
308+
let floorBreached = false;
309+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
310+
floorBreached = true;
311+
floorFailure(
312+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned `
313+
+ `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`,
314+
);
315+
}
316+
for (const [name, count] of batterySeen) {
317+
if (declaredBatteries.includes(name)) continue;
318+
floorBreached = true;
319+
floorFailure(
320+
`self-test battery "${name}" registered ${count} case(s) but is not declared in `
321+
+ 'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.',
322+
);
323+
}
324+
for (const name of declaredBatteries) {
325+
const count = batterySeen.get(name) ?? 0;
326+
if (count >= SELF_TEST_BATTERIES[name]) continue;
327+
floorBreached = true;
328+
floorFailure(
329+
count === 0
330+
? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. `
331+
+ 'The verdict below would have claimed those cases hold.'
332+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of `
333+
+ `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`,
334+
);
335+
}
336+
if (floorBreached) {
337+
floorFailure(
338+
'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the '
339+
+ 'number. Find what stopped registering (an early return, a deleted block, a guard that now '
340+
+ 'skips) and restore it.',
341+
);
342+
}
343+
assert(!floorBreached, floorMessages.join('\n '));
239344

240345
console.log(`✅ check-adr-symbol-anchors --self-test: every finding class provoked, healthy anchors silent, population live, wiring pinned (${live.counts.anchors} live anchors)`);
241346

scripts/check-i18n-walk-parity.mjs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,15 +521,65 @@ const RECORDED_WALKED = [
521521
*/
522522
const RECORDED_UNWALKED = ['messages', 'settings', 'settingsCommon'];
523523

524+
// ── The self-test's own battery roster and floor (#13489) ──────────────────
525+
//
526+
// `failures.length === 0` used to be this self-test's ONLY success
527+
// condition, so "every case held" and "the cases never ran" printed the
528+
// same line. Closed the way PR #13487 validated on check-doc-authoring:
529+
// what is pinned is the registered NAMES, not a number. The floor requires
530+
// the OPENED set to equal the DECLARED set with each battery at or above
531+
// its own count.
532+
//
533+
// This file declares ONE battery, opened at the top of the self-test body. It
534+
// carries fewer than the two named section banners the sectioning criterion
535+
// needs, and ⛔ a comment is NOT promoted to a section head — that is a
536+
// judgement per comment this transplant does not make. The hoisted single
537+
// battery is the shape PR #14896, PR #15003 and PR #15217 landed for exactly
538+
// this case.
539+
//
540+
// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3
541+
// keeps a total "right" the moment a sibling grows.
542+
//
543+
// The count is a FLOOR, not an equality — adding cases is ordinary work and must
544+
// not red. A battery BELOW its floor means cases stopped running; the remedy is
545+
// to find what stopped registering.
546+
const SELF_TEST_BATTERIES = Object.freeze({
547+
'check-i18n-walk-parity self-test': 23,
548+
});
549+
550+
// DELETING an entry silences that battery's floor exactly as effectively as
551+
// zeroing it, so the roster's own size is pinned too.
552+
const SELF_TEST_BATTERY_FLOOR = 1;
553+
554+
// The key an assertion is filed under when no battery is open. It is not a
555+
// declared battery, so it reds by the same set difference rather than silently
556+
// inflating whichever battery happened to run last.
557+
const UNATTRIBUTED_BATTERY = '(no battery open)';
558+
524559
let selfTestReachedVerdict = false;
525560

526561
function selfTest() {
562+
// The battery ledger this self-test's floor is evaluated against (#13489).
563+
// `battery()` opens a battery; every assertion below is attributed to the one
564+
// most recently opened, so a section that stops running stops registering and
565+
// names ITSELF at the floor rather than going quiet.
566+
const batterySeen = new Map();
567+
let openBattery = null;
568+
const battery = (name) => {
569+
openBattery = name;
570+
};
571+
const registerCase = () => {
572+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
573+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
574+
};
575+
battery('check-i18n-walk-parity self-test');
527576
const failures = [];
528577
let cases = 0;
529578
// Counted, never transcribed: a hand-typed case count in the success line is a
530579
// number that goes stale the first time a case is added, and a self-test whose
531580
// own report is wrong is the last place to keep one.
532581
const eq = (what, got, want) => {
582+
registerCase();
533583
cases += 1;
534584
const a = JSON.stringify(got);
535585
const b = JSON.stringify(want);
@@ -595,8 +645,55 @@ function selfTest() {
595645
ledgerRatchetProblems(KNOWN_NO_EXTRACTOR_FACE, LEDGER_CEILING).length, 0);
596646
eq('recorded sample: hints are live', DECLARED_WATCH_HINTS.length > 0, true);
597647

648+
// ── The floor: every declared battery RAN, and ran its cases (#13489) ────
649+
//
650+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
651+
// the success line below can only be printed by a run in which the set of
652+
// batteries that registered assertions EQUALS the set declared. A set
653+
// difference names WHICH battery stopped; a count says only that something did.
654+
// The floor's refusal joins the SAME sink the cases use — `failures`, read by
655+
// the verdict below — so a breached floor cannot be printed over by the
656+
// success line.
657+
const floorFailure = (message) => { failures.push(message); };
658+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
659+
let floorBreached = false;
660+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
661+
floorBreached = true;
662+
floorFailure(
663+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned `
664+
+ `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`,
665+
);
666+
}
667+
for (const [name, count] of batterySeen) {
668+
if (declaredBatteries.includes(name)) continue;
669+
floorBreached = true;
670+
floorFailure(
671+
`self-test battery "${name}" registered ${count} case(s) but is not declared in `
672+
+ 'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.',
673+
);
674+
}
675+
for (const name of declaredBatteries) {
676+
const count = batterySeen.get(name) ?? 0;
677+
if (count >= SELF_TEST_BATTERIES[name]) continue;
678+
floorBreached = true;
679+
floorFailure(
680+
count === 0
681+
? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. `
682+
+ 'The verdict below would have claimed those cases hold.'
683+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of `
684+
+ `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`,
685+
);
686+
}
687+
if (floorBreached) {
688+
floorFailure(
689+
'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the '
690+
+ 'number. Find what stopped registering (an early return, a deleted block, a guard that now '
691+
+ 'skips) and restore it.',
692+
);
693+
}
694+
598695
if (failures.length) {
599-
console.error(`✗ check-i18n-walk-parity self-test: ${failures.length} case(s) failed:\n`);
696+
console.error(`✗ check-i18n-walk-parity self-test: ${failures.length} failure(s) (cases and floor):\n`);
600697
for (const f of failures) console.error(` ${f}`);
601698
console.error('');
602699
selfTestReachedVerdict = true;

0 commit comments

Comments
 (0)