Green CI, Wrong Behaviour

The pull request looks great. Tidy functions, good names, twelve new tests, all passing. You approve it. Two weeks later a customer finds that invoices are rounding down, and when you dig in, the new test asserts the wrong value. The agent wrote the code, then wrote a test that confirmed what the code did. Both were consistent with each other, and both were wrong.

Agent-written code fails differently from a junior engineer's code, and your review habits were built for the junior engineer. As a rule of thumb, a person's mistakes usually look like mistakes: awkward code, a confused comment, a sign of struggle. An agent's mistakes are fluent. It writes wrong code in the same confident style as right code, so the cues you learned to rely on are gone. And there is a lot more of it.

This is for you if
  • Your team merges agent code and you are not sure how closely it's being read
  • Reviews feel like skimming because the volume went up
  • Tests pass, and bugs still get through
  • You are deciding what your review policy should be for agent-authored work

What to Look For

FailureWhat it looks likeHow to catch it
Tests that prove nothingAsserts what the code does, or mocks the thing under testRead the tests first, against the spec
Invented dependenciesA package that doesn't exist, or one a squatter registeredCheck every new dependency by hand
Scope creepUnrelated refactors mixed into the changeCompare the diff with the spec
Reinvented helpersA new utility that duplicates one you already haveSearch before accepting new helpers
Silent failure handlingA broad catch that hides errors and returns a defaultLook hard at every catch and fallback
Loosened checksA test or lint rule weakened to make things passFlag any change to existing assertions

Invented packages are worth a special mention. Models sometimes suggest package names that sound right and don't exist. Attackers register those names and wait, which is known as slopsquatting. The install succeeds, and CI stays green.

Change the Order You Read In

I read an agent's pull request in a different order from a colleague's, because the risk sits in different places.

  1. The spec or ticket. Know what should have happened before you see what did.
  2. The diff summary. Do the touched files make sense? Anything unexpected is your first question.
  3. The tests. Read these before the code. Would they fail if the feature were broken?
  4. New dependencies, config and migrations. These have the largest blast radius.
  5. Error handling and boundaries. This is where fluent code hides its mistakes.
  6. The rest of the code. By now, you know what you are looking for.

Make Tests Prove Something

The strongest check for a test is to break the code and see whether it fails. Mutation testing automates that. You can also do the cheap version: ask a fresh agent session, with no memory of the implementation, to write tests from the spec alone.

# Does the test suite notice when the code is wrong?
# First time only: npm init stryker  (installs the test-runner plugin and writes a config)
npx stryker run --mutate "src/billing/**/*.ts"

# Every new dependency, for a human to check
git diff origin/main -- package.json pnpm-lock.yaml | grep '^+' | grep -v '^+++'

Keeping generation and evaluation separate matters, because an agent grading its own work is a poor judge. A second session or a different model reviewing the change has seen none of the reasoning that led to it, so it isn't anchored on it. That review is useful as a first pass, and it finds real things. It doesn't replace a person.


sequenceDiagram
    participant S as Spec
    participant G as Implementing session
    participant T as Fresh test session
    participant R as Fresh review session
    participant H as Human reviewer
    S->>G: Build this
    S->>T: Write tests from the spec only
    T->>G: Tests must pass
    G->>R: Diff, with no reasoning attached
    R-->>H: Findings
    H->>H: Read the tests, risks and diff
      

Scale Review by Risk

TierExamplesReview
HighAuth, payments, data migrations, infrastructureTwo people, line by line, and the design agreed first
NormalFeatures and business logicOne person, in the order above
LowDocs, mechanical renames, test additionsAutomated checks and a quick read

Encode this with path-based code owners, so that a change to the billing folder always gets the right reviewers. Don't try to detect who wrote the code. Hold every change to the same bar, and let risk set the depth.

What Goes Wrong

⚠️ Common Mistakes

1. Treating green CI as a review

CI proves the tests pass. If the agent wrote the tests, that proves very little.

2. Reviewing huge diffs

Nobody reviews 3,000 lines well. Send it back to be split. The limit is the reviewer's attention, not the tool's speed.

3. Asking the agent if its code is correct

It will usually say yes, and it will sound convinced. Ask a fresh session to find problems instead.

4. Letting the author go missing

The person who opened the pull request owns it, and should be able to explain any line. “The agent wrote that” is not an answer in an incident review.

A Checklist for the Review Itself

When a large agent-written change arrives, I use a short list, because fluent code makes it easy to lose focus:

  • Does this solve the problem in the ticket, or a slightly different one?
  • Would each new test fail if the feature were broken?
  • Is every new dependency real, maintained and needed?
  • Are errors handled, or just caught and hidden?
  • Does anything touch auth, money, or data deletion?
  • Is there new code that duplicates something we already have?
  • Could I debug this at 2 a.m.?

If the answer to the last question is no, send it back, even if everything else passes. Readability is a safety property. Code that nobody on the team understands is a liability that only looks finished.

Be kind to your reviewers as well. If you opened the pull request, read it yourself first, and note where you're unsure. A reviewer shouldn't be the first human to see the code.

What I Would Do First

Add a check that flags new dependencies and any change to existing test assertions, since those are the two cheapest catches. Set a size limit on pull requests. Then agree one rule as a team: whoever opens a pull request can explain every line of it, whoever or whatever wrote it.