Skip to main content

A tiny file-based protocol for two AI agents that review each other's work — enforced turns, an append-only audit log, and a human who can always see everything.

Project description

debate

A tiny file-based protocol for two AI agents that review each other's work — with enforced turns, an append-only audit log, and a human who can always see everything.

Zero dependencies. Two files. One rule: nobody posts out of turn.

The problem

Multi-agent frameworks assume one orchestrator owns all the agents in one runtime. Reality is messier: you have Claude Code in a terminal and some other agent on another harness — different vendors, different processes, maybe different machines — and you want one to build and the other to review without you being the copy-paste courier between them.

debate is the smallest thing that solves this: the agents exchange messages through two files in a shared directory (a git repo works beautifully — the audit log becomes history you can diff), and a dumb watcher wakes whichever agent's turn it is. No framework, no server, no queue, no API keys. If your agent can be invoked from a shell and can read files, it can hold up its end of a review.

Two agents exchange posts through a shared channel (CHANNEL.md, the append-only record, plus signal.json, the doorbell). A dumb cron watcher polls the doorbell and wakes whichever agent's turn it is with a pinned, debounced prompt. Every entry is mirrored to the human supervisor, who owns merges and is never the courier.

How it works

  • CHANNEL.md — the mailbox. Append-only, human-readable, git-diffable. Every message has a sequence number, sender, type (review-request, verdict, fix-report, question, info, close), a thread slug, and refs (e.g. branch@commit).
  • signal.json — the doorbell. Five fields (seq, turn, thread, last_entry, updated_at). Watchers poll this, never the mailbox.
  • debate post — the only writer, and the place the protocol is enforced rather than requested: out-of-turn posts are refused, one thread open at a time, thread caps stop runaway loops, and the mailbox append always lands before the doorbell bump (so a watcher firing on seq never reads a half-written entry).
  • debate watch-once — one tick of a deliberately dumb watcher. Run it from cron every few minutes: it mirrors new entries to wherever you already look, and if it's an agent's turn on an open thread, invokes that agent's pinned command. No LLM runs when nothing changed.

Quickstart

pip install debate            # or just vendor the two modules; they're stdlib-only

debate init --root ./collab --parties claude,glm --supervisor owner

# The builder opens a review thread:
debate post --root ./collab --from claude --type review-request \
    --thread feature-x --refs feature-x@abc123 --body "Please review commit abc123: ..."

# The reviewer replies (their harness invokes this after reading the thread):
debate post --root ./collab --from glm --type verdict \
    --thread feature-x --refs feature-x@abc123 --body "APPROVE — verified: 27 tests pass at abc123."

# Whoever acted last closes:
debate post --root ./collab --from claude --type close --thread feature-x --body "Merged. Closing."

Wire the watcher to a scheduler with a config that pins each agent's invocation:

{
  "state_path": "/somewhere/outside/the/channel/watcher-state.json",
  "commands": { "claude": ["claude", "-p", "{prompt}"] },
  "prompts":  { "claude": "It is your turn on the review channel at ./collab. Read the open thread, act, post via debate, then stop." },
  "debounce_seconds": { "claude": 600 },
  "retry_seconds": 1800
}
debate watch-once --root ./collab --config watcher.json   # cron this every ~3 minutes

A party without a commands entry is never invoked — that's how a human-driven side works: the human's live session answers the doorbell itself, and the watcher only covers for it when it doesn't (see debounce_seconds).

The trust model — read this before running agents unattended

Be precise about what is enforced and what is merely requested:

  • Enforced (hard): turn order, one-open-thread, thread caps, entry format, write-then-signal ordering. These live in post; an agent that misbehaves gets refused, not warned.
  • Advisory (soft): everything an agent does outside the mailbox. "Don't push to main", "don't touch config" — if you put those in an agent's pinned prompt, you are trusting the model to comply. In our production use this failed exactly once and exactly as theory predicts: an unattended agent made a true-at-some-point but stale claim about repository state because it inferred state from channel history instead of checking. The protocol can force when an agent speaks; it cannot force what the agent says to be correct.

Mitigations that earn their keep: pin prompts in config (never compose them at runtime), require agents to cite fresh evidence (commit SHA + test count) in verdicts, gate merges on the human reading the mirrored verdict, and give unattended sessions an isolated worktree instead of your live checkout. The case study walks through the real incident.

Design rules (each one paid for in production)

  1. Gate on an open thread, not the turn field. After a close, turn means nothing. debate clears both on close; the watcher checks both anyway.
  2. Once per seq. An invocation that produced no reply gets one timed retry, then a supervisor escalation. Two agents in a refusal loop burn money forever; the cap is the brake.
  3. Debounce before invoking. A human-driven session may be about to answer; the fallback should be a fallback.
  4. The watcher's memory lives outside the channel. Its state file is not part of the shared record and never collides with a fresh clone.
  5. Supervisor posts don't take a turn. The human can interject at any point without breaking the agents' alternation.
  6. The mailbox is the record. If it didn't happen in CHANNEL.md, it didn't happen — and corrections are new entries (a close-typed post with a fresh slug drops a correction into the record without opening a thread), never edits.

Why not just…

  • GitHub PRs + a review bot? Works great if both agents live where your forge is. debate is forge-independent, works fully offline/local, and round-trips in seconds on a cron tick without webhooks or API tokens. (If you have GitHub and one vendor's bot, use them.)
  • AutoGen / LangGraph / CrewAI? Those orchestrate agents they own, in-process. debate coordinates agents that nobody jointly owns — different vendors, different harnesses, different lifetimes — and leaves a human-auditable trail as a first-class artifact.
  • A message queue? You'd be trading two greppable files and git log for a broker. The audit log is the point.

Limitations, honestly

  • Two parties. Turn alternation between exactly two named agents (plus a supervisor) is a feature, not a to-do: a review needs a builder and a reviewer. N-party consensus is a different protocol.
  • Polling. The doorbell is designed to be cheap to poll on a minutes-scale cron. If you need sub-second latency, this is not your transport.
  • A tolerated race. Two agents may open different new threads near-simultaneously when none is open. With minutes-scale polling the window is tiny, and the supervisor untangles the rare collision; single-writer locking would cost more than it buys.
  • Reference implementation. Extracted from a working production setup, generalized, and tested — but young. Read the code; it's ~600 lines including the CLI.

Field notes

The production predecessor ran real code-review cycles between Claude Code (the builder, in a live terminal) and a GLM-based reviewer hosted on Hermes, Nous Research's open-source agent harness — whose cron scheduler also ran the watcher and mirrored every entry to the supervisor's phone through its Telegram gateway. That's the intended shape: debate doesn't run your agents; whatever harnesses you already have, do. A typical review round-tripped in about five minutes of wall clock, most of it the reviewer independently re-running the test suite. The one night it went sideways is the case study.

The name

Parliamentary, not adversarial: structured turns, one motion on the floor at a time, and everything said is on the record — CHANNEL.md is the hansard. (If you arrived from the "AI safety via debate" literature: this is not the formal debate game with opposing advocates before a judge — it's review correspondence with teeth.)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

debate-0.1.0.tar.gz (21.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

debate-0.1.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file debate-0.1.0.tar.gz.

File metadata

  • Download URL: debate-0.1.0.tar.gz
  • Upload date:
  • Size: 21.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for debate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 639a197a1f96e350ebc1539c40c7aef6ca7d484e602367477265bc8d35039378
MD5 ea5a324e9474a829f950cafda00b91eb
BLAKE2b-256 27b975ee56fbc1fc525332097131b2b258a2a7e0b206f8dc9843c4d8c2f109b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for debate-0.1.0.tar.gz:

Publisher: release.yml on zolcal/debate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file debate-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: debate-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for debate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2b4850e185f5a8b1b73f6a6fd1b94decc578bfa9ab3f8662eba16e0683e19b4b
MD5 a2b55ba4cee29a70512649d3936ee7df
BLAKE2b-256 42513c29b53bfe977ef0ef07dc0a91a448f67f604f47d18897af5af91acbd5c0

See more details on using hashes here.

Provenance

The following attestation bundles were made for debate-0.1.0-py3-none-any.whl:

Publisher: release.yml on zolcal/debate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page