Skip to content

Commit e293038

Browse files
claude[bot]claude
andauthored
test(scripts): give six more self-tests a battery roster and floor (#13799 batch 3) (#15156)
* test(scripts): give check-clause2-carriers a battery roster and floor Part of #13799 (batch 3). The concise-arrow sink `t` gets a block body so it can call registerCase(); 12 batteries / 150 cases pinned. Case count before == after (150), measured against the branch point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): give check-adr-links a battery roster and floor Part of #13799 (batch 3). The module-level `assert` keeps its semantics (print and exit 1 on the first failure); a thin in-body `check()` wrapper attributes each case to the open battery and defers to it. 4 batteries / 23 cases pinned, and the floor reports through that same `assert`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): give check-doc-anchors a battery roster and floor Part of #13799 (batch 3). Same thin in-body `check()` wrapper over the module-level `assert`; 8 batteries / 22 cases pinned. Gate mode still green on the tree (298 fragment links across 409 sources). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): give check-kernel-hook-pairs a battery roster and floor Part of #13799 (batch 3). Third of the module-level-`assert` trio, same thin in-body wrapper; 10 batteries / 16 cases pinned. Its verdict line prints a LITERAL "10 cases" that tracks nothing and undercounts the 16 assertions the body actually runs — left as found (the card's scope is the floor, and the handshake stays exactly as landed), recorded instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): give check-published-files a battery roster and floor Part of #13799 (batch 3). The 7 inline `failures.push(...)` sites now route through one block-bodied `expect()` that registers the case whether it holds or not; message text is unchanged. 2 batteries / 59 cases, the first hoisted because cases run before the first banner. The count reconciles against the tables: 21 + 12 + 4 and 12 + 6 + 3 + 1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * test(scripts): give ablation-dist-preflight a battery roster and floor Part of #13799 (batch 3). The 6 inline `failed += 1` sites route through one block-bodied `check()` that keeps both the ✓ and ✗ branches verbatim and counts the case either way. 3 batteries / 36 cases, the first hoisted (10 + 3 + 12 + 1 run before the first banner). Not a repo sweep: with no args it prints usage and exits 2, unchanged from the branch point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5cf1c88 commit e293038

6 files changed

Lines changed: 716 additions & 111 deletions

scripts/ablation-dist-preflight.mjs

Lines changed: 120 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -551,13 +551,72 @@ function run(argv) {
551551
if (!v.ok || !tv.ok) process.exit(1);
552552
}
553553

554+
// -- The self-test's own battery roster and floor (#13489) ------------------
555+
//
556+
// A pass used to be this self-test's ONLY success condition, so "every case
557+
// held" and "the cases never ran" printed the same line. Closed the way
558+
// PR #13487 validated on check-doc-authoring: what is pinned is the registered
559+
// NAMES, not a number. Every section opens with `battery('<name>')`, every
560+
// assertion is attributed to the battery most recently opened, and the floor
561+
// requires the OPENED set to equal the DECLARED set with each battery at or
562+
// above its own count.
563+
//
564+
// The counts are a FLOOR, not an equality -- adding cases is ordinary work and
565+
// must not red. A battery BELOW its floor means cases stopped running; the
566+
// remedy is to find what stopped registering.
567+
const SELF_TEST_BATTERIES = Object.freeze({
568+
'whole-tree accounting: the pure table': 26,
569+
'porcelain parsing': 3,
570+
'whole-tree accounting: a real git tree': 7,
571+
});
572+
573+
// DELETING an entry silences that battery's floor exactly as effectively as
574+
// zeroing it, so the roster's own size is pinned too.
575+
const SELF_TEST_BATTERY_FLOOR = 3;
576+
577+
// The key an assertion is filed under when no battery is open. It is not a
578+
// declared battery, so it reds by the same set difference rather than silently
579+
// inflating whichever battery happened to run last.
580+
const UNATTRIBUTED_BATTERY = '(no battery open)';
581+
554582
// Returned by `selfTest()` only after its verdict is printed. The dispatch
555583
// refuses anything else: a `return` that leaves the function above that line
556584
// prints nothing and still exits 0 — a self-test that never finished, reported
557585
// as one that passed (#13798).
558586
const SELF_TEST_VERDICT = 'ablation-dist-preflight self-test reached its verdict';
559587

560588
function selfTest() {
589+
// The battery ledger this self-test's floor is evaluated against (#13489).
590+
// `battery()` opens a battery; every assertion below is attributed to the one
591+
// most recently opened, so a section that stops running stops registering and
592+
// names ITSELF at the floor rather than going quiet.
593+
const batterySeen = new Map();
594+
let openBattery = null;
595+
const battery = (name) => {
596+
openBattery = name;
597+
};
598+
const registerCase = () => {
599+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
600+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
601+
};
602+
// The one in-body assertion helper the 6 inline `failed += 1` sites now route
603+
// through. Each site already had a ✓ branch and a ✗ branch; both are kept
604+
// verbatim, and the only change is that a case is COUNTED either way — which
605+
// is what lets the floor below tell "held" from "never ran".
606+
const check = (label, ok, detail = '') => {
607+
registerCase();
608+
if (ok) {
609+
console.log(` ✓ ${label}`);
610+
return;
611+
}
612+
console.error(` ✗ ${label}${detail}`);
613+
failed += 1;
614+
};
615+
// Cases run before the first banner, so the first battery is opened at the
616+
// top of the body and that banner carries no second opener — PR #13487's own
617+
// shape, as batches 1b and 2 landed it.
618+
battery('whole-tree accounting: the pure table');
619+
let failed = 0;
561620
const cases = [
562621
['missing dist is red', { mode: 'present', distExists: false, scanned: 0, codeHits: 0, mapHits: 0 }, false],
563622
['empty dist is red, not a skip', { mode: 'present', distExists: true, scanned: 0, codeHits: 0, mapHits: 0 }, false],
@@ -570,15 +629,9 @@ function selfTest() {
570629
['absent mode: still in code is red', { mode: 'absent', distExists: true, scanned: 9, codeHits: 3, mapHits: 0 }, false],
571630
['absent mode: missing dist still red', { mode: 'absent', distExists: false, scanned: 0, codeHits: 0, mapHits: 0 }, false],
572631
];
573-
let failed = 0;
574632
for (const [label, input, expected] of cases) {
575633
const got = verdict(input).ok;
576-
if (got !== expected) {
577-
console.error(` ✗ ${label}: expected ok=${expected}, got ok=${got}`);
578-
failed += 1;
579-
} else {
580-
console.log(` ✓ ${label}`);
581-
}
634+
check(label, got === expected, `: expected ok=${expected}, got ok=${got}`);
582635
}
583636

584637
// Filesystem leg: a real dist tree where the marker lives only in a sourcemap
@@ -597,13 +650,7 @@ function selfTest() {
597650
['scan classifies a map-only token as a map hit', mapOnly.codeHits.length === 0 && mapOnly.mapHits.length === 1],
598651
['map-only scan is judged RED', verdict({ mode: 'present', distExists: true, scanned: mapOnly.scanned, codeHits: 0, mapHits: mapOnly.mapHits.length }).ok === false],
599652
];
600-
for (const [label, ok] of checks) {
601-
if (ok) console.log(` ✓ ${label}`);
602-
else {
603-
console.error(` ✗ ${label}`);
604-
failed += 1;
605-
}
606-
}
653+
for (const [label, ok] of checks) check(label, ok);
607654
} finally {
608655
rmSync(tmp, { recursive: true, force: true });
609656
}
@@ -631,12 +678,11 @@ function selfTest() {
631678
];
632679
for (const [label, input, expectedOk, expectedLeg] of treeCases) {
633680
const got = treeVerdict(input);
634-
if (got.ok !== expectedOk || got.leg !== expectedLeg) {
635-
console.error(` ✗ ${label}: expected ok=${expectedOk} leg=${expectedLeg}, got ok=${got.ok} leg=${got.leg}`);
636-
failed += 1;
637-
} else {
638-
console.log(` ✓ ${label}`);
639-
}
681+
check(
682+
label,
683+
got.ok === expectedOk && got.leg === expectedLeg,
684+
`: expected ok=${expectedOk} leg=${expectedLeg}, got ok=${got.ok} leg=${got.leg}`,
685+
);
640686
}
641687

642688
// A red restore leg must NAME the leaked path -- a refusal that does not say
@@ -645,14 +691,11 @@ function selfTest() {
645691
{
646692
const red = treeVerdict({ mode: 'absent', gitReadable: true, files: [f('packages/spec/authorable-surface/data.json', false, true)] });
647693
const named = red.paths.includes('packages/spec/authorable-surface/data.json');
648-
if (named) console.log(' ✓ a red restore leg names the leaked path');
649-
else {
650-
console.error(' ✗ a red restore leg names the leaked path');
651-
failed += 1;
652-
}
694+
check('a red restore leg names the leaked path', named);
653695
}
654696

655697
// ---- porcelain parsing ---------------------------------------------------
698+
battery('porcelain parsing');
656699
{
657700
const Z = String.fromCharCode(0);
658701
const parsed = parsePorcelainZ([' M packages/spec/authorable-surface/data.json', '?? scratch note.txt', 'R new/name.ts', 'old/name.ts', ''].join(Z));
@@ -661,16 +704,11 @@ function selfTest() {
661704
['parses an untracked path holding a space, unquoted', parsed[1]?.path === 'scratch note.txt' && parsed[1]?.untracked === true],
662705
['consumes a rename origin record instead of listing it', parsed.length === 3 && parsed[2]?.path === 'new/name.ts'],
663706
];
664-
for (const [label, ok] of checks) {
665-
if (ok) console.log(` ✓ ${label}`);
666-
else {
667-
console.error(` ✗ ${label}`);
668-
failed += 1;
669-
}
670-
}
707+
for (const [label, ok] of checks) check(label, ok);
671708
}
672709

673710
// ---- whole-tree accounting: a real git tree -------------------------------
711+
battery('whole-tree accounting: a real git tree');
674712
// The pure table cannot catch a broken `git status` read or a broken
675713
// HEAD-vs-worktree marker probe, and those are the wires that make the
676714
// verdict mean anything. This leg replays the measured incident end to end.
@@ -732,17 +770,60 @@ function selfTest() {
732770
['git leg: a deleted guard is a mutate leg, not a restore leg', deleteLeg.ok === true && deleteLeg.leg === 'mutate'],
733771
['git leg: an untracked path reds the restore leg', untrackedLeg.ok === false && untrackedLeg.paths.includes('scratch.txt')],
734772
];
735-
for (const [label, ok] of gitChecks) {
736-
if (ok) console.log(` ✓ ${label}`);
737-
else {
738-
console.error(` ✗ ${label}`);
739-
failed += 1;
740-
}
741-
}
773+
for (const [label, ok] of gitChecks) check(label, ok);
742774
} finally {
743775
rmSync(repo, { recursive: true, force: true });
744776
}
745777

778+
// -- The floor: every declared battery RAN, and ran its cases (#13489) -----
779+
//
780+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
781+
// the success line below can only be printed by a run in which the set of
782+
// batteries that registered assertions EQUALS the set declared. A set
783+
// difference names WHICH battery stopped; a count says only that something did.
784+
const floorMessages = [];
785+
const floorFailure = (message) => { floorMessages.push(message); };
786+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
787+
let floorBreached = false;
788+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
789+
floorBreached = true;
790+
floorFailure(
791+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned `
792+
+ `${SELF_TEST_BATTERY_FLOOR} — a battery deleted from the roster takes its own floor with it.`,
793+
);
794+
}
795+
for (const [name, count] of batterySeen) {
796+
if (declaredBatteries.includes(name)) continue;
797+
floorBreached = true;
798+
floorFailure(
799+
`self-test battery "${name}" registered ${count} case(s) but is not declared in `
800+
+ 'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.',
801+
);
802+
}
803+
for (const name of declaredBatteries) {
804+
const count = batterySeen.get(name) ?? 0;
805+
if (count >= SELF_TEST_BATTERIES[name]) continue;
806+
floorBreached = true;
807+
floorFailure(
808+
count === 0
809+
? `self-test battery "${name}" DID NOT RUN — 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. `
810+
+ 'The verdict below would have claimed those cases hold.'
811+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of `
812+
+ `${SELF_TEST_BATTERIES[name]} — cases that used to run no longer do.`,
813+
);
814+
}
815+
if (floorBreached) {
816+
floorFailure(
817+
'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the '
818+
+ 'number. Find what stopped registering (an early return, a deleted block, a guard that now '
819+
+ 'skips) and restore it.',
820+
);
821+
}
822+
for (const m of floorMessages) {
823+
console.error(` ✗ ${m}`);
824+
failed += 1;
825+
}
826+
746827
if (failed > 0) {
747828
console.error(`✗ ablation-dist-preflight self-test: ${failed} case(s) failed.`);
748829
process.exit(1);

0 commit comments

Comments
 (0)