Skip to content

Commit c6a62d0

Browse files
claude[bot]claude
andauthored
test(cli): route the three serve-* e2e spawners through childEnv(), 3 -> 0 on the child-env ratchet (#11692)
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. Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR Co-authored-by: Claude <noreply@anthropic.com>
1 parent 721ca92 commit c6a62d0

5 files changed

Lines changed: 38 additions & 15 deletions

‎packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
*
3636
* The spawn is written out here rather than taken from `test/helpers/
3737
* serve-process.ts` on purpose: that helper always runs the child WITH `cwd` set
38-
* to the app, which is the one shape this file must not use.
38+
* to the app, which is the one shape this file must not use. Only its
39+
* `childEnv()` choke point is borrowed (#11267) — what the child INHERITS is
40+
* orthogonal to which directory it is started in, and this file boots the real
41+
* stack, better-auth included, which reads `TEST` directly.
3942
*
4043
* ── The anti-vacuity floor ───────────────────────────────────────────────
4144
*
@@ -54,6 +57,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'nod
5457
import { tmpdir } from 'node:os';
5558
import { dirname, join, resolve } from 'node:path';
5659
import { fileURLToPath } from 'node:url';
60+
import { childEnv } from './helpers/serve-process.js';
5761

5862
const HERE = dirname(fileURLToPath(import.meta.url));
5963

@@ -156,16 +160,15 @@ function runServeFrom(
156160
const port = String(40000 + Math.floor(Math.random() * 20000));
157161
const child = spawn(TSX, [CLI, 'serve', configArg, '--port', port], {
158162
cwd,
159-
env: {
160-
...process.env,
163+
env: childEnv({
161164
NO_COLOR: '1',
162165
OS_DATABASE_URL: ':memory:',
163166
OS_LOG_LEVEL: '',
164167
OS_DISABLE_CONSOLE: '1',
165168
// The trigger: without a non-memory driver the cluster block is skipped
166169
// entirely and this file would measure nothing.
167170
OS_CLUSTER_DRIVER: 'redis',
168-
},
171+
}),
169172
});
170173

171174
let stdout = '';

‎packages/cli/test/serve-app-runtime-hooks.e2e.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { promisify } from 'node:util';
2727
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2828
import { tmpdir } from 'node:os';
2929
import { join } from 'node:path';
30-
import { runServe, randomPort, CLI, TSX } from './helpers/serve-process.js';
30+
import { childEnv, runServe, randomPort, CLI, TSX } from './helpers/serve-process.js';
3131

3232
const execFileP = promisify(execFile);
3333

@@ -81,7 +81,7 @@ beforeAll(async () => {
8181
await execFileP(TSX, [CLI, 'compile'], {
8282
cwd: dir,
8383
maxBuffer: 16 * 1024 * 1024,
84-
env: { ...process.env, NO_COLOR: '1' },
84+
env: childEnv({ NO_COLOR: '1' }),
8585
});
8686
}, 240_000);
8787

‎packages/cli/test/serve-host-fallback-base.e2e.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import { tmpdir } from 'node:os';
7878
import { dirname, join, resolve } from 'node:path';
7979
import { fileURLToPath, pathToFileURL } from 'node:url';
8080
import { promisify } from 'node:util';
81+
import { childEnv } from './helpers/serve-process.js';
8182

8283
const execFileAsync = promisify(execFile);
8384

@@ -184,7 +185,7 @@ beforeAll(async () => {
184185

185186
const { stdout } = await execFileAsync(TSX, [probeFile, SERVE_TS, appRoot], {
186187
cwd: neutralCwd,
187-
env: { ...process.env, NO_COLOR: '1' },
188+
env: childEnv({ NO_COLOR: '1' }),
188189
maxBuffer: 16 * 1024 * 1024,
189190
});
190191
const payload = stdout.slice(stdout.indexOf(SENTINEL) + SENTINEL.length);

‎scripts/check-cli-test-child-env.mjs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,12 +873,30 @@ export function selfTest() {
873873
// preceded this gate. A gate has no worklist, so they are members of the
874874
// population rather than extra scope -- pinned here because "the derivation
875875
// covers them" is exactly the kind of claim that rots silently.
876+
//
877+
// ⚠️ Pinned as membership of the SCANNED population -- walked, and classified
878+
// as a spawner, so `bulkEnvReferences` really runs over them. It is NOT
879+
// pinned as membership of `findings`, which is what the first revision of
880+
// these two cases asserted. That spelling reads identically on the day it is
881+
// written and means the opposite: it holds only while the two files still
882+
// LEAK, so it turned red the moment #11596's burn-down repaired them --
883+
// an anti-shrink pin sitting on a shrink-only ratchet, and one that reports
884+
// "the derivation lost this file" when the file is merely clean. A repaired
885+
// source is still derived and still scanned; that is the claim #11441
886+
// measured, and it is the one that has to survive the repair.
887+
const scanned = walkSources(join(REPO_ROOT, POPULATION_ROOT))
888+
.filter((abs) => {
889+
const text = readFileSync(abs, 'utf8');
890+
return /child_process|worker_threads/.test(text)
891+
&& isSpawnerSource(parseSourceFile(abs, text, scriptKindFor(abs)));
892+
})
893+
.map((abs) => relative(REPO_ROOT, abs).split(sep).join('/'));
876894
for (const named of [
877895
'packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts',
878896
'packages/cli/test/serve-host-fallback-base.e2e.test.ts',
879897
]) {
880898
t(`the enumerated population contains ${named.split('/').pop()}`,
881-
live.refusal === null && live.findings.some((f) => f.file === named));
899+
live.refusal === null && scanned.includes(named));
882900
}
883901
} finally {
884902
rmSync(dir, { recursive: true, force: true });

‎scripts/cli-test-child-env.baseline.json‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@
1212
"not a sanctioned move, it is a mute button -- and a count that FALLS must be lowered in",
1313
"the same PR. A ceiling left above reality silently licenses that many new leaks.",
1414
"",
15-
"Burn-down is tracked on https://github.com/objectstack-ai/objectstack/issues/11596.",
15+
"BURNED DOWN TO ZERO -- the ledger below is empty and stays that way. The burn-down",
16+
"ran on https://github.com/objectstack-ai/objectstack/issues/11596 and closed with it.",
17+
"An empty ratchet is the strongest one this file can hold: every bulk copy in the",
18+
"population is now either repaired or a DELIBERATE entry in the gate. ⛔ A new key here",
19+
"is not a baseline, it is a carve-out -- the gate's failure text says so and means it.",
1620
"",
1721
"The repair is `childEnv()` from packages/cli/test/helpers/serve-process.ts, which copies",
1822
"the environment minus the vitest worker family (TEST, VITEST, VITEST_*) and then applies",
1923
"the overrides you pass. Burning this list down is one card per neighbourhood; it is NOT",
20-
"the job of the PR that lands the gate, and two of the files below were held by another",
21-
"card in flight when it did.",
24+
"the job of the PR that lands the gate, and two of the files it baselined were held by",
25+
"another card in flight when it did.",
2226
"",
2327
"Sites that copy the whole environment ON PURPOSE do not belong here. They are DELIBERATE",
2428
"entries in the gate itself, carrying their reason, and they are pinned in both directions."
25-
],
26-
"packages/cli/test/serve-app-anchored-optional-import.e2e.test.ts": 1,
27-
"packages/cli/test/serve-app-runtime-hooks.e2e.test.ts": 1,
28-
"packages/cli/test/serve-host-fallback-base.e2e.test.ts": 1
29+
]
2930
}

0 commit comments

Comments
 (0)