fix(rescue): default to --background, make Bash-tool timeout terminal (closes #7)#8
Conversation
Closes #7 (rephrased from code, not the original misdiagnosis). The codex:codex-rescue subagent hung because it defaulted to a foreground `task` run; when Codex exceeded Claude's Bash-tool timeout the host auto- backgrounded the call, and the rescue model then violated its one-Bash-call rule by improvising `cat` / `sleep 60` / `until grep ...; do sleep 5; done` polling — which itself violates Claude Code's foreground-sleep restriction. The Codex turn had actually completed; the hang was the subagent's polling, not the companion. This was a behavioral-contract gap, not a broker bug (verified by code trace and the recorded transcript): the companion foreground path correctly blocks on state.completion, an explicit --cwd is preserved unchanged through to the broker process argv, and runtime SessionEnd already reaps brokers/sockets. The original issue's "wait pattern" and "wrong cwd" causes are refuted; the "no reaping" cause conflates a real test-harness leak (fixed in PR #5) with runtime behavior. Fix the contract that actually failed: - rescue now defaults to --background (rescue work is open-ended; a foreground run that the host auto-backgrounds at its Bash-tool timeout cannot be turned back into a foreground wait, so start in background). --wait still selects foreground explicitly. - terminal-on-timeout: if the single task Bash call returns by host Bash-tool timeout, the subagent makes no second call (no status/result/cat/sleep/ until-grep). The Codex turn keeps running and is recovered by the caller via /codex:status or /codex:result — never by the subagent. commands.test.mjs assertions updated to the new contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0189cGaohsYdpkB4otKZnpAt
🔍 Local review (cycle 1)Reviewed locally (Claude subagent + Codex companion), no bots pinged.
Why the Codex finding is SKIP / out-of-scope hereThe race is pre-existing, not introduced by this PR:
The race is a known, separately-tracked class of bug — upstream openai#135 ( Action: race stays out of scope for #8. Fork tracking issue + +1 to openai#135 + port of openai#137 as a separate PR (see fork plan). Claude subagent findingsClean — contract consistent across command↔agent↔test, Triage of cycle 1/3 complete: 0 FIX, 1 SKIP (out-of-scope, tracked separately). Local mode does not auto-merge. PR #8 is ready to merge as the narrow hang-fix for #7. |
Security hardening release: leave-branch worktree cleanup (#12) + 4 security fixes (#18 symlink guard, #20 core.symlinks=false neutralize, #22 eliminate info/exclude write, #23 accumulation warn). rescue default --background (#8). test-teardown (#5). All verified with cycle-review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes #7 — rephrased from code evidence, not the issue's original diagnosis.
What actually happened (verified by code trace + the recorded transcript)
The
codex:codex-rescuesubagent hung because it defaulted to a foregroundtaskrun. When Codex exceeded Claude's Bash-tool timeout, the host auto-backgrounded the call, and the rescue model then violated its one-Bash-call rule by improvisingcat/sleep 60/until grep ...; do sleep 5; donepolling — which itself trips Claude Code's foreground-sleep restriction. The Codex turn had actually completed (transcript record 20 contained the answer); the hang was the subagent's polling, not the companion.The issue's three "Likely causes" were checked against the code:
agents/codex-rescue.md:22-30mandates exactly one Bash call and forbids polling. The polling was improvised by the subagent after the host auto-backgrounded its foreground run.resolveCommandCwd(codex-companion.mjs:157) preserves an explicit absolute--cwdunchanged through to the broker argv (broker-lifecycle.mjs:117). The/private/tmp/pr523-review/...processes were a separate test checkout, not the installed companion. Transcript record 4 passed the correct cwd.So this is a behavioral-contract gap in the rescue command/agent, not a broker or runtime bug.
Fix
--background. Rescue work is open-ended; a foreground run that the host auto-backgrounds at its Bash-tool timeout cannot be turned back into a foreground wait — so start in background.--waitstill selects foreground explicitly.taskBash call returns by host Bash-tool timeout, the subagent makes no second call (nostatus/result/cat/sleep/until-grep). The Codex turn keeps running and is recovered by the caller via/codex:statusor/codex:result— never by the subagent.commands/rescue.md,agents/codex-rescue.md, andtests/commands.test.mjsupdated to the new contract (8/8 assertions green).Diagnosis provenance
Independently confirmed by a Codex rescue run against this repo (session
019f7899-a6f7-7e43-a426-65908274bf91): same verdict — causes 1 and 2 refuted, cause 3 partial, bottom line "caller-orchestration misdiagnosis, not a fork broker bug."Not changed (deliberately)
Broker cwd routing, runtime SessionEnd cleanup, and the test-only teardown from PR #5 — all correct as-is.