# I Made Claude Code and Codex Argue About My Code Until They Agreed

> Source: <https://dev.to/nunc/i-made-claude-code-and-codex-argue-about-my-code-until-they-agreed-1pkd>
> Published: 2026-07-22 10:48:22+00:00

**TL;DR**: I wired OpenAI's Codex CLI into Claude Code as an adversarial reviewer with a convergence loop. Then I pointed the loop at its own implementation. It failed the review three times before passing, and every single failure was a real bug. Here's the whole story, with the prompts and shell recipes you can steal.

A while ago Siqi Chen posted his favorite way of working with frontier models:

step 1. ask it to write a plan

step 2: "please get second opinions from codex CLI using gpt-5.6-sol @ max effort and kimi CLI using kimi 3. Revise your plan with any sound findings. repeat until convergence or up to 5 rounds."

There's also a companion repo, [adversarial-execution](https://github.com/blader/adversarial-execution), which applies the same idea at the other end: before you mark work as *done*, fresh reviewer sessions have to pass it with evidence, in a loop, until a fresh round finds nothing new.

Two ideas jumped out at me:

I already had a Claude Code skill that wraps the local `codex`

CLI as a second engineer: consult it read-only, delegate a fix, run a git-aware review, resume a session. All useful. All one-shot. No loop anywhere.

So I asked Claude Code to study the tweet and the repo and tell me whether the skill was worth upgrading.

Claude wrote a proposal: two new modes (a plan-review convergence loop and a done-gate), plus supporting rules. Decent plan. Then came the fun part. Before touching anything, I had Claude send the proposal itself to Codex for an adversarial review, using the exact contract style the proposal was recommending.

Verdict: **FAIL. Eight gaps, seven of them major.**

And they weren't nitpicks. My three favorites:

**The output capture in the existing skill was broken.** The recipes piped

`codex exec`

output through `tail -40`

and told Claude to grab the session id "from the header". But the header is at the `tail`

shows the end. On any long run the session id is simply gone. Better yet: this exact failure happened `PIPESTATUS`

you get `tail`

's exit code, not codex's.**False convergence.** My loop definition said: converged when a round brings "no new sound findings, only repeats". Codex pointed out that a finding I *accepted* but fixed incompletely would come back as a "repeat" and get waved through. A repeated unresolved finding has to stay open. And hitting the round cap needed an explicit NOT CONVERGED outcome instead of quietly presenting the last plan as final.

**Windows command lines have a length limit.** Passing a plan, a diff, and the round history as one quoted shell argument blows past ~32k characters. Codex suggested the CLI's stdin form (`codex exec ... - < prompt.txt`

), which my own skill was accidentally blocking with an unconditional `</dev/null`

.

I triaged all eight findings. All eight were sound. That triage step matters, by the way. The instruction is "revise with any *sound* findings", not "do whatever the reviewer says". The orchestrating model stays the decision maker; the reviewer is a hostile witness, not a boss.

The revised proposal had grown formal machinery: a findings ledger with four statuses, evidence manifests with exit codes and timestamps. My actual usage is a few Codex calls per month. That's how you end up with a skill nobody follows.

So I shipped a lean version instead. The concepts survived, compressed to one line each. A skill is instructions to a model, not code. The model can track a simple ledger in its head; what it needs written down are the rules it would otherwise get wrong:

`resume`

.New rules in place, one question left: is the upgrade itself actually done? Perfect job for the brand-new done-gate. Fresh Codex session, max reasoning effort, evidence pack attached (diffs, validator output), and a contract that forces a machine-checkable verdict:

```
INTENT: PASS | FAIL - does the work satisfy the stated acceptance criteria? (evidence)
WORKS: PASS | FAIL - does it actually function, per evidence, not per code reading? (evidence)
PLAN_FIT: PASS | FAIL - does it advance the overall plan without drift? (evidence)
VERDICT: PASS only if all three are PASS, else FAIL.
GAPS: numbered with stable IDs; severity, problem, evidence, smallest fix. If none: GAPS: NONE.
IMPROVEMENTS: optional non-blocking suggestions (max 5). These never affect the verdict.
```

**Round 1: FAIL.** Four gaps. The best one: I had fixed the output handling in modes 1 and 2 but not in the review and resume recipes, and my shiny new "pass long prompts via stdin" rule used a `$PROMPT_FILE`

variable that no example ever created. Copy-paste the doc as written and you'd get "No such file or directory".

**Round 2: FAIL.** Two gaps. One was a claim I refused to take on faith: Codex said the skill's *original* focused-review example, `codex exec review --uncommitted "focus on error handling"`

, is an invalid invocation, because the review target flags conflict with a positional prompt. That example predated this whole upgrade. One free local test later (argument parsing fails before any model call, so it costs nothing):

``` bash
$ codex exec review --uncommitted "focus on error handling" -o /dev/null
error: the argument '--uncommitted' cannot be used with '[PROMPT]'
```

Confirmed. A broken example had been sitting in the skill since day one, and a fresh pair of (artificial) eyes caught it on pass two.

**Round 3: PASS.** All three dimensions, `GAPS: NONE`

, nothing reopened. Convergence, for real this time.

Three rounds, three sets of genuine defects, then nothing left to find. That's exactly the behavior the pattern promises, and I got to watch it happen to my own work.

The reliable shell wrapper for non-interactive codex runs (Git Bash, works on Windows too):

```
OUT=$(mktemp codex-out-XXXX.txt)
LOG="${OUT%.txt}.log"
codex exec -s read-only --skip-git-repo-check \
  -c model_reasoning_effort="xhigh" \
  -o "$OUT" - < prompt.txt 2>&1 | tee "$LOG" | tail -20
RC=${PIPESTATUS[0]}; echo "rc=$RC"
grep -m1 -i "session id" "$LOG"
cat "$OUT"
```

Every piece earned its place the hard way: `-o`

captures the clean final message, `tee`

keeps the full log so the session id survives, `PIPESTATUS[0]`

gets the real exit code, and the trailing `- < prompt.txt`

feeds a prompt of any size through stdin.

The plan-review contract (the done-gate variant is above):

```
You are an adversarial reviewer of a PLAN (no code changes exist yet).
Judge the plan against the stated task and acceptance criteria AND against
the actual repo state (read the code the plan touches).
Review independently first; only then reconcile with the round history (if
provided), classifying each finding as NEW, REOPENED, or DUPLICATE.
Return exactly:
VERDICT: PASS | FAIL
GAPS: numbered with stable IDs; severity, problem, evidence, smallest fix.
If none: GAPS: NONE.
IMPROVEMENTS: optional non-blocking suggestions (max 5). These never affect
the verdict.
Do not restate the plan. Do not raise style nits as gaps.
```

And because nobody wants to memorize six modes, I added a `/codex`

slash command that acts as a dispatcher. I describe what I want in plain language; Claude walks a decision tree (continuation? edits? plan? finished work? whole diff? everything else) and picks the mode, assembles the context Codex can't see (the exact error, what was already ruled out, the diff, the evidence pack), and composes the prompt. The "what was already tried and ruled out" part is the single highest-value thing you can put in a delegate prompt. It stops the second model from re-walking your dead ends.

`VERDICT: FAIL`

+ numbered gaps can.The whole exercise took an afternoon and four Codex runs on a regular ChatGPT subscription. The skill now catches its own mistakes before I ship them, which is a sentence I didn't expect to write this year.

**Have you tried pitting two coding agents against each other? What broke first? Share in the comments!**
