Skip to content

feat(codex): harden turn timeout — interrupt on deadline, bound stalled turn/start, options threading#1

Open
russjhammond wants to merge 1 commit into
fix/bound-codex-turn-timeoutfrom
feat/turn-timeout-hardening
Open

feat(codex): harden turn timeout — interrupt on deadline, bound stalled turn/start, options threading#1
russjhammond wants to merge 1 commit into
fix/bound-codex-turn-timeoutfrom
feat/turn-timeout-hardening

Conversation

@russjhammond

Copy link
Copy Markdown
Owner

Follow-up to openai#376 — hardening the turn timeout

Stacked on openai#376 (the turn-await hang fix). This ports the
three follow-up improvements @axisrow found while
adapting openai#376 into their fork
(axisrow/codex-plugin-cc#28),
with attribution. On openai#376 I'd deliberately scoped these out as follow-ups; this
PR is that follow-up.

Base is fix/bound-codex-turn-timeout so the diff shows only the incremental
commit. Retarget to openai:main once openai#376 merges.

What changes

  1. Bound a stalled turn/start too. Arm the deadline before turn/start
    and race the whole turn lifecycle against it. Previously the deadline only
    covered the post-start completion await, so an app-server that accepted the
    connection but never answered turn/start still hung until the host's
    external ceiling.
  2. Interrupt on deadline. When the deadline (or any lifecycle error) fires,
    best-effort interruptAppServerTurn(...) so the broker stops executing a
    write-capable turn after we've already reported the job failed. Failures to
    interrupt (broker gone, network down) never mask the original error.
  3. Thread the budget via options, not process.env.
    resolveTurnTimeoutMsFromOptions resolves flag → env → mode-default and flows
    through buildTaskRequest → executeTaskRun/executeReviewRun → runAppServerTurn/Review → captureTurn, replacing the CODEX_TURN_TIMEOUT_MS
    mutation. This retires the foreground→background env-inheritance hazard
    entirely; background runs get the full default budget (no external ceiling to
    collide with).

Also drops the redundant exitRaceSettled guard — state.completed already
covers the late-exit-after-completion race.

One deliberate divergence from axisrow#28

I kept openai#376's exit-wiring placement after startRequest resolves (commit
688192a). Hoisting it up with the deadline (as openai#28 does) would reject an
unobserved state.completion when startRequest itself rejects —
reintroducing exactly the unhandled-rejection that openai#376's review already fixed.
The deadline alone bounds the stalled-turn/start case, so the hoist isn't
needed for that.

Test

New stalled-turn-start fake-codex behavior + a test asserting a foreground
task whose turn/start never responds times out on the turn budget instead of
hanging. tests/helpers.mjs run() gains an optional timeout so the hang
path can't block CI. Full suite: no new failures (the 4 reds are pre-existing,
env/temp-dir related, unchanged on main).

Refs openai#49. Credits axisrow#26 (port) and openai#28 (findings).

…ed turn/start, thread budget via options

Follow-up to openai#376 (the turn-await hang fix), porting the three improvements
axisrow found while adapting openai#376 into their fork, with attribution:

1. Arm the deadline BEFORE turn/start and race the whole turn lifecycle, so a
   stalled turn/start (app-server alive but never responding) is bounded too —
   not just the post-start completion await.
2. On the deadline (or any lifecycle error), best-effort interrupt the in-flight
   turn so the broker stops executing a write-capable task after the job is
   already reported failed.
3. Resolve the per-turn budget from options and thread it through the request
   (buildTaskRequest -> executeTaskRun/executeReviewRun -> runAppServerTurn/
   Review -> captureTurn) instead of mutating process.env — retiring the
   foreground->background env-inheritance hazard. Background runs get the full
   default budget.

Also drops the redundant exitRaceSettled guard (state.completed already covers
the late-exit-after-completion race).

Kept openai#376's exit-wiring placement AFTER startRequest resolves (commit 688192a):
hoisting it with the deadline would reject an unobserved state.completion when
startRequest itself rejects, reintroducing the unhandled-rejection that openai#376's
review already fixed. The deadline alone bounds the stalled-turn/start case.

Test: a foreground task whose turn/start never responds now times out on the
turn budget instead of hanging (new stalled-turn-start fake-codex behavior).
tests/helpers.mjs run() gains an optional timeout so the hang path can't block CI.

Credits: axisrow#26 (port) and openai#28 (these three findings).
Refs openai#49.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5iLDcuiEzAxs1vBEwafur
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant