# #1512 R1 candidate re-run against HEAD, 2026-09-26 Requested by Sage over T3 on 2026-09-26. Integration was held because of the row 21 engine test race, and `1685deb4` fixed that race. This record is new evidence. The R1 files next to it are unchanged. ## Snapshot - `/tmp/relaunch-activity-r1-rerun-oupRiqIC`: `git archive 21e3e908`, plus the working `packages/control-board`, `packages/webui` and `packages/seat` (node_modules excluded), plus a symlink to the root `node_modules`. - `CANDIDATE.sha256` in the snapshot hashes to `47769fad…`, which is the R1 candidate manifest. All 7 candidate pins match. - 71 of the 89 R1 dependency pins are unchanged. The other 18 are `packages/discord/**` and `scripts/test-discord.sh`, and they equal HEAD. `engine-pi.mjs`, `engine.test.mjs`, `fake-pi.mjs` and `helpers.mjs` in the snapshot are byte-identical to HEAD. - Node v26.8.1. Test union: control-board, webui, seat, mosaic, ledger and discord tests. The count rose from 351 at R1 to 397 because rows 23 to 25 added Discord tests. ## Results | Run | Flags | Result | Log (`/tmp/`) | sha256 prefix | |---|---|---|---|---| | A1 | `--test-concurrency=1 --test-timeout=15000` (R1 form) | 397/397, rc 0, 19.9 s | darkwing-1512-rerun-A1-bounded.txt | 8b34a64a660d5948 | | A2 | same | 397/397, rc 0, 19.7 s | darkwing-1512-rerun-A2-bounded.txt | 5c8b96209f5b8b2c | | A3 | same | 397/397, rc 0, 19.4 s | darkwing-1512-rerun-A3-bounded.txt | e9829f454bf2b63f | | B1 | `--test-concurrency=1` | 397/397, rc 0, 19.0 s | darkwing-1512-rerun-B1-serial-notimeout.txt | bb6eaedf6250c410 | | C1 | default concurrency | 395/397, rc 1, hung until I ended a leaked child | darkwing-1512-rerun-C1-default-concurrency.txt | 5200188e1bcd8b15 | | C2 | default concurrency | 395/397, rc 1, hung until I ended a leaked child | darkwing-1512-rerun-C2-default-concurrency.txt | 1f244a4835000e31 | In C1 and C2, the same two tests failed, both in `packages/discord/tests/engine.test.mjs`: - Test 205 (line 100), "a prompt while streaming is held…": ENOENT on `commands.jsonl`. - Test 208 (line 146), "tool events from a run that outlived its timeout…": `engine refused prompt: agent is streaming; specify streamingBehavior`. Both runs stopped making progress after every test had finished and the `after()` hook had removed the temp root. The only thing left was a `fake-pi.mjs` child. I sent that child SIGTERM (C1 pid 3636242, C2 pid 3668771). The file then exited and the runner printed its summary. Apart from that, I didn't touch either run. ## Control: clean HEAD, no #1512 files Snapshot `/tmp/darkwing-head-21e3e908-zinKkQ` (`git archive 21e3e908` only). Same union, default concurrency: | Run | Result | Log (`/tmp/`) | sha256 prefix | |---|---|---|---| | 1 | rc 124 at the 240 s cap, 178 ok, interrupted in seat and webui files | darkwing-head-default-1.txt | 799acad299d0b20e | | 2 | rc 124 at the 240 s cap, same shape | darkwing-head-default-2.txt | e9028b28c02ffc86 | | 3 | 369/370, rc 1, test 187 = the line 146 engine failure | darkwing-head-default-3.txt | fcd85bd474f2c210 | | discord only | 162/162, rc 0 | darkwing-head-discord-only.txt | 5f068557caee7fc7 | | seat + webui only | 21/21, rc 0 | darkwing-head-seat-webui.txt | 9952b57707577a92 | `engine.test.mjs` alone in the candidate snapshot passed 5 of 5 times, 11/11 each run (`darkwing-engine-alone-{1..5}.txt`). Dewey was running `packages/webui/tests/` from the canonical checkout during part of this window. That added load. It doesn't explain the HEAD control, and the two engine failures reproduce without it. ## Reading The #1512 candidate passes its R1 acceptance command 3 of 3 times at 397/397, and it passes the serial run with no timeout. Every default-concurrency failure is in committed `#1509` Discord engine code, and clean HEAD fails the same way. I found nothing that implicates the seven candidate files. Two engine defects are exposed under load. They belong to `#1509`, not to #1512: 1. **Test race plus leak, `engine.test.mjs:100`.** The test reads the fake's command log 20 ms after the second prompt. Under load the fake hasn't written the file yet, so the read fails with ENOENT. The test has no `try/finally`, so `engine.stop()` never runs, the fake-pi child stays alive, and the test file never exits. That is the hang. The fix is the pattern `1685deb4` already uses elsewhere: wait with `until()` and stop in `finally`. Six other tests in the file also stop without `finally`. 2. **Engine race, `engine-pi.mjs`, seen through `engine.test.mjs:146`.** `busy` is `state.busy || pending.some((t) => !t.done)`. Suppose a turn times out before the engine has read pi's `agent_start`. `failTurn` marks it done, `state.busy` is still false, and `busy` reads false. The next prompt then goes straight to pi, and pi refuses it because it's still streaming. This fails closed: the prompt errors and nothing is misattributed. Production timeouts are long, so this should be rare there. It's still a real race. One option is to count a sent turn toward `busy` until pi's own end or settle event arrives, not only until the client gives up on it. Rocko's finding C, a follow-up sent after a timeout, looks superseded. Since `1685deb4`, the engine never sends a follow-up (held prompts), and the line 100 test asserts that `streamingBehavior` is absent. Defect 2 is the timeout race that remains.