Skip to content

Commit 8a96e66

Browse files
claude[bot]claude
andauthored
test(scripts): give 13 more self-tests a battery roster and floor (#15003)
* test(scripts): battery roster and floor for four more self-tests Part of #13799 (batch 2, Tier B — the batch-1 shape). PR #13487's roster-and-floor shape as batches 1/1b/1c landed it, transplanted onto four `scripts/**` self-tests that already carry that shape. ⛔ No case is rewritten, none reordered, no assertion changes meaning. ⛔ No TOTAL is pinned — the roster pins registered NAMES. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): battery roster and floor for five more self-tests Part of #13799 (batch 2, Tier B — the batch-1 shape). ⛔ No case is rewritten, none reordered, no assertion changes meaning. ⛔ No TOTAL is pinned — the roster pins registered NAMES. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): battery roster and floor for four more PM self-tests Part of #13799 (batch 2, Tier B — the batch-1 shape). ⛔ No case is rewritten, none reordered, no assertion changes meaning. ⛔ No TOTAL is pinned — the roster pins registered NAMES. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): re-read the multi-repo battery floor after the hotcrm merge `origin/main`'s #14867 change (`hotcrm` added to `GOVERNED_REPOS`) grows the `multi-repo scope (#9619)` battery from 15 cases to 17: one assertion becomes two, and the new register row adds one more through the section's own loop. Re-read from a probe run rather than assumed — the declared set still equals the opened set (22/22), and the file's self-test output stays byte-identical to `origin/main`'s version of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6e6efac commit 8a96e66

13 files changed

Lines changed: 1365 additions & 1 deletion

scripts/check-filter-alias-parity.mjs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,41 @@ const ts = await requireDefaultExport('typescript', () => import('typescript'),
9191
import { parseSourceFile } from './ts-parse.mjs';
9292
import { isEntrypoint } from './invoked-as.mjs';
9393

94+
// ── The self-test's own battery roster and floor (#13489) ──────────────────
95+
//
96+
// `failures.length === 0` used to be this self-test's ONLY success condition, so
97+
// "every case held" and "the cases never ran" printed the same line. Closed the
98+
// way PR #13487 validated on check-doc-authoring: what is pinned is the
99+
// registered NAMES, not a number. Every section opens with `battery('<name>')`,
100+
// every assertion is attributed to the battery most recently opened, and the
101+
// floor requires the OPENED set to equal the DECLARED set with each battery at
102+
// or above its own count.
103+
//
104+
// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3
105+
// keeps a total "right" the moment a sibling grows.
106+
//
107+
// The counts are a FLOOR, not an equality — adding cases is ordinary work and
108+
// must not red. A battery BELOW its floor means cases stopped running; the
109+
// remedy is to find what stopped registering.
110+
const SELF_TEST_BATTERIES = Object.freeze({
111+
'1. The tree as it stands: both sides name the same four spellings.': 2,
112+
'2. A fifth spelling on the NORMALIZER side only — the direction #8002 is about, and the one nothing else in the repo refuses.': 3,
113+
'3. The same fifth spelling on BOTH sides is green again — the gate judges parity, not the size of the set.': 1,
114+
'4. A fifth spelling on the INGRESS side only.': 2,
115+
'5. A `$` alias folding INTO a filter spelling is a filter spelling. It reaches `where` through two hops, which is exactly the shape a reader comparing only the slot tables would miss.': 2,
116+
'6. Rot: an unreadable shape must fail LOUDLY. A reader that matches nothing would otherwise compare two empty sets and report a pass.': 5,
117+
'7. The wiring this gate depends on to run at all.': 3,
118+
});
119+
120+
// DELETING an entry silences that battery's floor exactly as effectively as
121+
// zeroing it, so the roster's own size is pinned too.
122+
const SELF_TEST_BATTERY_FLOOR = 7;
123+
124+
// The key an assertion is filed under when no battery is open. It is not a
125+
// declared battery, so it reds by the same set difference rather than silently
126+
// inflating whichever battery happened to run last.
127+
const UNATTRIBUTED_BATTERY = '(no battery open)';
128+
94129
const ROOT = join(fileURLToPath(new URL('.', import.meta.url)), '..');
95130

96131
/** The three files that declare a filter-slot spelling, by repo-relative path. */
@@ -491,14 +526,29 @@ export const FILTER_SLOT_QUERY_PARAMS: readonly string[] = (() => {
491526
const SELF_TEST_VERDICT = 'check-filter-alias-parity self-test reached its verdict';
492527

493528
function selfTest() {
529+
// The battery ledger this self-test's floor is evaluated against (#13489).
530+
// `battery()` opens a battery; every assertion below is attributed to the one
531+
// most recently opened, so a section that stops running stops registering and
532+
// names ITSELF at the floor rather than going quiet.
533+
const batterySeen = new Map();
534+
let openBattery = null;
535+
const battery = (name) => {
536+
openBattery = name;
537+
};
538+
const registerCase = () => {
539+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
540+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
541+
};
494542
const failures = [];
495543
const check = (name, condition, detail) => {
544+
registerCase();
496545
if (!condition) failures.push(`${name}${detail ? ` — ${detail}` : ''}`);
497546
};
498547
const run = (protocolText, restText, specText = FIXTURE_SPEC) =>
499548
judge({ specText, protocolText, restText });
500549

501550
// 1. The tree as it stands: both sides name the same four spellings.
551+
battery('1. The tree as it stands: both sides name the same four spellings.');
502552
{
503553
const { problems, protocolSet, restSet } = run(fixtureProtocol(), fixtureRest());
504554
check('agreeing sides are green', problems.length === 0, problems[0]);
@@ -512,6 +562,7 @@ function selfTest() {
512562

513563
// 2. A fifth spelling on the NORMALIZER side only — the direction #8002 is
514564
// about, and the one nothing else in the repo refuses.
565+
battery('2. A fifth spelling on the NORMALIZER side only — the direction #8002 is about, and the one nothing else in the repo refuses.');
515566
{
516567
const { problems } = run(fixtureProtocol(['filters', '$filter', 'where_clause']), fixtureRest());
517568
check('a normalizer-only fifth spelling is red', problems.length === 1, `saw ${problems.length}`);
@@ -529,6 +580,7 @@ function selfTest() {
529580

530581
// 3. The same fifth spelling on BOTH sides is green again — the gate judges
531582
// parity, not the size of the set.
583+
battery('3. The same fifth spelling on BOTH sides is green again — the gate judges parity, not the size of the set.');
532584
{
533585
const { problems } = run(
534586
fixtureProtocol(['filters', '$filter', 'where_clause']),
@@ -538,6 +590,7 @@ function selfTest() {
538590
}
539591

540592
// 4. A fifth spelling on the INGRESS side only.
593+
battery('4. A fifth spelling on the INGRESS side only.');
541594
{
542595
const { problems } = run(fixtureProtocol(), fixtureRest(['filters', '$filter', 'where_clause']));
543596
check('an ingress-only fifth spelling is red', problems.length === 1, `saw ${problems.length}`);
@@ -551,6 +604,7 @@ function selfTest() {
551604
// 5. A `$` alias folding INTO a filter spelling is a filter spelling. It
552605
// reaches `where` through two hops, which is exactly the shape a reader
553606
// comparing only the slot tables would miss.
607+
battery('5. A `$` alias folding INTO a filter spelling is a filter spelling. It reaches `where` through two hops, which is exactly the shape a reader comparing only the slot tables would miss.');
554608
{
555609
const { problems } = run(
556610
fixtureProtocol(['filters', '$filter'], [['$top', 'top'], ['$filters', 'filters']]),
@@ -562,6 +616,7 @@ function selfTest() {
562616

563617
// 6. Rot: an unreadable shape must fail LOUDLY. A reader that matches
564618
// nothing would otherwise compare two empty sets and report a pass.
619+
battery('6. Rot: an unreadable shape must fail LOUDLY. A reader that matches nothing would otherwise compare two empty sets and report a pass.');
565620
{
566621
const { problems } = run(fixtureProtocol(), '\nexport const SOMETHING_ELSE = [];\n');
567622
check('a missing ingress declaration is red', problems.length === 1, `saw ${problems.length}`);
@@ -595,6 +650,7 @@ function selfTest() {
595650
}
596651

597652
// 7. The wiring this gate depends on to run at all.
653+
battery('7. The wiring this gate depends on to run at all.');
598654
{
599655
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
600656
const entry = pkg.scripts?.['check:filter-alias-parity'];
@@ -609,6 +665,50 @@ function selfTest() {
609665
check('lint.yml runs the gate exactly once', wired.length === 1, `found ${wired.length}`);
610666
}
611667

668+
// ── The floor: every declared battery RAN, and ran its cases (#13489) ────
669+
//
670+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
671+
// the success line below can only be printed by a run in which the set of
672+
// batteries that registered assertions EQUALS the set declared. A set
673+
// difference names WHICH battery stopped; a count says only that something did.
674+
const floorFailure = (message) => { failures.push(message); };
675+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
676+
let floorBreached = false;
677+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
678+
floorBreached = true;
679+
floorFailure(
680+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` +
681+
`${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`,
682+
);
683+
}
684+
for (const [name, count] of batterySeen) {
685+
if (declaredBatteries.includes(name)) continue;
686+
floorBreached = true;
687+
floorFailure(
688+
`self-test battery "${name}" registered ${count} case(s) but is not declared in ` +
689+
'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.',
690+
);
691+
}
692+
for (const name of declaredBatteries) {
693+
const count = batterySeen.get(name) ?? 0;
694+
if (count >= SELF_TEST_BATTERIES[name]) continue;
695+
floorBreached = true;
696+
floorFailure(
697+
count === 0
698+
? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` +
699+
'The verdict below would have claimed those cases hold.'
700+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` +
701+
`${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`,
702+
);
703+
}
704+
if (floorBreached) {
705+
floorFailure(
706+
'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' +
707+
'number. Find what stopped registering (an early return, a deleted block, a guard that now ' +
708+
'skips) and restore it.',
709+
);
710+
}
711+
612712
if (failures.length) {
613713
console.error('check:filter-alias-parity --self-test FAILED');
614714
for (const f of failures) console.error(` - ${f}`);

scripts/check-i18n-bundles.mjs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,38 @@ import {
103103
import { EXIT_FINDINGS, EXIT_PREREQUISITE_NOT_MET } from './import-prerequisite.mjs';
104104
import { findExtractConfigs, flagsFromDocstring } from './i18n-bundle-surface.mjs';
105105

106+
// ── The self-test's own battery roster and floor (#13489) ──────────────────
107+
//
108+
// `failures.length === 0` used to be this self-test's ONLY success condition, so
109+
// "every case held" and "the cases never ran" printed the same line. Closed the
110+
// way PR #13487 validated on check-doc-authoring: what is pinned is the
111+
// registered NAMES, not a number. Every section opens with `battery('<name>')`,
112+
// every assertion is attributed to the battery most recently opened, and the
113+
// floor requires the OPENED set to equal the DECLARED set with each battery at
114+
// or above its own count.
115+
//
116+
// ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3
117+
// keeps a total "right" the moment a sibling grows.
118+
//
119+
// The counts are a FLOOR, not an equality — adding cases is ordinary work and
120+
// must not red. A battery BELOW its floor means cases stopped running; the
121+
// remedy is to find what stopped registering.
122+
const SELF_TEST_BATTERIES = Object.freeze({
123+
'Third classifier (#5217): the build prerequisite. Same anti-#4690 duty as': 34,
124+
'Fourth classifier (#7681): the OTHER prerequisite — a workspace package this': 24,
125+
'Fifth classifier (#11647): the POPULATION — is there anything to grade, and': 11,
126+
'The other cause, and the reason `=== 0` alone is not the whole condition: a': 8,
127+
});
128+
129+
// DELETING an entry silences that battery's floor exactly as effectively as
130+
// zeroing it, so the roster's own size is pinned too.
131+
const SELF_TEST_BATTERY_FLOOR = 4;
132+
133+
// The key an assertion is filed under when no battery is open. It is not a
134+
// declared battery, so it reds by the same set difference rather than silently
135+
// inflating whichever battery happened to run last.
136+
const UNATTRIBUTED_BATTERY = '(no battery open)';
137+
106138
/**
107139
* The module this gate's POPULATION is enumerated by, declared as a whole
108140
* literal so the derivation can see it (#9116).
@@ -436,8 +468,23 @@ function populationVerdict(population, activeFilter) {
436468
const SELF_TEST_VERDICT = 'check-i18n-bundles self-test reached its verdict';
437469

438470
function selfTest() {
471+
// The battery ledger this self-test's floor is evaluated against (#13489).
472+
// `battery()` opens a battery; every assertion below is attributed to the one
473+
// most recently opened, so a section that stops running stops registering and
474+
// names ITSELF at the floor rather than going quiet.
475+
const batterySeen = new Map();
476+
let openBattery = null;
477+
const battery = (name) => {
478+
openBattery = name;
479+
};
480+
const registerCase = () => {
481+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
482+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
483+
};
484+
battery('Third classifier (#5217): the build prerequisite. Same anti-#4690 duty as');
439485
const failures = [];
440486
const expect = (name, cond, detail) => {
487+
registerCase();
441488
if (!cond) failures.push(`${name}${detail}`);
442489
};
443490

@@ -635,6 +682,7 @@ function selfTest() {
635682
// send the reader to a rebuild that changes nothing — the #5862 defect (a
636683
// confident diagnosis pointing somewhere innocent) rebuilt one layer down.
637684
// -------------------------------------------------------------------------
685+
battery('Fourth classifier (#7681): the OTHER prerequisite — a workspace package this');
638686

639687
// The failure #7681 reported, as node actually prints it: produced locally by
640688
// importing a name that a package's built ESM does not export — a fixture
@@ -794,6 +842,7 @@ function selfTest() {
794842
// classifier in this file is proven red against a recorded string, but "did
795843
// this gate look at anything at all?" can only be proven by looking.
796844
// -------------------------------------------------------------------------
845+
battery('Fifth classifier (#11647): the POPULATION — is there anything to grade, and');
797846

798847
const popCwdBefore = process.cwd();
799848
let offRootPopulation;
@@ -896,6 +945,7 @@ function selfTest() {
896945

897946
// The other cause, and the reason `=== 0` alone is not the whole condition: a
898947
// filter that matched nothing is a typo, not an environment fact.
948+
battery('The other cause, and the reason `=== 0` alone is not the whole condition: a');
899949
const filterVerdict = populationVerdict(onRootPopulation, 'no-such-package');
900950
expect('#11647 an unmatched --filter is refused', !!filterVerdict, 'a filter matching nothing must not render as OK (0 package(s))');
901951
expect(
@@ -936,6 +986,50 @@ function selfTest() {
936986
const longWalkError = unreadablePopulationDetail(new Error('E'.repeat(400))).join('\n');
937987
expect('#11647 long walk errors are truncated', longWalkError.includes(`${'E'.repeat(160)}…`), 'a 400-char message must not be pasted whole');
938988

989+
// ── The floor: every declared battery RAN, and ran its cases (#13489) ────
990+
//
991+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
992+
// the success line below can only be printed by a run in which the set of
993+
// batteries that registered assertions EQUALS the set declared. A set
994+
// difference names WHICH battery stopped; a count says only that something did.
995+
const floorFailure = (message) => { failures.push(message); };
996+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
997+
let floorBreached = false;
998+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
999+
floorBreached = true;
1000+
floorFailure(
1001+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` +
1002+
`${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`,
1003+
);
1004+
}
1005+
for (const [name, count] of batterySeen) {
1006+
if (declaredBatteries.includes(name)) continue;
1007+
floorBreached = true;
1008+
floorFailure(
1009+
`self-test battery "${name}" registered ${count} case(s) but is not declared in ` +
1010+
'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.',
1011+
);
1012+
}
1013+
for (const name of declaredBatteries) {
1014+
const count = batterySeen.get(name) ?? 0;
1015+
if (count >= SELF_TEST_BATTERIES[name]) continue;
1016+
floorBreached = true;
1017+
floorFailure(
1018+
count === 0
1019+
? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` +
1020+
'The verdict below would have claimed those cases hold.'
1021+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` +
1022+
`${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`,
1023+
);
1024+
}
1025+
if (floorBreached) {
1026+
floorFailure(
1027+
'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' +
1028+
'number. Find what stopped registering (an early return, a deleted block, a guard that now ' +
1029+
'skips) and restore it.',
1030+
);
1031+
}
1032+
9391033
if (failures.length) {
9401034
console.error(`✗ check:i18n --self-test — ${failures.length} failure(s)\n`);
9411035
for (const f of failures) console.error(` ${f}`);

0 commit comments

Comments
 (0)