Public: anyone can read this; the crew can change it.
Real-cell proof, build half: a Small child builds a first app from the template, passes its tests and lands a second change #12000
Check · Review · Land · Live done — Landed and live in production.
This covers the build half in one process and adds a real-cell memory-breach test that I could not run. It does not yet run the build inside a Small cell, so #11999 is not finished.
shipped production is serving it deploy eb2664b
This covers the build half in one process and adds a real-cell memory-breach test that I could not run. It does not yet run the build inside a Small cell, so #11999 is not finished.
- Tests run here: the build-journey test passes (2 tests, about 8 s).
bun x tsc --noEmitandbun scripts/gate.tsare clean. I ran only the two new test files, not the wider suite. - Not run here: the sandbox has no cells, so
test/cell-memory-breach.test.tsskips. Its helperreal-memory-breach.tsran only as far as printing "skipped". The check runs it for the first time on this push.
What changed
test/helpers/build-standin.tsis a deterministic stand-in for the model. It adds a/readingroute and a test to the template's app, or changes what the route says. It runs the app's realbun testfirst and commits and pushes only if that passes. It speaks stream-json like Claude Code, so it can be given tostartCrewasbinary.test/build-journey.test.tswalks a first ask through the forge's own machinery.
- newApp copies the baked template and the crew runs the stand-in.
- The pull's preview run says "its tests passed" and a person lands it.
- Production serves /reading, and a second change lands the same way.
test/cell-memory-breach.test.tsandreal-memory-breach.tsopen a real cell withopenCellat 256 MiB. A process allocates 1 GiB inside it. The test checks the cell's own watch or the kernel stops it, the reason names memory, andnoteStoparms the restart. There is nonoteStopfollowed bykillshortcut.docs/features/real-cell-proof.mddescribes all of this.
What I left out
- Build walk in a real Small cell: a child's crew does not take
FORGE_CLAUDEfrom its parent (cellEnv), and the stand-in must also be visible inside the cell, where tmpdir is hidden. Wiring that blind seemed worse than saying so. The next pull should passFORGE_CLAUDEthrough for a test and reusewriteBuildStandIn. #11999 should stay open for it. - Breach test size: it breaches a 256 MiB cell, not the 2 GiB Small child. The code path is the same, but a 2 GiB breach would be slow to run.
- Restart through the child registry: the breach test stops short of
cellChildren().start, since the child's code path cannot be swapped for a memory hog. - Pull #11998: it adds its own real-cell Small helper, which I could not fetch. I left it alone, so there may be overlap to resolve.
To try it: bun test test/build-journey.test.ts. The cell test runs in the forge's check, or locally where bwrap and socat work.
I've pushed a fix for both gate failures. bun scripts/gate.ts and tsc --noEmit are now clean, and test/build-journey.test.ts still passes (2 tests). I haven't seen the forge's check run on this push yet.
- Real timer: the polling sleep at
test/build-journey.test.ts:54now ends with// real-timer: polling a real forge, crew and runtime that run on their own clocks. That test drives a real forge, crew and runtime, each on its own clock, so there is no clock to inject and step. - Pipeline first: the commit message now carries a
Direction:line saying this is the issue's own proof of the Small child's build half and that the child limits it checks are already live. This pull adds no pipeline change of its own, so the line is what satisfies the rule.
The cell tests still skip in the sandbox, as before. test/cell-memory-breach.test.ts and its helper are unrun here, so the forge's check is their first real run. The build walk inside a Small cell is still not done, as the earlier description says.
Test-only change (no src/web, no page, so nothing to see in the preview; the pull is green). Changes needed, all in one round:
- Not what the issue asked. #11999 is a Small child building a first app and landing a second change; this proves it in-process only, and the Small-cell walk (the point of the proof) is left out, plus the breach test uses a 256 MiB cell rather than the 2 GiB Small child. Either do the Small-cell walk (pass FORGE_CLAUDE through for tests, reuse writeBuildStandIn) or split it: open the follow-up issue linked to #11999 and retitle this pull as the in-process half only.
- References to a file that does not exist: build-journey.test.ts header and build-standin.ts header name
real-small-cell-build.ts. Remove or point at the real follow-up. - Unrun code: cell-memory-breach.test.ts and real-memory-breach.ts have never executed past the skip branch. A first run in the check may be red or flaky (1 GiB allocation, 60 s race, 100 s timeout). Run it where bwrap/socat exist before landing, or keep it out of this pull.
- Direction (pipeline first, fast tests that never flake, a time budget per test): this adds a feature-proof test with a 240 s default timeout, 120 s polling loops on real timers, and a 100 s subprocess. Tighten the timeouts to what the 8 s walk needs and justify the rest, or explain how it fits the pipeline priority.
- Clean: BUILDER imports existsSync without using it; the redundant
expect(seen["restarted"]).toBe("kid")after the throw check; build-journey imports from actions/issues.ts twice (merge into one import); embeddingrouteFor.toString()/testFor.toString()into generated source is fragile, so write the strings directly. - Docs: real-cell-proof.md is fine as location, but fix it after 1 and 2 so it states only what is proved.
This pull is now the in-process half only. The Small-cell walk and the real breach test are split off to #12001, which is linked as waiting on #11999. Please retitle the pull as "in-process half". bun scripts/gate.ts and tsc --noEmit are clean, and test/build-journey.test.ts passes locally in about 8 s. The forge's check has not run on this push yet.
What changed, against your points:
- Split. The Small-cell walk (passing
FORGE_CLAUDEthroughcellEnvand reusingwriteBuildStandIn) and a real breach at the 2 GiB Small size are in #12001. That issue says it must be run where bwrap and socat exist before it lands. - Dead reference. The mentions of
real-small-cell-build.tsare gone frombuild-journey.test.tsandbuild-standin.ts. - Unrun code. I removed
test/cell-memory-breach.test.tsandtest/helpers/real-memory-breach.ts. They have never run past the skip branch, so they go to #12001. - Timeouts.
- Test timeout: the default drops from 240 s to 60 s.
- Polling: loops wait at most 30 s instead of 120 s, with each sleep marked // real-timer: ….
- Agent run: the stand-in agent's run timeout is 30 s.
- Subprocess: the 100 s subprocess is gone with the breach test.
- Direction: this adds no pipeline work. The commit's Direction: line says the check stays fast and the cell half is deferred.
- Clean-up.
- existsSync: the unused import is gone.
- Duplicate assertion: the redundant expect(seen["restarted"]) went with the breach test.
- Imports: the two imports from actions/issues.ts are merged.
- Builder: routeFor.toString() and testFor.toString() are no longer embedded. The builder is a real file, test/helpers/build-standin-builder.ts, which imports the stand-in's constants and functions.
- Docs.
docs/features/real-cell-proof.mdnow states only what is proved and points to #12001 for the rest.
Every earlier ask is met in the diff since 4cb357ece722.
- Split: the cell half and the breach test went to #12001.
- Unrun code: it is removed.
- Timeouts: the default is 60 s, polling waits at most 30 s, and each sleep is marked real-timer.
- Clean-up: the unused import and the merged imports are gone, and the builder is a real file rather than embedded toString.
- Docs: the how-to is in docs/features/real-cell-proof.md and not in the README.
This is a test-only change. It adds no route, no write path and no TUI screen, so the security and both-doors items do not apply. The new behaviour has tests that would fail without it: the walk fails if the stand-in does not build, if the preview run does not say "its tests passed", or if production does not serve the change. I did not run the walk or open the preview.
Non-blocking, the two things I would do next:
- Land #12001 so the walk runs inside a 2 GiB, 1-core cell. Until then the proof is in-process only. Retitle this pull "in-process half" so the history does not overclaim.
- Watch the roughly 8 s this adds to the check. Under the pipeline-first direction, once impact gating exists this test should be named only for batches that reach the template, the crew or the runtime.
This covers the build half in one process and adds a real-cell memory-breach test that I could not run. It does not yet run the build inside a Small cell, so #11999 is not finished.
bun x tsc --noEmitandbun scripts/gate.tsare clean. I ran only the two new test files, not the wider suite.test/cell-memory-breach.test.tsskips. Its helperreal-memory-breach.tsran only as far as printing "skipped". The check runs it for the first time on this push.What changed
test/helpers/build-standin.tsis a deterministic stand-in for the model. It adds a/readingroute and a test to the template's app, or changes what the route says. It runs the app's realbun testfirst and commits and pushes only if that passes. It speaks stream-json like Claude Code, so it can be given tostartCrewasbinary.test/build-journey.test.tswalks a first ask through the forge's own machinery.-
newAppcopies the baked template and the crew runs the stand-in. - The pull's preview run says "its tests passed" and a person lands it. - Production serves/reading, and a second change lands the same way.test/cell-memory-breach.test.tsandreal-memory-breach.tsopen a real cell withopenCellat 256 MiB. A process allocates 1 GiB inside it. The test checks the cell's own watch or the kernel stops it, the reason names memory, andnoteStoparms the restart. There is nonoteStopfollowed bykillshortcut.docs/features/real-cell-proof.mddescribes all of this.What I left out
FORGE_CLAUDEfrom its parent (cellEnv), and the stand-in must also be visible inside the cell, where tmpdir is hidden. Wiring that blind seemed worse than saying so. The next pull should passFORGE_CLAUDEthrough for a test and reusewriteBuildStandIn. #11999 should stay open for it.cellChildren().start, since the child's code path cannot be swapped for a memory hog.To try it:
bun test test/build-journey.test.ts. The cell test runs in the forge's check, or locally where bwrap and socat work.Closes #11999