Layered AI Code Review: Commit-Time, PR-Time and Human Review That Don't Step on Each Other
The first time I wired an AI reviewer into a repository, I felt clever for about a week. Then I opened a pull request buried under dozens of comments. Some came from the local reviewer I'd left running while the agent worked. Some came from the bot on the PR. A few came from a teammate who had read the bot's comments and agreed with half of them. Three comments flagged the same null check. Two flagged code that no longer existed. Nobody, including me, could say which ones actually had to be fixed before merging.
That's when it clicked: adding an AI reviewer is easy, but an AI code review workflow is a different problem. You end up with several reviewers that each see the change at a different moment, with different context and different authority. Unless you decide who owns what, they step on each other, and the noise teaches everyone to ignore all of them.
This is the review stack I use in an AI-first SDLC, where coding agents write most first drafts. It's tool-agnostic: I've run it with Claude Code, Codex and Cursor's review features, on top of plain GitHub pull request reviews. The tables are meant to be adopted in an afternoon.
Why One AI Reviewer Is Not a Review Process
A reviewer is a function: diff in, findings out. A review process answers three questions the function can't:
- When does each reviewer look at the change?
- Which concerns does each one own?
- Who decides whether a finding blocks the merge?
Skip them and every reviewer tries to do everything. The local reviewer comments on architecture it can't see, the PR bot re-flags what the linter caught, and the human skims a wall of machine comments and approves out of fatigue. Each reviewer is fine. The system is broken.
Coding agents make this worse. An agent can push ten commits in the time a person reads one. Review only at PR time and it has built three layers on a bad assumption before anyone looks. Review every commit with no triage rules and it spends its time arguing with comments the next commit makes irrelevant.
The Three Layers of an AI Code Review Workflow
I split review into three layers. Each one has a clear moment, a clear scope and a clear kind of authority.
Layer 1: commit-time (local, continuous). A fast reviewer reads each commit's diff while the agent is still working and reports back into the agent's session. Its job is to catch mistakes while they're cheap: a wrong condition, a missing error path, a test that asserts nothing. It has no merge authority. Its findings are inputs, not gates.
Layer 2: PR-time (the verdict). Once there's a pull request, one reviewer issues the verdict: approve or request changes. It can be a bot, human collaborators, or both, and the repository records which. This layer sees the whole change against the spec and the base branch, and owns cross-file correctness and "does this do what the ticket asked".
Layer 3: human (judgement and sensitive changes). A person reviews what machines shouldn't decide alone: architecture trade-offs, security-sensitive paths, migrations, product behaviour, public contracts. They don't re-read every line, just the parts that need judgement plus a summary of what layers 1 and 2 settled.
Here's how a change moves through them:
The split that matters most is ownership by concern. If two layers own the same concern, you get duplicate findings. If none owns it, you get a gap nobody notices until production.
| Concern | Linters / CI | Commit-time AI | PR-time reviewer | Human |
|---|---|---|---|---|
| Formatting, naming, style | Owns | Ignores | Ignores | Ignores |
| Local correctness (one function, one file) | Tests | Owns | Checks | Spot-checks |
| Cross-file correctness, spec compliance | Partial (integration tests) | Can't see it | Owns | Reads summary |
| Security-sensitive code (auth, secrets, input handling) | Static analysis | Flags | Flags | Owns the decision |
| Architecture and boundaries | n/a | Ignores | Flags drift | Owns |
| Product behaviour, UX, contracts | n/a | n/a | Flags | Owns |
"Ignores" is a real instruction. I tell the commit-time reviewer in its prompt not to comment on style, because the formatter already decided. Saying what a reviewer shouldn't look at cuts more noise than any amount of tuning what it should look at. It's the same principle I wrote about for agent instruction files: constraints beat wishes.
Ordering Matters: Run the Expensive Reviewer Last
PR-time AI review is the most expensive layer in tokens, latency and attention. So it runs last, after the cheap gates are green.
Don't request the PR-level review until lint, types and tests pass. On a red build you're paying for a review of code that's about to change anyway.
Commit-time review follows the same logic: it runs asynchronously and never blocks the agent. Findings attach to their commit and the agent picks them up at its next pause. A reviewer that blocks every commit just teaches the agent to batch commits, which defeats the point.
Triage Rules for Findings: Blocking, Advisory, Superseded
Every finding ends up in one of three states:
- Blocking: must be fixed or explicitly answered before merge.
- Advisory: worth reading, fix if cheap, no one waits on it.
- Superseded: the code it refers to changed or disappeared in a later commit, so the finding is closed without action.
"Superseded" is the state most setups forget, and it's where most of the noise comes from. A commit-time reviewer that comments on commit 3 doesn't know that commit 5 rewrote the function. If you treat every finding as live forever, the agent wastes time "fixing" code that no longer exists, or worse, reintroduces an old version to satisfy a stale comment.
Here's the triage table I start every repository with:
| Source | Verdict on the PR | Finding's own label | State | Who decides |
|---|---|---|---|---|
| Commit-time reviewer | n/a (no PR yet) | any | Advisory | The agent, at its next pause |
| Commit-time reviewer | any | refers to lines changed since | Superseded | Automatic, with a note |
| Commit-time reviewer | PR is open | high severity, still present | Escalate to PR-time reviewer | PR-time reviewer |
| PR-time reviewer | Approved | nit / suggestion | Advisory | Author |
| PR-time reviewer | Approved | bug / high | Blocking | Author, re-review required |
| PR-time reviewer | Changes requested | any | Blocking | Reviewer re-checks |
| Human reviewer | any | any | Blocking unless marked "optional" | The human |
Two things in that table deserve a closer look.
First, commit-time findings never block the merge directly. If one is serious and still present when the PR opens, it gets escalated so the PR-time reviewer can confirm it. That keeps one place, the verdict, as the source of truth.
Second, the "changes requested" row.
When the Verdict Is "Changes Requested", Every Finding Is Blocking
This rule caused the most debate, and it's the one I'd defend hardest.
AI reviewers label their findings: "nit", "suggestion", "low", "high". It's tempting to let the agent filter on those labels and only fix the high ones. Don't. When the verdict is "changes requested", every finding in that review is blocking, regardless of its label.
Labels lie. A reviewer calls something a "nit" when it's the symptom of a bug it didn't fully trace. And an agent told to "fix the important ones" will sincerely decide the hard one isn't that important. The verdict is the reviewer's overall judgement. The labels are its per-line guesses. When they disagree, trust the verdict.
It's cheaper than it sounds. Most nits take seconds. The ones that are genuinely wrong get a one-line reply explaining why. What you never do is silently skip a finding, because silent skips are how it comes back three reviews later.
Recording Who Reviews a Repository
A failure I didn't expect: the agent didn't know who the reviewer was. One repository had a review bot, another had two human collaborators and no bot, a third was a solo experiment nobody reviewed. The agent kept waiting for bot reviews that never came, or treating a casual human comment as a verdict.
So now every repository records its reviewer mode in a place the agent reads at the start of each session, right next to the ADRs and handoff notes. Something like this, as an illustrative shape:
# review settings (illustrative)
review:
mode: humans # bot | humans | none
required_approvals: 2
commit_time_reviewer: on
human_required_for: [auth, migrations, public_api]
Three modes cover almost everything:
- Bot. The bot's approval is the verdict. Humans review only sensitive paths.
- Humans. The PR is ready when N people approve. The agent addresses every review; it doesn't decide.
- None. Commit-time review still runs, but the PR says plainly that no one checked it, so silence isn't mistaken for approval.
Back this file with the platform's own enforcement. GitHub's protected branches and CODEOWNERS let you require approvals and route sensitive paths to specific people. The config tells the agent what to expect. The branch rules make sure it can't skip it. How much each tier should require is a bigger question, which I cover in risk-tiered approval.
Failure Modes I Hit (and the Guards That Fixed Them)
Every one of these looked fine on a dashboard until I went looking.
Reviewers that look alive but stopped reading. On long PRs, a reviewer can hit a context or size limit, review only the first part of the diff, and still post a confident "looks good". The guard: the reviewer states what it covered, and large PRs get split. If the PR touches twenty files and the review mentions four, that's not an approval.
Findings that come back after a fix. The agent fixes something and the next review re-raises it word for word. The guard: the agent cites the fixing commit in its reply, and a re-raised finding with a cited fix goes to a human instead of looping.
Review daemons nobody supervises. A local reviewer keeps running on abandoned branches, or dies silently and you lose coverage without knowing. The guard: a heartbeat in the session, and a missing heartbeat is a visible warning.
| Failure mode | Symptom | Guard |
|---|---|---|
| Reviewer went blind on a long PR | Approval that mentions only a fraction of changed files | Reviewer states its coverage; split large PRs |
| Stale findings | Agent "fixes" code that no longer exists | Mark findings superseded when their lines change |
| Finding ping-pong | Same finding re-raised after a cited fix | Second recurrence escalates to a human |
| Label filtering | Agent skips "nits" under changes requested | Verdict overrides labels |
| Unsupervised daemon | Reviews on dead branches, or no reviews at all | Heartbeat in the session, alert when missing |
| Duplicate findings across layers | Same issue flagged by three reviewers | Concern ownership matrix, "ignore" instructions |
| Ghost reviewer | Agent waits for a bot that isn't installed | Recorded reviewer mode per repo |
None of these are exotic. They're what happens when a process built for one human reviewer meets several automated ones. The same mindset applies to letting agents push their own fixes, which I cover in bounded autonomy for coding agents.
Measuring a Review Layer
Every layer costs tokens, latency and attention. If it doesn't catch what the other layers miss, remove it.
I track three things per layer:
- Real bugs found. Findings that led to a change and that a person agrees, in hindsight, were real defects.
- False positives. Wrong findings, or right but not worth the time. This predicts whether people keep reading the reviewer.
- Time to green. From "PR opened" to "approved". A layer that catches bugs but doubles this needs tuning.
A simple scorecard, filled in once a month by whoever owns the workflow:
| Layer | Findings | Real bugs | False positives | Unique catches (no other layer found it) | Time to green impact | Keep / tune / drop |
|---|---|---|---|---|---|---|
| Commit-time | ||||||
| PR-time | ||||||
| Human |
The column that matters most is unique catches. If the PR-time reviewer would have caught every bug your commit-time reviewer found, the commit-time layer is only buying earlier feedback. That can be worth it with fast agents, but know that's what you're paying for. If you already run evals on what your agent does, this is the same idea applied to reviewers: measure outcomes, not activity. A month with a handful of PRs tells you very little, so don't drop a layer after one quiet month.
Checklist: Setting Up Layered Review on a New Repository
This is the order I follow on a fresh repository.
- Cheap gates first. Formatter, linter, type checks and tests run in CI and block merge. No AI reviewer until these exist.
- Write the ownership matrix. One table: which concern each layer owns and which it ignores. Put the "ignore" lines into each reviewer's instructions.
- Record the reviewer mode. Bot, N collaborators, or none, in a file the agent reads at session start.
- Enforce it on the platform. Protected branch, required approvals, CODEOWNERS for sensitive paths.
- Turn on commit-time review as advisory. Asynchronous, never blocking the agent, findings attached to their commit.
- Add the superseded rule. Findings on changed or deleted lines close automatically, with a note.
- Order the PR-time review after green CI. No verdict requests on red builds.
- Adopt "changes requested means everything blocks". Write it into the agent's instructions verbatim.
- Require coverage statements. Every PR-time review says what it read. Split PRs above your size threshold.
- Add a heartbeat for any long-running local reviewer.
- Define the human triggers. Auth, migrations, public contracts, infrastructure, anything the spec flags as sensitive.
- Schedule the scorecard. Monthly, by a named owner, with keep/tune/drop decisions.
If you work spec-first, link each PR to its spec so the PR-time reviewer checks the change against what was asked, not only against itself. That one habit turns a lot of vague "this seems off" findings into concrete ones. I explain the spec side in what spec-driven development is.
Conclusion
AI reviewers aren't the hard part anymore. The process around them is: when each one looks, what each one owns, and who gets the final word.
Three layers, each with one job. Commit-time catches mistakes while they're cheap and never blocks. PR-time owns the verdict, and when it says "changes requested", it means all of it. Humans own the decisions that need judgement. Add superseded findings, a recorded reviewer mode and a monthly scorecard, and you'll have something that holds up when the agents speed up.
Start with the ownership matrix. It takes twenty minutes, and it'll tell you more about your review process than any tool comparison.