fix(codex): recover ready after stale terminal event#997
Conversation
There was a problem hiding this comment.
Findings
- [Major] Stale retry failures can now finalize the active retry turn — during a same-thread retry,
sameThreadRetryAttemptstays > 0 after the replacement turn has started and until atask_completeresets it. With the new matching-thread allowance, any terminal event from the same thread but an old turn id is accepted, including a delayed duplicatetask_failedfor the already-finalized failed turn. Because the duplicate check is also bypassed for this recovery shape, that stale failure can setpending = activeMessage, clearturnInFlight, wake the loop, and start another retry while the real retry turn is still running. Evidencecli/src/codex/codexRemoteLauncher.ts:2466.
Suggested fix:const allowStaleSameThreadTerminalRecovery = msgType === 'task_complete' && (sameThreadRetryAttempt > 0 || sameThreadCompactAttempt > 0); const allowSameThreadTerminalRecovery = msg.terminal_source === 'thread_status' || allowStaleSameThreadTerminalRecovery;
Summary
Review mode: follow-up after new commits
Found one regression risk in the added stale-terminal recovery path. Residual risk: review was limited to diff and surrounding source inspection.
Testing
Not run (automation). Suggested coverage: add a launcher regression where a stale same-thread task_failed for the previous turn arrives after the retry task_started; assert it is ignored and does not enqueue a second retry or clear thinking before the real retry completion.
HAPI Bot
| eventThreadId, | ||
| currentThreadId: this.currentThreadId, | ||
| allowMatchingThreadIdTerminalEvent: msg.terminal_source === 'thread_status' | ||
| allowMatchingThreadIdTerminalEvent: allowSameThreadTerminalRecovery |
There was a problem hiding this comment.
[Major] This now accepts every stale same-thread terminal event while a retry/compact recovery attempt is active. A delayed duplicate task_failed from the already-finalized old turn can therefore pass the guard after the replacement turn has started, then run the retry branch again and clear turnInFlight even though the real retry is still running. Please scope the stale-turn exception to the recovery success signal this PR is handling.
Suggested fix:
const allowStaleSameThreadTerminalRecovery = msgType === 'task_complete'
&& (sameThreadRetryAttempt > 0 || sameThreadCompactAttempt > 0);
const allowSameThreadTerminalRecovery = msg.terminal_source === 'thread_status'
|| allowStaleSameThreadTerminalRecovery;
Summary
Fixes #996.
task_completeRoot cause
The launcher returned early on a stale/mismatched terminal event before clearing
turnInFlight, clearing thinking, waking the loop, or schedulingready. After same-thread retry, Codex can emit a terminaltask_completescoped to the active thread but with an older turn id; ignoring that event leaves the session permanently shown as thinking.Validation
bun typecheck— passbunx vitest run --config cli/vitest.issue996.config.ts src/codex/codexRemoteLauncher.test.ts src/codex/utils/terminalEventGuard.test.ts— 66 passedthread_status-only recovery condition (timeoutinstead ofexit)Note: local full
bun run testwas attempted earlier; it failed incli/src/runner/runner.integration.test.tsrunner integration cases in this environment, unrelated to this Codex launcher diff. Hub/web/shared tests passed separately.