# Cross-build outbox recovery proof

The comparison driver runs the real web app in Chromium at 1280 × 800 with dark system appearance. It authenticates before beginning video capture. Both app homes are copies of one synthetic writer home, never a live user database. No outbox rows are inserted or edited.

## Writer seed

1. Run the shell-command writer from [#31101](https://github.com/pingdotgg/t3code/pull/31101), commit [d0ce359e7c](https://github.com/pingdotgg/t3code/commit/d0ce359e7cd891ebc39292d957e34cd8c74af78b), on a fresh home with T3CODE_DEV_SEED=0.
2. Initialize a tiny git project with README.md, add it through `t3 project add`, and start a Replay (dev), todo-list (Plan and reads) thread with hello.
3. Configure `filter.slow.clean` to `sleep 45; cat`, `filter.slow.smudge` to `cat`, and write `README.md filter=slow` to .gitattributes.
4. Send !sleep 600 through the web composer (creationSource=web is required). A preliminary CLI !sleep 600 was treated as ordinary replay text and completed before this web command; it did not run a shell command.
5. Let the web shell command move to the background after its 120 second foreground window. Send go through the composer. Wait for this turn's checkpoint.capture to be running.
6. Click the composer's Stop generation. Confirm the outbox contains pending shell-command.stop. SIGKILL the writer server before that row is claimed.
7. Copy this home twice, one for base [b1050d3449](https://github.com/pingdotgg/t3code/commit/b1050d344962e1adfbe3309f880ae13c0fa98aad), the other for head [0f1041748b](https://github.com/pingdotgg/t3code/commit/0f1041748b410045cd3c344d691970650b82b2a5). Start each commit from its own installed checkout, with T3CODE_DEV_SEED=0 and its copy as --home-dir. A Node preload gives the synthetic environment the public label Recovery demo.

## Comparison

Run comparison-driver.mjs with arguments: installed checkout, copied home, server log, unique output directory, and before or after. Set CHROMIUM_PATH to the installed headless Chromium binary. The copied home contains proof-thread.json with the synthetic threadId. The driver pairs, opens that thread, closes the right panel, sends Reply with one word., waits 130 seconds, and captures the real app. Replay produces its fixture text rather than following the literal requested answer length.

Read the copied home's outbox and runs without modifying them. On base, the unreadable stop should remain running with a lease and no error, while the new provider-turn.start remains pending and its run starting. On head, the stop should fail with its lease cleared and a recorded decoding reason, and the later replay turn should run.

The final stills use stable-capture.mjs: it opens the existing run without sending another message, disables visual CSS animations during each screenshot, and requires two byte-identical screenshots 500 ms apart. The recordings preserve the original animations and the complete 130-second comparison interaction. The 45-second clean filter exceeds the current 30-second VCS timeout on both builds; checkpoint capture logs that timeout, then the outbox advances to the unreadable row. This does not prevent head from completing the later replay turn.
