Reviewing AI-written code: why we test it in a clean checkout
Code written by an agent can pass every test on your machine and fail everywhere else, because it quietly depends on files that were never committed. Review it twice: read the diff, then run the tests in a fresh checkout of what you are about to merge.
π― For A developer or reviewer who accepts code written by an AI agent, often called vibe coding
- Why green tests on the author's machine prove little
- A review routine with a clean-checkout test as the gate
- How to roll back safely when something slipped through
π² The risk with code you did not type
Vibe coding means accepting generated code because it seems to work. It is fast, and for a throwaway script it is fine. The trouble starts when the same habit reaches shared code. An agent works in your working tree, which holds files you changed and never committed, local settings and cached results. The tests it runs see all of that. A teammate who checks out the commit sees none of it.
π§Ύ One incident, in numbers
On 14 October 2026 an agent adopted a 1,123-line analytics module from a live server snapshot into git. In the working tree the module passed 56 of 56 tests. In a clean checkout of the same commit it failed 10 of 56, because it depended on uncommitted edits to a neighbouring file. The commit was reverted the same day. A second slip followed: the revert command has no quiet flag, the first attempt failed, and an amend then rewrote someone else's commit.
Nothing in the diff looked wrong. The failure was in what the diff did not contain.
π The review routine
Reading the diff is necessary, and it is not enough. Our routine has five gates.
- Scope. Does the diff touch only the files the task names? Unexpected paths, deleted files and changed modes are the first things to look for.
- Reading. Can the reviewer explain what each new function does and what it returns on bad input? If not, the code is not accepted yet.
- Clean checkout. Create a fresh worktree from the target branch, apply only the commit, and run the tests there. A pass in the working tree does not count.
- Exit codes. Run the test command without a pipe.
pytest | tail -1 && git commitcommits on red, because the exit code belongs totail. - Rollback ready. Know the command that undoes the change before you merge it, and check it on a throwaway branch.
π οΈ Practice: set up the clean-checkout gate
- Pick one repository that agents write to.
- Write a short script that creates a temporary worktree from the target branch, applies the candidate commit, runs the test suite and removes the worktree.
- Make the script exit non-zero on any test failure and on an unexpected file in the status output.
- Run it on the last five agent commits. Note how many fail.
- Make the script a required step before merge. A pass in the author's tree is information, not approval.
- Add the rule to your agent instructions: "show the clean-checkout result, not the local one".
β Check yourself
- Take a commit that depends on an uncommitted file. The script fails and names the missing import or file.
- Introduce a failing test and run the gate through a pipe. The gate still fails, because you read the exit code of the test command itself.
- Someone new can run the gate from the README in under five minutes.
- You can say which of the last ten agent commits touched a path the task did not name.
π§ When this does NOT work
- Tests that do not test the behaviour. A clean checkout with weak tests gives a clean pass for broken code. Agents also write the tests they pass.
- Dependencies on the environment. A test that needs a database, a key or a host cannot pass in a fresh worktree. Mark it clearly and run it where those exist, and do not fix it by faking the data.
- Large diffs. Past a few hundred lines a human review turns into skimming. Ask for smaller changes.
- Generated code you will never touch again. A one-off script may not deserve the gate. Decide that on purpose.
- Review by the same model. A model that wrote the code often agrees with it. Use a deterministic check first, a different reviewer second.
π Evidence
| What we measured | Tests passed in the working tree against a clean checkout for one agent-written module |
| When | Incident of 14 October 2026; records checked on 3 November 2026, 22:06 UTC |
| Artifact | Our git history: the revert commit of 14 October 2026, 14:14 UTC, which removed 1,123 lines, and the note in our agent rules file |
| Limit | One incident. It shows the failure shape, not how often it happens |
β‘οΈ Next
Write acceptance down before the work starts in Spec-driven development with existing task packets, and see why a claim of success is not proof in AI judging.
Sources
- AI judging: why βit says it is doneβ is not acceptance: acceptance by checks.
- Spec-driven development with existing task packets: handing work over with criteria.
- Agent task receipts: what they prove: what a receipt shows about a run.
- Recovery after a failed, stuck or uncertain run: what to do when a run goes wrong.
π Article connections
Open the full knowledge graph βPublished article links. Select a node to open its article at the heading.
Article links Β· keyboard / text view
Same topic
- A vector base is a map, not a source: what we learned from retrieval
- When you cannot tell what a model call cost: freeze, then reconcile
- Splitting a big task for a cheap model: the 24,000-byte lesson
- Skills, MCP tools and rules: three ways to give an agent capabilities
- Eight shell and git mistakes our agents made, and the rule for each
- Several agents, one repository: ownership rules from real incidents
Next steps
Related reading
A vector base is a map, not a source: what we learned from retrieval
A vector base finds passages that are close in meaning to your question. It does not know which are true or current. Treat every hit as a pointer: open the file it names, check the hash, and take numbers from the artifact, never from the retrieved paragraph.
AI agents and MCP explainedWhen you cannot tell what a model call cost: freeze, then reconcile
Sometimes a model call times out and you do not know whether the provider charged you. Never record that as zero. Mark it uncertain, stop new paid calls, and let a named person reconcile it with the provider's statement. Refusals before generation are the exception.
AI agents and MCP explainedSplitting a big task for a cheap model: the 24,000-byte lesson
A cheap model does well on one small, well-defined job and badly on a big pile. We cap a task at 24,000 bytes and refuse larger ones before any model is called. Split by unit, one file or one country per task, and check the size before you start.
AI agents and MCP explainedSkills, MCP tools and rules: three ways to give an agent capabilities
Agents get abilities in different ways. A rule constrains what they do, a skill tells them how to do a job, and an MCP tool lets them call a function. Each has its own risk, so declare all three in a register and test that nothing exists unlisted.
π¬ Comments