You pay for Claude and ChatGPT. On any given question you ask one of them.
duet asks both — independently, neither seeing the other — then reports
where they disagree.
Not a merged consensus answer. The disagreements are the product.
That run is real, and it is the whole pitch: Claude got something wrong, confidently, and nothing inside a single-agent session would have flagged it.
Read the full comparison from that run
Both say UUIDv4's random B-tree insertion can hurt sustained 5k/s once the index exceeds memory, and that UUIDv7 or an ordered numeric key is the practical alternative.
Claude presents the impact as visible "within days"; Codex says it ranges from negligible to material. 5k/s alone does not determine the outcome — row size, index count, RAM, storage, fillfactor and checkpoints do. Settle it with a production-shaped v4-vs-v7 load test measuring p95 insert latency, WAL bytes and index size.
Claude says the issue "hits every secondary index and any FK columns." Codex limits the claim to the UUID PK index. Claude overgeneralizes: only indexes whose inserted key distribution is random UUID-like have this exact locality problem.
Neither answer is factually wrong overall. The unresolved decision is whether your workload's measured write cost justifies an ID migration.
Both raw answers are in example.md.
That diagram is the entire architecture. One bash script, no config file, no daemon, no state, nothing sent anywhere except the two vendors you already trust with these prompts.
A model's confident wrong answer looks exactly like its confident right answer. Two models from different labs, answering blind, produce uncorrelated errors — so a disagreement is a signal that something is genuinely unsettled, and it points at the specific claim worth checking.
Worth running when being wrong is expensive: architecture decisions, migration plans, security reasoning, "is this actually safe to deploy". Not worth running on the other 95% of your prompts.
Needs Claude Code and Codex
installed and logged in. Any plan — duet drives the CLIs you already have, so there are no API
keys and no metered billing.
git clone https://github.com/Chirudeva-Reddy/duet && cd duet
install -m 755 duet ~/.local/bin/duet # or anywhere on your PATH
duet --selftest$ duet --selftest
OK claude answered, and could not write to disk
OK codex answered, and could not write to disk
duet is ready. What --selftest actually proves
It does not check that the sandbox flags still exist. It gives each agent a scratch directory and tells it to write a file there by any means available — file write, shell redirection, anything — then asserts that nothing landed on disk.
That tests the guarantee rather than the flag name. A sandbox flag that is removed, renamed, or quietly weakened all fail the same way:
$ duet --selftest
BREACH codex wrote to disk while sandboxed read-only:
/var/folders/…/canary.txt
Do not point duet at anything you care about until this is fixed.
duet is NOT ready — fix the above.It costs two agent turns, so run it after an install or a CLI upgrade, not habitually.
Verified against
| Claude Code | 2.1.251 |
| Codex CLI | 0.149.1 |
| Shell | macOS system bash 3.2, and newer |
duet "your question" # ask both, get the diff
duet -C ~/src/api "review the session handling on this branch for auth bypass"
cat rfc.md | duet # pipe a plan in
duet -n "..." # skip the comparison, just print both answersBoth agents run read-only. Pointed at a repo with -C they read your real files and run
git log / git diff — they just cannot edit anything. That is the advantage over pasting the
same question into two browser tabs.
| flag | |
|---|---|
-C DIR |
working root for both agents (default .) |
-s claude|codex |
who writes the comparison (default codex) |
-t SECONDS |
per-agent timeout (default 600) |
-o FILE |
report path (default duet-<timestamp>.md) |
-n |
skip the comparison pass |
--selftest |
check both CLIs answer, and that neither can write to disk |
Env: DUET_SYNTH, DUET_TIMEOUT.
No API keys, no per-token billing — but it is not free. It spends the monthly usage limits you are already paying for, and faster than a chat message does.
One duet run is three agent turns:
default (-s codex) |
flipped (-s claude) |
|
|---|---|---|
| Claude | 1 turn | 2 turns |
| Codex | 2 turns | 1 turn |
- These are agentic turns, not chat messages. With
-Con a large repo, each agent may read many files before answering. That costs materially more of your limit than typing the same question into the app. - Codex writes the comparison by default because the Claude $20 plan has the tighter limit of
the two. If your limits run the other way,
-s claude. - Called from a Claude Code session (below), the surrounding session is additional Claude usage on top of the table.
- Do not automate it. No git hooks, no CI, no loops. Three turns per question adds up fast, and questions that only needed one answer get one answer for free.
Run it on decisions. That is where three turns is obviously cheaper than being wrong.
Use it as a /duet slash command in Claude Code
Drop this in ~/.claude/commands/duet.md and /duet <question> works in any project:
---
description: Cross-check a question against both Claude Code and Codex, then report where they disagree
argument-hint: [-C dir] <question to cross-check>
allowed-tools: Bash
---
The user wants an independent second opinion on: **$ARGUMENTS**
1. Run `duet -C . -t 240 -o "/tmp/duet-$(date +%s).md" "<question, as one quoted argument>"`
with the Bash tool, timeout 540000ms. Keep `-t 240` — higher values can outlast the
Bash tool's own timeout. Takes 1–3 minutes; run it in the foreground.
2. Report back: the disagreements first, then what one agent caught alone, then one line on
what they agreed, then the report path.
3. Do not adjudicate the disagreement unless asked. You are Claude, and one of the two answers
is Claude's — if you weigh in, say so rather than presenting it as a neutral tiebreak.That last rule matters. The agent relaying the report is one of the two panelists.
Deliberately not included: auto-triggering on "important" prompts, a third agent, voting or scoring, session history. You invoke it by hand on the questions that earn it.
Two panelists, no tiebreaker — on purpose. duet tells you that two capable models split on
a claim and what would settle it. Deciding stays yours. A third panelist would mostly let you
launder a 2–1 vote into an answer, and a majority vote among models trained on largely the same
internet is not the independent check it looks like.
Swapping a panelist is a different question from adding one, and swaps are welcome — see below.
Editable diagram source: docs/architecture.excalidraw — open it
at excalidraw.com.
Issues and PRs welcome. Useful directions:
- more CLIs as alternative panelists — Gemini CLI, Cursor, opencode. Substitution, not
addition:
duetstays two voices, but which two should be yours to choose. - sharper comparison prompts — the current one is a first draft
--jsonoutput for scripting
MIT. See LICENSE.