fix(codex): accept thread-matching stale terminal events during same-thread recovery - #1189
Open
tiann wants to merge 1 commit into
Open
fix(codex): accept thread-matching stale terminal events during same-thread recovery#1189tiann wants to merge 1 commit into
tiann wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Findings
- [Major] Matching-thread fallback can accept an older terminal event as the active retry completion.
shouldIgnoreTerminalEventnow returnsfalsefor any mismatchedeventTurnIdwhen the thread ids match, andcodexRemoteLauncherenables that path whilesameThreadRetryAttemptorsameThreadCompactAttemptis nonzero. If a duplicate/late terminal forturn-1arrives after retryturn-2has started, the terminal block clearscurrentTurnId/turnInFlightand schedules ready even though the retry turn is still running. Evidence:cli/src/codex/utils/terminalEventGuard.ts:24,cli/src/codex/codexRemoteLauncher.ts:2644.
Suggested fix:Then handle the known same-thread recovery terminal explicitly inif (input.eventTurnId) { if (!input.currentTurnId || input.eventTurnId === input.currentTurnId) { return false; } return true; }
codexRemoteLauncher, scoped to the stalelastFinalizedTurnIdcase, instead of letting the shared guard accept every turn-id mismatch for that thread.
Summary
- Review mode: initial
- Found one stale-terminal handling regression risk in the latest diff.
Testing
- Not run (automation)
HAPI Bot
| if (!input.currentTurnId || input.eventTurnId === input.currentTurnId) { | ||
| return false; | ||
| } | ||
| return !(allowMatchingThreadIdTerminalEvent && hasMatchingThreadId); |
There was a problem hiding this comment.
[Major] This now accepts any mismatched eventTurnId when allowMatchingThreadIdTerminalEvent is true and the thread ids match. In the launcher that flag is enabled during same-thread retry/compact recovery, so a duplicate/late terminal for an older turn can pass while a new retry turn is active; the terminal handler then clears currentTurnId/turnInFlight and may emit ready before the retry actually finishes.
Suggested fix:
if (input.eventTurnId) {
if (!input.currentTurnId || input.eventTurnId === input.currentTurnId) {
return false;
}
return true;
}Handle the intended stale lastFinalizedTurnId recovery case explicitly in codexRemoteLauncher, so the shared guard does not accept arbitrary turn-id mismatches for the current thread.
swear01
added a commit
to swear01/hapi
that referenced
this pull request
Jul 28, 2026
fix(codex): ignore stale terminal events after same-thread recovery
swear01
added a commit
to swear01/hapi
that referenced
this pull request
Jul 29, 2026
fix(codex): ignore stale terminal events after same-thread recovery
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1070
Root cause
After a same-thread retry/compact recovery opens a new Codex turn, a terminal event carrying the previous
turn_idnever finalizes the new turn:shouldIgnoreTerminalEvent()unconditionally ignores stale-turn events, and the launcher only passedallowMatchingThreadIdTerminalEventforterminal_source === 'thread_status'. The session then stays non-ready (thinkingstuck, noready).Fix
Following the fork patch referenced in the issue (mouriya-s-lab/hapi@6c675985):
cli/src/codex/utils/terminalEventGuard.ts: a stale-turn terminal event is accepted when the event thread matches the current thread and the caller explicitly allows it (allowMatchingThreadIdTerminalEvent). Events for a different thread are still ignored.cli/src/codex/codexRemoteLauncher.ts: the allow condition is extended toterminal_source === 'thread_status' || sameThreadRetryAttempt > 0 || sameThreadCompactAttempt > 0, i.e. thread-matching stale terminal events are accepted while a same-thread recovery is in flight so the turn finalizes andreadyis emitted.Deviation from the fork patch (code drift since the issue was filed): on current
mainthe stale event is dropped even earlier by thelastFinalizedTurnIdduplicate-terminal check, before it ever reaches the guard. The same recovery exemption was therefore added to that check as well; without it the new regression tests time out exactly as the production session does.Note on issue wording: the issue text frames stale terminal events as something that must stay scoped to the active turn, but the fork patch's actual semantics are the opposite for the recovery window — accept thread-matching stale terminal events so the session recovers
ready. This PR follows the fork semantics.Tests
cli/src/codex/codexRemoteLauncher.test.ts: same-thread retry and same-thread compact recovery completing with a staleturn_idboth emitreadyand clearthinking.cli/src/codex/utils/terminalEventGuard.test.tsfor the thread-match exemption (accepted on matching thread, still ignored on different thread).bun typecheck— passes.bunx vitest run src/codex/codexRemoteLauncher.test.ts src/codex/utils/terminalEventGuard.test.ts— 79/79 pass.bun run test(cli, full suite) — 154 files / 1482 tests pass.Known trade-offs / risks
turn_idterminal events on this recovery path.src/modules/common/cursorModels.test.ts("does not spawn agent --list-models while ACP transport is active") failed in 2 of 4 full-suite runs on this branch and passes standalone on both this branch and a cleanorigin/mainbaseline; it touches no Codex code and appears load/ordering sensitive.