6565 * ACCIDENT exit != 0 AND it printed nothing -- a non-zero exit with no
6666 * refusal behind it. NOT counted among HELD, ever.
6767 *
68+ * A FOURTH reading sits UNDER all three, and it is a PRECONDITION rather than a
69+ * verdict: if the UNMUTATED file already exits non-zero, this tree cannot run
70+ * it at all, so the mutation had nothing to defeat and NOTHING WAS MEASURED.
71+ * That case does not look like an absence -- both runs exit non-zero and both
72+ * print a module-resolution stack, so the mutated run "speaks" and the verdict
73+ * above reads HELD. A checkout that has not been `pnpm install`ed therefore
74+ * reports the FLATTERING answer for every file it cannot load, and the same row
75+ * reads ACCIDENT once the tree is installed (#15391).
76+ *
6877 * The `mutatedBytes` / `mutatedHead` fields the row already carried are what this
6978 * reads; `mutatedSpoke` publishes the reading. Deliberately the verdict does NOT
7079 * match the refusal WORDING: the repair landed in three spellings and teaching
@@ -104,6 +113,9 @@ const DISPATCH = /(?:includes|has)\(\s*['"`]--self-test['"`]\s*\)/;
104113/** Marker injected by the probe. Its presence on disk is the mutation's proof. */
105114const PROBE_MARKER = 'OS_SELF_TEST_FLOOR_PROBE' ;
106115
116+ /** "Did this run SAY anything": its first non-blank line, or '' if it said nothing. */
117+ const firstNonBlankLine = ( out ) => out . split ( '\n' ) . find ( ( l ) => l . trim ( ) ) ?? '' ;
118+
107119// ---------------------------------------------------------------------------
108120// Instrument 1 -- the static assertion-floor criterion
109121// ---------------------------------------------------------------------------
@@ -212,6 +224,15 @@ export function injectEarlyReturn(src, name) {
212224 * resolution still answer the same, and the marker is re-read FROM DISK before
213225 * the run: an editor step that matched nothing exits 0 just as happily as one
214226 * that landed, and an unmutated file would report "held" for no reason at all.
227+ *
228+ * THE BASELINE IS A PRECONDITION, NOT A DATA POINT. It is read BEFORE any
229+ * reading of the mutated run, and a non-zero one ends the probe as NOT MEASURED
230+ * with the reason -- see the fourth reading in this file's header. The mutated
231+ * run is then not spawned AT ALL: in the shape that motivates this (a checkout
232+ * whose dependencies are absent) every row is baseline-red, so running each
233+ * doomed mutation would double a whole sweep of spawns to learn nothing. The
234+ * row still publishes what the baseline said, because `baselineHead` is usually
235+ * the whole diagnosis (`Cannot find package ...` reads as "run pnpm install").
215236 */
216237export function probeEarlyReturn ( absFile , entry , { timeout = 120000 } = { } ) {
217238 const src = readFileSync ( absFile , 'utf8' ) ;
@@ -228,10 +249,28 @@ export function probeEarlyReturn(absFile, entry, { timeout = 120000 } = {}) {
228249 if ( onDisk !== 1 ) return { verdict : 'NOT MEASURED' , why : `mutation not on disk (marker x${ onDisk } )` } ;
229250
230251 const base = spawnSync ( cmd , [ absFile , '--self-test' ] , { cwd : ROOT , timeout, encoding : 'utf8' } ) ;
231- const mut = spawnSync ( cmd , [ probePath , '--self-test' ] , { cwd : ROOT , timeout, encoding : 'utf8' } ) ;
232252 const baseOut = ( base . stdout ?? '' ) + ( base . stderr ?? '' ) ;
253+ if ( base . signal ) return { verdict : 'NOT MEASURED' , why : `killed by ${ base . signal } ` } ;
254+ // PRECONDITION. A file the tree cannot run offered the mutation nothing to
255+ // defeat, so no verdict below is available -- however loudly the mutated run
256+ // would have exited and spoken. Read before the mutated run is spawned.
257+ if ( base . status !== 0 ) {
258+ return {
259+ verdict : 'NOT MEASURED' ,
260+ why :
261+ base . status === null
262+ ? `baseline run could not start (${ base . error ?. code ?? 'no exit status' } )`
263+ : `baseline run failed (exit ${ base . status } )` ,
264+ entry,
265+ baselineExit : base . status ,
266+ baselineBytes : baseOut . length ,
267+ baselineHead : firstNonBlankLine ( baseOut ) ,
268+ } ;
269+ }
270+
271+ const mut = spawnSync ( cmd , [ probePath , '--self-test' ] , { cwd : ROOT , timeout, encoding : 'utf8' } ) ;
233272 const mutOut = ( mut . stdout ?? '' ) + ( mut . stderr ?? '' ) ;
234- if ( mut . signal || base . signal ) return { verdict : 'NOT MEASURED' , why : `killed by ${ mut . signal ?? base . signal } ` } ;
273+ if ( mut . signal ) return { verdict : 'NOT MEASURED' , why : `killed by ${ mut . signal } ` } ;
235274 // A mutation that changed nothing observable did not reach the executed
236275 // path, whatever its exit code says.
237276 if ( baseOut === mutOut && base . status === mut . status ) {
@@ -240,7 +279,7 @@ export function probeEarlyReturn(absFile, entry, { timeout = 120000 } = {}) {
240279 // Did the mutated run SAY anything? Read as "printed a non-blank line", the
241280 // same reading `mutatedHead` already publishes and quotes -- a run whose whole
242281 // output is a newline has non-zero bytes and still refused nothing.
243- const mutatedHead = mutOut . split ( '\n' ) . find ( ( l ) => l . trim ( ) ) ?? '' ;
282+ const mutatedHead = firstNonBlankLine ( mutOut ) ;
244283 const mutatedSpoke = mutatedHead !== '' ;
245284 return {
246285 // Exit code alone cannot tell a refusal from an accident -- see the header.
@@ -323,6 +362,34 @@ const ACCIDENT_GATE = [
323362 '' ,
324363] . join ( '\n' ) ;
325364
365+ /**
366+ * The measured BASELINE-RED shape, reduced: a file this tree cannot run at all,
367+ * because one of its imports does not resolve. Its self-test is otherwise
368+ * perfectly ordinary and injectable -- the point is that neither run ever
369+ * reaches it. Both runs die in module resolution, both exit non-zero, and both
370+ * PRINT a stack trace, so `mutatedSpoke` is true and an exit-code-and-speech
371+ * verdict scores it HELD: a hold awarded for the tree being broken. This is the
372+ * shape `scripts/audits/14744-before-update-per-row-value-census.mjs` takes in a
373+ * checkout whose `node_modules` lacks its `typescript` dependency, where it read
374+ * HELD while reading ACCIDENT in an installed one (#15391).
375+ *
376+ * The unresolvable import is a name no registry can supply, and it is the
377+ * FIRST statement, so the failure is the module loader's and cannot be confused
378+ * with anything the self-test did.
379+ */
380+ const UNRUNNABLE_GATE = [
381+ '#!/usr/bin/env node' ,
382+ "import 'os-self-test-floor-control-no-such-package';" ,
383+ 'function selfTest() {' ,
384+ ' const failures = [];' ,
385+ " if (1 !== 1) failures.push('x');" ,
386+ " if (failures.length) { console.error('nope'); process.exit(1); }" ,
387+ " console.log('fixture self-test: 1 case passes');" ,
388+ '}' ,
389+ "if (process.argv.includes('--self-test')) selfTest();" ,
390+ '' ,
391+ ] . join ( '\n' ) ;
392+
326393/**
327394 * The ternary exit, reduced: a roster floor whose ONLY failure production is
328395 * `process.exit(<cond> ? 0 : 1)`. It carries none of the NAMED spellings -- no
@@ -404,12 +471,15 @@ export function runControls() {
404471 const holed = join ( dir , 'holed-gate.mjs' ) ;
405472 const sound = join ( dir , 'sound-gate.mjs' ) ;
406473 const accident = join ( dir , 'accident-gate.mjs' ) ;
474+ const unrunnable = join ( dir , 'unrunnable-gate.mjs' ) ;
407475 writeFileSync ( holed , HOLED_GATE ) ;
408476 writeFileSync ( sound , SOUND_GATE ) ;
409477 writeFileSync ( accident , ACCIDENT_GATE ) ;
478+ writeFileSync ( unrunnable , UNRUNNABLE_GATE ) ;
410479 const h = probeEarlyReturn ( holed , 'selfTest' ) ;
411480 const s = probeEarlyReturn ( sound , 'selfTest' ) ;
412481 const a = probeEarlyReturn ( accident , 'runSelfTest' ) ;
482+ const u = probeEarlyReturn ( unrunnable , 'selfTest' ) ;
413483 say ( h . verdict === 'DEFEATED' ,
414484 `POSITIVE CONTROL FAILED: the probe read a known-holed gate as ${ h . verdict } (${ h . why ?? '' } )` ) ;
415485 say ( h . mutatedBytes === 0 ,
@@ -425,6 +495,16 @@ export function runControls() {
425495 `POSITIVE CONTROL FAILED: the probe read a silent non-zero exit as ${ a . verdict } (${ a . why ?? '' } ) -- an exit code is not a handshake` ) ;
426496 say ( a . mutatedExit !== 0 && a . mutatedBytes === 0 && a . mutatedSpoke === false ,
427497 `POSITIVE CONTROL FAILED: the accident fixture no longer produces the measured shape (exit ${ a . mutatedExit } , ${ a . mutatedBytes } byte(s)); the ACCIDENT verdict above would then be passing for the wrong reason` ) ;
498+ // The precondition, in the direction that matters: a baseline that already
499+ // failed ends the probe, and it must end it as NOT MEASURED -- never as the
500+ // HELD an exit-code-and-speech reading would award it (#15391).
501+ say ( u . verdict === 'NOT MEASURED' && / ^ b a s e l i n e r u n f a i l e d \( e x i t / . test ( u . why ?? '' ) ,
502+ `POSITIVE CONTROL FAILED: a file whose BASELINE run already exits non-zero was read as ${ u . verdict } (${ u . why ?? '' } ); the mutation had nothing to defeat, so nothing was measured` ) ;
503+ // ... and for the RIGHT reason: this fixture has to be the flattering shape,
504+ // a red baseline that SPEAKS. A fixture that fell silent, or that stopped
505+ // being red, would satisfy the verdict above while testing nothing.
506+ say ( u . baselineExit !== 0 && u . baselineBytes > 0 && u . baselineHead !== '' ,
507+ `POSITIVE CONTROL FAILED: the unrunnable fixture no longer produces the measured shape (baseline exit ${ u . baselineExit } , ${ u . baselineBytes } byte(s)); the NOT MEASURED verdict above would then be passing for the wrong reason` ) ;
428508 } finally {
429509 rmSync ( dir , { recursive : true , force : true } ) ;
430510 }
0 commit comments