Skip to content

Commit 6ca9f93

Browse files
claude[bot]claude
andauthored
fix(scripts): measure-self-test-floor recognises a ternary process.exit as a failure producer (#15339) (#15413)
* fix(scripts): measure-self-test-floor recognises a ternary process.exit as a failure producer `classifyFloor` awards ROSTER only when a roster comparison AND a failure production both match. The failure half read five literal spellings, none of which covers `process.exit(cond ? 0 : 1)` -- so a file could carry a complete, working roster floor and still classify NONE. Measured on `scripts/check-regen-pending.mjs` before its verdict was respelled as an explicit branch; the cost is prospective, since nothing on main mis-classifies today. The criterion is now published in two halves, NAMED and TERNARY, so each can be controlled on its own. TERNARY reads a ternary exit whose failing arm is a non-zero literal, in both orders. A bare `process.exit(<expr>)` stays unrecognised on purpose: over a self-test that returned early it is `process.exit(undefined)` -- exit 0, the accident shape this instrument exists to tell apart from a handshake. Six inline controls come with it, run on every invocation like the rest: a roster floor whose only failure production is the ternary reads ROSTER (with a setup control proving the fixture carries no other recognised spelling), the non-1 and inverted arms read ROSTER, a bare opaque exit reads NONE, the same fixture with the roster renamed reads NONE, and the recognised shape is pinned to the dispatch line the ACCIDENT fixture already carries. Census tallies are unchanged: 170 rows, 163 ROSTER / 6 NONE / 1 COUNT, with `--json` byte-identical before and after. No key renamed; additive. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk * docs(scripts): correct a direction word in the ternary-exit control comment The ACCIDENT fixture is defined above `runControls`, not below it. 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 d786b16 commit 6ca9f93

1 file changed

Lines changed: 85 additions & 1 deletion

File tree

scripts/measure-self-test-floor.mjs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,32 @@ const PROBE_MARKER = 'OS_SELF_TEST_FLOOR_PROBE';
111111
/**
112112
* A FLOOR must PRODUCE A FAILURE, not merely be named. Keying on a name is the
113113
* mistake this card is about one level up, and the control below proves it.
114+
*
115+
* The criterion is published in two halves so a control can test each on its own:
116+
*
117+
* NAMED the failure is produced by a spelling that names itself --
118+
* `failures.push`, a literal `process.exit(1)`, `throw new Error`,
119+
* `exitCode = 1`, `ok(false`.
120+
* TERNARY the EXIT CODE is the verdict: `process.exit(<cond> ? 0 : 1)`, the
121+
* same with any non-zero failing arm (`? 0 : N`), and the inverted
122+
* `process.exit(<cond> ? 1 : 0)`. A failing arm that is a non-zero
123+
* LITERAL produces a failure exactly as `process.exit(1)` does; it is
124+
* an ordinary spelling, and reading only the literal form called a
125+
* complete, working roster floor NONE (#15339) -- the roster half
126+
* matched, the file carried no other recognised spelling, and the
127+
* failure half missed.
128+
*
129+
* BOUNDARY -- a bare `process.exit(<expr>)` is deliberately NOT a failure
130+
* producer, however likely the expression is to be non-zero. That is the
131+
* accident shape this file's header is about: over a self-test that returned
132+
* early, `process.exit(runSelfTest())` is `process.exit(undefined)` -- exit 0,
133+
* nothing produced, nothing noticed. Source text cannot tell what an opaque
134+
* expression yields; a non-zero literal in the failing arm it can.
114135
*/
115-
const PRODUCES_FAILURE = /failures\.push|process\.exit\(1\)|throw new Error|exitCode = 1|ok\(false/;
136+
const PRODUCES_FAILURE_NAMED = /failures\.push|process\.exit\(1\)|throw new Error|exitCode = 1|ok\(false/;
137+
const PRODUCES_FAILURE_TERNARY_EXIT =
138+
/process\.exit\(\s*[^;]{0,200}?\?\s*(?:0\s*:\s*[1-9]\d*|[1-9]\d*\s*:\s*0)\s*\)/;
139+
const PRODUCES_FAILURE = new RegExp(`${PRODUCES_FAILURE_NAMED.source}|${PRODUCES_FAILURE_TERNARY_EXIT.source}`);
116140
const ROSTER_COMPARISON =
117141
/(?:declaredBatteries|SELF_TEST_BATTERIES|BATTERY_FLOOR)[^;]{0,400}?(?:\.length|\.size|includes\(|has\(|!==|===|<)/s;
118142
const COUNT_COMPARISON = [
@@ -299,6 +323,41 @@ const ACCIDENT_GATE = [
299323
'',
300324
].join('\n');
301325

326+
/**
327+
* The ternary exit, reduced: a roster floor whose ONLY failure production is
328+
* `process.exit(<cond> ? 0 : 1)`. It carries none of the NAMED spellings -- no
329+
* `failures.push`, no literal `process.exit(1)`, no `throw`, no `exitCode = 1`,
330+
* no `ok(false` -- and a control asserts that, so reading it ROSTER can only be
331+
* the ternary being recognised and never some other half of the criterion
332+
* sneaking in. This is the shape `scripts/check-regen-pending.mjs` carried while
333+
* its floor was sound, its ablations all fired, and this instrument still said
334+
* NONE (#15339).
335+
*
336+
* The classifier is a pure function of source text, so unlike the three fixtures
337+
* above this one is never spawned -- it is read, not run.
338+
*/
339+
const TERNARY_EXIT_GATE = [
340+
'#!/usr/bin/env node',
341+
'const SELF_TEST_BATTERIES = Object.freeze({ only: 1 });',
342+
'function runSelfTest() {',
343+
' const seen = new Map();',
344+
' const battery = (n) => seen.set(n, (seen.get(n) ?? 0) + 1);',
345+
" battery('only');",
346+
' const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);',
347+
' let held = declaredBatteries.length >= 1;',
348+
' for (const n of declaredBatteries) if ((seen.get(n) ?? 0) < SELF_TEST_BATTERIES[n]) held = false;',
349+
" console.log('fixture self-test: floor ' + (held ? 'held' : 'shrank'));",
350+
' return held;',
351+
'}',
352+
"if (process.argv.includes('--self-test')) {",
353+
' process.exit(runSelfTest() ? 0 : 1);',
354+
'}',
355+
'',
356+
].join('\n');
357+
358+
/** The one dispatch line of `TERNARY_EXIT_GATE`, the anchor the variants replace. */
359+
const TERNARY_EXIT_DISPATCH = 'runSelfTest() ? 0 : 1';
360+
302361
/**
303362
* Both instruments, against both directions. Returns the failures; the caller
304363
* refuses on any. Nothing here reads the repo, so a control failure is always
@@ -314,6 +373,31 @@ export function runControls() {
314373
say(classifyFloor(maskComments(SOUND_GATE)) === 'ROSTER',
315374
'NEGATIVE CONTROL FAILED: a roster-floored self-test was not classified ROSTER');
316375

376+
// The ternary exit, both directions. The failing arm being a NON-ZERO LITERAL
377+
// is what produces the failure; the roster half still has to match on its own,
378+
// and an opaque expression still has to produce nothing.
379+
const ternaryExit = (dispatchArgs) =>
380+
maskComments(TERNARY_EXIT_GATE.replace(TERNARY_EXIT_DISPATCH, dispatchArgs));
381+
say(!PRODUCES_FAILURE_NAMED.test(maskComments(TERNARY_EXIT_GATE)),
382+
'CONTROL FIXTURE INVALID: the ternary fixture picked up one of the NAMED failure spellings; every verdict below it would then be passing for the wrong reason');
383+
say(classifyFloor(maskComments(TERNARY_EXIT_GATE)) === 'ROSTER',
384+
'NEGATIVE CONTROL FAILED: a roster floor whose only failure production is `process.exit(<cond> ? 0 : 1)` was not classified ROSTER');
385+
say(classifyFloor(ternaryExit('runSelfTest() ? 0 : 2')) === 'ROSTER',
386+
'NEGATIVE CONTROL FAILED: a ternary exit whose failing arm is a non-zero literal other than 1 was not classified ROSTER');
387+
say(classifyFloor(ternaryExit('!runSelfTest() ? 1 : 0')) === 'ROSTER',
388+
'NEGATIVE CONTROL FAILED: the inverted ternary exit (`? 1 : 0`, the failing arm first) was not classified ROSTER');
389+
say(classifyFloor(ternaryExit('runSelfTest()')) === 'NONE',
390+
'POSITIVE CONTROL FAILED: a bare `process.exit(<expr>)` was read as producing a failure -- an opaque expression is the accident shape (it is `process.exit(undefined)` after an early return), not a floor');
391+
say(classifyFloor(maskComments(TERNARY_EXIT_GATE
392+
.replaceAll('SELF_TEST_BATTERIES', 'FIXTURE_BATTERIES_BY_NAME')
393+
.replaceAll('declaredBatteries', 'declaredNames'))) === 'NONE',
394+
'POSITIVE CONTROL FAILED: the ternary exit alone was classified ROSTER -- producing a failure is HALF the criterion; a roster this criterion can NAME is the other half');
395+
// Reuse rather than invention: the shape now recognised is the one the ACCIDENT
396+
// fixture above actually dispatches with (and the audit file it is reduced
397+
// from), so the criterion stays pinned to a spelling measured in this tree.
398+
say(PRODUCES_FAILURE_TERNARY_EXIT.test(ACCIDENT_GATE.split('\n').find((l) => l.includes('process.exit(')) ?? ''),
399+
'CONTROL FAILED: the accident fixture no longer dispatches with the ternary exit this criterion was extended to read; the two have drifted apart');
400+
317401
// Instrument 2, both directions, against real processes on disk.
318402
const dir = mkdtempSync(join(tmpdir(), 'self-test-floor-control-'));
319403
try {

0 commit comments

Comments
 (0)