feat: add codex-reviewer subagent for programmatic reviews (#354)#462
feat: add codex-reviewer subagent for programmatic reviews (#354)#462ayobamiseun wants to merge 3 commits into
Conversation
Slash commands do not compose, so downstream plugins that want to chain a Codex review into a pipeline had to dispatch the write-capable codex-rescue subagent and trust prompt-level instructions to keep it read-only, or shell into the plugin cache directly. Add codex:codex-reviewer, a thin forwarding subagent with the same shape as codex-rescue but hard-coded to review semantics: it forwards exactly one Bash call to codex-companion.mjs review (or adversarial-review when the request carries focus text or an adversarial framing), never adds --write, never forwards to task, has no resume semantics, and returns the companion stdout verbatim. Document the subagent in the README and pin its review-only contract in tests alongside the existing codex-rescue assertions. Fixes openai#354
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55f935be64
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| - Only pass through flags the review runtime accepts: `--wait`, `--background`, `--base <ref>`, `--scope <auto|working-tree|branch>`, `--json`, `--model <model>`, and `--cwd <dir>`. Do not invent other flags. | ||
| - If the request did not explicitly choose `--background` or `--wait`, run in the foreground and wait for the review to finish. |
There was a problem hiding this comment.
Use Bash background mode for --background reviews
When this subagent is invoked with --background, these rules only pass the flag through to codex-companion. I checked handleReviewCommand in plugins/codex/scripts/codex-companion.mjs, and the review path parses --background but still unconditionally calls runForegroundCommand; the existing slash-command docs likewise note that Claude Code's Bash(..., run_in_background: true) is what actually detaches reviews. As a result, Agent({ subagent_type: "codex:codex-reviewer", prompt: "review --background" }) will block inside the subagent until the review finishes instead of returning a background launch/status message, which breaks the advertised programmatic background-review workflow for large reviews.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — addressed in dd70a99. The agent now mirrors the slash commands: --background requests launch the single Bash call with run_in_background: true (no BashOutput, returns the /codex:status pointer message instead of stdout), and foreground runs keep the verbatim-stdout contract. Pinned in the commands.test.mjs assertions as well.
The companion's review path parses --background but still calls runForegroundCommand unconditionally, so forwarding the flag alone would block the subagent until the review finishes. Mirror the slash commands: launch the single Bash call with run_in_background: true, skip BashOutput, and return the background status message instead of stdout. Foreground runs keep the verbatim-stdout contract.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd70a99f37
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| - Only pass through flags the review runtime accepts: `--wait`, `--background`, `--base <ref>`, `--scope <auto|working-tree|branch>`, `--json`, `--model <model>`, and `--cwd <dir>`. Do not invent other flags. | ||
| - The companion script parses `--wait` and `--background`, but Claude Code's `Bash(..., run_in_background: true)` is what actually detaches the run. Do not strip these flags yourself. | ||
| - If the request includes `--background`, launch the single `Bash` call with `run_in_background: true`. Do not call `BashOutput` or wait for completion. Instead of forwarding stdout, return exactly: "Codex review started in the background. Check `/codex:status` for progress." |
There was a problem hiding this comment.
Honor --json for background reviewer launches
When a programmatic caller requests --background --json, this path accepts --json but then always returns fixed prose after launching the Bash command and discards the companion stdout. handleReviewCommand parses --json for reviews and runForegroundCommand emits JSON in that mode, so automation that combines background execution with machine-readable output gets a non-JSON response despite requesting JSON; either reject/document that combination or return a structured launch response.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 8bb9a40 with the structured-launch-response option: --background --json now returns a fixed JSON launch object ({"status":"started",...}) instead of the prose message, with the note that the review payload doesn't exist at launch time and callers fetch it via result --json. Pinned in the test assertions.
Background launches detach via Bash background mode, so the review's JSON payload does not exist when the subagent returns. Callers that combined --background with --json previously got the fixed prose launch message - non-JSON output despite requesting JSON. Return a fixed structured launch object for that combination and point callers at result --json for the completed payload.
Fixes #354
Problem
/codex:reviewand/codex:adversarial-reviewcan't be invoked from another plugin's command — slash commands don't compose. The only Codex artifact dispatchable via theAgenttool iscodex-rescue, which is hard-coded tocodex-companion.mjs task(write-capable by default, not review-shaped). Downstream pipelines that want to chain a Codex review either misusecodex-rescuewith prompt-level "stay read-only" instructions, or shell into~/.claude/plugins/cache/.../codex-companion.mjsdirectly and break on version updates.Change
Adds
plugins/codex/agents/codex-reviewer.md— a sibling subagent with the same thin-forwarding shape ascodex-rescue, hard-coded to review semantics:Bashcall forwarding tocodex-companion.mjs review ..., oradversarial-review ...when the request carries focus text / custom instructions / an adversarial framing (plainreviewmaps to the built-in reviewer and rejects focus text).--wait,--background,--base,--scope,--json,--model,--cwd); defaults to foreground when neither execution flag is given.--write, never forwards totask, no--resume/--fresh/--resume-lastsemantics, forbidden from callingtask/setup/transfer/status/result/cancel.codex-rescue(unset by default,spark→gpt-5.3-codex-spark) and same no-independent-work / return-stdout-verbatim / return-nothing-on-failure contract.One
Agentcall now dispatches a Codex review:Also in this PR
codex:codex-reviewersection documents the programmatic-dispatch use case.codex-reviewer agent is a review-only thin forwardertest intests/commands.test.mjspins the review-only contract (no--write, notask, verbatim stdout, README coverage), mirroring the existingcodex-rescueassertions.Deliberately no
codex-cli-runtimeskill reference — that skill's contract is task-specific ("use this skill only inside thecodex:codex-rescuesubagent", "do not callreview,adversarial-review…") and would contradict the reviewer.Tests
Full suite: 91/92 pass; the one failure (
resolveStateDir uses a temp-backed per-workspace directory) fails identically onmainwhen run inside a live Claude Code session (pre-existing #455, unrelated).