Coding agents propose more code every month. Review capacity is flat.
Cognition, who sell Devin, say code
review rather than code generation is now the bottleneck to shipping, and that
the ease of reviewing a small pull request collapses once the diffs get large.
Two kinds of tool exist. Neither closes the gap.
AI reviewers read the diff and leave comments: CodeRabbit, Greptile,
Graphite Diamond, Qodo, Devin Review. A comment is an opinion someone still has
to judge. Ten pull requests with AI comments on them is twenty things to review.
Devin Review sorts findings by severity and the reviewer copies or dismisses
each flag. A flag someone dismisses is not a decision.
Policy engines decide deterministically: OPA, Conftest, Kyverno, branch
protection, merge queues. They evaluate config. Rego checks a Terraform plan.
Branch protection checks for a green build and two approvals. None of them reads
a diff and asks whether it does what it claims.
Tools that can read cannot decide. Tools that can decide cannot read.
The decision#
A gate does not comment. It answers one of three ways.
| Outcome | When |
|---|
| Applied | Small, reversible, tested, one consumer. Nobody looks. |
| Refused | Red build, freeze window, denied licence, PII with no review. Nobody looks. |
| Human decides | Sent up with the reason, and with what would change the answer. |
Sixty synthetic change proposals through this repo:
60 proposed
6 applied nobody looks
24 refused nobody looks
30 escalated
4 back to the proposer only a CI run or a rollback was missing
26 to a human
Policy as code, extended#
Policy as code evaluates config. Extending it to change proposals takes two
additions. The record carries the change’s evidence, and something reads the
parts no threshold reaches.
One change, as the gate receives it. Credential, author and branch are stripped
by the projection.
CHG-2244 acme/platform-config #4612 @80a757cb11d5 local
diff_summary ALTER TABLE feature_flags
ALTER COLUMN rollout_pct TYPE numeric(18,4)
stated_intent Tighten feature_flags.rollout_pct, which was created
without a foreign key constraint
reversible False
rollback_ref migrations/175_down.sql
consumers 11
ci_status passed
coverage_delta -0.20
open_incident INC-5584 sev2
records_affected 1900
review_state approved
A down migration exists, so there is a rollback reference. The change is still
irreversible: narrowing the type discards precision the down migration cannot
restore. Both fields are true.
Nine conditions decide it. The same rows produce the outcome, the reason codes
and the remediation.
PASS PRODUCTION_SCOPE local needs local or staging
PASS CI_NOT_RUN passed needs passed
PASS NO_ROLLBACK migrations/175_down.sql needs a recorded rollback
FAIL IRREVERSIBLE False needs True
FAIL INCIDENT_OPEN INC-5584 sev2 needs no open incident
PASS DEPENDENCY_MAJOR_BUMP False needs False
FAIL COVERAGE_REGRESSION -0.20 needs >= -0.1
PASS UNREVIEWED approved needs approved
FAIL SHARED_TARGET 11 needs <= 1
FAIL BLAST_RADIUS 1900 needs <= 1000
Five fail, so it escalates, and each failure becomes a remediation row. A
condition cannot be enforced without being reported, because one table does both.
What the table cannot see#
Read the first two fields again.
stated_intent Tighten feature_flags.rollout_pct, which was created
without a foreign key constraint
diff_summary ALTER TABLE feature_flags
ALTER COLUMN rollout_pct TYPE numeric(18,4)
The intent promises a constraint. The diff adds no constraint at all; it
narrows a column’s type, destructively, on a table eleven services read. No
threshold catches the difference, and none of the five reason codes mentions
it.
That is what the AI is for. An assessor reads both fields and reports the
mismatch. Its finding is recorded beside the decision, never folded into it, so
the model can be confident and wrong without moving the outcome. An AI reader
you do not have to trust.
The reviewer gets one item.
CHG-2244 acme/platform-config #4612 @80a757cb11d5 local
ALTER TABLE feature_flags ALTER COLUMN rollout_pct TYPE numeric(18,4)
IRREVERSIBLE, INCIDENT_OPEN, COVERAGE_REGRESSION, SHARED_TARGET, BLAST_RADIUS
would run unattended if:
reversible is False needs True
open_incident is INC-5584 sev2 needs no open incident
coverage_delta is -0.20 needs >= -0.1
consumers is 11 needs <= 1
records_affected is 1900 needs <= 1000
NOTE: stated intent promises a foreign key constraint. The diff adds
no constraint; it narrows the column type instead.
DECIDE: Eleven services read this flag and the narrowed type discards
precision the down migration cannot restore. Accept that, or
require the constraint the intent describes?
An AI reviewer cannot say what would change a decision, having never made one.
A policy engine returns deny and stops.
Those rows are also why 4 of the 30 never reach a reviewer. The policy lists
four conditions an author can satisfy without anyone deciding anything: no
pipeline run, no recorded review, no rollback, coverage below the floor. Where
every unmet condition is one of those, the matrix marks the change
proposer_fixable and it goes back rather than into the queue. Everything else
describes the change itself and cannot be fixed by doing more work around it.
The gate reports the split:
routing: {'back_to_proposer': 4, 'needs_a_decision': 26}
Does the reader actually work#
Measurable, because the fixture records which mismatches it planted, outside
actions where no projection and no coded tool can reach them. A model given
only the projected records and the assessor standards, blind to that key,
rated all sixty: precision 1.00, recall 0.93. Fourteen of fifteen caught,
no false alarms. The one it did not rate high it rated moderate, for the right
reason.
It also corrected the key, flagging five dependency bumps whose diff says
requirements.lock while the file counts show the package vendored in
wholesale. Real mismatches the generator had been creating without recording.
Score any assessment with score_assessor.py.
The shape of it#
┌─► applied 6 ─► executed, nobody looks
│
┌─► DETERMINISTIC ─► DECISION ─┼─► refused 24 ─► nothing happens, nobody looks
│ code, 9 checks binding │
a change ─► │ └─► escalated 30
projection │ ├─► sent back to the author 4
strips the │ └─► reviewer's item 26
credential │ ▲
and author └─► AI READS ─► notes ─ ─ advisory ─ ─ ─ ─ ─ ─ ─ ─ ┘
3 agents,
no tools
Solid lines bind. The dashed one cannot: it reaches the reviewer’s item and
never the decision.
What the framework adds#
It runs on Neuro SAN. Every box
above is an agent or a tool, and which agent may call which tool is written in a
config file rather than buried in code. The diagram is a picture of that
file. In plain terms:
- The projection is a tool. Only agents handed it can call it.
- The three readers hold no tools at all. They read what they are given and
return text. That is the whole of what they can do.
- One agent holds the tool that applies a change, and that tool is the only
route to a credential.
- What the readers are told to look for lives in
skills/ as
documents, versioned the same way the thresholds are.
- Tools are the only way an agent affects anything, five of them here, which is
how
validate.py can check the whole graph before a run.
One property of the framework is why the shape is like this. In Neuro SAN every
tool in a request shares one dictionary. Put a deploy token in it and any agent
holding any tool can read it. So nothing secret goes in: credentials are
references, resolved inside the executor at the moment of running and never
handed back. The approval seal works the same way, fingerprinting the change
including its commit, so a later push cannot inherit an approval.
evaluate_one is policy as code and could be Rego in OPA. Swapping it changes
nothing above. What a policy engine cannot do is run an AI reader in the same
request without giving it a route to the decision.
A policy engine next to an agent loop gives you the first two rows and none of
the boundaries.
Try it#
pip install -r requirements.txt
python run.py validate # config, tool classes, framework resolution
python run.py test # 134 checks, no API key
python run.py probe # needs ANTHROPIC_API_KEY. start here
python run.py approval # the full gate
The 134 checks cover both boundaries, every branch of the policy, the
commit-bound seal, the executor’s four refusals, and guards on the fixture. No
model, no network, no key.
What this is not#
Not a product. The executor is inert, a seam where a real one would call the
migration runner. The queue is synthetic and seed-locked, so the sixty-change
breakdown shows how the gate routes, not how often real changes fail. The seal
is a fingerprint rather than a signature: it catches accidental revision, not
deliberate.
It does not solve the expertise problem either.
DORA describes an expertise
paradox, where lowering the barrier to unfamiliar domains removes the struggle
that builds skill. A gate routes attention. It cannot give a reviewer context
they never built.
What it does is narrower. Policy stays deterministic, agents stay probabilistic,
neither reaches around the other.
Sources: Cognition on code review as
the bottleneck and the Lazy LGTM problem;
DORA on balancing AI
tensions, from 1,110 open-ended responses by Google engineers surveyed Q3 2025;
Cognizant AI Lab
on the agentic SDLC. Product names describe categories, not benchmarks.