How to make Codex review Claude Code's commits: done means, house rules and round rules A developer has published a follow-up guide to a two-engine AI coding workflow in which Claude Code writes commits and Codex reviews them read-only, adding three mechanisms to stop review loops from running indefinitely: a line-by-line "Done means" definition of done, project-wide house rules, and round rules that cap iterations. The approach requires each new test to be run against the base commit to prove it fails before the change, and the free MIT-licensed gate script at github.com/IliyaOblakov/two-engines-gate refuses to merge unless both a passing test run and an ACCEPT verdict exist. In my last post https://dev.to/iliyaoblakov/how-i-make-claude-code-and-codex-review-each-others-work-5dmh I showed the basic loop: Claude Code writes in its own worktree, Codex reviews the commit read-only, the tests run on that same commit, and a script refuses the merge unless both pieces of evidence exist. That loop has a weak spot. A reviewer that doesn't know what "done" means reviews against its own taste. It flags style, misses the one requirement that mattered, and finds something new every round. The loop never ends, or it ends because you got tired. This post fixes that with three things: a definition of done the reviewer checks line by line, house rules it checks the same way, and round rules so the loop stops. I've been building this since the first Codex and Claude CLIs, and these three go into every review on my own projects. A review without a finish line is an opinion. Claude Code writes. It commits each change on a branch and stops. Codex reviews that commit in a read-only sandbox: it can read files and run read-only commands like git diff , but it can't write anything. The prompt tells it never to hand back a patch, so every fix goes back to Claude Code as a new commit, and the new commit gets a new review. The verdict is one line, Verdict: ACCEPT or Verdict: CHANGES REQUIRED , saved in a file named after the commit it reviewed. If you want that part ready to run, the free gate is at github.com/IliyaOblakov/two-engines-gate https://github.com/IliyaOblakov/two-engines-gate MIT . Everything below sits on top of it, or on top of your own scripts. Most review noise starts before any code exists. If the only instruction Claude Code got was "add CSV export", the reviewer has nothing to check the result against except its own opinion. So every task starts with a short brief. Mine has four parts: Brief: CSV export for the orders page Done means 1. A "Download CSV" button on /orders downloads the orders currently shown, with the current filters applied. 2. Columns, in order: id, created at ISO 8601, UTC , customer email, total two decimals, no currency sign , status. 3. A customer email that contains a comma or a quote is escaped, so the file opens in a spreadsheet with the right columns. 4. A new test covers point 3 and fails on the code before this change. Must stay exactly as it is - The JSON response of GET /api/orders. - Existing filters and their URL parameters. How it is checked - Point 1: the route and the button in the diff. - Points 2 and 3: the new test. - Point 4: the test run once against the base commit, output pasted in the commit body. Out of scope - Exports for other pages. Scheduled exports. Every line under "Done means" has to be something a reviewer can confirm from the diff or from a test. "Export works well" isn't a line. "A customer email with a comma is escaped" is. Point 4 is worth stealing on its own. The engine that writes the code often writes the tests too, and a test that already passes on the old code proves nothing about the change. Ask the writer to run each new test once against the base commit and show that it failed there. Where the brief lives matters. Commit it with the change, or keep it under .ai/ . The free gate refuses to review a checkout with uncommitted changes or untracked files anywhere else, and it skips .ai/ for exactly this kind of working file. If you add .ai/ to your .gitignore , commit that change too, or the edited .gitignore itself gets the review refused. The brief covers this task. House rules cover every task: how errors are handled, where config lives, what never goes into a log. The things you'd otherwise repeat in every review. Write them like the brief, as things a reviewer can find in a diff: House rules - No new dependency without a line in the commit body saying why. - Config comes from environment variables read in config.ts only. No process.env anywhere else. - Never log a full email address, token or request body. - User-facing strings go through t , not hard-coded text. Put them in the files both engines already read: CLAUDE.md for Claude Code, AGENTS.md for Codex. The writer follows them while writing, and the reviewer checks against the same text. Keep the AGENTS.md section short: Codex stops adding instruction files once their combined size reaches project doc max bytes , 32 KiB by default. Write the rule once. Check it every time. Now change what you ask for. Instead of "review this diff", ask for an answer per line: The brief for this change is in .ai/brief.md. For each line under "Done means" and each house rule, answer one of: - met: give the file:line in the diff that shows it - not met: give the file:line and what is missing - not checkable: say what evidence would be needed Treat the commit message and the writer's summary as claims to verify, not as evidence. If a section is empty, say "not given" instead of guessing what it should have said. Findings outside the brief and the house rules need a concrete failure: the input or steps, and what goes wrong. Style preferences are not findings. Keep the verdict rule: exactly one line, Verdict: ACCEPT or Verdict: CHANGES REQUIRED. ACCEPT only when every line above is met. With the free gate, add this to review-prompt.md , which exists to be edited with your project rules. With your own scripts, append it to whatever prompt you send Codex. The reviewer now checks a list instead of free-styling. And a "not checkable" answer is a finding about your brief: you wrote a requirement nobody can verify. Without round rules, a reviewer at high effort can find a new nit every round, and the change never lands. So each round gets one job: Add the rules to the prompt: Round rules. Round 1: list every finding. Round 2: confirm the fixes and add only what the fixes broke. Round 3 and later: only blockers and security findings may hold the merge; mark everything else as a nit for the follow-ups file. The reviewer also needs to know which round it's in. Count rounds in a small file per change: bash /usr/bin/env bash rounds.sh