Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion scripts/check-exported-any-returns.mts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,13 @@ function readLedger(target: Target): Ledger {
* nested type-literal namespaces), so it exercises the walk and not just the
* predicate.
*/
// Set by `selfTest()` only after its verdict is printed, and read at the
// dispatch: a `return` that leaves the function above that line prints nothing
// and still exits 0 — a self-test that never finished, reported as one that
// passed (#13798). The self-test's own exit code stays load-bearing, so the
// handshake is a flag rather than a returned sentinel.
let selfTestReachedVerdict = false;

function selfTest(): never {
const fail = (msg: string): never => {
console.error(`✗ self-test: ${msg}`);
Expand Down Expand Up @@ -484,6 +491,7 @@ function selfTest(): never {
'✅ self-test: flags awaited-`any` returns through nested namespaces, and NOT caller-supplied generics, ' +
'`any`-containing types, or named data properties. Ledger is exact in both directions.',
);
selfTestReachedVerdict = true;
process.exit(0);
}

Expand All @@ -492,7 +500,17 @@ function selfTest(): never {
// Both modes live behind the guard: `--self-test` calls `process.exit` too, so
// running it on import would be the same defect wearing a friendlier name.
if (isEntrypoint(import.meta.url)) {
if (SELF_TEST) selfTest();
if (SELF_TEST) {
selfTest();
if (!selfTestReachedVerdict) {
console.error(
'\n✗ check-exported-any-returns 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);
}
}

const target = resolveTarget();

Expand Down
9 changes: 8 additions & 1 deletion scripts/check-filter-alias-parity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,14 @@ function main() {
}
return;
}
selfTest();
if (selfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-filter-alias-parity self-test: selfTest() returned without reaching its verdict,\n'
+ 'so no success line was printed. Running the gate on top of a self-test\n'
+ 'that never finished would report an unverified gate as a verified one.\n',
);
process.exit(1);
}

const read = (rel) => readFileSync(join(ROOT, rel), 'utf8');
const { problems, protocolSet, restSet } = judge({
Expand Down
9 changes: 8 additions & 1 deletion scripts/check-meta-type-normalized.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,14 @@ function main() {
}
return;
}
selfTest();
if (selfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-meta-type-normalized self-test: selfTest() returned without reaching its verdict,\n'
+ 'so no success line was printed. Running the gate on top of a self-test\n'
+ 'that never finished would report an unverified gate as a verified one.\n',
);
process.exit(1);
}

const files = [];
for (const dir of SCAN_DIRS) walkFiles(join(ROOT, dir), files);
Expand Down
22 changes: 21 additions & 1 deletion scripts/check-osv-exemptions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ function validateLedger(text, today) {
}

/** @returns {{ passed: boolean, lines: string[] }} */
// Set by `selfTest()` only after its verdict is printed, and read at the
// dispatch: a `return` that leaves the function above that line prints nothing
// and still exits 0 — a self-test that never finished, reported as one that
// passed (#13798). The self-test's own exit code stays load-bearing, so the
// handshake is a flag rather than a returned sentinel.
let selfTestReachedVerdict = false;

function selfTest() {
const today = new Date(Date.UTC(2026, 7, 4)); // 2026-08-04, fixed
const good = [
Expand Down Expand Up @@ -487,12 +494,25 @@ function selfTest() {
(ok ? '' : `\n got: ${problems.length === 0 ? '(no problems)' : problems.join('\n ')}`),
);
}
selfTestReachedVerdict = true;
return { passed, lines };
}

function main() {
if (process.argv.includes('--self-test')) {
const { passed, lines } = selfTest();
// Read BEFORE destructuring: an early return yields `undefined`, and
// destructuring that throws a TypeError before the handshake is reached —
// an accidental non-zero exit is not a verdict handshake (#13798).
const selfTestResult = selfTest();
if (!selfTestReachedVerdict) {
console.error(
'\n✗ check-osv-exemptions 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);
}
const { passed, lines } = selfTestResult;
console.log('check-osv-exemptions self-test (both directions):');
for (const line of lines) console.log(line);
if (!passed) {
Expand Down
19 changes: 18 additions & 1 deletion scripts/check-page-declaration-shape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,13 @@ function findingMessage({ file, line, name, decl }) {
// Self-test
// ---------------------------------------------------------------------------

// Set by `selfTest()` only after its verdict is printed, and read at the
// dispatch: a `return` that leaves the function above that line prints nothing
// and still exits 0 — a self-test that never finished, reported as one that
// passed (#13798). The self-test's own exit code stays load-bearing, so the
// handshake is a flag rather than a returned sentinel.
let selfTestReachedVerdict = false;

function selfTest() {
let failed = 0;
const t = (label, ok) => {
Expand Down Expand Up @@ -481,14 +488,24 @@ function selfTest() {
PAGE_CARRIER_GLOBS.every((g) => g.includes('/')));

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

// ---------------------------------------------------------------------------

if (isEntrypoint(import.meta.url)) {
if (process.argv.includes('--self-test')) {
process.exit(selfTest() ? 0 : 1);
const selfTestOk = selfTest();
if (!selfTestReachedVerdict) {
console.error(
'\n✗ check-page-declaration-shape 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(selfTestOk ? 0 : 1);
}
const result = scan();
const computed = computedCarrierSites();
Expand Down
20 changes: 18 additions & 2 deletions scripts/check-skill-identifier-liveness.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,12 @@ function allSpecSources() {

// ── Self-test ───────────────────────────────────────────────────────────────

// Returned by `selfTest()` only after its verdict is printed. The dispatch
// refuses anything else: a `return` that leaves the function above that line
// prints nothing and still exits 0 — a self-test that never finished, reported
// as one that passed (#13798).
const SELF_TEST_VERDICT = 'check-skill-identifier-liveness self-test reached its verdict';

/**
* Why this exists at all, in the words of the wiring gate that requires it: a
* gate whose defect class is its MATCHING RULE cannot detect its own regression
Expand Down Expand Up @@ -1141,6 +1147,8 @@ function selfTest() {
process.exit(1);
}
console.log('check-skill-identifier-liveness --self-test OK');

return SELF_TEST_VERDICT;
}

let SELF_SOURCE = null;
Expand All @@ -1152,6 +1160,14 @@ function selfSource() {
}

if (isEntrypoint(import.meta.url)) {
if (argv.includes('--self-test')) selfTest();
else main();
if (argv.includes('--self-test')) {
if (selfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-skill-identifier-liveness 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 main();
}
20 changes: 18 additions & 2 deletions scripts/check-skills-token-ratchet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,12 @@ function fixtureTree() {
return { root, cleanup: () => rmSync(root, { recursive: true, force: true }) };
}

// Returned by `selfTest()` only after its verdict is printed. The dispatch
// refuses anything else: a `return` that leaves the function above that line
// prints nothing and still exits 0 — a self-test that never finished, reported
// as one that passed (#13798).
const SELF_TEST_VERDICT = 'check-skills-token-ratchet self-test reached its verdict';

function selfTest() {
const rel = 'skills/objectstack-ui/SKILL.md';
const over = verdict(rel, 26000, 25154).msg;
Expand Down Expand Up @@ -922,9 +928,19 @@ function selfTest() {
process.exit(1);
}
console.log(`✓ check-skills-token-ratchet self-test: ${cases.length} cases pass.`);

return SELF_TEST_VERDICT;
}

if (isEntrypoint(import.meta.url)) {
if (process.argv.includes('--self-test')) selfTest();
else run();
if (process.argv.includes('--self-test')) {
if (selfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-skills-token-ratchet 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 run();
}
9 changes: 8 additions & 1 deletion scripts/check-test-completeness.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,14 @@ function main() {
return;
}
// Every invocation, not a lint step -- see the header note on why.
selfTest({ quiet: true });
if (selfTest({ quiet: true }) !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-test-completeness self-test: selfTest() returned without reaching its verdict,\n'
+ 'so no success line was printed. Running the gate on top of a self-test\n'
+ 'that never finished would report an unverified gate as a verified one.\n',
);
process.exit(1);
}

const { verdict, paths } = invocationVerdict(argv);
if (verdict) reportVerdict(verdict);
Expand Down
17 changes: 16 additions & 1 deletion scripts/check-type-source-resolution.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,12 @@ function buildFixtureTree() {
return root;
}

// Returned by `selfTest()` only after its verdict is printed. The dispatch
// refuses anything else: a `return` that leaves the function above that line
// prints nothing and still exits 0 — a self-test that never finished, reported
// as one that passed (#13798).
const SELF_TEST_VERDICT = 'check-type-source-resolution self-test reached its verdict';

function selfTest() {
const root = buildFixtureTree();
const problems = [];
Expand Down Expand Up @@ -2062,13 +2068,22 @@ function selfTest() {
process.exit(1);
}
console.log('check-type-source-resolution --self-test OK');

return SELF_TEST_VERDICT;
}

// ── entry point ─────────────────────────────────────────────────────────────

const argv = process.argv.slice(2);
if (argv.includes('--self-test')) {
selfTest();
if (selfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-type-source-resolution 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 (argv.includes('--list')) {
printList(REPO_ROOT);
} else {
Expand Down
20 changes: 18 additions & 2 deletions scripts/check-workspace-manifest-cycles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,12 @@ export function selfTest() {
return failures;
}

// Returned by `runSelfTest()` only after its verdict is printed. The dispatch
// refuses anything else: a `return` that leaves the function above that line
// prints nothing and still exits 0 — a self-test that never finished, reported
// as one that passed (#13798).
const SELF_TEST_VERDICT = 'check-workspace-manifest-cycles self-test reached its verdict';

function runSelfTest() {
const failures = selfTest();
if (failures.length) {
Expand All @@ -705,10 +711,20 @@ function runSelfTest() {
process.exit(1);
}
console.log('OK: check-workspace-manifest-cycles --self-test — all cases passed.');

return SELF_TEST_VERDICT;
}

// Exports bindings, so an import for those exports alone must run nothing (#10667).
if (isEntrypoint(import.meta.url)) {
if (process.argv.includes('--self-test')) runSelfTest();
else main();
if (process.argv.includes('--self-test')) {
if (runSelfTest() !== SELF_TEST_VERDICT) {
console.error(
'\n✗ check-workspace-manifest-cycles self-test: runSelfTest() 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 main();
}
20 changes: 19 additions & 1 deletion scripts/checklist-select.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ function isBlocked(it) {
}

// ── self-test ────────────────────────────────────────────────────────────────
// Set by `selfTest()` only after its verdict is printed, and read at the
// dispatch: a `return` that leaves the function above that line prints nothing
// and still exits 0 — a self-test that never finished, reported as one that
// passed (#13798). The self-test's own exit code stays load-bearing, so the
// handshake is a flag rather than a returned sentinel.
let selfTestReachedVerdict = false;

function selfTest() {
const FIX = [
{ id: 'a.one', status: 'active', priority: 'P0', surface: 'browser', since: 'v16', source: ['packages/foo/bar.ts'] },
Expand Down Expand Up @@ -150,6 +157,7 @@ function selfTest() {
eq(ids('bar.ts'), ['a.one'], 'bare source basename (code ext) → file: mode');
eq(ids('missing.json'), [], 'unmatched .json name → empty, no throw');
console.log('✓ checklist-select self-test: 17 cases pass.');
selfTestReachedVerdict = true;
process.exit(0);
}

Expand Down Expand Up @@ -197,6 +205,16 @@ function main() {
// (the skill's front half is a pure resolver), the old top-level CLI printed a
// usage block to the importer's stderr and killed it with exit 2 mid-import.
if (isEntrypoint(import.meta.url)) {
if (process.argv.includes('--self-test')) selfTest();
if (process.argv.includes('--self-test')) {
selfTest();
if (!selfTestReachedVerdict) {
console.error(
'\n✗ checklist-select 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);
}
}
main();
}
Loading
Loading