MCP finished-thread recovery proof Environment: Linux, real source T3 server and ACP adapter started with vp run dev, separate disposable checkouts and separate initially empty T3 homes, no live data. No browser is involved in these MCP protocol errors. Base: dd3d4a4e4a20dc54287c049e54fb13fa7ba8c708 Head: 2ed4560556005c14b7feb45912cb2990cf90bf78 The replay-agent fixture is the only modification to either checkout. It adds scripted parent behavior and a deterministic DONE child. All delegation, send, error construction, ID lookup, persistence, and turn completion execute through the real running server. No server service or response is mocked. Reproduce in a disposable checkout at either commit: 1. vp i 2. python3 install-fixture.py CHECKOUT ABSOLUTE_PATH_TO_mcp-proof.mjs 3. T3CODE_DEV_SEED=0 vp run dev --no-seed --port PORT Use port 14991 for head, 14992 for base (used only for artifact labels). 4. node apps/server/src/bin.ts project add CHECKOUT --title 'MCP recovery proof' --base-dir CHECKOUT/.t3 5. node apps/server/src/bin.ts thread start CHECKOUT RUN_MCP_RECOVERY_PROOF --title 'MCP recovery proof capture' --provider devReplay --model multi-turn --wait --json --base-dir CHECKOUT/.t3 The CLI starts a real parent provider session. Its scripted ACP parent initializes an HTTP MCP session with the normal per-thread credentials, asks capabilities, and delegates a child with mode wait. That child emits DONE and completes. The parent then sends steer, restart, auto with %3A replaced by colons, and auto with the exact returned childThreadId. It waits for the new turn to complete. transcript.json captures the returned server messages. session.cast is an asciicast v2 terminal-output recording with measured response arrival times; play with an asciicast player. It contains the same captured response data, not a screen recording. CLI summaries can report the parent's later automatic result-delivery wake turn; the transcript captures the original parent MCP calls. The final matched captures use clientRequestId proof-done-child. An exploratory run used proof:done-child, which introduced nested percent escapes; replacing only %3A there did not produce a suggestion on head. This is a partially decoded ID, rather than the fully unencoded ID in the final reproduction. The scenario runner documentation was read first. A browser screenshot of seeded messages would not exercise these MCP errors, so this proof uses the server/CLI capture path. No tests are used as proof.