From 5fdcae8777802c27bb8d974d5e526d3ee4d1b6d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 11:31:22 +0000 Subject: [PATCH] test(cli): route the three serve-* e2e spawners through childEnv(), 3 -> 0 on the child-env ratchet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the `check:cli-test-child-env` burn-down. The three remaining bulk `{ ...process.env, … }` child environments under `packages/cli/test/**` now go through `childEnv()`, which strips the vitest worker family (TEST, VITEST, VITEST_*) before applying overrides, and `scripts/cli-test-child-env.baseline.json` loses its last three keys in the same commit. The gate's own self-test had to be re-spelled to let the ratchet reach zero: two cases pinned #11441's two files as members of `findings`, which holds only while those files still LEAK. They are now pinned as members of the SCANNED population instead -- walked and classified as a spawner -- which is the claim #11441 actually measured and the one that survives the repair. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR --- ...e-app-anchored-optional-import.e2e.test.ts | 11 ++++++---- .../test/serve-app-runtime-hooks.e2e.test.ts | 4 ++-- .../test/serve-host-fallback-base.e2e.test.ts | 3 ++- scripts/check-cli-test-child-env.mjs | 20 ++++++++++++++++++- scripts/cli-test-child-env.baseline.json | 15 +++++++------- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts b/packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts index 2c8d67cccd..703afee852 100644 --- a/packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts +++ b/packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts @@ -35,7 +35,10 @@ * * The spawn is written out here rather than taken from `test/helpers/ * serve-process.ts` on purpose: that helper always runs the child WITH `cwd` set - * to the app, which is the one shape this file must not use. + * to the app, which is the one shape this file must not use. Only its + * `childEnv()` choke point is borrowed (#11267) — what the child INHERITS is + * orthogonal to which directory it is started in, and this file boots the real + * stack, better-auth included, which reads `TEST` directly. * * ── The anti-vacuity floor ─────────────────────────────────────────────── * @@ -54,6 +57,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'nod import { tmpdir } from 'node:os'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { childEnv } from './helpers/serve-process.js'; const HERE = dirname(fileURLToPath(import.meta.url)); @@ -156,8 +160,7 @@ function runServeFrom( const port = String(40000 + Math.floor(Math.random() * 20000)); const child = spawn(TSX, [CLI, 'serve', configArg, '--port', port], { cwd, - env: { - ...process.env, + env: childEnv({ NO_COLOR: '1', OS_DATABASE_URL: ':memory:', OS_LOG_LEVEL: '', @@ -165,7 +168,7 @@ function runServeFrom( // The trigger: without a non-memory driver the cluster block is skipped // entirely and this file would measure nothing. OS_CLUSTER_DRIVER: 'redis', - }, + }), }); let stdout = ''; diff --git a/packages/cli/test/serve-app-runtime-hooks.e2e.test.ts b/packages/cli/test/serve-app-runtime-hooks.e2e.test.ts index 5d43f7fda8..0b51830bb4 100644 --- a/packages/cli/test/serve-app-runtime-hooks.e2e.test.ts +++ b/packages/cli/test/serve-app-runtime-hooks.e2e.test.ts @@ -27,7 +27,7 @@ import { promisify } from 'node:util'; import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { runServe, randomPort, CLI, TSX } from './helpers/serve-process.js'; +import { childEnv, runServe, randomPort, CLI, TSX } from './helpers/serve-process.js'; const execFileP = promisify(execFile); @@ -81,7 +81,7 @@ beforeAll(async () => { await execFileP(TSX, [CLI, 'compile'], { cwd: dir, maxBuffer: 16 * 1024 * 1024, - env: { ...process.env, NO_COLOR: '1' }, + env: childEnv({ NO_COLOR: '1' }), }); }, 240_000); diff --git a/packages/cli/test/serve-host-fallback-base.e2e.test.ts b/packages/cli/test/serve-host-fallback-base.e2e.test.ts index 8fd7c8871e..bc783c6dcd 100644 --- a/packages/cli/test/serve-host-fallback-base.e2e.test.ts +++ b/packages/cli/test/serve-host-fallback-base.e2e.test.ts @@ -78,6 +78,7 @@ import { tmpdir } from 'node:os'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { promisify } from 'node:util'; +import { childEnv } from './helpers/serve-process.js'; const execFileAsync = promisify(execFile); @@ -184,7 +185,7 @@ beforeAll(async () => { const { stdout } = await execFileAsync(TSX, [probeFile, SERVE_TS, appRoot], { cwd: neutralCwd, - env: { ...process.env, NO_COLOR: '1' }, + env: childEnv({ NO_COLOR: '1' }), maxBuffer: 16 * 1024 * 1024, }); const payload = stdout.slice(stdout.indexOf(SENTINEL) + SENTINEL.length); diff --git a/scripts/check-cli-test-child-env.mjs b/scripts/check-cli-test-child-env.mjs index f8b0fbe9ea..69bb55102b 100644 --- a/scripts/check-cli-test-child-env.mjs +++ b/scripts/check-cli-test-child-env.mjs @@ -873,12 +873,30 @@ export function selfTest() { // preceded this gate. A gate has no worklist, so they are members of the // population rather than extra scope -- pinned here because "the derivation // covers them" is exactly the kind of claim that rots silently. + // + // ⚠️ Pinned as membership of the SCANNED population -- walked, and classified + // as a spawner, so `bulkEnvReferences` really runs over them. It is NOT + // pinned as membership of `findings`, which is what the first revision of + // these two cases asserted. That spelling reads identically on the day it is + // written and means the opposite: it holds only while the two files still + // LEAK, so it turned red the moment #11596's burn-down repaired them -- + // an anti-shrink pin sitting on a shrink-only ratchet, and one that reports + // "the derivation lost this file" when the file is merely clean. A repaired + // source is still derived and still scanned; that is the claim #11441 + // measured, and it is the one that has to survive the repair. + const scanned = walkSources(join(REPO_ROOT, POPULATION_ROOT)) + .filter((abs) => { + const text = readFileSync(abs, 'utf8'); + return /child_process|worker_threads/.test(text) + && isSpawnerSource(parseSourceFile(abs, text, scriptKindFor(abs))); + }) + .map((abs) => relative(REPO_ROOT, abs).split(sep).join('/')); for (const named of [ 'packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts', 'packages/cli/test/serve-host-fallback-base.e2e.test.ts', ]) { t(`the enumerated population contains ${named.split('/').pop()}`, - live.refusal === null && live.findings.some((f) => f.file === named)); + live.refusal === null && scanned.includes(named)); } } finally { rmSync(dir, { recursive: true, force: true }); diff --git a/scripts/cli-test-child-env.baseline.json b/scripts/cli-test-child-env.baseline.json index ef87928123..fdea1db58f 100644 --- a/scripts/cli-test-child-env.baseline.json +++ b/scripts/cli-test-child-env.baseline.json @@ -12,18 +12,19 @@ "not a sanctioned move, it is a mute button -- and a count that FALLS must be lowered in", "the same PR. A ceiling left above reality silently licenses that many new leaks.", "", - "Burn-down is tracked on https://github.com/objectstack-ai/objectstack/issues/11596.", + "BURNED DOWN TO ZERO -- the ledger below is empty and stays that way. The burn-down", + "ran on https://github.com/objectstack-ai/objectstack/issues/11596 and closed with it.", + "An empty ratchet is the strongest one this file can hold: every bulk copy in the", + "population is now either repaired or a DELIBERATE entry in the gate. ⛔ A new key here", + "is not a baseline, it is a carve-out -- the gate's failure text says so and means it.", "", "The repair is `childEnv()` from packages/cli/test/helpers/serve-process.ts, which copies", "the environment minus the vitest worker family (TEST, VITEST, VITEST_*) and then applies", "the overrides you pass. Burning this list down is one card per neighbourhood; it is NOT", - "the job of the PR that lands the gate, and two of the files below were held by another", - "card in flight when it did.", + "the job of the PR that lands the gate, and two of the files it baselined were held by", + "another card in flight when it did.", "", "Sites that copy the whole environment ON PURPOSE do not belong here. They are DELIBERATE", "entries in the gate itself, carrying their reason, and they are pinned in both directions." - ], - "packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts": 1, - "packages/cli/test/serve-app-runtime-hooks.e2e.test.ts": 1, - "packages/cli/test/serve-host-fallback-base.e2e.test.ts": 1 + ] }