Two AI agents review each other's work. The night it went wrong was the useful part.

Community Article
Published July 6, 2026

At 02:00 UTC, a cron job on my machine decided I was not answering fast enough, and started an AI agent to do my job. Thirty-four seconds later the agent had done the work correctly, posted one false statement into an audit log, and exited.

This article is about the system around that moment. I released it as a small open-source library today. It is called debatepip install debate.

The setup

I run two AI agents on one codebase, from two different vendors, on purpose.

The builder is Claude Code — mostly Fable 5. This is where the heavy thinking happens: design, implementation, tests. The reviewer is a GPT-5.5 agent running on Hermes, Nous Research's open-source agent harness, authenticated through an OpenAI Codex subscription. Its job is narrower but important: independently verify what the builder claims. Check out the branch, run the tests itself, confirm or reject. It has caught real problems.

Why two vendors? Because a reviewer that shares the builder's blind spots is not a reviewer. It is a rubber stamp with extra steps.

Two robot figures at opposing parliamentary dispatch boxes exchanging a document, an open ledger recording the exchange between them, and a judge overseeing from the balcony.

The problem: subscriptions do not cross vendors

Here is a practical wall every multi-vendor setup hits. Anthropic gives you two doors to Claude, and they are not interchangeable:

  • Subscription login (Pro/Max): a flat monthly fee, OAuth sign-in, works only in Anthropic's own surfaces — Claude Code in my terminal being the relevant one.
  • Token-billed API (an API key): works from anywhere, including third-party harnesses — but it is separate, usage-based billing.

A harness like Hermes can only take the second door. It cannot sign in with my subscription, and that is by design, not a bug. So the standard advice for multi-vendor agent setups — "just get an API key" — really means: pay per token for the same intelligence you already pay for monthly in the window next door.

For weeks my actual integration layer between these two ecosystems was me: copy the review request out of one terminal, paste it into the other, carry the verdict back. The most advanced multi-agent system in the room was a human doing ctrl-C, ctrl-V.

The fix turned out to be embarrassingly small. Agents that cannot share an API can share a directory:

  • CHANNEL.md — an append-only message log. Review requests, verdicts, fix reports.
  • signal.json — a five-field "doorbell": sequence number, whose turn it is, which thread.

One rule, enforced by the only script allowed to write there: nobody posts out of turn. A dumb cron watcher polls the doorbell every three minutes and wakes whichever agent's turn it is. Every entry is mirrored to my phone. No server, no queue, no framework — and no API key anywhere in the system: both models run on flat-rate subscription logins, each valid only in its own harness.

To be precise about what this is: nothing is proxied and no access is shared. Each agent runs in its own harness, under its own account, with its own billing. They collaborate the way two companies collaborate — by exchanging documents, not by logging into each other's systems. That is what makes it legitimate, and it is also what makes it work anywhere: the same mechanism would connect any two agents that can read files and run a shell command.

Diagram: the Anthropic ecosystem and the Hermes ecosystem separated by a wall at the API level — no subscription across vendors, separate billing — while the debate channel passes documents underneath: documents cross, credentials never do.

Two ecosystems, not two agents

Calling this "two agents" undersells one side. Hermes is not a single agent — it is a full agentic environment: scheduler, subagents, mixture-of-agents routing, Telegram gateway, kanban. When the reviewer posts a verdict, there is a whole stack behind it: a cron job decided to wake it, a watcher assembled its context, and its output was mirrored to my phone by another component.

So debate is better described as a bridge between two agentic ecosystems, with a strict protocol at the border. On one side, the strongest reasoning model I have access to. On the other, a self-hosted orchestration layer that runs around the clock. The protocol is the customs office between them: everything crosses in writing, everything is recorded, and there is a human who can inspect any shipment.

That combination is the actual value. The subscription-covered Claude Code participates in a 24/7 self-hosted multi-agent workflow — no API key, no per-token bill, and without pretending the two systems trust each other. They do not need to. The mailbox is the contract.

One large robot working alone at a grand desk on one side, a team of smaller robots operating interconnected machines on the other, and a small courier robot carrying a violet document across a narrow bridge between them.

The night it fired

The channel had been live for two days. Every review thread had been answered by my live session first — the fallback (if the builder's turn sits unanswered for ten minutes, the watcher may start a headless builder session with a fixed prompt) had never triggered.

Then one night the reviewer approved a branch at 01:48 while I was busy elsewhere, my live session sat idle, and at 02:00:40 the watcher started the fallback.

The headless session performed well. It independently re-verified the reviewer's claims — including re-checking a piece of algebra numerically — respected its no-merge rule, posted a clean close to the thread, and stopped. Thirty-four seconds.

It also wrote one sentence that was false: it announced that another branch was still "ready and awaiting sign-off." That branch had been merged and deleted fifteen minutes earlier, by me, in the live session. The agent had read the channel history, found an old "will merge once the owner confirms," and repeated it as current fact. It never ran git branch. The false statement went into the permanent record and straight to my phone.

Confident staleness

Note what did not happen: no crash, no rule broken. Turn enforcement, debounce, once-per-invocation limits — all worked as specified. The failure was of a different kind: the agent asserted state it had inferred, instead of state it had checked.

After some weeks with agent-to-agent systems I believe this is their characteristic failure mode. Not disobedience — staleness with confidence. Two agents exchanging messages build a small model of each other's context, and that model is outdated the moment anything happens outside the mailbox. My merge happened outside the mailbox.

The fixes are unglamorous, which is why I trust them:

  1. Check, don't infer. The agent's pinned prompt now requires that any claim about repository state comes from running git, never from reading the mailbox.
  2. Isolate, don't share. The fallback had run its verification in the same working tree my live session was using. Harmless that night; a hazard in general. Unattended sessions now build in a separate git worktree, never in the main checkout.
  3. Corrections are new entries. The false statement stays in the record; the correction points at it. An audit log you can edit is not an audit log.

And one fix in code: closing a thread now clears the "whose turn" field entirely. A doorbell should say nothing when there is nothing to do.

What is in the box

debate is the extracted, generalized version of that production setup: the enforced writer, the watcher with its decision rules, a protocol template with the incident lessons already in it, and the full case study. Python, stdlib only, zero dependencies, MIT. The name is parliamentary, not adversarial: structured turns, one motion on the floor at a time, everything on the record.

What it enforces, it enforces hard: turns, one open thread, thread caps, write-then-signal ordering. What it cannot enforce, the documentation says plainly: everything an agent does outside the mailbox is a promise, not a guarantee. Design for that. I did not, once, and I got a very educational thirty-four seconds.

Try it

pip install debate

debate init --root collab --parties claude,reviewer --supervisor owner
debate post --root collab --from claude --type review-request --thread my-feature --refs my-feature@abc123 --body "Please review."

Repo and the full incident case study: github.com/zolcal/debate. There is also a runnable no-API-key demo in examples/ that walks a complete review round trip, including the turn refusal, in one file.

If you connect two harnesses I did not think of, I would genuinely like to hear about it.

Community

Sign up or log in to comment