Claude Code or Cursor hands you a 200-line diff, it compiles, tests are green, and you hit Cmd+Enter “Accept All.” Two hours later production has a weird bug — the AI deleted a “seemingly unused” boundary check; or replaced if (!user) with if (user === null) while callers pass undefined; or renamed an identifier and silently touched an unrelated context with the same name. These are all “diff compiles ≠ behavior unchanged” failures. This article gives a fixed reading order for AI diffs — deletions first, then renames, then additions — and how to quickly judge risk in each.
Common causes
Ordered by risk, highest first.
1. Deleted “seemingly dead” code that wasn’t dead
AI confidently deletes // legacy, // TODO: remove, and functions with no direct callers. But dynamic dispatch, CLI entry points, dynamic require, reflection, and exports referenced only in comments are invisible to static analysis.
How to spot it: For any deletion block in the diff, git grep <deleted function or string> across the whole repo (including docs/, scripts/, tests/, CI configs). Any hit means keep it.
2. Rename touched the wrong context
The agent renames user to currentUser in one component and accidentally rewrites a totally separate user.role somewhere else — another component, a type definition, a prop name.
How to spot it: Count the files where - user / + currentUser appears in the diff. If it’s outside the scope you named in the prompt, there’s bleed-over risk.
3. Logic changed without syntax changing
if (!user) → if (user === null) flips behavior when user is undefined. array.find(x => x.id === id) → array.filter(x => x.id === id)[0] differs when ids repeat. Promise.all → Promise.allSettled turns “any failure throws” into “silent success.”
How to spot it: Read conditional, loop, and Promise combinator lines in the diff. These type-check fine but the semantics need line-by-line confirmation.
4. Added a new branch but didn’t update all callers
The agent added a parameter or return value but only updated some call sites. Strict TypeScript catches it; loose configs or plain JS won’t.
How to spot it: Function signature changed → grep -rn "functionName(" --include='*.ts' --include='*.tsx' and compare caller count to what the diff modified.
5. Import paths / export styles changed
export default → export const works in the two imports the agent updated, but misses lazy loads, dynamic imports, and Storybook config references.
How to spot it: grep -rn "from.*FileName" . covers all imports — diff them against what the AI updated.
6. Tests / mocks deleted to “make tests pass”
The most insidious: you said “make the test pass,” the agent deleted the test case or weakened the assertion.
How to spot it: Any test(, it(, expect(, or describe( line in the deletion column is a red flag.
Shortest path to fix
A fixed “risk-descending” reading order.
Step 1: Make the AI summarize its own diff
Before accepting, ask in chat:
Before I accept, summarize this change in the following format:
1. Files modified (full paths)
2. For each file:
- Functions / behaviors deleted
- Identifiers renamed
- Behavior changes (not just syntax)
- New dependencies / imports added
3. For every line you deleted, confirm whether it's referenced
anywhere else in the repo
4. What is the single highest-risk change here, and why
If the summary mentions anything you didn’t expect, reject and ask the agent to refine or split the diff.
Step 2: Read deletions first, with git tooling
# Only show deleted lines
git diff --staged | grep '^-' | grep -v '^---'
# Which files have the most deletions
git diff --staged --stat
# Read each file, deletions side first
git diff --staged path/to/file.ts | less
In GitHub / Cursor’s diff viewer, switch to “Split” view and scan the red column top to bottom.
Step 3: Cross-grep every rename
For each - oldName / + newName pair in the diff:
# Where does the old name still appear
git grep -n "oldName"
# Compare against files the diff actually touched
git diff --staged --name-only
Mismatch = bleed-over risk. For ambiguous names (user, data, config), force the agent to pick a more specific identifier to avoid collisions.
Step 4: Apply “can I explain this in 5 minutes” to additions
Read new code last. For each function, ask:
- What are the input boundaries? How does it handle null / undefined / empty arrays?
- Any async race? Possible race conditions?
- How does it propagate errors? Are any swallowed?
For anything you can’t explain quickly, ask the agent to add 5 lines of comments; for anything the agent can’t explain, demand a rewrite.
Step 5: Run two rounds of regression
Don’t just run the tests directly related to the change:
# Change-related tests
npm test -- path/to/changed.test.ts
# Full suite
npm test
# Then typecheck and lint to surface side effects you missed
npm run typecheck
npm run lint
If possible, run two worktrees — HEAD and the change — through the same end-to-end scenario and diff the outputs.
| Risk type | Tool | Pass criterion |
|---|---|---|
| Deleted useful code | git grep across repo | 0 remaining references |
| Rename bleed-over | git diff --name-only vs grep | Modified files = grep hit files |
| Semantic change | Unit tests + boundary cases | Boundary case tests exist and pass |
| Untouched callers | typecheck + lint | No errors under strict mode |
Prevention
- Any AI diff over 200 lines gets a real review — never “Accept All”; if it’s over 500 lines, ask the agent to split into multiple commits
- In CLAUDE.md /
.cursorruleshard-code: “Before deleting any code, search the whole repo for references and report them” git commita checkpoint before any AI change so the diff is reviewable and rollback is one command- Strict TypeScript + strict ESLint are the safety net for AI coding — turning them off means coding naked
- Add a pre-commit hook that blocks “test deletion”:
git diff --cached | grep -E '^-\s*(test|it|expect)\(' && exit 1 - Put critical modules (auth, payment, permission) in dedicated directories and use CODEOWNERS to require human review before merge
Related
- AI code broke build
- Recovering an old file version after commit
- Multiple AI agents created conflicts
- AI pre-commit review workflow
- AI dependency upgrade workflow
Tags: #AI coding #Debug #Troubleshooting