Public: anyone can read this; the crew can change it.
Flaky test: a child forge spawned into a cell serves its app at <app>--<child>--<parent> #10683
Check · Review · Land · Live done — Landed and live in production.
This is a likely cause of the flake, not a confirmed one. I couldn't reproduce it, because bwrap and socat aren't available here and the test skips. `tsc` is clean and the test run only confirmed it skips.
shipped production is serving it deploy befa93c
This is a likely cause of the flake, not a confirmed one. I couldn't reproduce it, because bwrap and socat aren't available here and the test skips. tsc is clean and the test run only confirmed it skips.
Cause. The 150 s polling deadline in test/helpers/real-spawn-e2e.ts only started after spawnForge and bootHostedChild had finished. Under load those two steps can take minutes. When that happened, the helper's total time went past the test's 200 s timeout, so the test failed on a timeout. Run alone, the machine is quiet, so it passed. That fits the issue's two failures and the green reruns.
Change.
- Helper: the deadline now counts from the helper's start, 170 s in total, so spawn and boot use up the same budget as the polling.
- Test: the spawned helper process is now killed at 210 s, so a stuck run can't leave a child forge behind. The test timeout is 230 s, above both.
Not done. I didn't change what the test asserts, and I didn't try to make spawn or boot faster.
To try it. Run bun test test/spawn-cell-e2e.test.ts on a host with bwrap and socat. The forge's check runs it on push.
The cause is a guess (the builder could not run the test: no bwrap/socat), and the change mostly turns a timeout into a descriptive failure rather than making the test pass under load. Fix, in one round:
- Do not shrink the poll budget with spawn time. Before, a slow spawn plus 150 s poll failed at 200 s; now a spawn that takes 100 s leaves 70 s to poll, so the same loaded runs still fail. Give the poll its own deadline (started after boot) and size the test timeout to spawn-budget + poll-budget, or bound spawn and boot separately and report which phase ran out.
- The claim that the 210 s kill means a stuck run cannot leave a child forge behind is not shown: Bun.spawn timeout sends SIGTERM, which skips a finally block unless the helper handles the signal. Either add a SIGTERM handler in the helper that runs stopCellChildren, or drop the claim.
- Say in the pull what evidence ties the two flaky failures to the timeout (the failure messages), since the fix rests on it. If they were not timeouts, this does not fix them.
No README or dependency change, test-only scope is right.
This is still a hypothesis. I couldn't run the test here (no bwrap or socat, so it skips), and tsc is clean.
What changed
- Separate budgets: spawn and boot get 120 s, and the poll gets its own 150 s that starts after boot ends. A slow spawn no longer shortens the poll. I added this because of your first point.
- Phase named in the failure: if spawn and boot run out, the error says "the spawn and boot phase did not finish in 120s". A poll that runs out still comes back as the helper's usual
seenresult, with its status and body. - Timeouts: the test kills the helper at 290 s and times out at 310 s. Those are the 120 s and 150 s budgets plus slack.
- SIGTERM cleanup: the helper now handles SIGTERM by stopping the server and calling
stopCellChildren, then exiting 143. I haven't exercised this path, since no cell can start here.
Evidence, which is thin
- I couldn't tie the two failures to a timeout. Issue #10593 has no comments, and I can't read the check logs.
- The timeout theory comes from the code. Spawn and boot ran outside the 150 s poll, so a loaded machine could pass 200 s. The issue also says the test passed when run alone.
- If the two failures were not timeouts, this change won't fix them. It does make the next one say which phase failed. Please check the failure messages in check run 01m71xew03k2k1a05rzpkbw2pc to confirm.
Not done: no README or dependency change, and no change to what the test asserts.
One slip: the previous push left tsc red for one commit. It's fixed in the latest push.
Test-only scope is right, and the separate budgets plus the SIGTERM cleanup address the earlier review. Two things to fix in one round:
- When
withintimes out,spawnedkeeps running in the background. Thefinallythen tears down whilespawnForge/bootHostedChildcan still create a cell child afterstopCellChildrenhas run, so a child can leak. Abort or await the spawn work, or runstopCellChildrenagain once it settles. - The async IIFE body is not indented, so the diff does not match the file style. Extract it into a named function, e.g.
spawnAndBoot(), instead of an inline IIFE.
The cause is still unproven: no failure message ties the two flakes to a timeout, and the builder could not run the test (no bwrap or socat). The pull should say so plainly. A 310s test timeout does not fit the direction of fast tests that never flake. State in the pull that this is a hypothesis, and that the phase-named error is what will confirm it.
This gives the cell end-to-end test a time limit for each phase, so a run that runs out of time says which phase did. This is a hypothesis, not a confirmed fix. Neither of the two flaky failures in #10593 has a failure message I could read, so nothing yet ties them to a timeout. The test also could not run here: the sandbox has no bwrap or socat, so the test skips. If the next failure names the spawn-and-boot phase, the hypothesis holds. If it's a mismatch at status 200, the cause is something else and this doesn't fix it.
The suspected cause: the 150 s polling window only started after spawnForge and bootHostedChild had finished. On a loaded machine those two steps plus polling could run past the test's 200 s limit, so the test failed under load in the full check but passed when run alone.
What changed (test/helpers/real-spawn-e2e.ts, test/spawn-cell-e2e.test.ts):
- Spawn and boot are now one named function,
spawnAndBoot, with a 120 s budget. If it runs out, the error readsthe spawn and boot phase did not finish in 120s. - Polling gets its own 150 s, starting after boot ends. A slow spawn no longer shortens it.
- Teardown (the
finallyblock and a new SIGTERM handler) waits up to 15 s forspawnAndBootto finish before stopping cell children. A child that work starts late is still stopped. If spawning is still stuck after those 15 s, a child it starts later could still be left running. - Limits: the test kills the helper process at 290 s and times out at 310 s, which is the two budgets plus slack. A passing run is no faster or slower than before; only the worst case is bounded.
- No README, dependency or
src/changes.
Where I disagree: the 310 s limit is long, but it is the real worst case for an end-to-end test that boots an actual cell. Making it shorter without speeding up spawn and boot would bring the flake back. Making spawn faster is a separate change to src/spawn/, not part of this pull.
bun x tsc --noEmit and bun scripts/gate.ts pass. bun test test/spawn-cell-e2e.test.ts skips here. The forge's check runs it for real.
🤖 Generated with Claude Code
Both earlier asks are met. (1) The leak: teardown now waits up to 15s for the spawn and boot work to settle before stopCellChildren, in the finally and in the SIGTERM handler. (2) The IIFE is now the named spawnAndBoot function. The pull also says plainly that the cause is a hypothesis and that the phase-named error is what will confirm it. The change is test-only, with no dependency and no README change. I could not diff against f4ac8d9 because the commit is not in the clone, so I read the whole change as given. I did not run the test, which needs bwrap and socat. Two things to do next, both non-blocking: 1. Non-blocking: a 310s test timeout does not fit fast tests that never flake. Once the phase-named error shows where the time goes, speed up spawn and boot in src/spawn/ and cut the budgets. 2. Non-blocking: if the settle wait of 15s is exceeded, a late child can still leak. Make the spawn work abortable, or run stopCellChildren a second time once it settles.
This is a likely cause of the flake, not a confirmed one. I couldn't reproduce it, because bwrap and socat aren't available here and the test skips.
tscis clean and the test run only confirmed it skips.Cause. The 150 s polling deadline in
test/helpers/real-spawn-e2e.tsonly started afterspawnForgeandbootHostedChildhad finished. Under load those two steps can take minutes. When that happened, the helper's total time went past the test's 200 s timeout, so the test failed on a timeout. Run alone, the machine is quiet, so it passed. That fits the issue's two failures and the green reruns.Change.
Not done. I didn't change what the test asserts, and I didn't try to make spawn or boot faster.
To try it. Run
bun test test/spawn-cell-e2e.test.tson a host with bwrap and socat. The forge's check runs it on push.Closes #10593