· 5 min read

Your AI Builds the Code. Who Reviews It?

ai architecture devtools

I’ve been using Claude Code to build Actions for months. The governance system works — CLAUDE.md, ARCHITECTURE.md, ROADMAP.md keep the agent aligned across sessions. The code ships. The methodology holds.

But there’s a gap I kept ignoring: the agent that writes the code is the same agent that evaluates the code. Every time I asked it to review its own work, it would find some things, miss others, and generally approve its own patterns. Because of course it would. It wrote them.

This is the same reason human code review exists. You don’t ask the person who wrote the PR to also approve it. A different pair of eyes catches what the author is blind to — not because the author is bad, but because familiarity breeds pattern blindness. AI doesn’t change this. It accelerates it.

The agent that exists to disagree

So I built one. I call it The Adversary.

It’s a separate Claude Code agent in its own repo with its own governance files. It doesn’t share context with the building agent. It doesn’t know what decisions were made or why. It receives a symlinked directory — read-only access to the target codebase — and produces a structured code review report.

It doesn’t modify a single file in the reviewed repo. That’s not its job. Its job is to find everything wrong and write it down.

The prompt is the methodology

Here’s the initial prompt that bootstraps The Adversary:

You are an adversarial code reviewer. I will symlink a directory to your working directory, and you will do a comprehensive code review to identify shortcomings, security issues, and optimization pathways.

The coding standards you are looking for are: DRY, separation of concerns, simplicity of code navigation and maintenance, and good code practice.

Many items will have “justifications” in the comments for why things are done the way they are. These are probably ok, but it is still important to verify the justifications.

Do not change any code on the symlinked repo — your report will be given to the other agent to assess and act upon accordingly.

That last line is the critical one. The Adversary’s report goes to the building agent, not to the codebase directly. The human decides what gets fixed. Separation of concerns at every level.

It gets its own brain

Like every agent in this system, The Adversary maintains its own set of governance files:

  • CLAUDE.md — identity, review standards, severity ratings, file structure, rules of engagement
  • architecture.md — the five-phase review pipeline (discovery → architecture analysis → file-by-file → cross-cutting → report generation)
  • roadmap.md — a checklist for each review covering discovery, architecture, code quality, security, performance, and testing

These files serve the same purpose as the building agent’s governance docs: persistent memory across compaction boundaries. The Adversary remembers its own methodology, its own standards, and its own process. It doesn’t inherit any of that from the building agent — and that’s the point.

102 findings in the first run

The first full review targeted the Actions codebase. Approximately 100 source files across Electron main process, React renderers, modal windows, and raw HTML windows.

The results:

SeverityCount
CRITICAL10
HIGH17
MEDIUM31
LOW26
INFO18
Total102

Ten critical issues. The building agent had been shipping this code for months. I’d been reviewing it manually at a high level. Neither of us caught what The Adversary found in a single pass.

What it found

The critical findings were overwhelmingly security issues. A few examples:

Path traversal in an IPC handler. A help content endpoint accepted a filename parameter and read from a directory without sanitization. An attacker controlling the renderer could request ../../etc/passwd or similar. The fix is trivial — validate that the resolved path stays within the expected directory — but the building agent never flagged it because it built the handler to serve help content, not to consider adversarial input.

executeJavaScript injection across multiple windows. Theme values, action names, and configuration objects were being interpolated into executeJavaScript calls as string templates. If any of those values contained quotes or JavaScript syntax, they’d execute as code in the renderer. The building agent used this pattern everywhere because it worked. The Adversary flagged nine separate injection sites. The fix: replace every executeJavaScript call with proper IPC messaging.

Production debug endpoints. Crash, hang, and out-of-memory test handlers were registered unconditionally — no app.isPackaged guard. Any renderer could invoke them in a production build.

These aren’t edge cases. They’re the kinds of issues that exist in every codebase where the builder is also the reviewer. The patterns work, the code ships, and the vulnerabilities hide in plain sight because nobody with fresh eyes ever looked.

The feedback loop

The report doesn’t go directly into a fix-everything sprint. It goes back to me first. I read each finding, decide whether to fix, defer, or dismiss, and then hand the prioritized list to the building agent.

Some findings are immediately actionable — the path traversal and injection vectors got fixed the same day. Others are architectural observations (the monolithic React context, tripled CSS base styles) that go into the backlog. Some are style preferences the reviewer happens to disagree with, and those get dismissed.

The human stays in the loop as the decision-maker. The Adversary provides the information. The building agent does the work. Nobody grades their own homework.

Why this matters beyond one tool

The broader pattern here is separation of concerns at the agent level.

If you’re running AI coding agents, you probably have some version of “ask it to review its own code.” Maybe you prompt it explicitly. Maybe you rely on its built-in tendency to check its work. Either way, you have a single agent performing both roles — and getting the same blind spots every time.

The Adversary is my answer to that problem: a purpose-built agent with its own governance documents, its own review methodology, and zero shared context with the builder. It found 102 issues that months of building and manual review had missed. Not because the building agent is bad — it’s excellent. But because independent review catches what self-review cannot.

Code review has always been a “different pair of eyes” discipline. AI doesn’t eliminate that requirement. It makes it easier to implement.