Uh oh!
There was an error while loading. Please reload this page.
test(cli): route the last three serve-* e2e spawners through childEnv(), 3 -> 0 on the child-env ratchet - #11692
Conversation
… -> 0 on the child-env ratchet
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. |
os-zhuang
commented
Aug 24, 2026
ACCEPT — PM seat review ( |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11596 — the final slice of the
check:cli-test-child-envburn-down. Slice 1 (#11653)took the cheap batch, 18 → 3; this one closes the ledger.
What changed
The three remaining bulk
{ ...process.env, … }child environments underpackages/cli/test/**stop building their spawned child out of the whole of the runner's environment:
serve-app-anchored-optional-import.e2e.test.ts:160runServeFrom()— a realos servewith the full stack, better-auth includedserve-app-runtime-hooks.e2e.test.ts:84os compile(itsrunServe()call was already on the choke point)serve-host-fallback-base.e2e.test.ts:187tsxon a probe script that importsserve.tsand drivescreateHostImporterchildEnv()(#11267,packages/cli/test/helpers/serve-process.ts) copies the environment minusthe vitest worker family —
TEST,VITEST,VITEST_*— and then applies the overrides.scripts/cli-test-child-env.baseline.jsonloses its last three keys in the same commit; theratchet fails on a stale ceiling in that direction too, so leaving
3there would silentlylicense three new leaks.
Measured, rather than asserted, on the recipe itself — parent seeded exactly as vitest seeds a
worker, same overrides, the only difference being the choke point:
The red this slice was warned about, and what it actually was
The card predicted the
serve-*children might need their environment re-established explicitly— the
OS_SECRET_KEYprecedent. They did not. A different thing went red, and it is thefinding.
1. The gate's own self-test blocked the burn-down
pnpm check:cli-test-child-envruns--self-testfirst. With the three files repaired itreported:
Those two cases carry #11441's measurement, and their own comment states the intent: "they are
members of the population rather than extra scope". The predicate did not say that. It said
live.findings.some((f) => f.file === named)— membership of the findings list, which holdsonly while those two files still LEAK. So it is an anti-shrink pin sitting on a shrink-only
ratchet: it passes on the day it is written, and reds the moment the burn-down it is filed
alongside does its job — reporting "the derivation lost this file" when the file is merely clean.
The ledger could not reach 0 while it stood.
Re-spelled to the claim the comment already makes: membership of the scanned population —
walked by
walkSources, and classified byisSpawnerSource, sobulkEnvReferencesreally runsover them. A repaired source is still derived and still scanned.
The gate's RULE is untouched:
judge,bulkEnvReferences,countByFileand theDELIBERATEregistry are byte-identical, and no gate was widened.
Reverse-verified non-vacuous. Narrowing
POPULATIONfrompackages/cli/test/**topackages/cli/test/helpers/**reds both cases again (5 of 54 case(s) failed). The mutation wasconfirmed on disk before the run —
grep -con the old literal1 → 0and on the new one0 → 1, plus a non-emptygit diff --numstat— and the script carrieda
traponEXIT INT TERMrestoring the file from a saved copy; after restore the old literal greps1again and the self-testis back to
54 cases pass. No build is involved: the gate is an.mjsrun directly.2. The crypto re-check the card asked for: the mechanism it names no longer exists
The card, its slice-2 dispatch and
helpers/serve-process.ts's header all predict that a childwhich stops claiming to be a vitest worker flips
local-crypto-provider'sdetectModefromtesttodevelopment, minting and persisting a key to$HOME/.objectstack/dev-crypto-key. Theheader quotes the line at
local-crypto-provider.ts:133:a58eac3e(#11448, merged 2026-08-23 22:36Z — before this card was even filed, 2026-08-2407:25Z) deleted that arm.
detectModenow readsNODE_ENVand nothing else, andpnpm check:runner-env-posturekeeps the class shut.git grep 'VITEST' -- 'packages/**/src/**'returns 6 hits, every one of them prose in that file's own header explaining the removal.
So stripping the runner family cannot move crypto posture here. And these children were never in
testposture to begin with: they run throughbin/run-dev.js, which setsprocess.env.NODE_ENV = 'development'before argv is parsed, andNODE_ENVis deliberatelyoutside
childEnv()'s strip family. Confirmed empirically —$HOME/.objectstack/dev-crypto-keywas byte-identical and mtime-identical (
md5 60a62ec9…,2026-08-23 11:55:13) across every runof all three files, before and after the repair: the boots settle on their marker before the
settings service mints anything.
⇒ No
OS_SECRET_KEYwas added. Adding one would have been the move the dispatch forbids inreverse — a variable justified by nothing that was measured. The stale header is filed as
#11691 (
finding, unassigned); it is a helper this slice was fenced out of editing.Anti-vacuity: the leaks are gone, not moved
git grep -n '\.\.\.process\.env' packages/cli/test/returns 11 lines. Ten are comment or proselines (the
childEnvexplanations the sweep left behind, plus the two measurement tables).Exactly one code site survives:
—
DELIBERATE, the pin leg that keeps the pre-repair recipe executable. The secondDELIBERATEsite spells its bulk read
Object.entries(process.env)(helpers/serve-process.ts:154), which aliteral-spread grep cannot match;
--listreports it and it is untouched. That is exactly the twoDELIBERATEsites and nothing else.A zero-hit grep certifies nothing on its own, so the same machinery ran for a term known to be
present:
git grep -l 'childEnv(' packages/cli/test/returns 24 files — 21 after slice 1,plus these three.
--list, the source of truth rather than any hand-maintained worklist, agreed exactly with theslice boundary before the edit and reports zero after:
Fences held
packages/cli/testspawners pass noenvat all, so the child inherits the vitest worker environment verbatim — the purer form of #11341's leak, and the new gate is silent on it #11595 not folded in. Every spawn site in all three files carries anenvoption, so the"no
envat all" form does not occur here — nothing to leave alone, and the gate was notwidened to reach it.
...process.envworklist is two files short, and one of them spawns a realos serve#11441 not acted on beyond reading its measurement. This PR removes ledger entries and addsnone.
DELIBERATEsites untouched.the child inherits. Three comment additions accompany it: the header note in
serve-app-anchored-optional-importrecording that onlychildEnv()is borrowed from thehelper it deliberately does not take its spawn from, the
⚠️block on the re-spelled self-testcases, and the baseline's
$commentrecording that the ledger is now empty (its"two of the files below" sentence no longer had files below it).
Why no changeset
Test-only under
packages/cli/test/**plus two repo-root artifacts underscripts/. Neitherships. Measured rather than assumed:
@objectstack/clideclaresfiles: ["dist","README.md","CHANGELOG.md"], andnpm pack --dry-runon the package yields 433files of which 0 are under
test/, 0 match*.e2e.test.*and 0 are underscripts/— the same measurement slice 1 made, re-run here rather than cited.
skip-changesetapplies.Clause ②:
no. Test-only, no published surface, and no contract accept/reject behaviourchanges — the gate's rule (
judge,bulkEnvReferences,DELIBERATE) is byte-identical and onlytwo of its self-test predicates moved.
Verification
Gate union derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack,which reports its own source: "gate list derived from the tree of 'objectstack-ai/objectstack' at
commit 5fdcae8" and "change set derived from git — 5 path(s) vs merge base c251ef4". Derived
on the final committed diff, so the
scripts/*.jsonandscripts/*.mjsfamilies it pulls in areincluded rather than missed by a first pass.
Everything below ran under
scripts/pm/os-verify-lock.shagainst the final commit5fdcae87on a clean tree, over a workspace built with
pnpm exec turbo run build --filter="./packages/*" --filter="./packages/*/*"(70/70 taskssuccessful) — several of these refuse outright on an unbuilt closure, and a refusal is not a
pass. Exit codes were captured before any pipe; each family is quoted from the verdict line the
gate itself prints. No log contains
PREREQUISITE NOT MET, "Nothing was checked" orREFUSED.All 20 families green:
check:type-check-debt's twoℹsurplus notices (plugin-approvals−1,runtime−1) arepre-existing, in packages this diff does not touch, and the gate states they are not errors.
Tests
Each touched file, individually, at
5fdcae87on a clean tree:The full
packages/clisuite, sharded 4 ways (npx vitest run --shard=N/4 --maxWorkers=2; thepackage's own config header prices the whole thing at ~495s):
pnpm --filter @objectstack/cli typecheck→tsc --noEmit, exit 0 (the script name is echoed inthe output, so this is not a zero-match
--filterrun).Findings filed
serve-process.ts's header quotes adetectModeline #11448 deleted — the VITEST/crypto measurement it teaches can no longer happen #11691 —serve-process.ts's header quotes thedetectModeline fix(service-settings): select crypto posture from the deployment signal, never the test runner #11448 deleted, so theVITEST/crypto measurement it teaches can no longer happen.
finding, unassigned. Comment-onlyrepair in a helper this slice was fenced out of editing.
Generated by Claude Code