Skip to main content
日本語

AI Review Should Not Become a Reassurance Ritual

Tomohiro Iida · Published August 25, 2026 · Updated August 25, 2026

Adding AI code review to a workflow makes every reviewed diff look safer. If the review scope, the strength of the review, the per-diff limit, the rerun condition, and the exit condition are undefined, review stops being an inspection and becomes a ritual for reducing anxiety. At Netsujo we ran repeated reviews on the same diff and a human re-read the same class of finding every time. This is the design we moved to: decide review scope, risk classification, review budget, rerun conditions, and exit conditions first, and keep review demand separate from execution authorization.

Key takeaways

  • Decide the review scope, risk classification, per-diff budget, rerun condition, and exit condition before adding AI review.
  • “Until I stop feeling anxious” is not an exit condition. When the automated review count reaches its limit, automation stops and a human decides.
  • A machine-check PASS and a risk classification calculate review demand only. They never authorize a runtime execution.
  • Move findings into tests, CI, and gates so that a person does not read the same finding again.
  • Budget figures you cannot observe are recorded as UNOBSERVABLE rather than invented.

Incident card

FieldValue
IncidentAI review ran on the same diff without a defined scope, count, cost, or exit condition.
SymptomFindings never converged, and a person re-read the same class of finding on every pass.
False assumptionMore review passes mean higher quality, so more review is always safer.
Root causeThe review scope, strength rule, per-diff limit, rerun condition, and exit condition were not implemented.
Immediate fixCap the automated review count per diff and hand the decision to a human once the cap is reached.
System fixSeparate review demand (machine checks and risk classification) from execution authorization (runtime status, budget, and owner approval).
Remaining riskA finding within the cap is still not guaranteed to be useful, and spend telemetry may be unobservable.

What we observed

The false assumption: more review is safer

Review is not free. It consumes time, money, waiting, and human attention, which are all finite. A review loop with no count limit effectively has no termination condition, and delegating termination to a feeling turns the condition into “until the anxiety is gone”. That is not a condition; the same diff can be reviewed indefinitely. The second error is reading review output as a guarantee. Review returns findings, not proof of correctness.

Root cause: review demand and execution authorization sat in the same layer

The original design called a review automatically whenever machine checks passed, which made “checks passed” equal to “execution permitted”. The current repository separates the two. The header of scripts/quality/run-quality-gate.mjs states that machine checks and risk classification calculate review demand only and never authorize a runtime execution. The same rule appears in .quality/codex-review-policy.json as classification_or_machine_check_pass_never_authorizes_runtime_execution, together with missingTelemetry set to unknown_not_invented.

Immediate fix: a per-diff review count limit

The eligibility decision lives in decideCodexEligibility inside scripts/quality/prepare-codex-review.mjs, and only three gates remain: there must be a reviewable change, machine checks must have been run and passed, and the automated review count for this diff must be below the limit. maxReviewsPerPullRequest is 3 in .quality/codex-review-policy.json. We deliberately do not stop on diff size, because a size cap creates the inversion where the largest diffs receive the least review. Instead largeDiffFiles (25 files) and largeDiffChangedLines (800 lines) are advisory: exceeding them produces a warning that splitting the change would improve finding precision.

System fix: split review into five layers

LayerWhat it decidesWhere it lives
Review scopeWhat is included in the review inputexclude in .quality/codex-review-policy.json
Risk classificationHow much reasoning strength to usescripts/quality/classify-change-risk.mjs
Review budgetCount, context volume, and output volume per difflimits and context in the same policy
Rerun conditionWhen a re-review may startscripts/quality/prepare-codex-review.mjs
Exit conditionWhen review endsStop automation at the cap and hand over to a human

Review scope is decided by naming what is never read. The exclude list covers build output and binaries such as .next, node_modules, coverage, minified JavaScript, source maps, images, and fonts; excluded files are reported as a count only. package-lock.json is manifestOnly: its full text is not sent, but it still counts as a dependency-change signal. Only a diff-limited bundle is handed over. Letting a reviewer explore the whole repository makes the input volume independent of the diff, and the budget estimate stops working.

Risk classification: never treat “undecidable” as safe

Review budget: cap the input and the output

KindSettingValue
PassesmaxReviewsPerPullRequest3
FindingsmaxFindings5
Output wordsmaxOutputWords1200
Context linesmaxTotalContextLines2000

Capping the output side is not only about cost. When dozens of findings come back, a person reads from the top and gets tired. The findings limit is a budget for the reader as well.

Exit conditions written in advance

Move findings into tests, CI, and gates

Nature of the findingDestinationExample
Mechanically decidableTest or check scriptForbidden classes, canonical URLs, wording rules
Out of the allowed change boundaryGateEdits outside the assigned paths or shared ledgers
Mistaken stateControllerPull request state, SHA match, deploy target
Design and priority judgementHumanWhat to build and how far to build it

What stops, and what stays, when the budget runs out

The common mistake is to treat a stop as a removal of capability. In .claude/agent-os/vendor/runtime-tools.json the Codex runtime has status emergency_stopped with reason_code CODEX_SPEND_EMERGENCY_STOP, while capability_revoked is false: the stop is a temporary runtime state and the reviewer and implementer capabilities remain. Blocked operations are start_session, exec, automated_review, rerun, and delegated_invocation; reading existing artifacts remains allowed. The resume contract defaults to deny. An exception holds only when owner_authorization_id, token_budget_snapshot, spend_budget_remaining, exact_scope, expected_sha, spend_cap_usd, and expires_at are all present and the expiry, SHA, and scope match. In the recorded scoped override the two budget fields are UNOBSERVABLE with invented set to false. Not being able to see the budget is different from having budget left.

Reusable checklist

Evidence

ClaimClassSource
The review-demand loop guard per diff is three.IMPLEMENTED.quality/codex-review-policy.json, limits.maxReviewsPerPullRequest. Its limitsNotes states this is a bundle-preparation guard, and that runtime execution has separate budget and availability gates.
Findings, output words, and context lines are capped.IMPLEMENTEDlimits and context in the same policy
Only three eligibility gates remain.IMPLEMENTEDdecideCodexEligibility in scripts/quality/prepare-codex-review.mjs
Machine checks and classification never authorize execution.IMPLEMENTEDscripts/quality/run-quality-gate.mjs header and the policy executionAuthorization block
Unmatched files are classified medium, not low.IMPLEMENTEDclassifyFile in scripts/quality/classify-change-risk.mjs
Review executions and volumes are recorded and summarised.IMPLEMENTEDscripts/quality/record-codex-usage.mjs
The stopped runtime keeps its capabilities.IMPLEMENTEDcapability_revoked false in .claude/agent-os/vendor/runtime-tools.json
Resume defaults to deny and needs seven fields.IMPLEMENTEDresume_contract in the same file
The review-everything decision ran from 2026-08-11 and was superseded on 2026-08-21.OBSERVEDgovernance/decisions/DEC-020.yaml
Repeated reviews returned the same class of finding.OBSERVEDOperational observation
Cost reduction from the caps.INFERREDUnmeasured; only the presence of the caps is confirmed.
Automatic routing of every finding to its destination.PROPOSEDCurrently a classification policy, not an implementation.

Limitations

Remaining risk

Frequently asked questions

Is a limit of three review passes correct?
It is a Netsujo operating default, not a universal answer. When findings do not converge, splitting the diff improves precision more than adding passes. Record how often the cap hands work to a human and adjust on real data.
Can review end when there are zero findings?
Zero means nothing was found from that viewpoint. It is not proof of correctness, so termination is decided by count and state rather than by a zero result.
Can low-risk diffs skip review?
Using the classification to decide whether to review at all means nothing is detected when the classification itself is wrong. Classification selects reasoning strength, while every diff that passes machine checks stays in scope.
How is quality protected while AI review is stopped?
What stops is the execution, not the role. Machine checks keep running, and anything they cannot decide returns to human review.
When should a finding become a test?
A useful trigger is the second occurrence. The first is an individual lesson; from the second, treat it as a system defect rather than a lapse of human attention.

Previous: separating ChatGPT, Claude Code, Codex, and human judgementEpisode 00 of the incident record covers the role split before adding more agents.

Next: a second correction is an orchestration bugEpisode 02 moves repeated corrections into a Controller, Evidence Ledger, and Gate.

AI agent development and operations incident logThe series hub lists all twelve episodes and the order to read them in.

We review your AI review scope, risk classification, budget, rerun conditions, exit conditions, and evidence design as one operating system.

Talk to Netsujo about AI development and operations design