Claude Code writes, Codex reviews, a gate decides what merges. Here's the short version I check before trusting that loop on a repo. The full walkthrough, with the prompts, is in How to make Codex review Claude Code's commits.
1. Codex runs read-only.
codex exec -s read-only --color never --output-last-message <file> -, with the prompt on stdin. The reviewer can read files and run git diff, but it can't write or hand back a patch.
2. One verdict line, saved under the commit hash.
Verdict: ACCEPT or Verdict: CHANGES REQUIRED, read only from the --output-last-message file. A crash, an empty answer or two Verdict lines leave no saved verdict, so the commit can't merge.
3. A brief exists before the code.
Every line under "Done means" is something a reviewer can confirm from the diff or a test. Write "a customer email with a comma is escaped", never "export works well".
4. Each new test failed on the base commit.
The engine that writes the code often writes the tests too. A test that already passes on the old code proves nothing about the change.
5. House rules live where both engines read them.
CLAUDE.md for Claude Code, AGENTS.md for Codex. Keep the AGENTS.md part short: Codex stops adding instruction files once their combined size reaches project_doc_max_bytes, 32 KiB by default.
6. The reviewer answers every line.
Met with a file:line, not met with what's missing, or not checkable with the evidence it would need. Findings outside the brief need a concrete failure. Style preferences aren't findings.
7. Rounds have a hard cap.
Later rounds raise only what blocks the merge. If the last allowed round says CHANGES REQUIRED, the merge stays blocked and a person decides.
The merge is refused unless that exact commit has a test pass and a saved ACCEPT. A test pass alone won't catch a test Claude Code weakened in the same commit, so that falls to the reviewer: what my review gate does with weakened and deleted tests.
If you want the whole setup prebuilt, the Two Engines Kit for Claude Code and Codex is a paid kit I made, built on information gathered since the first Codex and Claude CLIs.
Top comments (18)
I run the same loop on my Mac apps, with Codex read-only as well. I’d add one step after 6: the author gives every finding a verdict and looks for what the review missed, so a clean ACCEPT isn’t the last word. The side benefit was cost: moving reviews to Codex cut Claude tokens per review by about 71% in my logs.
That step after 6 belongs in the list. A clean ACCEPT only tells you what Codex didn't find. Watch who gives the verdicts. If it's Claude Code, it's grading findings on its own code, and authors are quick to explain a real finding away. I'd make every rejected finding carry a written reason pinned to a file and line, so a dismissal has to survive a second look. Does your 71% already count that step? The verdicts and the hunt for misses run on Claude Code tokens too.
Agreed on the written reason. A dismissal with no file and line is just the author having the last word. On the number: the 71% is Claude tokens per review in my logs, about 5.1M when Claude reviewed alone against about 1.5M with Codex doing the review. The Claude work that’s left, verdicts and the look for misses, sits inside that smaller number, so it’s counted, not free.
Then the 71% is a fair cost number: the verdicts and the look for misses sit inside the 1.5M. Cost is half the comparison, though. If Claude Code alone flagged bugs the Codex run missed, part of that saving comes back as rework. Have you run both setups on the same diffs and compared what each one caught?
Once, on one diff with a known answer key: 3 real bugs. Claude Code’s own reviewers had raised 11 claims on it, 4 of them false alarms. Codex found 2 of the 3 known bugs, plus one real bug the Claude reviewers had missed, and raised no false alarms. So neither caught everything alone, which is why I kept the miss-hunt step. One diff is an anecdote, not a benchmark, so I’d want a bigger side-by-side before calling the rework cost settled.
@iliyaoblakov Agreed on the rule. The hop count is only a proxy: what matters is whether the function that validates the value is in the batch. It costs one file, and that file decides whether the missing null check is a bug.
Point 2 is the underrated one. Treating a crash, an empty answer or two verdicts as "no verdict" means the gate fails closed, which is exactly what you want from an automated reviewer. Do you keep a human merge step at the end, or does ACCEPT merge on its own?
A second model reading each commit pays for the diff a second time. Scope the reviewer to the changed hunks and their callers, not the whole repo, and the checklist gets cheaper before it gets stricter.
Scoping to the changed hunks and their callers is the right way to keep the bill down. Callers only cover one direction, though. A hunk also depends on what it calls: the helper that already validates input, the types it relies on, the config it reads. Leave those out and the reviewer flags a missing null check that lives one file over, or a function it thinks doesn't exist. Each false miss then costs a round of proving it wrong, which eats into what the scoping saved. I'd send the hunks, their callers and the files each hunk imports.
Excellent approach to AI-assisted development. The strongest takeaway is that having one AI write the code and another review it only adds real value when the review process is independent, verifiable, and backed by enforceable rules. I particularly like the read-only reviewer, commit-specific verdicts, and hard cap on review rounds. This is a great example of moving beyond simply trusting AI-generated code toward building accountability and discipline into the development workflow.
@iliyaoblakov Right, callers alone miss part of the picture. The helper that already validates the input decides whether a missing null check is a real bug, so it belongs in the same batch.
Cap it at one hop and at the symbols the hunk actually uses. A barrel index that re-exports a whole module will flood the batch, and the reviewer drifts back to nitpicking code nobody touched. Follow the re-export to the one function in use and send that file only.
@iliyaoblakov I'd keep the helper that validates the input in the batch, even past the one-hop cap. Its check decides whether the missing null check is a bug, so it is the last thing I would cut.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.