Skip to content

fix(rescue): default to --background, make Bash-tool timeout terminal (closes #7)#8

Merged
axisrow merged 1 commit into
mainfrom
rescue-default-background
Jul 19, 2026
Merged

fix(rescue): default to --background, make Bash-tool timeout terminal (closes #7)#8
axisrow merged 1 commit into
mainfrom
rescue-default-background

Conversation

@axisrow

@axisrow axisrow commented Jul 19, 2026

Copy link
Copy Markdown
Owner

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-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 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:

  1. "Wait pattern = sleep/until-grep poll loop" — REFUTED. agents/codex-rescue.md:22-30 mandates exactly one Bash call and forbids polling. The polling was improvised by the subagent after the host auto-backgrounded its foreground run.
  2. "Broker spawns under wrong cwd" — REFUTED. resolveCommandCwd (codex-companion.mjs:157) preserves an explicit absolute --cwd unchanged 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.
  3. "No reaping" — PARTIAL. Runtime SessionEnd IS registered (hooks.json:15) and reaps (session-lifecycle-hook.mjs:83-114). Brokers are shared + warm by design (15-min idle timeout); abrupt process death can bypass cleanup. The observed orphans were test-harness leaks, addressed by PR test: reap orphaned brokers and clean temp dirs on npm test exit (closes openai#163) #5.

So this is a behavioral-contract gap in the rescue command/agent, not a broker or runtime bug.

Fix

  • 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/rescue.md, agents/codex-rescue.md, and tests/commands.test.mjs updated 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.

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
@axisrow

axisrow commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

🔍 Local review (cycle 1)

Reviewed locally (Claude subagent + Codex companion), no bots pinged.

Verdict Reviewer Finding Location
SKIP (out-of-scope) codex Default --background + --write → write-race in the shared checkout (no writer isolation) agents/codex-rescue.md:36, codex-companion.mjs:532

Why the Codex finding is SKIP / out-of-scope here

The race is pre-existing, not introduced by this PR:

The race is a known, separately-tracked class of bug — upstream openai#135 (feat: git worktree isolation for write-capable rescue tasks), with a ready closing PR openai#137 (@peterdrier, --worktree flag, 12 tests). The right fix is worktree isolation, which is its own substantial change and doesn't belong in a hang-fix PR.

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 findings

Clean — contract consistent across command↔agent↔test, --wait override preserved, terminal-on-timeout unambiguous, routing (--cwd/--resume/--fresh/--model/--effort/--write) undamaged, tests green (8/8). No FIX items.

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.

@axisrow
axisrow merged commit 52bd80f into main Jul 19, 2026
@axisrow
axisrow deleted the rescue-default-background branch July 19, 2026 06:08
axisrow added a commit that referenced this pull request Jul 20, 2026
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>
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.

codex:codex-rescue subagent hangs on poll loop; task broker runs under wrong cwd

1 participant