fix(server): Codex rollback works after the app-server restarts - #13787
Conversation
Codex 0.156 only runs thread/revert on a thread loaded in the current app-server process. After a server restart or an idle session release, the rollback reached a fresh process, thread/revert failed with "thread not found", and Edit from here hung until the client timed out. The adapter now resumes an unloaded thread (thread/read reports notLoaded) before reverting. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a narrowly scoped Codex rollback bug fix: unloaded threads are conditionally resumed before No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After a server restart (or the 30-minute idle release of the Codex session), "Edit from here" on a Codex thread never finishes. The rollback effect fails five times with
thread not found: <native id>fromthread/revert, and the web client sits on "Thinking" until its 120 s timeout.Seen live on Codex 0.156.1 / gpt-6-luna: I ran two turns, restarted the server, then used Edit from here on the first message. The provider log shows the fresh app-server answering
thread/readwith"status":{"type":"notLoaded"},thread/turns/listsucceeding, andthread/revertreturning-32600 thread not found. That repeated for all 5 effect attempts, and the outbox row endedfailed.Why
In Codex 0.156,
thread/revertgoes throughload_thread, which only returns threads loaded in the current app-server process (thread_processor.rs,thread_revert_response→load_thread→thread_manager.get_thread).thread/readandthread/turns/listread the stored rollout, so they work on an unloaded thread, and the adapter got all the way to the revert. The rollback service opens a fresh session but never resumes the thread. Resume normally happens only in turn start.Fix
The Codex adapter's
rollbackThreadalready callsthread/readto check the history mode. It now also readsthread.status, and when that isnotLoadedit callsthread/resume(with the same runtime params a turn start would send) beforethread/revert. Threads that are already loaded skip the extra request, so the existingthread_rollbacktranscript is unchanged.Proof
thread_rollback_after_restart, recorded live from Codex 0.156.1 with gpt-6-luna and not hand-edited. The recording has two turns, an app-server restart,thread/read(notLoaded),thread/resume,thread/turns/list,thread/revert, and a post-rollback turn. The fixture input advances the clock 31 minutes so the orchestrator releases the session between the second turn and the rollback. The assertions reuse thethread_rollbackchecks: run 2 endsrolled_back, and the post-rollback answer contains only the first exchange. They also check that the resume comes right before listing and reverting.thread/turns/listwhere the transcript expectsthread/resume, so the rollback never completes and the next run never leavesstarting. After the fix it passes.thread_rollback_after_restartscenario, so the fixture can be re-recorded.Verification
vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts -t "thread_rollback|thread_fork": 4 passed (thread_rollback for Codex, Claude, and Pi, plus the new fixture).vp test run src/orchestration-v2/testkit/ThreadFork.integration.test.ts: 6 passed. The fork path shares thethread/readhelper.vp test run src/orchestration-v2/testkit/CodexReplayFixtures.integration.test.ts src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts: 4 passed.vp test run src/orchestration-v2/Adapters/CodexAdapterV2.test.ts -t "rollback|revert|Rollback": 5 passed.vp exec tsc --noEmit -p .inapps/server: no errors or warnings.vp lint/vp fmton the touched files show only the twono-unused-varswarnings that already exist inCodexAdapterV2.ts.vp run knip:checkis clean.T3_CODEX_BIN=<codex 0.156.1> node scripts/record-codex-app-server-replay-fixture.ts --scenario thread_rollback_after_restart.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code