I had an incident at work where an AI-powered agent made a change that passed CI and seemed entirely reasonable in the PR. The problem wasn’t just with the code—the issue was that we had never properly verified what the agent was actually allowed to do, which tools it could run, and where its authority ended. And to be honest, this incident completely changed my view of the review process!
I hadn't really thought about what the agent does behind the scenes. I mean, where exactly it sends the data or if it sends it at all. If a problem came up while checking the code or comparing something in the code, I’d report it as a bug with the agents and ask for it to be fixed! After the agents fixed the bug, I’d test it again and was satisfied with the result, but I didn’t notice what the agents were doing behind the scenes.
For most of us, code review means just one thing - someone reviews the changes in the code. One person made the changes, another reviewed them, and the reading was the control. Not the tests, not the linter, the reading.
That stopped being true on a lot of teams and I don't think most of them have said it out loud yet. Picture the pull request an agent opens a few hundred changed lines across a dozen files, a tidy description, green CI. The reviewer opens it and reads the description. Spot-checks the migration and the tests. Approves. Nobody read the few hundred lines. Nobody was ever going to.
What got reviewed in that PR wasn't the code. It was the agent. Which instructions it ran under and which tools it had. What it was allowed to touch. Whether a human stood between its output and production. Whether the team admits it or not, that's where the review moved. This is about that move, why I think it's the right one, and why it's more dangerous than it looks.
The diff-based review rested on an assumption so old nobody stated it: writing code is slower than reading it. One person produces a couple of hundred lines in a day and a colleague can read them carefully in twenty minutes, and that ratio held for decades, through every language and framework change, and it's the entire reason "read the diff" worked as a control.
An agent produces a day's worth of diff in under an hour and a team can run several of them at once. Reading didn't get any faster. So the ratio flipped. The control stopped holding on its own.
What happens next is the pattern that shows up on almost every team that adopted agents seriously. Review turns into skim. The reviewer reads the PR description (written by the agent), checks that CI is green (on tests written by the agent, against a spec the agent summarized from the ticket it was handed), scrolls past the longest file because it's mostly generated boilerplate and approves. It looks like review. It's a signature.
I'm not saying that to shame anyone, it's rational. Reading every generated line is not a strategy that scales and pretending it is just means the skim happens in secret. The honest version is to admit the line-by-line read is gone for most changes and ask what replaced it.
Here's what replaced it, though nobody wrote a policy. When a team can't inspect every output, it starts inspecting the thing that makes outputs. Nothing new there, it's how anyone hires a contractor. Nobody stands behind an electrician checking every wire nut. They check the license and which walls the electrician is allowed to open and that an inspector signs off before the drywall goes back up. Process trust in place of keystroke trust.
With agents the process is unusually concrete because most of it is config. Here's the shape of the actual review target on a project running Claude Code (the rule syntax is from the permissions docs; the specific rules are a made-up example):
.claude/settings.json
{
"permissions": {
"allow": ["Read(src/**)", "Edit(src/**)", "Bash(npm run *)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Bash(rm *)", "Bash(curl *)"]
}
}
That file says more about what the agent can do to a codebase than any single PR it opens. Rules are evaluated deny first, then ask, then allow. The first match wins. So a broad deny can't carry an allowlist exception. Bash(npm run *) matches npm run test --watch and doesn't match npm install.
The file is meant to be checked into version control and shared with the whole team so it shows up in git as a diff. And in my experience it gets about the same attention as a .prettierrc. That's the drift in one picture: the control moved into a config file and the review process didn't follow it there.
Honestly, I think this is the most useful reframe available right now. The agent's permissions file, its system prompt and its tool list are the code now, at least in the sense that matters for review: they're the artifacts whose contents decide what ends up in production next week while nobody is looking, and they deserve the slow read the diff used to get.
Not a checklist. Three questions I'd want answered before I trusted a PR from an agent, and in practice they cover most of what matters.
What can it call. Every tool an agent can reach is a capability someone granted. A model with a shell plus a database client plus an outbound HTTP tool is a very different colleague from one that can only read files. The sneaky part is that tools arrive through config rather than through code, so they skip the review that code gets, and I wrote up the worst case of this in Your .mcp.json Is a Backdoor Nobody Reviewed: six lines of JSON that download and run a third party's program with your credentials, plus a tool list that lands in the model's context before anyone approves anything, all of it merged under a commit message like "wire up the agent". Reviewing the agent means reading that JSON the way a new dependency gets read because that's what it is.
What this might affect? Scope of application! Which paths, which branches, which environments. The Claude Code documentation contains an important caveat that, as it turns out, is of great significance: the read and edit restrictions apply to built-in file-handling tools and to file-handling commands that the agent recognizes in Bash, such as cat and sed. They don't apply to a Python or Node script the agent writes and then runs, because that script opens files by itself. For that the docs point at the OS-level sandbox, and that's a separate mechanism with its own config block and its own defaults. So a sentence like "it's denied from reading .env" is true only at the layer where somebody configured it, and the review has to ask which layer that is (and whether the agent can reach the other one by writing a five-line script, because usually it can).
Who sits between it and production. This is the one that saves a team when the first two fail. GitHub's coding agent is a decent reference for what a gate looks like. By default Actions workflows don't run when Copilot pushes to a pull request until a maintainer clicks "Approve and run workflows". If the repo requires approvals, the approval of the person who asked the agent for the change doesn't count toward the number. A second human has to look. That's a deliberate design choice.
It's also the part that got a little weaker on September 1, 2026 when GitHub shipped an opt-in preview in which Copilot code review can itself approve a pull request and have that approval count toward the required-approvals rule. It's off by default, admins gate it, file paths can be restricted and the approval is dismissed when new commits land, so I don't want to overstate it, but the direction is clear enough: the reviewer of the agent's output can now be another agent, and at that point the only human review left anywhere in the loop is the review of the setup.
That's the objection. And it's a valid one. Here's the compromise, to put it simply: when the check shifts from the diff to the agent, the team stops detecting this specific error. The off-by-one in the pagination, the migration that drops a column it should have renamed, the retry wrapped around a call that was never idempotent, the kind of thing a careful reviewer used to catch on a quiet Thursday afternoon with twenty minutes and a coffee, and a permissions file catches none of it. It bounds the blast radius, it doesn't touch correctness.
So the thing being bought is leverage. One review, a hundred PRs. And the thing being paid is the specific catch. That's a good trade only if the guardrails are real and "real" has a precise meaning here.
OWASP's entry on excessive agency puts it in one line I keep coming back to: implement authorization in downstream systems rather than relying on an LLM to decide if an action is allowed or not. A guardrail that lives in the prompt ("never touch production") is a suggestion. A guardrail that lives in a deny rule, a scoped token, or a branch protection is a control. Reviewing the agent means checking which kind is actually there because the prompt kind fails silently the first time someone pastes a clever issue title into the agent's context (and I'm not sure most teams have tried that against their own setup, for what it's worth it's a ten-minute experiment).
And the liability didn't move with the review. Whoever merged the PR owns what it does, legally and in the incident channel; I went through the legal half of that in AI and code ownership. Process trust doesn't transfer responsibility. It only changes where the attention goes.
I'm not arguing for reading less code. I'm arguing about where the read sits. In the model that's forming, the diff read becomes a sample, not the gate. The migration gets read closely because the blast radius is a table. The forty renamed imports get skimmed because the worst case is a failed build, and the attention that used to go to line 312 of a generated file goes instead to the layer that decides what the next hundred PRs are able to do, meaning the tool list, the scope and the gate, in roughly that order of how often they turn out to be wrong.
If the agent's diff still needs a slow careful human read every time then the agent isn't saving anyone anything. 🤷♂️
What's uncomfortable is that most teams made this move already, by attrition rather than by decision, in the sense that the skim happened one PR at a time, the approvals kept coming because the builds stayed green, and the permissions file that now decides what the agent can do to the codebase never got a reviewer because it never looked like the kind of file that needed one. If the review moved up a level, the reviewers have to move with it. Otherwise nobody is reviewing anything and the green check is the only one who noticed.
The diff is still worth reading. The review that protects you now happens one level up.
Thanks for reading! English isn't my first language, so I use AI to polish the grammar. Everything else here - the ideas, the code, the opinions - is mine.
Enjoyed this one? Let's stay in touch — I'm on LinkedIn, always happy to chat, swap ideas, or just say hi. 👋