Skip to content

Commit c47a5ac

Browse files
claude[bot]claude
andauthored
fix(scripts): give 24 more self-tests a verdict handshake at their dispatch (#13798 batch 2) (#14853)
* fix(scripts): give 24 more self-tests a verdict handshake at their dispatch Batch 2 of the #13798 remedy: the boundary-only PR #13797 shape, transplanted to the 14 files the probe still reads DEFEATED and released by their holding PRs, the six that hold today only by accident (a downstream TypeError or a usage error, not a handshake), the three that also call their self-test on the production path and discard the result, and check-workspace-manifest-cycles.mjs (runSelfTest() shape, NOT MEASURED by the census for want of an ENTRY_BY_HAND row, defeated in fact). Two shapes, one per dispatch spelling, both from batch 1 verbatim: - sentinel: the self-test returns SELF_TEST_VERDICT only after its verdict line, and the dispatch refuses anything else (where the return value is discarded); - module-level `selfTestReachedVerdict` flag (where the self-test's own exit code is load-bearing, or where it ends in process.exit and an appended sentinel would be dead code). Nothing inside any self-test body changes beyond the verdict marker on the line after the success line. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAwHpn4uVuf4N1geBcD5i3 * fix(scripts): read check-osv-exemptions selfTest() before destructuring it The handshake was placed after `const { passed, lines } = selfTest()`, so an early return made the destructuring throw a TypeError before the check could run — the very accident that made this gate read HELD without a handshake. The census now reports the named refusal instead of a stack trace. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAwHpn4uVuf4N1geBcD5i3 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent a610775 commit c47a5ac

24 files changed

Lines changed: 421 additions & 31 deletions

scripts/check-exported-any-returns.mts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ function readLedger(target: Target): Ledger {
385385
* nested type-literal namespaces), so it exercises the walk and not just the
386386
* predicate.
387387
*/
388+
// Set by `selfTest()` only after its verdict is printed, and read at the
389+
// dispatch: a `return` that leaves the function above that line prints nothing
390+
// and still exits 0 — a self-test that never finished, reported as one that
391+
// passed (#13798). The self-test's own exit code stays load-bearing, so the
392+
// handshake is a flag rather than a returned sentinel.
393+
let selfTestReachedVerdict = false;
394+
388395
function selfTest(): never {
389396
const fail = (msg: string): never => {
390397
console.error(`✗ self-test: ${msg}`);
@@ -484,6 +491,7 @@ function selfTest(): never {
484491
'✅ self-test: flags awaited-`any` returns through nested namespaces, and NOT caller-supplied generics, ' +
485492
'`any`-containing types, or named data properties. Ledger is exact in both directions.',
486493
);
494+
selfTestReachedVerdict = true;
487495
process.exit(0);
488496
}
489497

@@ -492,7 +500,17 @@ function selfTest(): never {
492500
// Both modes live behind the guard: `--self-test` calls `process.exit` too, so
493501
// running it on import would be the same defect wearing a friendlier name.
494502
if (isEntrypoint(import.meta.url)) {
495-
if (SELF_TEST) selfTest();
503+
if (SELF_TEST) {
504+
selfTest();
505+
if (!selfTestReachedVerdict) {
506+
console.error(
507+
'\n✗ check-exported-any-returns self-test: selfTest() returned without reaching its verdict,\n'
508+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
509+
+ 'that never finished as a self-test that passed.\n',
510+
);
511+
process.exit(1);
512+
}
513+
}
496514

497515
const target = resolveTarget();
498516

scripts/check-filter-alias-parity.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,14 @@ function main() {
636636
}
637637
return;
638638
}
639-
selfTest();
639+
if (selfTest() !== SELF_TEST_VERDICT) {
640+
console.error(
641+
'\n✗ check-filter-alias-parity self-test: selfTest() returned without reaching its verdict,\n'
642+
+ 'so no success line was printed. Running the gate on top of a self-test\n'
643+
+ 'that never finished would report an unverified gate as a verified one.\n',
644+
);
645+
process.exit(1);
646+
}
640647

641648
const read = (rel) => readFileSync(join(ROOT, rel), 'utf8');
642649
const { problems, protocolSet, restSet } = judge({

scripts/check-meta-type-normalized.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,14 @@ function main() {
251251
}
252252
return;
253253
}
254-
selfTest();
254+
if (selfTest() !== SELF_TEST_VERDICT) {
255+
console.error(
256+
'\n✗ check-meta-type-normalized self-test: selfTest() returned without reaching its verdict,\n'
257+
+ 'so no success line was printed. Running the gate on top of a self-test\n'
258+
+ 'that never finished would report an unverified gate as a verified one.\n',
259+
);
260+
process.exit(1);
261+
}
255262

256263
const files = [];
257264
for (const dir of SCAN_DIRS) walkFiles(join(ROOT, dir), files);

scripts/check-osv-exemptions.mjs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,13 @@ function validateLedger(text, today) {
378378
}
379379

380380
/** @returns {{ passed: boolean, lines: string[] }} */
381+
// Set by `selfTest()` only after its verdict is printed, and read at the
382+
// dispatch: a `return` that leaves the function above that line prints nothing
383+
// and still exits 0 — a self-test that never finished, reported as one that
384+
// passed (#13798). The self-test's own exit code stays load-bearing, so the
385+
// handshake is a flag rather than a returned sentinel.
386+
let selfTestReachedVerdict = false;
387+
381388
function selfTest() {
382389
const today = new Date(Date.UTC(2026, 7, 4)); // 2026-08-04, fixed
383390
const good = [
@@ -487,12 +494,25 @@ function selfTest() {
487494
(ok ? '' : `\n got: ${problems.length === 0 ? '(no problems)' : problems.join('\n ')}`),
488495
);
489496
}
497+
selfTestReachedVerdict = true;
490498
return { passed, lines };
491499
}
492500

493501
function main() {
494502
if (process.argv.includes('--self-test')) {
495-
const { passed, lines } = selfTest();
503+
// Read BEFORE destructuring: an early return yields `undefined`, and
504+
// destructuring that throws a TypeError before the handshake is reached —
505+
// an accidental non-zero exit is not a verdict handshake (#13798).
506+
const selfTestResult = selfTest();
507+
if (!selfTestReachedVerdict) {
508+
console.error(
509+
'\n✗ check-osv-exemptions self-test: selfTest() returned without reaching its verdict,\n'
510+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
511+
+ 'that never finished as a self-test that passed.\n',
512+
);
513+
process.exit(1);
514+
}
515+
const { passed, lines } = selfTestResult;
496516
console.log('check-osv-exemptions self-test (both directions):');
497517
for (const line of lines) console.log(line);
498518
if (!passed) {

scripts/check-page-declaration-shape.mjs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,13 @@ function findingMessage({ file, line, name, decl }) {
365365
// Self-test
366366
// ---------------------------------------------------------------------------
367367

368+
// Set by `selfTest()` only after its verdict is printed, and read at the
369+
// dispatch: a `return` that leaves the function above that line prints nothing
370+
// and still exits 0 — a self-test that never finished, reported as one that
371+
// passed (#13798). The self-test's own exit code stays load-bearing, so the
372+
// handshake is a flag rather than a returned sentinel.
373+
let selfTestReachedVerdict = false;
374+
368375
function selfTest() {
369376
let failed = 0;
370377
const t = (label, ok) => {
@@ -481,14 +488,24 @@ function selfTest() {
481488
PAGE_CARRIER_GLOBS.every((g) => g.includes('/')));
482489

483490
console.log(failed ? `\ncheck-page-declaration-shape --self-test: ${failed} FAILED` : '\ncheck-page-declaration-shape --self-test: all passed');
491+
selfTestReachedVerdict = true;
484492
return failed === 0;
485493
}
486494

487495
// ---------------------------------------------------------------------------
488496

489497
if (isEntrypoint(import.meta.url)) {
490498
if (process.argv.includes('--self-test')) {
491-
process.exit(selfTest() ? 0 : 1);
499+
const selfTestOk = selfTest();
500+
if (!selfTestReachedVerdict) {
501+
console.error(
502+
'\n✗ check-page-declaration-shape self-test: selfTest() returned without reaching its verdict,\n'
503+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
504+
+ 'that never finished as a self-test that passed.\n',
505+
);
506+
process.exit(1);
507+
}
508+
process.exit(selfTestOk ? 0 : 1);
492509
}
493510
const result = scan();
494511
const computed = computedCarrierSites();

scripts/check-skill-identifier-liveness.mjs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,12 @@ function allSpecSources() {
868868

869869
// ── Self-test ───────────────────────────────────────────────────────────────
870870

871+
// Returned by `selfTest()` only after its verdict is printed. The dispatch
872+
// refuses anything else: a `return` that leaves the function above that line
873+
// prints nothing and still exits 0 — a self-test that never finished, reported
874+
// as one that passed (#13798).
875+
const SELF_TEST_VERDICT = 'check-skill-identifier-liveness self-test reached its verdict';
876+
871877
/**
872878
* Why this exists at all, in the words of the wiring gate that requires it: a
873879
* gate whose defect class is its MATCHING RULE cannot detect its own regression
@@ -1141,6 +1147,8 @@ function selfTest() {
11411147
process.exit(1);
11421148
}
11431149
console.log('check-skill-identifier-liveness --self-test OK');
1150+
1151+
return SELF_TEST_VERDICT;
11441152
}
11451153

11461154
let SELF_SOURCE = null;
@@ -1152,6 +1160,14 @@ function selfSource() {
11521160
}
11531161

11541162
if (isEntrypoint(import.meta.url)) {
1155-
if (argv.includes('--self-test')) selfTest();
1156-
else main();
1163+
if (argv.includes('--self-test')) {
1164+
if (selfTest() !== SELF_TEST_VERDICT) {
1165+
console.error(
1166+
'\n✗ check-skill-identifier-liveness self-test: selfTest() returned without reaching its verdict,\n'
1167+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
1168+
+ 'that never finished as a self-test that passed.\n',
1169+
);
1170+
process.exit(1);
1171+
}
1172+
} else main();
11571173
}

scripts/check-skills-token-ratchet.mjs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,12 @@ function fixtureTree() {
704704
return { root, cleanup: () => rmSync(root, { recursive: true, force: true }) };
705705
}
706706

707+
// Returned by `selfTest()` only after its verdict is printed. The dispatch
708+
// refuses anything else: a `return` that leaves the function above that line
709+
// prints nothing and still exits 0 — a self-test that never finished, reported
710+
// as one that passed (#13798).
711+
const SELF_TEST_VERDICT = 'check-skills-token-ratchet self-test reached its verdict';
712+
707713
function selfTest() {
708714
const rel = 'skills/objectstack-ui/SKILL.md';
709715
const over = verdict(rel, 26000, 25154).msg;
@@ -922,9 +928,19 @@ function selfTest() {
922928
process.exit(1);
923929
}
924930
console.log(`✓ check-skills-token-ratchet self-test: ${cases.length} cases pass.`);
931+
932+
return SELF_TEST_VERDICT;
925933
}
926934

927935
if (isEntrypoint(import.meta.url)) {
928-
if (process.argv.includes('--self-test')) selfTest();
929-
else run();
936+
if (process.argv.includes('--self-test')) {
937+
if (selfTest() !== SELF_TEST_VERDICT) {
938+
console.error(
939+
'\n✗ check-skills-token-ratchet self-test: selfTest() returned without reaching its verdict,\n'
940+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
941+
+ 'that never finished as a self-test that passed.\n',
942+
);
943+
process.exit(1);
944+
}
945+
} else run();
930946
}

scripts/check-test-completeness.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,14 @@ function main() {
925925
return;
926926
}
927927
// Every invocation, not a lint step -- see the header note on why.
928-
selfTest({ quiet: true });
928+
if (selfTest({ quiet: true }) !== SELF_TEST_VERDICT) {
929+
console.error(
930+
'\n✗ check-test-completeness self-test: selfTest() returned without reaching its verdict,\n'
931+
+ 'so no success line was printed. Running the gate on top of a self-test\n'
932+
+ 'that never finished would report an unverified gate as a verified one.\n',
933+
);
934+
process.exit(1);
935+
}
929936

930937
const { verdict, paths } = invocationVerdict(argv);
931938
if (verdict) reportVerdict(verdict);

scripts/check-type-source-resolution.mjs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1775,6 +1775,12 @@ function buildFixtureTree() {
17751775
return root;
17761776
}
17771777

1778+
// Returned by `selfTest()` only after its verdict is printed. The dispatch
1779+
// refuses anything else: a `return` that leaves the function above that line
1780+
// prints nothing and still exits 0 — a self-test that never finished, reported
1781+
// as one that passed (#13798).
1782+
const SELF_TEST_VERDICT = 'check-type-source-resolution self-test reached its verdict';
1783+
17781784
function selfTest() {
17791785
const root = buildFixtureTree();
17801786
const problems = [];
@@ -2113,13 +2119,22 @@ function selfTest() {
21132119
process.exit(1);
21142120
}
21152121
console.log('check-type-source-resolution --self-test OK');
2122+
2123+
return SELF_TEST_VERDICT;
21162124
}
21172125

21182126
// ── entry point ─────────────────────────────────────────────────────────────
21192127

21202128
const argv = process.argv.slice(2);
21212129
if (argv.includes('--self-test')) {
2122-
selfTest();
2130+
if (selfTest() !== SELF_TEST_VERDICT) {
2131+
console.error(
2132+
'\n✗ check-type-source-resolution self-test: selfTest() returned without reaching its verdict,\n'
2133+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
2134+
+ 'that never finished as a self-test that passed.\n',
2135+
);
2136+
process.exit(1);
2137+
}
21232138
} else if (argv.includes('--list')) {
21242139
printList(REPO_ROOT);
21252140
} else {

scripts/check-workspace-manifest-cycles.mjs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,12 @@ export function selfTest() {
697697
return failures;
698698
}
699699

700+
// Returned by `runSelfTest()` only after its verdict is printed. The dispatch
701+
// refuses anything else: a `return` that leaves the function above that line
702+
// prints nothing and still exits 0 — a self-test that never finished, reported
703+
// as one that passed (#13798).
704+
const SELF_TEST_VERDICT = 'check-workspace-manifest-cycles self-test reached its verdict';
705+
700706
function runSelfTest() {
701707
const failures = selfTest();
702708
if (failures.length) {
@@ -705,10 +711,20 @@ function runSelfTest() {
705711
process.exit(1);
706712
}
707713
console.log('OK: check-workspace-manifest-cycles --self-test — all cases passed.');
714+
715+
return SELF_TEST_VERDICT;
708716
}
709717

710718
// Exports bindings, so an import for those exports alone must run nothing (#10667).
711719
if (isEntrypoint(import.meta.url)) {
712-
if (process.argv.includes('--self-test')) runSelfTest();
713-
else main();
720+
if (process.argv.includes('--self-test')) {
721+
if (runSelfTest() !== SELF_TEST_VERDICT) {
722+
console.error(
723+
'\n✗ check-workspace-manifest-cycles self-test: runSelfTest() returned without reaching its verdict,\n'
724+
+ 'so no success line was printed. Exiting 0 here would report a self-test\n'
725+
+ 'that never finished as a self-test that passed.\n',
726+
);
727+
process.exit(1);
728+
}
729+
} else main();
714730
}

0 commit comments

Comments
 (0)