Writing

The decision gate for agent-proposed code

AI reviewers leave comments and policy engines return deny, but neither reads a diff and decides on it. A gate that routes sixty agent-proposed changes into applied, refused, or one reviewer item — with the model kept strictly advisory.

Where 60 agent-proposed changes went

Decided by code, nobody looks

Applied 6
Refused 24

Escalated, 30

Back to the proposer 4
To a human 26
A synthetic, seed-locked queue: it shows how the gate routes, not how often real changes fail.

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.

OutcomeWhen
AppliedSmall, reversible, tested, one consumer. Nobody looks.
RefusedRed build, freeze window, denied licence, PII with no review. Nobody looks.
Human decidesSent 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:

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.

ConcernWhere
The decisionaction_policy.py
The thresholdsaction_policy.json, 18 reason codes, 4 author-fixable
What the AI may seeaction_store.py
What the AI reads foraction_policy/SKILL.md
Approval bound to a commitapproval_seal.py
The only path to a credentialaction_executor.py
The network, 11 agentsagent_action_approval.hocon

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.