A /codex:rescue task or a background / stop-gate review can get stuck in running indefinitely - it never returns, never fails, and never times out, so /codex:status keeps showing it active until a manual /codex:cancel.
Root cause: captureTurn awaits a state.completion that only resolves via a main-thread turn/completed or the inferred-completion timer (which needs a final_answer). If Codex stops emitting terminal events on a still-open connection (both final_answer and turn/completed dropped), that promise never settles and the job stays running. The captured output sits in state.lastAgentMessage but is never returned.
In local job state across many workspaces this had already wedged ~100 jobs in running (most with a dead worker process, stuck for more than a day - a lower bound, since older job records are pruned).
Proposed fix: #390 (an idle watchdog + an exitPromise race in captureTurn that salvage the stalled turn and report it as incomplete), with #389 as a small build prerequisite.
A
/codex:rescuetask or a background / stop-gate review can get stuck inrunningindefinitely - it never returns, never fails, and never times out, so/codex:statuskeeps showing it active until a manual/codex:cancel.Root cause:
captureTurnawaits astate.completionthat only resolves via a main-threadturn/completedor the inferred-completion timer (which needs afinal_answer). If Codex stops emitting terminal events on a still-open connection (bothfinal_answerandturn/completeddropped), that promise never settles and the job staysrunning. The captured output sits instate.lastAgentMessagebut is never returned.In local job state across many workspaces this had already wedged ~100 jobs in
running(most with a dead worker process, stuck for more than a day - a lower bound, since older job records are pruned).Proposed fix: #390 (an idle watchdog + an
exitPromiserace incaptureTurnthat salvage the stalled turn and report it as incomplete), with #389 as a small build prerequisite.