Why this is worth reading: a free coding model can return a patch that passes your test suite and still create a costly change. It might delete a seed file, flip an executable bit, rewrite a 400-line module to change two lines, or bundle a new dependency lockfile into a one-line fix. You do not need to read every line first. You need to read the shape of the diff.
Most code review tools show you a unified diff and ask you to evaluate it line by line. That is the wrong order for model-generated work. The first pass should be a shape check: which paths changed, what was deleted, whether file modes or binary content changed, and how much churn each file carries. This article gives you a small local script that prints that shape and a decision table for acting on it.
Start from the diff metadata, not the code
git diff can print the raw numbers used to build the visual diff you see on GitHub or GitLab. The --numstat option outputs one line per file with added lines, removed lines, and path. The --summary option reports renames, mode changes, symlink changes, and create/delete events. Those two commands are enough to catch several classes of bad patch before you read code.
git diff --numstat origin/main HEAD
git diff --summary origin/main HEAD
git diff --check origin/main HEAD
git diff --check is especially useful because it flags trailing whitespace and conflict markers, two common artifacts of model-generated multi-line edits. Do not skip it just because a CI linter may run later.
A reproducible autopsy script
Save this as diff_autopsy.sh and run it with two refs. It prints a per-file churn table, flags zero-add and zero-delete files, and reports mode changes and low-similarity renames.
#!/usr/bin/env bash
set -euo pipefail
base=${1:-origin/main}
head=${2:-HEAD}
echo 'PATH|ADDED|REMOVED|CHURN'
git diff --numstat "$base" "$head" | while IFS=$'\t' read -r added removed path; do
if [[ $added == - || $removed == - ]]; then
churn=binary
else
churn=$((added + removed))
fi
printf '%s|%s|%s|%s\n' "$path" "$added" "$removed" "$churn"
done
echo
echo 'only-removals'
git diff --numstat "$base" "$head" | while IFS=$'\t' read -r added removed path; do
[[ $added == 0 ]] && printf '%s\n' "$path"
done
echo
echo 'only-additions'
git diff --numstat "$base" "$head" | while IFS=$'\t' read -r added removed path; do
[[ $removed == 0 ]] && printf '%s\n' "$path"
done
echo
echo 'mode-changes'
git diff --summary "$base" "$head" | grep 'mode change' || true
echo
echo 'renames-at-60-percent'
git diff --find-renames=60% --summary "$base" "$head" | grep -E 'similarity|=>' || true
echo
echo 'whitespace-check'
git diff --check "$base" "$head" || true
The script is deliberately boring: no API keys, no model SDK, no external service. It works on any branch, any model, and any host. The path parser treats tab as the field separator, so filenames containing spaces do not break the churn calculation.
Read the shape through a decision table
Use this table after the script, not before it. The goal is to direct your limited review time to the riskiest files first.
| Signal | What you see | What to do |
|---|---|---|
| Test deletion | A file under a test/ or tests/ path has 0 additions |
Block until the model or the author explains the lost coverage |
| Seed or fixture deletion |
db/seeds.rb, *.sql, .env.example, or similar only removed lines |
Treat as a broken local setup; do not merge silently |
| Binary change |
ADDED or REMOVED shows -, or --summary reports a new binary |
Reject and ask for the source or a reproducible build step |
| Low-similarity rename |
--summary reports a rename below 60% similarity |
Treat it as a delete plus an add; review both sides fully |
| Executable bit shift |
--summary reports a mode change |
Verify it was intentional; generated patches rarely need to chmod anything |
| Pure addition |
REMOVED is 0 and the file is not new source code |
Check whether it is generated output that should be ignored |
| Pure deletion |
ADDED is 0
|
Count as a deletion, even if the file still exists in the working tree |
| High churn |
CHURN is above 150 in a single file for a small task |
Ask the model to split the change or justify the rewrite |
| Unrelated path | Files outside the issue's module or service show up | Scope creep; strip those files from the patch |
These flags are not proof of a bug. They are invitations to ask a specific question. A large churn value may be a legitimate extraction or rename. A deleted seed file may have been replaced by a generator. The table just makes the question explicit.
Test the sheet on a synthetic repo
You can verify the script behavior without touching a real project. Create a scratch repository, make a normal commit, then make a branch with one dangerous change.
mkdir autopsy-demo && cd autopsy-demo
git init -b main
printf 'hello\n' > a.txt
mkdir -p tests
printf 'assert true\n' > tests/smoke.txt
git add . && git commit -m "base"
git checkout -b change
: > a.txt # zero additions, one removal
git mv tests/smoke.txt tests/smoke-old.txt
printf '#!/bin/sh\n' > run.sh # pure addition
chmod +x run.sh # mode change
git add -A && git commit -m "model-generated change"
./diff_autopsy.sh main change
The output should show a.txt as a file that only removed lines, tests/smoke-old.txt as a low-similarity rename, run.sh as a pure addition, and a mode change. That synthetic repo is also a useful fixture to keep in CI when you change the script.
Where free model access fits
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
If you generate a candidate patch with MonkeyCode's free model access through its free server option, run the autopsy script against the resulting branch before opening a pull request. The script consumes no model quota and does not need the model endpoint to expose any special API; it only reads the diff already written to your repository. This makes it a cheap first gate when you are trying several free-model attempts against the same issue and want to reject obviously bad shapes without burning review cycles.
Limitations and when to skip it
The autopsy sheet is a triage step, not a review. It cannot detect a one-line logic error that leaves all shape metrics unchanged. It does not look for secrets, vulnerable dependencies, broken imports, or flaky tests. Path-based rules such as the test-deletion ratio assume your repository follows a naming convention, so adjust the patterns to your tree.
Skip or simplify this workflow if your patches are almost always tiny and your review process already surfaces the same signals. It is most useful when you are reviewing many model-generated branches in a row, or when a free model has produced a suspiciously large diff and you need a fast way to decide which files deserve human attention first.
Try the sheet on the next four model-generated diffs before reading the code. If the shape flags change your review order, keep it. If every diff comes back clean and uneventful, it has served its purpose and you can move on.
Top comments (1)
The file-mode and deletion checks are the ones I would put first too. Model patches often look fine in the changed function, while the real damage is a removed fixture, a rewritten lockfile, or churn that hides the two-line fix.