From 85d66753226c2185869c50c757adbb2f0e2b36da Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 10:55:23 +0000 Subject: [PATCH 1/2] test(lint): start the lazy-deps dist probes together, beside the in-process import Each of the two boot-path contract files spawned its built-entry probes one after another (two in lazy-deps, three in runtime-lazy-deps), then cold-imported the source entry in-process. The children are independent processes, so they now start together in beforeAll and each case awaits its own child's stdout, after the in-process cases, which overlap them. Every assertion and child script is unchanged. Claude-Session: https://claude.ai/code/session_01PaFAtTG4vuRcRwFYo3F5z5 Co-authored-by: Claude --- packages/lint/src/lazy-deps.test.ts | 72 ++++++---- packages/lint/src/runtime-lazy-deps.test.ts | 146 +++++++++++--------- 2 files changed, 128 insertions(+), 90 deletions(-) diff --git a/packages/lint/src/lazy-deps.test.ts b/packages/lint/src/lazy-deps.test.ts index fb1dec22892..0d544cdb280 100644 --- a/packages/lint/src/lazy-deps.test.ts +++ b/packages/lint/src/lazy-deps.test.ts @@ -41,12 +41,13 @@ // has not been built); // 3. behavioral — each lazy path really loads its dep on demand and the // gate still produces findings. -import { execFileSync } from 'node:child_process'; +import { execFile } from 'node:child_process'; import { existsSync, readFileSync, readdirSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import { describe, it, expect } from 'vitest'; +import { promisify } from 'node:util'; +import { afterAll, beforeAll, describe, it, expect } from 'vitest'; const srcDir = dirname(fileURLToPath(import.meta.url)); const distDir = join(srcDir, '..', 'dist'); @@ -64,6 +65,20 @@ const depLoaded = (cache: Record | undefined, dep: string) => // default 5s timeout flakes while the assertion set is pure contract, not latency. const COLD_LOAD_TIMEOUT_MS = 30_000; +// The two dist probes are independent child processes, so they start together +// in `beforeAll` and each case awaits its own child's output: the children +// cold-load in parallel with each other and with the in-process case, which +// runs first. Run one after another, they were most of this file's wall time, +// and nothing they assert depends on it. A child that exits non-zero rejects +// with its stderr, as `execFileSync` threw it. The no-op `catch` only marks +// the promise as handled until its case awaits it. +const execFileAsync = promisify(execFile); +const spawnProbe = (args: string[]) => { + const stdout = execFileAsync(process.execPath, args, { encoding: 'utf8' }).then((r) => r.stdout); + stdout.catch(() => {}); + return stdout; +}; + describe('lazy dependency loading (kernel boot-path contract)', () => { it('no src file eagerly imports a lazy dep (import type only)', () => { // Static `import`/`export ... from ''` executes at module init in @@ -153,30 +168,27 @@ describe('lazy dependency loading (kernel boot-path contract)', () => { }; `; - it.skipIf(!existsSync(join(distDir, 'index.cjs')))('built CJS dist loads no lazy dep at import time, and each only on its own trigger', () => { - const out = execFileSync( - process.execPath, - ['-e', `${childBody}; check(require(${JSON.stringify(join(distDir, 'index.cjs'))}));`], - { encoding: 'utf8' }, - ); - expect(out).toContain('OK'); - }, COLD_LOAD_TIMEOUT_MS); - - it.skipIf(!existsSync(join(distDir, 'index.js')))('built ESM dist loads no lazy dep at import time, and each only on its own trigger', () => { - const out = execFileSync( - process.execPath, - [ - '--input-type=module', - '-e', - `import { createRequire } from 'node:module'; - const require = createRequire(process.cwd() + '/probe.js'); - ${childBody}; - check(await import(${JSON.stringify(pathToFileURL(join(distDir, 'index.js')).href)}));`, - ], - { encoding: 'utf8' }, - ); - expect(out).toContain('OK'); - }, COLD_LOAD_TIMEOUT_MS); + const probes = new Map<'cjs' | 'esm', Promise>(); + beforeAll(() => { + if (existsSync(join(distDir, 'index.cjs'))) { + probes.set('cjs', spawnProbe(['-e', `${childBody}; check(require(${JSON.stringify(join(distDir, 'index.cjs'))}));`])); + } + if (existsSync(join(distDir, 'index.js'))) { + probes.set( + 'esm', + spawnProbe([ + '--input-type=module', + '-e', + `import { createRequire } from 'node:module'; + const require = createRequire(process.cwd() + '/probe.js'); + ${childBody}; + check(await import(${JSON.stringify(pathToFileURL(join(distDir, 'index.js')).href)}));`, + ]), + ); + } + }); + // No child outlives the file, including one whose case a `-t` filter skipped. + afterAll(() => Promise.allSettled(probes.values())); it('loads each dep lazily in-process and the gates still work', async () => { const req = createRequire(import.meta.url); @@ -301,4 +313,12 @@ describe('lazy dependency loading (kernel boot-path contract)', () => { expect(depLoaded(req.cache, 'typescript')).toBe(true); expect(props.some((f) => f.rule === 'react-prop-missing-required' && /objectName/.test(f.message))).toBe(true); }, COLD_LOAD_TIMEOUT_MS); + + it.skipIf(!existsSync(join(distDir, 'index.cjs')))('built CJS dist loads no lazy dep at import time, and each only on its own trigger', async () => { + expect(await probes.get('cjs')).toContain('OK'); + }, COLD_LOAD_TIMEOUT_MS); + + it.skipIf(!existsSync(join(distDir, 'index.js')))('built ESM dist loads no lazy dep at import time, and each only on its own trigger', async () => { + expect(await probes.get('esm')).toContain('OK'); + }, COLD_LOAD_TIMEOUT_MS); }); diff --git a/packages/lint/src/runtime-lazy-deps.test.ts b/packages/lint/src/runtime-lazy-deps.test.ts index 2cbe3abb70c..df45e935351 100644 --- a/packages/lint/src/runtime-lazy-deps.test.ts +++ b/packages/lint/src/runtime-lazy-deps.test.ts @@ -44,12 +44,13 @@ // adjudication accepted is now REQUIRED rather than forbidden (tier 3), so it // can neither regress into an eager import nor quietly stop meaning anything. -import { execFileSync } from 'node:child_process'; +import { execFile } from 'node:child_process'; import { existsSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import { describe, it, expect } from 'vitest'; +import { promisify } from 'node:util'; +import { afterAll, beforeAll, describe, it, expect } from 'vitest'; const srcDir = dirname(fileURLToPath(import.meta.url)); const distDir = join(srcDir, '..', 'dist'); @@ -234,45 +235,79 @@ const childBody = ` }; `; +// The positive control's child (the last spawned case below says why it is +// spawned and what it proves); it is a const only so `beforeAll` can start it. +const controlBody = `${probeHelpers} + const rt = require(${JSON.stringify(runtimeCjs)}); + const full = require(${JSON.stringify(indexCjs)}); + for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { + if (loaded(dep)) fail(dep + ' was loaded merely by importing the full @objectstack/lint barrel'); + } + const snapshots = rt.buildRuntimeWriteSnapshots({ + type: 'object', + item: ${JSON.stringify(SCHEMA_OBJECT)}, + context: { objects: ${JSON.stringify(OBJECT_CONTEXT)} }, + }); + if (!snapshots) fail('the gate builds no snapshot for an object write — the tier legs above are then untested'); + const findings = full.validateRuleCompilability(snapshots.candidate); + if (!findings.some((f) => f.rule === 'validation-rule-json-schema-uncompilable')) { + fail('SCHEMA_OBJECT no longer carries a schema ajv refuses to compile, so the tier-2 legs ' + + 'have degraded into "a body with nothing in it loads nothing" and tier 3 refuses ' + + 'nothing. Restore a schema ajv rejects rather than deleting this test.'); + } + for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { + if (!loaded(dep)) { + fail(dep + ' did NOT load even though the compilability rule just compiled this body. The ' + + 'require-cache probe cannot see the load it exists to catch, so every clean result in ' + + 'this file is vacuous — fix the probe, do not trust the greens.'); + } + } + console.log('OK');`; + // Cold-loading a dist entry in a spawned node on a busy runner takes seconds. const COLD_LOAD_TIMEOUT_MS = 30_000; -describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { - it.skipIf(!existsSync(runtimeCjs))( - 'built CJS runtime entry honours the three-tier dep contract, at import AND while gating', - () => { - const out = execFileSync( - process.execPath, - ['-e', `${childBody}; check(require(${JSON.stringify(runtimeCjs)}));`], - { encoding: 'utf8' }, - ); - expect(out).toContain('OK'); - }, - COLD_LOAD_TIMEOUT_MS, - ); +// The three spawned cases are independent child processes, so they start +// together in `beforeAll` and each case awaits its own child's output: the +// children cold-load in parallel with each other and with the in-process +// cases, which run first. Run one after another, they were most of this file's +// wall time, and nothing they assert depends on it. A child that exits +// non-zero rejects with its stderr, as `execFileSync` threw it. The no-op +// `catch` only marks the promise as handled until its case awaits it. +const execFileAsync = promisify(execFile); +const spawnProbe = (args: string[]) => { + const stdout = execFileAsync(process.execPath, args, { encoding: 'utf8' }).then((r) => r.stdout); + stdout.catch(() => {}); + return stdout; +}; - it.skipIf(!existsSync(runtimeEsm))( - 'built ESM runtime entry honours the three-tier dep contract, at import AND while gating', - () => { - const out = execFileSync( - process.execPath, - [ +describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { + const probes = new Map<'cjs' | 'esm' | 'control', Promise>(); + beforeAll(() => { + if (existsSync(runtimeCjs)) { + probes.set('cjs', spawnProbe(['-e', `${childBody}; check(require(${JSON.stringify(runtimeCjs)}));`])); + } + if (existsSync(runtimeEsm)) { + probes.set( + 'esm', + spawnProbe([ '--input-type=module', '-e', `import { createRequire } from 'node:module'; const require = createRequire(process.cwd() + '/probe.js'); ${childBody}; check(await import(${JSON.stringify(pathToFileURL(runtimeEsm).href)}));`, - ], - { encoding: 'utf8' }, + ]), ); - expect(out).toContain('OK'); - }, - COLD_LOAD_TIMEOUT_MS, - ); + } + if (existsSync(indexCjs) && existsSync(runtimeCjs)) probes.set('control', spawnProbe(['-e', controlBody])); + }); + // No child outlives the file, including one whose case a `-t` filter skipped. + afterAll(() => Promise.allSettled(probes.values())); - // Same cold load as the two above, just in-process: the first `import()` of - // `./runtime.js` in this file pulls the whole gate graph through the transform. + // Same cold load as the two built-entry cases below, just in-process: the + // first `import()` of `./runtime.js` in this file pulls the whole gate graph + // through the transform. // So it gets the same budget — the claim being pinned (the boot path loads no // source parser) is proved by the assertions below, never by the wall clock, // and a 5 s default only turns a busy runner into a false red on the gate. @@ -336,6 +371,22 @@ describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { } }, COLD_LOAD_TIMEOUT_MS); + it.skipIf(!existsSync(runtimeCjs))( + 'built CJS runtime entry honours the three-tier dep contract, at import AND while gating', + async () => { + expect(await probes.get('cjs')).toContain('OK'); + }, + COLD_LOAD_TIMEOUT_MS, + ); + + it.skipIf(!existsSync(runtimeEsm))( + 'built ESM runtime entry honours the three-tier dep contract, at import AND while gating', + async () => { + expect(await probes.get('esm')).toContain('OK'); + }, + COLD_LOAD_TIMEOUT_MS, + ); + // #4716 — the probe's own non-vacuity, and the diagnostic that splits the // two ways tier 3 can fail. // @@ -354,41 +405,8 @@ describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { // very one the in-process leg reads. it.skipIf(!existsSync(indexCjs) || !existsSync(runtimeCjs))( 'the schema-carrying body DOES load ajv when the compilability rule judges it directly (probe is not vacuous)', - () => { - const out = execFileSync( - process.execPath, - [ - '-e', - `${probeHelpers} - const rt = require(${JSON.stringify(runtimeCjs)}); - const full = require(${JSON.stringify(indexCjs)}); - for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { - if (loaded(dep)) fail(dep + ' was loaded merely by importing the full @objectstack/lint barrel'); - } - const snapshots = rt.buildRuntimeWriteSnapshots({ - type: 'object', - item: ${JSON.stringify(SCHEMA_OBJECT)}, - context: { objects: ${JSON.stringify(OBJECT_CONTEXT)} }, - }); - if (!snapshots) fail('the gate builds no snapshot for an object write — the tier legs above are then untested'); - const findings = full.validateRuleCompilability(snapshots.candidate); - if (!findings.some((f) => f.rule === 'validation-rule-json-schema-uncompilable')) { - fail('SCHEMA_OBJECT no longer carries a schema ajv refuses to compile, so the tier-2 legs ' + - 'have degraded into "a body with nothing in it loads nothing" and tier 3 refuses ' + - 'nothing. Restore a schema ajv rejects rather than deleting this test.'); - } - for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { - if (!loaded(dep)) { - fail(dep + ' did NOT load even though the compilability rule just compiled this body. The ' + - 'require-cache probe cannot see the load it exists to catch, so every clean result in ' + - 'this file is vacuous — fix the probe, do not trust the greens.'); - } - } - console.log('OK');`, - ], - { encoding: 'utf8' }, - ); - expect(out).toContain('OK'); + async () => { + expect(await probes.get('control')).toContain('OK'); }, COLD_LOAD_TIMEOUT_MS, ); From aed22b27f5c981ebd6c1d015184a91d6c5aaefa7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 11:01:52 +0000 Subject: [PATCH 2/2] test(lint): the two lazy-deps contract files declare their whole-package-import budget Takes back the previous commit's concurrent dist probes. Measured in the package's own run, starting the probes together moved each file's in-process import ahead of every other worker's, so each file then paid the cold transform of @objectstack/spec's inlined dist bundles itself: lazy-deps went from 5.2 s to 12.7-14.2 s and runtime-lazy-deps from 5.3 s to 12.3-13.9 s. Both files are back to their base bodies. Both files cold-import a @objectstack/lint entry in-process (the barrel in lazy-deps, the runtime entry in runtime-lazy-deps) to prove what that import and each gate load. Run alone, that one import is 11.8-14.5 s of each file's wall: vitest inlines and transforms every @objectstack/spec dist bundle the graph reaches, while a native import of the built entry takes 0.86 s. The spawned built-dist probes are one node per format and are needed. So each file takes ruling 6181973363's "whole-package import" line: the card's slowest CI reading (18.0 s and 19.2 s) x 1.25, rounded up to 30 s. Claude-Session: https://claude.ai/code/session_01PaFAtTG4vuRcRwFYo3F5z5 Co-authored-by: Claude --- packages/lint/src/lazy-deps.test.ts | 73 ++++------ packages/lint/src/runtime-lazy-deps.test.ts | 147 +++++++++----------- 2 files changed, 92 insertions(+), 128 deletions(-) diff --git a/packages/lint/src/lazy-deps.test.ts b/packages/lint/src/lazy-deps.test.ts index 0d544cdb280..d209cddc776 100644 --- a/packages/lint/src/lazy-deps.test.ts +++ b/packages/lint/src/lazy-deps.test.ts @@ -1,4 +1,5 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// test-budget: 30 — whole-package import: the in-process case imports the whole @objectstack/lint barrel cold, so the file's floor is vitest's transform of that graph and of every inlined @objectstack/spec dist bundle it reaches, and the two built-dist probes each spawn node once; slowest CI reading 18.0 s (hourly run 38267594064) x 1.25, rounded up // // Boot-path contract: importing @objectstack/lint must NOT load its heavy, // gate-only dependencies. The package sits on the kernel boot path, while each @@ -41,13 +42,12 @@ // has not been built); // 3. behavioral — each lazy path really loads its dep on demand and the // gate still produces findings. -import { execFile } from 'node:child_process'; +import { execFileSync } from 'node:child_process'; import { existsSync, readFileSync, readdirSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import { promisify } from 'node:util'; -import { afterAll, beforeAll, describe, it, expect } from 'vitest'; +import { describe, it, expect } from 'vitest'; const srcDir = dirname(fileURLToPath(import.meta.url)); const distDir = join(srcDir, '..', 'dist'); @@ -65,20 +65,6 @@ const depLoaded = (cache: Record | undefined, dep: string) => // default 5s timeout flakes while the assertion set is pure contract, not latency. const COLD_LOAD_TIMEOUT_MS = 30_000; -// The two dist probes are independent child processes, so they start together -// in `beforeAll` and each case awaits its own child's output: the children -// cold-load in parallel with each other and with the in-process case, which -// runs first. Run one after another, they were most of this file's wall time, -// and nothing they assert depends on it. A child that exits non-zero rejects -// with its stderr, as `execFileSync` threw it. The no-op `catch` only marks -// the promise as handled until its case awaits it. -const execFileAsync = promisify(execFile); -const spawnProbe = (args: string[]) => { - const stdout = execFileAsync(process.execPath, args, { encoding: 'utf8' }).then((r) => r.stdout); - stdout.catch(() => {}); - return stdout; -}; - describe('lazy dependency loading (kernel boot-path contract)', () => { it('no src file eagerly imports a lazy dep (import type only)', () => { // Static `import`/`export ... from ''` executes at module init in @@ -168,27 +154,30 @@ describe('lazy dependency loading (kernel boot-path contract)', () => { }; `; - const probes = new Map<'cjs' | 'esm', Promise>(); - beforeAll(() => { - if (existsSync(join(distDir, 'index.cjs'))) { - probes.set('cjs', spawnProbe(['-e', `${childBody}; check(require(${JSON.stringify(join(distDir, 'index.cjs'))}));`])); - } - if (existsSync(join(distDir, 'index.js'))) { - probes.set( - 'esm', - spawnProbe([ - '--input-type=module', - '-e', - `import { createRequire } from 'node:module'; - const require = createRequire(process.cwd() + '/probe.js'); - ${childBody}; - check(await import(${JSON.stringify(pathToFileURL(join(distDir, 'index.js')).href)}));`, - ]), - ); - } - }); - // No child outlives the file, including one whose case a `-t` filter skipped. - afterAll(() => Promise.allSettled(probes.values())); + it.skipIf(!existsSync(join(distDir, 'index.cjs')))('built CJS dist loads no lazy dep at import time, and each only on its own trigger', () => { + const out = execFileSync( + process.execPath, + ['-e', `${childBody}; check(require(${JSON.stringify(join(distDir, 'index.cjs'))}));`], + { encoding: 'utf8' }, + ); + expect(out).toContain('OK'); + }, COLD_LOAD_TIMEOUT_MS); + + it.skipIf(!existsSync(join(distDir, 'index.js')))('built ESM dist loads no lazy dep at import time, and each only on its own trigger', () => { + const out = execFileSync( + process.execPath, + [ + '--input-type=module', + '-e', + `import { createRequire } from 'node:module'; + const require = createRequire(process.cwd() + '/probe.js'); + ${childBody}; + check(await import(${JSON.stringify(pathToFileURL(join(distDir, 'index.js')).href)}));`, + ], + { encoding: 'utf8' }, + ); + expect(out).toContain('OK'); + }, COLD_LOAD_TIMEOUT_MS); it('loads each dep lazily in-process and the gates still work', async () => { const req = createRequire(import.meta.url); @@ -313,12 +302,4 @@ describe('lazy dependency loading (kernel boot-path contract)', () => { expect(depLoaded(req.cache, 'typescript')).toBe(true); expect(props.some((f) => f.rule === 'react-prop-missing-required' && /objectName/.test(f.message))).toBe(true); }, COLD_LOAD_TIMEOUT_MS); - - it.skipIf(!existsSync(join(distDir, 'index.cjs')))('built CJS dist loads no lazy dep at import time, and each only on its own trigger', async () => { - expect(await probes.get('cjs')).toContain('OK'); - }, COLD_LOAD_TIMEOUT_MS); - - it.skipIf(!existsSync(join(distDir, 'index.js')))('built ESM dist loads no lazy dep at import time, and each only on its own trigger', async () => { - expect(await probes.get('esm')).toContain('OK'); - }, COLD_LOAD_TIMEOUT_MS); }); diff --git a/packages/lint/src/runtime-lazy-deps.test.ts b/packages/lint/src/runtime-lazy-deps.test.ts index df45e935351..d8e06f055a4 100644 --- a/packages/lint/src/runtime-lazy-deps.test.ts +++ b/packages/lint/src/runtime-lazy-deps.test.ts @@ -1,4 +1,5 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// test-budget: 30 — whole-package import: the in-process cases import the @objectstack/lint/runtime entry cold, which reaches the whole gate graph, so the file's floor is vitest's transform of it and of every inlined @objectstack/spec dist bundle it reaches, and the three built-dist probes each spawn node once; slowest CI reading 19.2 s (hourly run 38314183560) x 1.25, rounded up // // The kernel boot-path contract for `@objectstack/lint/runtime` (#4463). // @@ -44,13 +45,12 @@ // adjudication accepted is now REQUIRED rather than forbidden (tier 3), so it // can neither regress into an eager import nor quietly stop meaning anything. -import { execFile } from 'node:child_process'; +import { execFileSync } from 'node:child_process'; import { existsSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import { promisify } from 'node:util'; -import { afterAll, beforeAll, describe, it, expect } from 'vitest'; +import { describe, it, expect } from 'vitest'; const srcDir = dirname(fileURLToPath(import.meta.url)); const distDir = join(srcDir, '..', 'dist'); @@ -235,79 +235,45 @@ const childBody = ` }; `; -// The positive control's child (the last spawned case below says why it is -// spawned and what it proves); it is a const only so `beforeAll` can start it. -const controlBody = `${probeHelpers} - const rt = require(${JSON.stringify(runtimeCjs)}); - const full = require(${JSON.stringify(indexCjs)}); - for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { - if (loaded(dep)) fail(dep + ' was loaded merely by importing the full @objectstack/lint barrel'); - } - const snapshots = rt.buildRuntimeWriteSnapshots({ - type: 'object', - item: ${JSON.stringify(SCHEMA_OBJECT)}, - context: { objects: ${JSON.stringify(OBJECT_CONTEXT)} }, - }); - if (!snapshots) fail('the gate builds no snapshot for an object write — the tier legs above are then untested'); - const findings = full.validateRuleCompilability(snapshots.candidate); - if (!findings.some((f) => f.rule === 'validation-rule-json-schema-uncompilable')) { - fail('SCHEMA_OBJECT no longer carries a schema ajv refuses to compile, so the tier-2 legs ' + - 'have degraded into "a body with nothing in it loads nothing" and tier 3 refuses ' + - 'nothing. Restore a schema ajv rejects rather than deleting this test.'); - } - for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { - if (!loaded(dep)) { - fail(dep + ' did NOT load even though the compilability rule just compiled this body. The ' + - 'require-cache probe cannot see the load it exists to catch, so every clean result in ' + - 'this file is vacuous — fix the probe, do not trust the greens.'); - } - } - console.log('OK');`; - // Cold-loading a dist entry in a spawned node on a busy runner takes seconds. const COLD_LOAD_TIMEOUT_MS = 30_000; -// The three spawned cases are independent child processes, so they start -// together in `beforeAll` and each case awaits its own child's output: the -// children cold-load in parallel with each other and with the in-process -// cases, which run first. Run one after another, they were most of this file's -// wall time, and nothing they assert depends on it. A child that exits -// non-zero rejects with its stderr, as `execFileSync` threw it. The no-op -// `catch` only marks the promise as handled until its case awaits it. -const execFileAsync = promisify(execFile); -const spawnProbe = (args: string[]) => { - const stdout = execFileAsync(process.execPath, args, { encoding: 'utf8' }).then((r) => r.stdout); - stdout.catch(() => {}); - return stdout; -}; - describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { - const probes = new Map<'cjs' | 'esm' | 'control', Promise>(); - beforeAll(() => { - if (existsSync(runtimeCjs)) { - probes.set('cjs', spawnProbe(['-e', `${childBody}; check(require(${JSON.stringify(runtimeCjs)}));`])); - } - if (existsSync(runtimeEsm)) { - probes.set( - 'esm', - spawnProbe([ + it.skipIf(!existsSync(runtimeCjs))( + 'built CJS runtime entry honours the three-tier dep contract, at import AND while gating', + () => { + const out = execFileSync( + process.execPath, + ['-e', `${childBody}; check(require(${JSON.stringify(runtimeCjs)}));`], + { encoding: 'utf8' }, + ); + expect(out).toContain('OK'); + }, + COLD_LOAD_TIMEOUT_MS, + ); + + it.skipIf(!existsSync(runtimeEsm))( + 'built ESM runtime entry honours the three-tier dep contract, at import AND while gating', + () => { + const out = execFileSync( + process.execPath, + [ '--input-type=module', '-e', `import { createRequire } from 'node:module'; const require = createRequire(process.cwd() + '/probe.js'); ${childBody}; check(await import(${JSON.stringify(pathToFileURL(runtimeEsm).href)}));`, - ]), + ], + { encoding: 'utf8' }, ); - } - if (existsSync(indexCjs) && existsSync(runtimeCjs)) probes.set('control', spawnProbe(['-e', controlBody])); - }); - // No child outlives the file, including one whose case a `-t` filter skipped. - afterAll(() => Promise.allSettled(probes.values())); + expect(out).toContain('OK'); + }, + COLD_LOAD_TIMEOUT_MS, + ); - // Same cold load as the two built-entry cases below, just in-process: the - // first `import()` of `./runtime.js` in this file pulls the whole gate graph - // through the transform. + // Same cold load as the two above, just in-process: the first `import()` of + // `./runtime.js` in this file pulls the whole gate graph through the transform. // So it gets the same budget — the claim being pinned (the boot path loads no // source parser) is proved by the assertions below, never by the wall clock, // and a 5 s default only turns a busy runner into a false red on the gate. @@ -371,22 +337,6 @@ describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { } }, COLD_LOAD_TIMEOUT_MS); - it.skipIf(!existsSync(runtimeCjs))( - 'built CJS runtime entry honours the three-tier dep contract, at import AND while gating', - async () => { - expect(await probes.get('cjs')).toContain('OK'); - }, - COLD_LOAD_TIMEOUT_MS, - ); - - it.skipIf(!existsSync(runtimeEsm))( - 'built ESM runtime entry honours the three-tier dep contract, at import AND while gating', - async () => { - expect(await probes.get('esm')).toContain('OK'); - }, - COLD_LOAD_TIMEOUT_MS, - ); - // #4716 — the probe's own non-vacuity, and the diagnostic that splits the // two ways tier 3 can fail. // @@ -405,8 +355,41 @@ describe('@objectstack/lint/runtime (kernel boot-path contract, #4463)', () => { // very one the in-process leg reads. it.skipIf(!existsSync(indexCjs) || !existsSync(runtimeCjs))( 'the schema-carrying body DOES load ajv when the compilability rule judges it directly (probe is not vacuous)', - async () => { - expect(await probes.get('control')).toContain('OK'); + () => { + const out = execFileSync( + process.execPath, + [ + '-e', + `${probeHelpers} + const rt = require(${JSON.stringify(runtimeCjs)}); + const full = require(${JSON.stringify(indexCjs)}); + for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { + if (loaded(dep)) fail(dep + ' was loaded merely by importing the full @objectstack/lint barrel'); + } + const snapshots = rt.buildRuntimeWriteSnapshots({ + type: 'object', + item: ${JSON.stringify(SCHEMA_OBJECT)}, + context: { objects: ${JSON.stringify(OBJECT_CONTEXT)} }, + }); + if (!snapshots) fail('the gate builds no snapshot for an object write — the tier legs above are then untested'); + const findings = full.validateRuleCompilability(snapshots.candidate); + if (!findings.some((f) => f.rule === 'validation-rule-json-schema-uncompilable')) { + fail('SCHEMA_OBJECT no longer carries a schema ajv refuses to compile, so the tier-2 legs ' + + 'have degraded into "a body with nothing in it loads nothing" and tier 3 refuses ' + + 'nothing. Restore a schema ajv rejects rather than deleting this test.'); + } + for (const dep of ${JSON.stringify(SCHEMA_DEPS)}) { + if (!loaded(dep)) { + fail(dep + ' did NOT load even though the compilability rule just compiled this body. The ' + + 'require-cache probe cannot see the load it exists to catch, so every clean result in ' + + 'this file is vacuous — fix the probe, do not trust the greens.'); + } + } + console.log('OK');`, + ], + { encoding: 'utf8' }, + ); + expect(out).toContain('OK'); }, COLD_LOAD_TIMEOUT_MS, );