Skip to content

fix: unblock /codex:rescue (context: fork) and long /codex:review runs (120s Bash cap)#239

Open
moabualruz wants to merge 1 commit intoopenai:mainfrom
moabualruz:fix/rescue-fork-and-review-timeout
Open

fix: unblock /codex:rescue (context: fork) and long /codex:review runs (120s Bash cap)#239
moabualruz wants to merge 1 commit intoopenai:mainfrom
moabualruz:fix/rescue-fork-and-review-timeout

Conversation

@moabualruz
Copy link
Copy Markdown

@moabualruz moabualruz commented Apr 17, 2026

Summary

Two independent bugs prevented Codex from ever being reached from the plugin's slash commands. Both are fixed here with surgical edits:

  1. /codex:rescue silently no-ops because the forked subagent's body tells it to delegate to another subagent, which Claude Code does not allow.
  2. /codex:review and /codex:adversarial-review in foreground get killed mid-run because the foreground flow uses a plain bash code block, which Claude Code's Bash tool runs with its default 120 s timeout — well under a real Codex review's 3-10 min runtime.

No frontmatter semantics change. context: fork, disable-model-invocation, and allowlists are untouched.

Bug 1: /codex:rescue — unexecutable delegation inside a fork

Repro

Run /codex:rescue <anything> (or mention /codex:rescue in a longer prompt so Claude dispatches via the Skill tool). Command appears to hang and nothing Codex-side ever runs.

Root cause

commands/rescue.md declares context: fork. Per the official Claude Code docs:

Subagents cannot spawn other subagents. If your workflow requires nested delegation, use Skills or chain subagents from the main conversation.

The rule is categorical and overrides allowed-tools. But the command body says:

Route this request to the codex:codex-rescue subagent.

The forked subagent therefore has no executable path: it can't call the Agent tool (subagents can't spawn subagents), and the companion script is never invoked. The visible artifact in session transcripts is a Skill({skill:\"codex:rescue\"}) tool use followed by toolUseResult: \"User rejected tool use\" — no Bash, no codex-companion, no subagent call.

Fix

Keep context: fork (the isolation is valuable — long Codex output stays out of the main conversation's context). Rewrite the body's self-references so the fork itself runs codex-companion.mjs task directly via its existing Bash(node:*) allowlist entry. The instructions the body already spells out (flag stripping, --write default, spark model mapping, resume question flow) remain intact — only the "subagent" references change:

before after
Route this request to the codex:codex-rescue subagent. Forward this request to Codex via the codex-companion.mjs task script.
run the codex:codex-rescue subagent in the background run the codex-companion.mjs task Bash call in the background
run the codex:codex-rescue subagent in the foreground run the codex-companion.mjs task Bash call in the foreground
add --resume before routing to the subagent add --resume before invoking codex-companion.mjs task
add --fresh before routing to the subagent add --fresh before invoking codex-companion.mjs task
The subagent is a thin forwarder only. It should use one Bash call ... You are a thin forwarder only. Use one Bash call ...
Do not ask the subagent to inspect files ... or do follow-up work of its own. Do not inspect files ... or do follow-up work of your own.
The subagent handles that routing when it builds the task command. Apply them when building the task command.

agents/codex-rescue.md is unchanged and still works for callers that invoke the rescue subagent via the Agent tool from a main-session context (e.g. proactive use by the main Claude outside this slash command).

Bug 2: /codex:review and /codex:adversarial-review — 120 s Bash cap kills long reviews

Repro

Run /codex:review --wait against a working tree with ~30+ file diff. Companion starts, issues real tool calls, then is killed before finishing; the job is left with status: running in state.

Root cause

The Foreground flow in both commands is documented as:

node \"${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs\" review \"\$ARGUMENTS\"

Claude Code's Bash tool, when the prompt doesn't tell it otherwise, defaults to a 120 000 ms timeout. Real Codex reviews over non-trivial diffs take several minutes (verified locally at ~5 m 30 s for a 44-file / 827-insertion working tree). The Bash call is SIGTERM'd before Codex returns; the detached companion state is left dangling.

Fix

Switch the Foreground flow to the same TypeScript-style Bash({...}) form the Background flow already documents, with timeout: 600000 (the 10-minute maximum Claude Code allows):

Bash({
  command: `node \"\${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs\" review \"\$ARGUMENTS\"`,
  description: \"Codex review\",
  timeout: 600000
})

Same change applied to adversarial-review.md. Tiny reviews are unaffected; larger ones now have room to finish.

Not fixed (flagged for maintainers)

commands/status.md runs !\node ... status "$ARGUMENTS"`via the!-prefix bang-exec form. If a user passes /codex:status --wait --timeout-ms withn > 120000, the same 120 s cap likely applies, since bang-exec goes through the same Bash runner. The DEFAULT_STATUS_WAIT_TIMEOUT_MSincodex-companion.mjsis 240 000, so the default behaviour can also hit the cap when--waitis passed with no--timeout-ms`. Changing the dispatch style is a more invasive edit and the flag combination is rarer, so leaving it here for the maintainers to decide.

Verification

  • Before patch: /codex:rescue reply the single word PONG produced no Bash call, no Codex call, and the Skill-tool dispatch was rejected (reproduced in live session transcript).
  • After patch: /codex:rescue reply the single word PONG returns PONG in a fresh Claude Code session.
  • /codex:review --wait on small diffs still returns as before. Large-diff runs that previously died at ~120 s now complete end-to-end up to the 10-min Bash cap.

Test plan

  • /codex:rescue reply the single word PONG returns PONG in a fresh session
  • /codex:rescue <multi-line task> runs codex-companion with --write, returns stdout verbatim
  • /codex:rescue --fresh <task> skips resume prompt; /codex:rescue --resume <task> passes --resume-last
  • /codex:rescue --background <task> returns immediately with a job id; /codex:status <id> tracks it
  • /codex:review --wait on a ~30-file diff completes past the old 120 s cap
  • /codex:adversarial-review --wait <focus> also completes past the old cap
  • /codex:review (no flag) still asks "Wait for results" vs "Run in background"

Two independent bugs prevented Codex from ever being reached from
/codex:rescue, /codex:review, and /codex:adversarial-review:

1. /codex:rescue silently no-ops.

   commands/rescue.md runs with `context: fork`, which spawns a
   forked subagent whose prompt is the command body. The body says
   "Route this request to the `codex:codex-rescue` subagent", but
   Claude Code subagents cannot spawn other subagents (per
   code.claude.com/docs/en/sub-agents), so the fork has no
   executable path: it can't call the Agent tool, and the rescue
   companion is never invoked.

   Fix: keep `context: fork` (the isolation is valuable for long
   Codex turns) and rewrite the fork's self-references in the body
   so it runs `codex-companion.mjs task` directly via its existing
   `Bash(node:*)` allowlist entry. Frontmatter is unchanged.
   `agents/codex-rescue.md` stays in place for callers that invoke
   the rescue subagent via the Agent tool from the main session.

2. /codex:review and /codex:adversarial-review time out in
   foreground.

   Both commands' Foreground flow runs the companion script via a
   plain ```bash ...``` block, which Claude Code's Bash tool runs
   with the default 120 000 ms timeout. Real Codex reviews on
   non-trivial diffs take 3-10 minutes, so the Bash call is killed
   before Codex returns and the job record is left as `running`.

   Fix: switch Foreground flow to the same TypeScript-style
   `Bash({...})` form the Background flow already uses, with
   `timeout: 600000` (the 10-minute maximum). Tiny reviews stay
   fast; larger ones now have room to finish.

Repro + verification

   - Repro for /codex:rescue: tail a recent session transcript and
     you'll see `Skill({skill:"codex:rescue"})` rejected with
     "User rejected tool use" — the fork's body has no executable
     path, so the tool call is the only visible attempt, and
     Codex never runs.
   - Verified fix: after the patch, `/codex:rescue reply the
     single word PONG` returns `PONG` in a fresh session.

Scope and non-goals

   - Not fixing: `/codex:status <job> --wait --timeout-ms <n>`
     when n > 120000 uses `!`-prefix bang-exec in status.md and
     can hit the same framework cap. Changing the dispatch style
     is a separate, more invasive edit and a rarer flag
     combination. Flagged here for maintainers.
@moabualruz moabualruz requested a review from a team April 17, 2026 19:41
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