Skip to content

fix: route /codex:rescue through the Agent tool to stop Skill recursion (#234)#235

Open
pengyou200902 wants to merge 1 commit intoopenai:mainfrom
pengyou200902:fix/rescue-command-agent-routing
Open

fix: route /codex:rescue through the Agent tool to stop Skill recursion (#234)#235
pengyou200902 wants to merge 1 commit intoopenai:mainfrom
pengyou200902:fix/rescue-command-agent-routing

Conversation

@pengyou200902
Copy link
Copy Markdown

@pengyou200902 pengyou200902 commented Apr 16, 2026

Summary

Fixes #234. When the main Claude Code agent called Skill(codex:rescue) programmatically (not via the user-typed slash command), the forked runner re-entered this same command and hung the session for minutes, burning tokens and never starting Codex.

Root cause: commands/rescue.md told the fork to "Route this request to the codex:codex-rescue subagent", without naming the transport. codex:codex-rescue is an Agent-tool subagent, not a skill, but the fork resolved the ambiguous prose by:

  1. Trying Skill(codex:codex-rescue)Unknown skill.
  2. Falling back to Skill(codex:rescue) → re-forked into itself → infinite recursion.

The slash command path is unaffected because Claude Code's slash-command router dispatches directly and does not go through that prose resolution.

Fix

  • commands/rescue.md: replace the ambiguous prose with an explicit instruction to use the Agent tool with subagent_type: "codex:codex-rescue".
  • Add Agent to allowed-tools so the fork can invoke it without a permission prompt.
  • Add a "Critical routing rule" block forbidding Skill(codex:rescue) / Skill(codex:codex-rescue) and telling the fork not to retry via Skill on failure.
  • Map --background onto Agent(..., run_in_background: true) so the existing execution-mode semantics still work.
  • tests/commands.test.mjs: add regression assertions that pin the Agent-tool routing and reject the old "Route this request to the codex:codex-rescue subagent" phrasing.

Both of the workarounds suggested in the issue are applied (explicit transport + narrowed tool surface); the prose fix is the load-bearing change.

Test plan

Automated:

  • node --test tests/commands.test.mjs -t "rescue command absorbs continue semantics" — rescue regression test passes against the patched file.
  • node --test tests/*.test.mjs — full suite: 85/86 pass, same baseline as main. The single remaining failure (result \$ARGUMENTS vs result "\$ARGUMENTS" in the quoting assertion) predates this PR; it was introduced in fix: quote $ARGUMENTS in cancel, result, and status commands #168 and is unrelated.
  • Negative regression: with rescue.md temporarily reverted to the pre-fix HEAD, the new assertions fail on the missing Agent allow-listing, the missing subagent_type: "codex:codex-rescue" routing line, and the lingering "Route this request to the codex:codex-rescue subagent" sentence — confirming they actually catch Skill(codex:rescue) infinite-recurses when invoked programmatically from main agent #234.

Surrogate manual (verifying the pieces the fork touches):

  • rescue.md frontmatter parses as valid YAML; allowed-tools is Bash(node:*), AskUserQuestion, Agent; Skill is not allow-listed.
  • agents/codex-rescue.md has name: codex-rescue, so Agent(subagent_type: "codex:codex-rescue") resolves under the codex plugin namespace.
  • .claude-plugin/plugin.json and the marketplace manifest still declare the codex plugin at 1.0.3 with source: ./plugins/codex.
  • The pre-route helper the fork runs first still works: node scripts/codex-companion.mjs task-resume-candidate --json{"available": false, ...} on a clean session.

Live end-to-end (requires a fresh Claude Code session with this branch's plugin loaded; not runnable from the session that prepared this PR because the installed plugin there is published 1.0.3 scoped to other project dirs):

  • From a main Claude Code agent, call Skill(codex:rescue, args: "<task>") and confirm the fork invokes Agent(subagent_type: "codex:codex-rescue")node codex-companion.mjs task ... without recursion.
  • Type /codex:rescue <task> as a user and confirm the slash-command path is unchanged.

…on (openai#234)

`commands/rescue.md` previously told the forked runner to "Route this
request to the `codex:codex-rescue` subagent", but `codex:codex-rescue`
is an `Agent` subagent — not a `Skill`. When the main Claude Code agent
called `Skill(codex:rescue)` programmatically, the fork interpreted the
prose as a skill route, tried `Skill(codex:codex-rescue)` (unknown),
then fell back to `Skill(codex:rescue)`, which re-entered this command
and hung the session with no Codex job ever starting.

- Name the transport explicitly: use the `Agent` tool with
  `subagent_type: "codex:codex-rescue"`.
- Add `Agent` to `allowed-tools` so the fork can invoke it without a
  permission prompt.
- Forbid `Skill(codex:rescue)` / `Skill(codex:codex-rescue)` and
  instruct the fork not to retry on failure.
- Map `--background` onto `Agent(..., run_in_background: true)`.
- Add regression tests in `tests/commands.test.mjs` that pin the
  Agent-tool routing and block the old "Route this request to the
  `codex:codex-rescue` subagent" phrasing.
@pengyou200902 pengyou200902 requested a review from a team April 16, 2026 23:03
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.

Skill(codex:rescue) infinite-recurses when invoked programmatically from main agent

1 participant