Skip to content

fix: route Codex "Approve for me" reviews in the shipped Switchyard configs - #872

Merged
elyasmnvidian merged 2 commits into
mainfrom
emehtabuddin/switch-1630-codex-approve-for-me-switchyard
Oct 2, 2026
Merged

elyasmnvidian merged 2 commits into
mainfrom
emehtabuddin/switch-1630-codex-approve-for-me-switchyard

Conversation

@elyasmnvidian

@elyasmnvidian elyasmnvidian commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What

When Codex runs through Switchyard with "Approve for me" on, Codex declines every action that needs approval. With "Approve for me" on, Codex asks a reviewer model to approve each of these actions, and it sends that review request to the model ID codex-auto-review. Neither examples/run_codex.sh nor scripts/config/composite.toml (the server config that scripts/linux/install.sh copies) had a route with that ID. So the server returned 404 model_not_found, and Codex treated the failed review as a denial.

This affects anyone who uses Codex through Switchyard with "Approve for me" on in the Default or Read Only mode, including Codex logged in with ChatGPT. It does not affect Full Access mode, which never asks for approval, or sessions with "Approve for me" off.

This PR adds a codex-auto-review route to both shipped configs. The route sends the review request unchanged to codex-auto-review on the ChatGPT backend, using the chatgpt_backend client the configs already have (forward_auth = true, so Switchyard forwards the caller's login):

[targets.reviewer]
id = "codex-auto-review"
llm_client = "chatgpt_backend"

[routes.codex_auto_review]
id = "codex-auto-review"
type = "passthrough"
target = "reviewer"

dev-server/config.toml has no ChatGPT backend client, so its new codex-auto-review route uses its existing efficient target (gpt-5.6-luna). Reviews still run at low effort, because Codex asks for low and the target's extra_body only fills in a missing effort. The dev server needs the file copied over and a restart after merge (see dev-server/README).

There are no code changes. A new server README section explains the route and recommends a small, fast reviewer at low reasoning effort for anyone who points it at another model. The installer never overwrites an existing ~/.switchyard/composite.toml, so existing installs must add the two blocks by hand.

Why

Codex sends each review request to the session's own model provider, so when Codex points at Switchyard, the review goes to Switchyard. Codex picks the model ID from its login type, not from Switchyard: every login uses codex-auto-review, except an OpenAI API-key login, which uses gpt-5.6-luna. The README covers the API-key case and its catch: a gpt-5.6-luna route also gets Codex's other requests for that ID.

Codex waits for each review before it runs the action, so a slow reviewer slows down every step that needs approval. Codex keeps its own reviews light: every review request asks for low reasoning effort, and OpenAI's Auto-review post says its reviewer runs GPT-5.4 Thinking at low reasoning. The same post says stronger models catch risky actions more reliably, so a small model trades some safety for speed and cost.

Codex has no simple setting to pick the reviewer model. The only way is a full replacement model catalog (model_catalog_json) whose session-model entry sets auto_review_model_override. That catalog must list every model Codex uses and changes with Codex releases, so a Switchyard route is simpler.

An earlier version of this PR also added a route type that approved every action without a review. I removed it, because Codex's own settings already do that: Full Access (--dangerously-bypass-approvals-and-sandbox) runs every action without a review, and approval_policy = "never" keeps the sandbox but sends no reviews. The README points people to those settings.

Notes for reviewers

Start with the new section in crates/switchyard-server/README.md. The second commit adds the dev server route and the reviewer-model advice. The macOS menu bar price for codex-auto-review is suggested on #902.

The new route changes GET /v1/models: the server sorts route IDs, and codex-auto-review sorts before switchyard, so default_model and the startup banner's example curl now use codex-auto-review. I found no client in this repo or in Codex that reads default_model.

No test below sent traffic to OpenAI or ChatGPT. Model IDs on the OpenAI-compatible LiteLLM gateway are shown as their public IDs.

Dry runs of the three configs

switchyard-server --dry-run built from this branch:

scripts/config/composite.toml:           server OK: codex-auto-review, switchyard
config written by examples/run_codex.sh: server OK: codex-auto-review, switchyard
dev-server/config.toml:                  server OK: switchyard/advisor, switchyard/classifier, codex-auto-review, switchyard/noop, switchyard/passthrough, switchyard/random, switchyard/stage
Replay: what the shipped route forwards to the ChatGPT backend

I copied scripts/config/composite.toml and pointed its chatgpt_backend client at a local stub. Then I sent a review request captured from Codex 0.152.0 to POST /v1/responses with model: codex-auto-review, fake authorization and chatgpt-account-id values, and Codex's x-openai-subagent: guardian header. The server returned 200 and streamed the stub's {"outcome":"allow"} verdict. The stub recorded:

path: /backend-api/codex/responses
model: codex-auto-review
authorization: Bearer fake-test-token     (the caller's value)
chatgpt-account-id: acct-test-0000        (the caller's value)
body: byte-for-byte identical to the request sent to Switchyard

Server log:

passthrough selected target target=codex-auto-review
LLM request handled wire_format=openai_responses status=200 requested_model="codex-auto-review" selected_model="codex-auto-review" streaming=true
Dev server config: reviews run on gpt-5.6-luna at low effort

What reaches the gateway. I copied dev-server/config.toml, pointed its gateway client at a local stub, and replayed the captured Codex review request. Then I sent one plain request with no reasoning effort to switchyard/passthrough, which uses the same target. The stub recorded:

Request Upstream model Upstream reasoning.effort
Codex review, model: codex-auto-review gpt-5.6-luna low, Codex's value
Plain request to switchyard/passthrough, no effort set gpt-5.6-luna medium, from the target's extra_body

The review also kept Codex's JSON output schema (codex_output_schema).

Real gateway. With the unmodified dev-server/config.toml, the same replay returned HTTP 200 in 2.5 s and streamed {"outcome":"allow"}. The model used 130 output tokens, 112 of them for reasoning.

Codex end to end. Codex 0.152.0, codex exec --ephemeral --skip-git-repo-check --approve-for-me, a temporary CODEX_HOME with no OpenAI login, and switchyard/passthrough as the session model. Codex ran with approval: on-request and sandbox: workspace-write. Its curl https://example.com command needed network access, so Codex sent it for review, then ran it and printed 200. Server log, trimmed:

status=200 requested_model="switchyard/passthrough" selected_model="gpt-5.6-luna" streaming=true
status=200 requested_model="codex-auto-review" selected_model="gpt-5.6-luna" streaming=true handling_duration_ms=766.852208
status=200 requested_model="switchyard/passthrough" selected_model="gpt-5.6-luna" streaming=true
Codex end to end, reviewer on the gateway

Codex 0.152.0, codex exec --ignore-user-config --ephemeral --skip-git-repo-check --approve-for-me --json, a temporary CODEX_HOME with no OpenAI login, and a custom model provider pointing at a local Switchyard server (env_key, wire_api = "responses"). The prompt asked Codex to run curl https://example.com with network access, which the sandbox blocks, so Codex asked for approval.

Config:

[llm_clients.gateway]
format = "openai_chat"
base_url = "https://<gateway>/v1"
api_key_env = "GATEWAY_API_KEY"

[targets.haiku]
id = "claude-haiku-4-5"
llm_client = "gateway"

[targets.reviewer]
id = "gpt-5.6-luna"
llm_client = "gateway"

[routes.switchyard]
id = "switchyard"
type = "passthrough"
target = "haiku"

[routes.codex_auto_review]
id = "codex-auto-review"
type = "passthrough"
target = "reviewer"

Server log:

status=200 requested_model="switchyard" selected_model="claude-haiku-4-5" streaming=true
status=200 requested_model="codex-auto-review" selected_model="gpt-5.6-luna" streaming=true
status=200 requested_model="switchyard" selected_model="claude-haiku-4-5" streaming=true

Codex events, trimmed:

{"type":"command_execution","command":"/bin/zsh -lc \"curl -sS -o /dev/null -w '%{http_code}' https://example.com\"","aggregated_output":"200","exit_code":0,"status":"completed"}
{"type":"agent_message","text":"200"}

In an earlier run of the same setup without the route, the server logged status=404 requested_model="codex-auto-review" and Codex reported the command as declined.

@elyasmnvidian
elyasmnvidian requested a review from a team as a code owner September 29, 2026 18:08
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1

🚀 View preview at
https://NVIDIA-NeMo.github.io/Switchyard/pr-preview/pr-872/

Built to branch gh-pages at 2026-10-02 18:13 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch from ee44fbc to 3e9a724 Compare October 1, 2026 20:07
@messiaen
messiaen force-pushed the grclark/mac-deamon branch 3 times, most recently from c871c35 to 042211d Compare October 2, 2026 00:02
@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch 2 times, most recently from 36124e8 to c3da05d Compare October 2, 2026 18:07
@elyasmnvidian
elyasmnvidian changed the base branch from grclark/mac-deamon to main October 2, 2026 18:08
@elyasmnvidian

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA-NeMo/Switchyard/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8b417fad-0fa8-4c43-a319-3cdb7948e94e

📥 Commits

Reviewing files that changed from the base of the PR and between 16cbe59 and c3da05d.

📒 Files selected for processing (11)
  • crates/libsy/src/algorithms.rs
  • crates/libsy/src/algorithms/codex_approve_all.rs
  • crates/libsy/src/algorithms/noop.rs
  • crates/libsy/src/algorithms/util.rs
  • crates/libsy/src/lib.rs
  • crates/switchyard-runner/src/algorithm.rs
  • crates/switchyard-server/README.md
  • crates/switchyard-server/tests/server.rs
  • docs/reference/toml_schema.md
  • examples/run_codex.sh
  • scripts/config/composite.toml

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 7 remain after this review.


Walkthrough

The change adds a CodexApproveAll algorithm that returns an allow verdict without calling an upstream model. It registers the algorithm with the runner, tests streamed and buffered responses, and documents and configures Codex review routing.

Changes

Codex review routing

Layer / File(s) Summary
Fixed replies and Codex approval
crates/libsy/src/algorithms/util.rs, crates/libsy/src/algorithms/codex_approve_all.rs, crates/libsy/src/algorithms.rs, crates/libsy/src/lib.rs, crates/libsy/src/algorithms/noop.rs
Adds fixed_reply for streamed or buffered synthetic responses. CodexApproveAll uses it to return an allow verdict, and Noop uses it for its existing reply. The crate publicly exports CodexApproveAll.
Runner configuration and route test
crates/switchyard-runner/src/algorithm.rs, crates/switchyard-server/tests/server.rs
Adds the CodexApproveAll algorithm specification and construction. The runner does not assign it completion targets or runtime model groups. An integration test checks allow verdicts in streamed and buffered responses.
Codex route setup and documentation
crates/switchyard-server/README.md, docs/reference/toml_schema.md, examples/run_codex.sh, scripts/config/composite.toml
Documents Codex review model selection and route options, including codex_approve_all. Adds example configurations that forward codex-auto-review to a reviewer target.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to c3da0

The Codex review route and approval option have no identified issue requiring a fix before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 8 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: routing Codex "Approve for me" reviews in the shipped Switchyard configurations. It is concise and specific.
Full details: Docstring Coverage

Explanation

Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 8 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


A rabbit checks the review route at night,
An allow reply comes back just right.
Stream or buffer, both replies appear,
No upstream model is needed here.
I nibble clover, then hop away,
While Codex reviews take a new pathway.

Comment @coderabbitai help to get the list of available commands.

@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch from c3da05d to 91092be Compare October 2, 2026 18:12
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

No files to review.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch from 91092be to 79c3328 Compare October 2, 2026 18:39
@elyasmnvidian elyasmnvidian changed the title feat(routing): route Codex "Approve for me" reviews and add codex_approve_all fix: route Codex "Approve for me" reviews in the shipped Switchyard configs Oct 2, 2026
…onfigs

Signed-off-by: Elyas Mehtabuddin <emehtabuddin@nvidia.com>
…wer model

Signed-off-by: Elyas Mehtabuddin <emehtabuddin@nvidia.com>
@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch from a797312 to 97a3139 Compare October 2, 2026 19:35
@elyasmnvidian
elyasmnvidian merged commit 5b670a1 into main Oct 2, 2026
15 checks passed
@elyasmnvidian
elyasmnvidian deleted the emehtabuddin/switch-1630-codex-approve-for-me-switchyard branch October 2, 2026 19:43
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.

2 participants