diff --git a/scripts/check-closing-keyword-parity.mjs b/scripts/check-closing-keyword-parity.mjs index 2af8351605..efcce3bdce 100644 --- a/scripts/check-closing-keyword-parity.mjs +++ b/scripts/check-closing-keyword-parity.mjs @@ -413,6 +413,15 @@ const MUTATIONS = [ }, ]; +// Set by `selfTest()` only after a verdict is printed -- either verdict -- and +// read at the dispatch below: a `return` that leaves the function above those +// lines prints nothing and still exits 0, so a self-test that never finished +// reports as one that passed. The self-test's own exit code stays load-bearing, +// so the handshake is a flag rather than a returned sentinel. The failure path +// sets it too: the refusal below must fire only when NEITHER verdict was +// printed, never on a genuine red that already said what failed. +let selfTestReachedVerdict = false; + function selfTest() { const root = repoRoot(); const failures = []; @@ -465,16 +474,28 @@ function selfTest() { if (failures.length === 0) { console.log(`✓ check-closing-keyword-parity --self-test: ${checked} assertions, ${MUTATIONS.length} mutations of the shipped parsers each driven to red.`); + selfTestReachedVerdict = true; return 0; } console.error(`✗ check-closing-keyword-parity --self-test -- ${failures.length} failure(s)\n`); for (const f of failures) console.error(` • ${f}`); + selfTestReachedVerdict = true; return 1; } if (isEntrypoint(import.meta.url)) { const arg = process.argv[2]; if (arg === '--list') list(); - else if (arg === '--self-test') process.exit(selfTest()); - else process.exit(run()); + else if (arg === '--self-test') { + const code = selfTest(); + if (!selfTestReachedVerdict) { + console.error( + '\n✗ check-closing-keyword-parity self-test: selfTest() returned without reaching its verdict,\n' + + 'so no success line was printed. Exiting 0 here would report a self-test\n' + + 'that never finished as a self-test that passed.\n', + ); + process.exit(1); + } + process.exit(code); + } else process.exit(run()); } diff --git a/scripts/check-pnpm-filter-targets.mjs b/scripts/check-pnpm-filter-targets.mjs index 72b1117b3e..d91ff6433a 100644 --- a/scripts/check-pnpm-filter-targets.mjs +++ b/scripts/check-pnpm-filter-targets.mjs @@ -375,6 +375,15 @@ function list() { return 0; } +// Set by `selfTest()` only after a verdict is printed -- either verdict -- and +// read at the dispatch below: a `return` that leaves the function above those +// lines prints nothing and still exits 0, so a self-test that never finished +// reports as one that passed. The self-test's own exit code stays load-bearing, +// so the handshake is a flag rather than a returned sentinel. The failure path +// sets it too: the refusal below must fire only when NEITHER verdict was +// printed, never on a genuine red that already said what failed. +let selfTestReachedVerdict = false; + export function selfTest() { const failures = []; let checked = 0; @@ -526,16 +535,28 @@ export function selfTest() { + 'four carriers (workflow, package.json, shell, JS) and the same fixtures observed SILENT with a ' + `real name; ${live.occurrences.length} live occurrence(s) swept.`, ); + selfTestReachedVerdict = true; return 0; } console.error(`✗ check-pnpm-filter-targets --self-test — ${failures.length} failure(s)\n`); for (const failure of failures) console.error(` • ${failure}`); + selfTestReachedVerdict = true; return 1; } if (isEntrypoint(import.meta.url)) { const flag = process.argv[2]; - if (flag === '--self-test') process.exit(selfTest()); - else if (flag === '--list') process.exit(list()); + if (flag === '--self-test') { + const code = selfTest(); + if (!selfTestReachedVerdict) { + console.error( + '\n✗ check-pnpm-filter-targets self-test: selfTest() returned without reaching its verdict,\n' + + 'so no success line was printed. Exiting 0 here would report a self-test\n' + + 'that never finished as a self-test that passed.\n', + ); + process.exit(1); + } + process.exit(code); + } else if (flag === '--list') process.exit(list()); else process.exit(run()); } diff --git a/scripts/check-settings-bind-window.mjs b/scripts/check-settings-bind-window.mjs index 74df37bf1e..7869e1eeda 100644 --- a/scripts/check-settings-bind-window.mjs +++ b/scripts/check-settings-bind-window.mjs @@ -727,6 +727,15 @@ function list() { // ── Self-test ──────────────────────────────────────────────────────────────── +// Set by `selfTest()` only after a verdict is printed -- either verdict -- and +// read at the dispatch below: a `return` that leaves the function above those +// lines prints nothing and still exits 0, so a self-test that never finished +// reports as one that passed. The self-test's own exit code stays load-bearing, +// so the handshake is a flag rather than a returned sentinel. The failure path +// sets it too: the refusal below must fire only when NEITHER verdict was +// printed, never on a genuine red that already said what failed. +let selfTestReachedVerdict = false; + function selfTest() { const assert = (cond, msg) => { if (!cond) { console.error('✗ self-test: ' + msg); process.exit(1); } }; @@ -1039,11 +1048,21 @@ function selfTest() { } console.log(`✓ settings bind-window guard self-test: all cases pass.`); + selfTestReachedVerdict = true; } // ── Entry ──────────────────────────────────────────────────────────────────── const arg = process.argv[2]; -if (arg === '--self-test') selfTest(); -else if (arg === '--list') list(); +if (arg === '--self-test') { + selfTest(); + if (!selfTestReachedVerdict) { + console.error( + '\n✗ check-settings-bind-window self-test: selfTest() returned without reaching its verdict,\n' + + 'so no success line was printed. Exiting 0 here would report a self-test\n' + + 'that never finished as a self-test that passed.\n', + ); + process.exit(1); + } +} else if (arg === '--list') list(); else audit(); diff --git a/scripts/pnpm-filter-targets.mjs b/scripts/pnpm-filter-targets.mjs index a0c982b46c..5d813c5367 100644 --- a/scripts/pnpm-filter-targets.mjs +++ b/scripts/pnpm-filter-targets.mjs @@ -531,6 +531,15 @@ function cliPreflight(command) { return 0; } +// Set by `selfTest()` only after a verdict is printed -- either verdict -- and +// read at the dispatch below: a `return` that leaves the function above those +// lines prints nothing and still exits 0, so a self-test that never finished +// reports as one that passed. The self-test's own exit code stays load-bearing, +// so the handshake is a flag rather than a returned sentinel. The failure path +// sets it too: the refusal below must fire only when NEITHER verdict was +// printed, never on a genuine red that already said what failed. +let selfTestReachedVerdict = false; + export async function selfTest() { const failures = []; let checked = 0; @@ -683,17 +692,29 @@ export async function selfTest() { `✓ pnpm-filter-targets --self-test: ${checked} assertions over ${names.length} real workspace packages ` + '(match rule pinned against measured pnpm behaviour; preflight observed both REFUSING and SILENT).', ); + selfTestReachedVerdict = true; return 0; } console.error(`✗ pnpm-filter-targets --self-test -- ${failures.length} failure(s)\n`); for (const failure of failures) console.error(` • ${failure}`); + selfTestReachedVerdict = true; return 1; } if (isEntrypoint(import.meta.url)) { const [flag, argument] = process.argv.slice(2); - if (flag === '--self-test') process.exit(await selfTest()); - else if (flag === '--list') process.exit(cliList()); + if (flag === '--self-test') { + const code = await selfTest(); + if (!selfTestReachedVerdict) { + console.error( + '\n✗ pnpm-filter-targets self-test: selfTest() returned without reaching its verdict,\n' + + 'so no success line was printed. Exiting 0 here would report a self-test\n' + + 'that never finished as a self-test that passed.\n', + ); + process.exit(1); + } + process.exit(code); + } else if (flag === '--list') process.exit(cliList()); else if (flag === '--preflight') process.exit(cliPreflight(argument ?? '')); else { console.error('usage: pnpm-filter-targets.mjs [--list | --preflight | --self-test]');