Yesterday afternoon I was about to ship a change to a production pipeline. The code was written, tested, deployed to the runtime directory. The nightly cron would pick it up at 22:00.
Then a second AI looked at the diff and said NO-GO.
Not a human. Not the AI that wrote the code. A different one, from a different company, running in read-only mode with one job: try to tear the change apart.
It said NO-GO three times in a row. Each time with a concrete, reproducible bug. On the fourth round it said GO, and the cron ran clean that night.
This post is about that setup: using OpenAI's Codex CLI as a second engineer inside Claude Code sessions. What it looks like, what it actually caught, and when it's worth the quota.
I do most of my engineering with Claude Code these days. It writes the code, runs the tests, deploys, checks the logs. For context: I wrote about the AI assistant we built on top of 20 years of support tickets in a previous post. That assistant is still running. This post is about the tooling around building things like it.
Here's the uncomfortable part. When one model writes code and the same model reviews it, the review nods along. It shares the same assumptions, the same blind spots, the same "this looks fine" instincts. It wrote the bug, so it doesn't see the bug.
Human teams solved this ages ago: you don't review your own PR. So I gave my AI the same rule.
The pieces:
The important part is the policy. I don't manually decide when to ask Codex. Claude decides, based on rules I put in my global config:
## Codex as a co-engineer
A local OpenAI Codex CLI is installed and authenticated. Claude may use it
on its own judgment as an independent second engineer. Good reasons:
- stuck after 2+ failed attempts at a bug
- independent review of a risky or security-sensitive change
before calling it done
- cross-checking a hard-to-reverse design decision
- a parallel investigation while Claude keeps working
Announce in one sentence when doing so (it spends Codex quota).
Read-only runs don't need permission; write-capable delegation does.
Keep self-initiated runs to ~2 per task.
That's the whole contract. Codex runs read-only by default (it can read the repo, it cannot touch it). Claude announces every call because it burns my OpenAI quota. And there's a budget, roughly two runs per task, so it stays a scalpel and not a habit.
The project: an automated code-review pipeline for Oracle PL/SQL patches in a large, old back-office system. Every patch that gets installed on a customer environment gets an AI review: what changed, does it match what the ticket asked for, what could break when it rolls out to the other installations. Findings get scored, risky ones trigger an email to the patch author.
The scoring part is where it gets dangerous. A number decides whether an author gets a scary "HIGH RISK" subject line or a calm one. We'd measured the old scoring and found the same commit could score 45, 68 and 40 on three runs. That's not a metric, that's a mood. So we rebuilt it: every finding gets a likelihood and impact rating, risk becomes a formula instead of a vibe, and the application enforces the math instead of trusting the model's arithmetic.
Claude designed and implemented all of that. Then, per the policy, it handed the diff to Codex with an explicit instruction: this ships to production tonight, try to refute it.
Codex came back with a structured verdict. Not "looks good with minor suggestions". A NO-GO with six concrete findings, each with file and line number. The two that hurt:
The thread truncation bug. Ticket threads get trimmed to fit the context window. The old code did the equivalent of substr(0, 200000)
. Sounds harmless until you realize helpdesk threads grow chronologically, so a naive head-cut keeps the oldest messages and silently drops the newest ones. The newest messages are where the actual decisions live. Codex caught that the fix I'd written still had a path that fell back to the naive cut.
The fudge factor. My scoring prompt said risk equals the maximum of the per-finding scores, "±5 for rollout context". Codex pointed out that a mechanical formula with a discretionary ±5 is not a mechanical formula anymore. It's the old vibes with extra steps. We deleted it.
Fair. Fixed all six. Sent the diff back.
This is the finding that sold me on the whole approach.
The new enforcement code parses the model's findings (each one annotated like [V=4 I=5]
), computes the risk, and overwrites the model's number if it doesn't match. Codex looked at my parser and asked: what happens when the parser only sees some of the findings?
Then it answered its own question. It took my actual regexes, constructed a review where one finding is written as K1 [V=2 I=2]
and another as K2: [V=5 I=5]
(note the colon, a format my parser missed), and showed the parse returns only the first one. Result: the app "corrects" a risk of 100 down to 16. A critical finding, silently erased by my own safety mechanism.
The fix was to make the parser fail-closed: it now tracks every finding ID it can see anywhere in the text, and if even one of them can't be fully parsed, the enforcement steps aside and the original score stands. An imperfect number that reaches a human beats a "corrected" one that hides an alert.
Round 3 found one more variant of the same hole (two findings on the same physical line). Round 4: GO.
Three real bugs in my safety net, found before production instead of three weeks into it. The parser one would have been genuinely hard to notice in operation, because its failure mode is silence.
You could run this loop with the same model reviewing itself, and it would still catch things. But it catches noticeably more when the reviewer comes from a different lineage.
My theory: models from one family share failure modes the same way code from one author shares bugs. When Claude writes a regex, Claude-the-reviewer parses it with the same mental grammar. Codex parses it with a different one, which is exactly how it found the K2:
case. The disagreement is the feature.
We liked this effect enough to build it into the product itself. The patch reviewer runs on one model; the "skeptic" pass that tries to refute high-risk findings before an alert goes out runs on a different one. Decorrelation all the way down.
Plugging two AI CLIs together is easy. Getting reviews that are worth the quota took some iteration. What stuck:
Read-only by default. Codex analyzes, Claude implements. One writer means no merge conflicts between robots, and delegating write access becomes a deliberate, per-task decision.
Demand evidence, not opinions. Every delegation prompt asks for findings ranked by impact, each with file:line and either a reproduction or an explicit "plausible, unverified" label. Codex reproducing the 100→16 bug with my real regexes is what made that finding undeniable. A vague "parsing might be fragile" would have been ignored.
Forbid anchoring. When I want a fresh assessment of a system, the prompt explicitly says: do not read our existing TODO and improvement docs. Otherwise the review converges on what we already believe. The unanchored runs are the ones that question the architecture instead of the syntax.
Feed it production data, not just code. The best review round included a week of anonymized log lines next to the source. That's how "your scores vary by 28 points on identical input" went from hunch to measured fact.
Keep the loop in one session. Codex CLI supports resuming a session, so the NO-GO → fix → re-verify cycle keeps its context. Round 3 knew what round 1 had already flagged. Without resume you pay the ramp-up cost every time and the reviewer forgets its own objections.
This isn't free, in three currencies.
Quota. Every run spends my OpenAI subscription. The ~2-runs-per-task budget plus the "announce it" rule keeps this visible. Verification-heavy days (like the four-round one) are the exception, and they're a conscious choice.
Time. A thorough Codex review of a non-trivial diff takes around ten minutes. Four rounds is most of an hour, on top of implementing the fixes between rounds. For a config tweak that would be absurd. For code that emails risk scores to my colleagues, it was cheap.
Judgment. Codex is not an oracle. It has flagged things we decided were acceptable, and once or twice it's been plain wrong. Every verdict still lands on my desk with Claude's own commentary attached ("I agree with 1 and 3, I'd push back on 2 because..."). Two AIs disagreeing is information. Me not reading either of them would be negligence.
The strange part is how normal it feels after a week. Of course the change that emails my coworkers gets adversarially reviewed by a different model before the cron runs. Of course NO-GO means we fix it first. It's just code review. The reviewers happen to have different parents.
Anyone else running multi-vendor AI review loops? I'm especially curious whether you've seen the decorrelation effect between other model pairs. What did one catch that the other waved through?