diff --git a/plugins/codex/agents/codex-rescue.md b/plugins/codex/agents/codex-rescue.md index 2e8a028d..b985cda6 100644 --- a/plugins/codex/agents/codex-rescue.md +++ b/plugins/codex/agents/codex-rescue.md @@ -20,9 +20,9 @@ Selection guidance: Forwarding rules: - Use exactly one `Bash` call to invoke `node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task ...`. -- If the user did not explicitly choose `--background` or `--wait`, prefer foreground for a small, clearly bounded rescue request. -- If the user did not explicitly choose `--background` or `--wait` and the task looks complicated, open-ended, multi-step, or likely to keep Codex running for a long time, prefer background execution. -- For any task expected to exceed a few minutes, add `--background` to the `task` invocation by default so a caller timeout cannot terminate it. +- Default to `--background`. Rescue tasks are open-ended by nature and routinely exceed Claude's Bash-tool timeout; a foreground `task` that gets auto-backgrounded by the host at its timeout MUST be treated as terminal (see below) — it cannot be turned back into a foreground wait, so backgrounding from the start avoids the trap entirely. Only use foreground (no `--background`) when the caller passed `--wait` explicitly. +- Add `--background` to the `task` invocation unless the caller explicitly chose `--wait`. +- If the single `task` Bash call returns because it hit the host's Bash-tool timeout (foreground run that the host auto-backgrounded) — STOP. Do NOT issue a second Bash call. Do NOT poll `status`, `result`, `cat`, `sleep`, or `until grep`. Return the companion's stdout so far (possibly empty) as-is. The Codex turn keeps running in the background and is recovered later via `/codex:status` / `/codex:result` by the caller — never by this subagent. - Treat `--cwd ` and `-C ` as workspace routing controls. Pass `--cwd ` explicitly on every `task` invocation, using the intended workspace root forwarded by the caller. - You may use the `gpt-5-4-prompting` skill only to tighten the user's request into a better Codex prompt before forwarding it. - Do not use that skill to inspect the repository, reason through the problem yourself, draft a solution, or do any independent work beyond shaping the forwarded prompt text. @@ -41,7 +41,7 @@ Forwarding rules: - Otherwise forward the task as a fresh `task` run. - Preserve the user's task text as-is apart from stripping routing flags. - Return the stdout of the `codex-companion` command exactly as-is. -- If the Bash call fails or Codex cannot be invoked, return nothing. +- If the Bash call fails, returns by host Bash-tool timeout, or Codex cannot be invoked, return nothing (or the partial stdout captured so far) and make NO further Bash calls. A timed-out foreground run is terminal, not a signal to poll. Response style: diff --git a/plugins/codex/commands/rescue.md b/plugins/codex/commands/rescue.md index 6fed2a0f..dd30cfed 100644 --- a/plugins/codex/commands/rescue.md +++ b/plugins/codex/commands/rescue.md @@ -15,7 +15,7 @@ Execution mode: - If the request includes `--background`, run the `codex:codex-rescue` subagent in the background. - If the request includes `--wait`, run the `codex:codex-rescue` subagent in the foreground. -- If neither flag is present, default to foreground. +- If neither flag is present, default to `--background`. Rescue tasks are open-ended and routinely exceed Claude's Bash-tool timeout; starting in background avoids the auto-background trap (see Operating rules). - `--background` and `--wait` are execution flags for Claude Code. Do not forward them to `task`, and do not treat them as part of the natural-language task text. - `--model` and `--effort` are runtime-selection flags. Preserve them for the forwarded `task` call, but do not treat them as part of the natural-language task text. - `--cwd ` and `-C ` are workspace-routing flags. Preserve the directory for the resume preflight and the forwarded `task` call, but do not treat either form as part of the natural-language task text. @@ -46,7 +46,8 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task-resume-candidate - Operating rules: - The subagent is a thin forwarder only. It should use one `Bash` call to invoke `node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task ...` and return that command's stdout as-is. -- Pass `--cwd ` explicitly for the intended workspace root. For work expected to exceed a few minutes, have the subagent add the task command's own `--background` flag so a caller timeout cannot terminate it. +- Pass `--cwd ` explicitly for the intended workspace root. Add `--background` to the `task` invocation unless the caller explicitly chose `--wait`, so a caller Bash-tool timeout cannot terminate the run or force an auto-background. +- Terminal-on-timeout: if the single `task` Bash call returns because the host auto-backgrounded it at the Bash-tool timeout, the subagent MUST make no second Bash call (no `status`, `result`, `cat`, `sleep`, or `until grep`). The Codex turn keeps running and is recovered later by the caller via `/codex:status` / `/codex:result`. - Return the Codex companion stdout verbatim to the user. - Do not paraphrase, summarize, rewrite, or add commentary before or after it. - Do not ask the subagent to inspect files, monitor progress, poll `/codex:status`, fetch `/codex:result`, call `/codex:cancel`, summarize output, or do follow-up work of its own. diff --git a/tests/commands.test.mjs b/tests/commands.test.mjs index d3c4f8e1..5dce734a 100644 --- a/tests/commands.test.mjs +++ b/tests/commands.test.mjs @@ -133,7 +133,7 @@ test("rescue command absorbs continue semantics", () => { assert.match(rescue, /Continue current Codex thread/); assert.match(rescue, /Start a new Codex thread/); assert.match(rescue, /run the `codex:codex-rescue` subagent in the background/i); - assert.match(rescue, /default to foreground/i); + assert.match(rescue, /default to `--background`/i); assert.match(rescue, /Do not forward them to `task`/i); assert.match(rescue, /`--model` and `--effort` are runtime-selection flags/i); assert.match(rescue, /Leave `--effort` unset unless the user explicitly asks for a specific reasoning effort/i); @@ -155,9 +155,10 @@ test("rescue command absorbs continue semantics", () => { assert.match(agent, /--resume/); assert.match(agent, /--fresh/); assert.match(agent, /thin forwarding wrapper/i); - assert.match(agent, /prefer foreground for a small, clearly bounded rescue request/i); - assert.match(agent, /If the user did not explicitly choose `--background` or `--wait` and the task looks complicated, open-ended, multi-step, or likely to keep Codex running for a long time, prefer background execution/i); - assert.match(agent, /expected to exceed a few minutes.*`--background`/i); + assert.match(agent, /Default to `--background`/i); + assert.match(agent, /Add `--background` to the `task` invocation unless the caller explicitly chose `--wait`/i); + assert.match(rescue, /Terminal-on-timeout/i); + assert.match(agent, /A timed-out foreground run is terminal, not a signal to poll/i); assert.match(agent, /pass `--cwd ` explicitly/i); assert.match(agent, /`--cwd ` and `-C `.*workspace routing controls/i); assert.match(agent, /Use exactly one `Bash` call/i); @@ -168,7 +169,7 @@ test("rescue command absorbs continue semantics", () => { assert.match(agent, /If the user asks for `spark`, map that to `--model gpt-5\.3-codex-spark`/i); assert.match(agent, /If the user asks for a concrete model name such as `gpt-5\.4-mini`, pass it through with `--model`/i); assert.match(agent, /Return the stdout of the `codex-companion` command exactly as-is/i); - assert.match(agent, /If the Bash call fails or Codex cannot be invoked, return nothing/i); + assert.match(agent, /If the Bash call fails, returns by host Bash-tool timeout, or Codex cannot be invoked, return nothing/i); assert.match(agent, /gpt-5-4-prompting/); assert.match(agent, /only to tighten the user's request into a better Codex prompt/i); assert.match(agent, /Do not use that skill to inspect the repository, reason through the problem yourself, draft a solution, or do any independent work/i);