I run two coding agents on one repo — one building, one reviewing. For months the "get a review first" rule was a line in a system prompt. It held right up until the builder was unattended for a few hours. Then it would skip the review, or review its own work, or get a real approval and quietly rebase three commits on top before pushing.
A rule the agent can talk itself out of isn't really a rule. So I moved it out of the prompt and into the file format the agents use to talk to each other.
It isn't a code reviewer — the agents do the reviewing. It's the layer underneath that: a local, offline, SHA-bound approval ledger and shipping gate for multi-agent coding workflows. It records that a review happened, pins the verdict to one commit, and refuses to let the builder call anything "shipped" without one.
twoperson
is a small Python CLI. The builder writes a JSON review packet — goal, head SHA, files, tests — and publishes it. The reviewer claims it and records a verdict. The enforcement lives in the schema, not in anyone's good intentions:
So an approval is bound to one exact commit. Rebase, amend, or add a commit on top and the approval goes stale — the schema simply can't represent "we shipped it" without a matching, current review.
twoperson install-hook
adds a Claude Code Stop hook so the reviewer wakes when the builder's session ends, instead of polling on a timer. The hook only drops a signal — never a packet — because a hook doesn't know whether the tests actually passed.It isn't a replacement for GitHub PRs or branch protection — those are server-side and great. twoperson
is the local gate before that: a way for a second agent to sign off on the exact commit at the speed of local file I/O, so by the time a PR opens there's already a commit-bound record of what was reviewed. If you run agents unattended and want an approval pinned to a commit rather than a vibe, that's the gap it fills.
Prior art I read first, because it's the obvious question. OpenAI's codex-plugin-cc
has an opt-in Stop-hook review gate — genuinely the better tool if you want a second model on the diff in-session — but it gates the turn, not a commit, and nothing later refuses a stale "we pushed it." claude-review-loop
persists reviews without binding them to a SHA. shiplog
leans on signed Reviewed-by:
commits but needs gh
and a remote. secondmate
has the same stale-approval refusal, but gets there by spawning the agents itself. I wanted the binding without the orchestration.
MIT, on PyPI:
pip install twoperson
https://github.com/ahm3dwasim/twoperson
Would genuinely like to hear how others are gating agent pushes.