The ask was simple: the PR was 3,000 lines and nobody wanted to review it. The agent split it into three PRs — one for the write path, one for the audit log, one for the tenant filter — each around a thousand lines, each with its own clean summary and a focused set of tests. Reviewers were delighted. CI went green. The feature shipped.
Two weeks later, we found that audit rows were being written with a tenant id derived from the session token, while the read path filtered on a tenant id derived from the resolved organization. They disagreed for every user who belonged to more than one org, which was roughly 4% of the base. Those users saw the audit trail of a stranger.
The bug lived in PR 2, the audit PR. Or maybe it lived in the seam between PR 1 and PR 3. Either way, no single reviewer saw it.
Why splitting feels like rigor
A thousand lines is a reviewable size. Three thousand is not. Splitting a giant PR along clean module boundaries is good engineering, and an agent that proposes to do it is doing something useful. The problem is not the split itself.
The problem is that the split moves the thing you actually need reviewed from the code to the relationship between the code. When the whole change lived in one PR, a reviewer could hold the invariant in their head: "every write to orders also writes to audit, and the tenant id must match." After the split, that invariant is spread across three diffs. Each diff looks correct in isolation. The reviewer of PR 2 reads ten functions that all use session.tenantId and sees no reason to wonder where the read path gets its tenant id.
Agents are especially good at this failure mode because they optimize for what they can measure: PR size, test count, green CI. They do not optimize for "which invariant now spans more than one PR." From the agent's point of view, the split is a strict improvement. From the system's point of view, the cross-cutting contract just left the review surface.
The half-fixes I tried first
We tried asking the agent to add a fourth PR that "just touched the contract." That mostly produced a doc diff, which reviewers skimmed and approved. We tried requiring a diagram in the description of the first PR. That produced a nice Mermaid graph that nobody maintained.
We tried asking each later PR to explicitly list the invariants from earlier PRs it depended on. That helped, but it was self-certified: the agent wrote "depends on: tenant id is consistent across audit and read" and then used a different derivation anyway, because it had forgotten its own note two PRs later.
What actually stuck
The cheap version: require the first PR in a series to contain a behavioral contract, not a prose one — a recorded request/response pair or a small golden test that pins the cross-cutting invariant. "Given a user in org A, writing an order produces an audit row whose tenant id equals the read path's filter." Later PRs in the series have to keep that test green. If PR 2 introduces a new tenant derivation, the golden test fails on the spot, in PR 2, in front of the reviewer who is already looking at PR 2.
That's a different thing from "write down the invariant." A written invariant is an aspiration. A recorded pair that the later PR must keep green is a test, and tests are how we actually stop trusting summaries. We wound up doing this locally in Powerduck: record the relevant request/response once, keep it next to the spec, and re-run it against the working tree before a split series can be merged — instead of asking the agent to remember what it promised three PRs ago.
The other half is a one-line convention in the first PR's description: "this series has N PRs; the contract that must hold across all of them is X." Not "depends on," not "related to" — the thing that, if it breaks, the feature is wrong even though every individual PR is correct.
The pattern worth naming
An agent that splits a giant PR is not making the bug smaller. It is redistributing the bug's visibility. Smaller PRs are easier to read; they are also easier to read past the seams. The invariant that no one module owns is the one that now has no natural reviewer.
Before you celebrate the tidy three-PR series, ask the question the split hid: what is the behavior that has to stay true across all three, and what machine-enforced thing will tell us if it doesn't? If the answer is "the human reviewer will notice," you have not reduced review burden. You have moved the bug somewhere harder to see.
Comments
Join the discussionNo comments yet. Be the first to comment.

