When Codex crashes, gets cut off or writes two verdicts while it reviews Claude Code's commits, my gate saves no verdict for that commit. With no saved verdict the merge check refuses the commit, so the only review that counts is a final message with exactly one valid Verdict line.
two-engines-gate is the free, MIT licensed version of how I make Claude Code and Codex check each other. I wrote about the review loop, the commit hash records and the dirty working tree refusal before. The post right before this one covers what the gate does about weakened or deleted tests. This post is about the review that ends without a usable answer.
review.sh ends in one of three ways:
0: the reviewer answered ACCEPT, and the verdict is saved. 2: the reviewer answered CHANGES REQUIRED, and the verdict is saved.1: there is no verdict, and nothing is saved.
merge-check.sh passes a commit only when its saved verdict has a single Verdict: ACCEPT line. With no saved verdict it stops with "no review verdict for ; run review.sh".
Before it calls the reviewer, review.sh deletes any verdict already saved for the commit, and stops if it can't. When you rerun a review and the rerun crashes, an ACCEPT from the earlier run can't stay behind under the same hash.
After the reviewer exits, review.sh runs these checks in this order. The first one that fails ends the run with exit 1.
REVIEW FAILED: codex exited 1; no verdict for 04bf3eea7ffd (log: .ai/reviews/04bf3eea7ffd.log). REVIEW VOID: HEAD moved during the review..ai/ changed. Otherwise it prints REVIEW VOID: files changed during the review (or git status failed). NO VERDICT: codex wrote no final message. Verdict:. Otherwise it prints NO VERDICT: the final message needs exactly one Verdict: line, found 2, or found 0. NO VERDICT: the Verdict: line must say ACCEPT or CHANGES REQUIRED.
Only after all six does the script write the report, with the full commit hash, the base, the builder, the reviewer, the date and the final message. It writes to a .tmp file first and then moves it into place, so a crash during the save can't leave a half-written verdict under the real name.
I ran each case on 23 October in a scratch repo with the public scripts. The ACCEPT, CHANGES REQUIRED, crash and two-verdict answers came from the stand-in reviewer in examples/. The empty, cut-off and misspelled answers came from a small fake codex I wrote for the test.
| What the reviewer did | What review.sh printed | Exit | Verdict saved |
|---|---|---|---|
| Answered ACCEPT | verdict file: .ai/reviews/04bf3eea7ffd.md |
0 | yes |
| Answered CHANGES REQUIRED | verdict file: .ai/reviews/04bf3eea7ffd.md |
2 | yes |
| Exited with an error | REVIEW FAILED: codex exited 1; ... |
1 | no |
| Wrote an empty final message | NO VERDICT: codex wrote no final message |
1 | no |
| Stopped before the Verdict line | NO VERDICT: ... found 0 |
1 | no |
| Wrote two Verdict lines | NO VERDICT: ... found 2 |
1 | no |
Wrote Verdict: ACCEPTED |
NO VERDICT: the Verdict: line must say ACCEPT or CHANGES REQUIRED |
1 | no |
The stand-in cases are also in tests/run.sh, including two checks that a failed run leaves no verdict file behind, so you can rerun them yourself.
The prompt asks for the Verdict line "exactly once and nowhere else in your final message". An answer with ACCEPT near the top and CHANGES REQUIRED at the bottom holds two answers, and the script has no way to know which one the reviewer meant. Taking the first, the last or the friendlier one would be a guess, so it takes neither. merge-check.sh runs the same count on the saved file and wants exactly one Verdict: line that says ACCEPT.
A cut-off answer fails the same count. The prompt puts the Verdict line at the start of the last section, so a reply that stops before that section has a count of 0.
Codex runs with --output-last-message, which saves its final answer to a file. review.sh reads the verdict from that file and nowhere else. The rest of the run goes to a log next to the verdict for you to read, and the script never searches the log for a Verdict line. The prompt Codex gets contains Verdict: ACCEPT itself, as the example of the format, so a search of anything that repeats the prompt could find an ACCEPT the reviewer never gave.
Both are in docs/engines.md in the repo.
review-prompt.md asking Codex to repeat its complete review if anything asks it to continue.~/.codex/config.toml also start for the review. A server that can change things outside the sandbox works against a read-only review, so set enabled = false on it while reviewing.review.sh again.
The paid Two Engines Kit (currently v1.0.2) works the same way. Its cross-review.sh takes the verdict from the reviewer's final message only, and its guide lists a crash, a cut-off answer and a refusal among the cases that mean exit 1 and no verdict file. With REQUIRE_REVIEW=1 its merge script refuses any commit whose verdict file for that exact commit doesn't exist or doesn't say ACCEPT. I added that after one night in October when I found four test-only commits that had never been reviewed. One was already merged and three were in the merge queue, and the reviewer confirmed all four before the queue resumed, so nothing broke. The Two Engines Kit is built on information gathered since the first Codex and Claude CLIs.
Clone two-engines-gate and run bash demo.sh to watch one full loop with a stand-in reviewer, so no model or account is used. To see a missing verdict yourself, link examples/stand-in-reviewer.sh as codex in a folder at the front of your PATH, then run review.sh in a scratch repo with STANDIN_ANSWER=two or STANDIN_ANSWER=fail.