Every repository holds knowledge nobody wrote down. It lives in whoever has reviewed every pull request for three years, and it leaves when they do.
Murat Kirazkaya6 min readon context, not models
Fig. 01An AI reviewer is handed the band. A human reviewer is handed the band and also happens to be carrying the field.
01 · the repetition
You have written this comment before
Three pull requests, six months apart, three contributors. The same comment every time, worded a little differently every time. The rule is real and everyone knows it. It is just nowhere a machine can read it.
MKmuratreviewed #218Mar 3
We keep amounts as integer minor units. amount * 0.029 turns a Money into a float, and the rounding is already gone by the time it reaches the ledger. Use mulRate.
MKmuratreviewed #264Jun 11
Same thing as #218. This multiplies a Money by a float. mulRate(amount, 29, 1000) does it in one rounding step.
MKmuratreviewed #301Sep 2
Float math on money again, third time this quarter. mulRate.
co-maintainer noted this in CODEBASE.md
Money is an integer count of minor units, defined in src/money.ts. Never multiply or divide a Money by a float. Rate math goes through mulRate(amount, numerator, denominator), which rounds once, at the end.
Fig. 02Nobody typed the sentence at the bottom. co-maintainer read the repository's review history and wrote it, in the repository's own words.
02 · the gap
What the diff does not say
A reviewer reads the patch while quietly holding four years of this codebase. Here is what that is worth.
in the diff · src/billing/refund.ts
src/billing/refund.ts#312
40export functionrefundFee(amount: Money) {
41const fee = amount * 0.029;
42return { fee, net: amount - fee };
43}
tests pass·types pass·lint passes
the thing that makes it wrong
not in the diff · untouched for 14 months
src/money.tsmain
7// minor units. 1299 is 12.99
8export typeMoney = number & { __minor: true };
9
10export constmulRate = (m, n, d): Money =>
11Math.round((m * n) / d) asMoney;
ledger.post() rejects37.671at runtime, in production
Fig. 03The changed line is in the patch. The reason it is wrong is in a file nobody touched.
The types compile, because a branded number is still a number. The fee comes out fractional, ledger.post() asserts integers, and the refund throws on the first amount that does not divide evenly.
A missed finding is usually a missing input, not a weak model.
03 · the reading
Read the repository once, not once per review
Handing the model a whole repository on every review is possible, and absurd. So the reading happens once, off to the side, and what comes out is small enough to carry.
probe
Start with the part that costs nothing
It reads the repository through the GitHub API and prints the init line you should run, limits already filled in. No model is called, nothing is written.
init · extract
A cheap model turns the repository into evidence
Tree, files, pull requests, their diffs, the discussions on them, commit history. Every observation stays bound to the file or pull request that produced it, because a rule nobody can trace back is how a review tool starts inventing them.
init · synthesise
A reasoning model turns evidence into three guides
How to contribute here, how the code is actually written, and what reviewers here have historically stopped pull requests for. They land in your config directory, not in the clone, so a fresh checkout keeps them.
remake
Then keep it alive for the price of a diff
It refetches, reuses every piece of evidence the cache says is unchanged, and rewrites only the guides the new commits touch. init is the once. This is the loop.
zsh · ~/src/payments-apionce per repository
$co-maintainer probe northwind/payments-api --auth=gh repository northwind/payments-api default branch main pull requests 312 sampled over 14 months commits 4,180 on main recommended co-maintainer init northwind/payments-api --include-codebase \ --include-pull-requests --include-pull-request-changes \ --include-commit-history --max-pr-months=12 --max-commits=500 no model was called, nothing was written$co-maintainer init northwind/payments-api --auth=gh --ai=openrouter extract 1,284 files, 312 pull requests low model synth SKILL.md CODEBASE.md PR_REVIEW_GUIDE.md high model written ~/.config/co-maintainer/repos/northwind/payments-api done in 6m 12s
Fig. 04Two commands, one of which is free. Output trimmed to the shape of it.
The reason it happens once:
Fig. 05Drawn to scale. The small square is what a review costs when the repository was read in advance.
04 · the artifact
The guides are the product
Models are rented. Whichever one is best this quarter will not be best next quarter. The context is the part you own.
SKILL.md14.2 KB
## Shipping
Pull requests run CI via .github/workflows/ci.yml, which defines a test job and a contract job that replays recorded provider webhooks.
Run npm run verify-pack before tagging a release.
## Tests
A new provider adapter is expected to ship with a webhook replay fixture under test/fixtures/providers/.
how to contribute here
CODEBASE.md9.8 KB
## Money
Money is an integer count of minor units, defined in src/money.ts. Never multiply or divide a Money by a float. Rate math goes through mulRate(amount, numerator, denominator).
ledger.post() asserts an integer amount, so a float that reaches it throws rather than rounding quietly.
## Code layout
Each provider adapter lives in src/providers/ and exports only charge, refund and webhook.
how the code is actually written
PR_REVIEW_GUIDE.md6.1 KB
## What reviewers here ask for
Float arithmetic on an amount is the most frequently raised issue in this repository's history (#218, #264, #301).
Three adapters have been blocked for shipping without a replay test. Treat a missing fixture as blocking, not as a nit.
## Tone
Reviewers here state the consequence, then the fix, in that order.
what reviewers here stop PRs for
Fig. 06Generated text, not hand written. The highlighted line is the one from Fig. 02, and the numbers under it are the pull requests it came from.
Read them out loud and they sound like the person who has been merging into this repository for two years. That is where the title comes from. It is not trying to be a better reviewer than your team, it is trying to be a cheap imitation of the one you already have, awake at 2am, on the boring pull requests too.
05 · the review
What it writes on the diff
A pull request opens. The guides are already on disk, so the reviewer starts from a repository it has read instead of a patch it has never seen.
Review/ co-maintainerreading guides and diff…2 findings, 1 blocking24sDetails
co-maintainerbot reviewed just now
Reviewed against SKILL.md, CODEBASE.md and PR_REVIEW_GUIDE.md for this repository, with codegraph enabled.
1 blocking1 non blocking9,312 tokens24.7s$0.0041
src/billing/refund.ts+3−0
3939exportfunctionrefundFee(amount: Money) {
40const fee = amount * 0.029;
41return { fee, net: amount - fee };
4042}
P1 · blocking
refundFee() returns a fractional amount. ledger.post() asserts an integer count of minor units, so every refund whose fee does not divide evenly throws in production instead of rounding. CODEBASE.md puts rate math through mulRate for exactly this reason.
src/billing/refund.ts:41 · 3 call sites reach this
Suggested change
41const fee = amount * 0.029;
41const fee = mulRate(amount, 29, 1000);
Commit suggestionAdd to batch
Fig. 07The green check is not the interesting part. The comment naming an assertion in a file the patch never touched is.
Without the guides there are two honest options here. Say nothing, because a percentage fee is a normal thing to write. Or say something vague about floating point, which everybody has learned to scroll past.
Findings carry over. Push a fix, run it again, and the finding closes instead of being raised twice.
A blocking finding exits non zero. That is all CI needs.
--json on stdout, logs on stderr. Findings, counts, tokens, duration, codegraph state, one document.
Codegraph is optional. On by default, and the binary is installed on request, not behind your back.
06 · the three doors
Same guides, three ways in
There are only three answers people actually want: the mess in my working tree, that pull request over there, and the first one without making every laptop pay for init.
reviewlocal review
review owner/repo 312pull request review
review --remoteremote review
what it reads
Staged, unstaged and untracked files in your clone
The pull request and its patch through gh, no checkout
The same local diff as the first column
where the guides are
Your disk, your own init
Your disk, your own init
The server, built once for the team
where the model runs
Your machine, your key
Your machine, your key
The server, with its key
what you need
A clone and an init
gh and an init
A host and a token
when you reach for it
Before you open the pull request
Reviewing someone else's work
Your first week on a new team
Fig. 08Pull request review and remote review do not combine. One takes a number, the other takes a diff.
07 · remote
Use shared context without running init on your machine
Somebody joins on Monday and clones the repository. Local review wants guides, guides want init, and init wants minutes and money. Paying that once per laptop is how a tool dies in its third week.
your laptop
$co-maintainer review --remote
·git diff against main, 2 files
·codegraph runs here, on your clone
·no guides, no provider key, no init
never leaves this machine the clone · your credentials · everything you did not stage
diff
tool call
tool result
findings
co-maintainer serve
·guides for northwind/payments-api
·built once, by whoever set the server up
·the model runs here, with the team key
·job queue, per token concurrency
never sees your working tree · your history · anything outside the diff
1 · handshaketoken cmr_…, schema and body limits agreed
2 · submitthe diff goes up, and only the diff
3 · queuethe server runs the model against its own guides
4 · tool bridgeit asks your laptop to run codegraph and waits
5 · syncfindings come back, identical to a local run
Fig. 09The tool bridge is the part worth staring at. The model is on the server, the code it needs to inspect is on your machine.
The boring safety bits. Tokens are hashed at rest, and deactivating one cancels every review in flight for it. The CLI prints a one time notice per host the first time it uploads a diff, so nobody finds out by reading the source six months later.
08 · the server
When you want the boring half automated
co-maintainer serve is one process and one app.db. It carries the GitHub App webhook, a job worker, the dashboard, and the remote review API from the last act.
localhost:5000
Fig. 10The knowledge column is the one that matters. It says needs update when main has moved past the guides, and a remake is one click from there.
Add a repository and init runs as a job you can watch. Flip auto review on and a pull request opening is enough to start one. The repo above has been connected for 8 days. Everything, init, remake and every review included, cost $1.20 in total.
09 · the evidence
Does any of this actually work
Thirty pull requests on a repository of mine. Fourteen carry a deliberately seeded, hand verified defect. Sixteen are clean controls, which is the part most review benchmarks skip and the part that makes precision measurable.
recall0.85712 of the 14 seeded defects, found
precision0.353it flags clean code too, this is the honest half
diff_local0.800needs nothing, it is all in the patch
file_local0.500needs the rest of the same file
Fig. 11bench core v3, GroophyLifefor/heap-analysis, Sep 14, 2026. Recall by what kind of context the defect needed.
Every defect that needed something outside the diff was caught. The two misses are on the axes where everything needed was already sitting in the patch, which is funny and slightly humbling: the context machinery works, ordinary close range reading is what still slips.
Thirty pull requests, no repeat runs, so variance is unmeasured. It raises about one unnecessary finding per clean pull request, and that number is what decides whether a team keeps a review bot or mutes it.
10 · the point
Context optimisation is not a loss of yield
The question was never which model reviews best. Models change every few months and the answer expires with them. The question is what the model is holding when it starts. Giving it more context does not make the prediction more accurate.
01less context
02less hallucination
03faster reviews
04more accurate predictions
get startednode 24+
$npm install -g co-maintainer$co-maintainer probe owner/repo --auth=gh then run the init line it prints, then:$co-maintainer review