Diff review catches what changed. Only a whole-repository adversarial pass catches what the change quietly broke somewhere else, which is why shipping should gate on the second.
A diff review answers a narrow question well: does this specific change do what it claims to do, correctly, in isolation. That's a useful question and I still ask it every time. But it is structurally blind to an entire category of bug, because a diff only shows you the lines that changed, and the bugs that matter most in a codebase of any real size are rarely contained inside the lines that changed. They're in the places the change touches without editing — a shared type whose contract shifted, an assumption three call sites away that the diff's author never saw because it wasn't in their diff. The diff, by its nature, only shows the reviewer what the author already decided was relevant, and the author deciding what's relevant is precisely the judgement that was wrong when the bug slips through.
I've had a change pass diff review cleanly — small, focused, obviously correct on its own terms — and still break a feature nowhere near the file it touched, because it altered the shape of a shared function's return value in a way that was fine for every caller the author checked and silently wrong for one they didn't know existed. No diff review catches that, by construction, because the caller that broke wasn't in the diff. It was in the repo. Finding it required someone, or something, actually looking at the repo as a whole and asking what else depends on the thing that just changed.
This is why I've moved to gating anything that ships on a whole-repository adversarial pass, not just a review of the diff — a review that starts from the assumption that the change is trying to hide a problem from you and goes looking through the surrounding codebase for where that problem would surface. It's a slower, more expensive check than diff review, and it should be, because it's answering a fundamentally harder question: not "is this change correct" but "does this change, correct as it may be by itself, break something else that depends on it."
The independence of the reviewer matters as much as the scope. A review run by the same session, same context, same assumptions that produced the change is prone to inheriting exactly the blind spot that let the bug through in the first place — if the author didn't think to check a call site, a reviewer sharing the author's context often won't either. A fresh session, with no memory of why the change was made the way it was and an explicit brief to find what it broke, is far more likely to actually look at the call site nobody thought to check, precisely because it isn't carrying the same assumptions in.
What this costs is time and, if you're running it through a second model or engine, a second round of compute on every non-trivial change. What it buys is catching the category of bug that a passing test suite and a clean diff review both structurally miss — not because anyone was careless, but because "correct within the diff" and "correct within the repo" are different claims, and only one of them is the one that actually matters to whoever uses the product.
I still do the fast diff review first, because most of what's wrong with a change is wrong within the change, and there's no reason to pay for a full-repo pass to catch a typo. But nothing ships on the strength of the diff review alone any more. The gate that decides whether something is actually safe to release is the one that looked at the whole repository and tried, adversarially, to find what the change broke that the diff never showed.