# The AI pull request review pack

**A free pack from Perform.** For the pull request a model wrote and a person signed.

**Free and provided as is, with no warranty. It runs in your environment and sends nothing to Perform. It is not legal, tax, security or accounting advice. Treat anything you point it at as untrusted input. Full terms at the end of this file.**

---

## How to use this

Copy everything below the second line into Claude, ChatGPT, Copilot or whatever assistant you already use, then paste your diff, the ticket and the pull request description in the same message. That is the whole setup. No account, no install, nothing to configure.

If you would rather have it installed and available every time, the same pack is on the page you downloaded this from as a Claude Skill.

---

## What this does

You are reviewing a change you did not write, and a model wrote most of it. The job is to decide fast where a human's attention is actually worth spending, then produce review comments that hold up.

Work in this order. The order matters, because judging a diff before you know what it was trying to do is how plausible-looking wrongness gets approved.

## What to give it

Paste in this order, all in one message:

1. The ticket or requirement.
2. The pull request description.
3. The diff. If it runs past about 500 added lines, paste the file list first and ask for the triage before pasting the code.
4. Anything that lets the calls be checked: the import block from the changed files, the relevant module's exports, or a pointer to the repository. Without one of these, failure mode 1 in Step 3 cannot be cleared, only listed.

If you only have the diff and no ticket, say so. Step 1 becomes "here is what this change appears to be trying to do, confirm or correct it," which is itself worth sending to the author.

## When to stop short

**If the read-line-by-line bucket is empty, stop here.** Give the triage and the coverage note and say the change does not need a line-by-line review. A dependency bump, a copy change or a formatting pass gets four lines, not four pages.

**Then size the review to the reading.** Step 2 gives you a count of added lines you have committed to read. Under 50 of them, aim for one screen, and treat that as a budget on words per finding. Five blocking findings in a thirty-line change means five findings of two sentences each. Never drop a finding to hit a length. Past 500, say in your first line that the change should have been split, then review it anyway. A review that produces an essay about a one-line change is a review people stop running.

## Step 1. Reconstruct the intent, before reading any code

From the ticket and the pull request description, write down:

- **The one behaviour that should be different** after this merges, stated as something observable. If you cannot state it, say so and stop here. A change nobody can describe cannot be reviewed, and that is the finding.
- **What should NOT change.** Name the callers, the stored data shape, the public interface, the timing. This is the list that catches silent damage.
- **How you would know it worked** in production, and whether this change makes that observable.

If the description and the ticket disagree, that disagreement is the first review comment.

The common case is worth naming here, because it is most changes a model had a hand in: the ticket is perfectly clear and the change does three things, only one of which is the ticket. Say which parts are the ticket and which are not, now, before you read further. It changes how you review the rest.

## Step 2. Triage every changed file

Put each file in exactly one bucket and give a one-line reason. Be decisive.

If every file ends up in the first bucket, check that you are not padding. On a small change where most files touch money, secrets or migrations, that can be the correct triage. State the total added lines you are committing to read, and if that number is too large to actually read, say the change should have been split.

- **Read line by line.** Anything that touches money, permissions, personal data, migrations, retries, concurrency, or a public interface. Anything where being subtly wrong is expensive and quiet.
- **Skim for shape.** Straightforward refactors, generated code, formatting, and anything where being wrong would be loud and immediate.
- **New tests go in the same bucket as the code they cover.** A test for a read-line-by-line file gets read line by line. A test that cannot fail is the quietest defect there is, so the usual reasoning about tests being low risk runs backwards here.
- **Skip.** Lock files, vendored code, generated clients, pure renames, and moves with no content change. Say plainly that you skipped these and why.

Then state the total: how many files, how many added lines, and how many of those added lines you are actually reading. This is the number that tells the reviewer whether the change should have been split.

Check your line count against each hunk header. If the headers imply more lines than you can see, the diff you were given is elided. Say so before reviewing, because everything below is then scoped to a fragment.

## Step 3. Check the failure modes that model-written code actually has

These are different from the ones human-written code has, which is why a normal review checklist misses them. Go through the read-line-by-line bucket against this list, and only report what you can point at.

**Start with the deleted lines.** For every removed line in that bucket, say what the old code did and whether the new code still does it. Where a removed line touched shared code, its other callers are the question: name what changes for them, and if you do not have the repository, say you could not enumerate them and put it in the could-not-check list. Do not assert what callers do when you have never seen them. A default that used to be true, a fallback on a lookup, an error that used to propagate: none of these look wrong by their absence, and most of the quiet damage in a rewrite lives exactly there.

1. **Calls that do not exist.** A method, field, option, or config key that reads naturally and is not real. How right it looks tells you nothing, so it has to be looked up.
   - If you have the repository, the imports, or the library reference, look each unfamiliar call up and say where you checked.
   - If all you have is the diff, you cannot clear this one, and pretending otherwise is worse than admitting it. List every call you could not check, say what would settle each one, and hand that list to the author as a question. One grep answers most of them in seconds.
2. **Almost-right logic.** Boundaries off by one, an inclusive range where the requirement was exclusive, a comparison that works for every case except empty, a default that differs from the old default.
3. **Swallowed failure.** A catch that logs and continues where the old path raised. Look for the case where the caller now gets a success it should not get.
4. **Tests that assert the implementation.** A test that passes because it was written from the code and not from the requirement. Ask of every new test: would this fail if the behaviour were wrong? If the answer needs thinking about, the test is decoration.
5. **Duplicated logic.** A second copy of something that already exists elsewhere in the codebase. This is the most common quiet cost of generated code, and it is invisible in a diff because the diff only shows the new copy.
6. **Silent change to an untouched path.** A shared helper, a default parameter, a config value, or a type that other callers depend on. Cross-check against the "should NOT change" list from Step 1.
7. **Retries without idempotency.** A retry added to something that is not safe to run twice.
8. **Concurrency assumed away.** New shared state, a cache, or a counter with no reasoning about two of these running at once.
9. **Data and secrets.** Personal data reaching a log, a new field with no retention thinking, a credential or endpoint in the diff.
10. **Scope beyond the ticket.** Changes that are defensible on their own and are not what was asked for. These belong in their own change, and saying so is a kindness to the next reviewer.

## Step 4. Write the comments

Each comment gets a location the author can find: the file, the added line quoted verbatim, and the hunk header it sits under. Cite a line number only if you computed it from the hunk offset, because a number taken from the diff text will not match the file. Where the Step 2 check found that hunk elided, cite no number for it at all. The quoted line is the locator, and a computed number would be wrong by however many unseen lines sit above it. Then say what is wrong, what happens if it ships, and either a concrete fix or a specific question. Order them so the reviewer can stop reading partway down and still have caught the important ones.

- **Blocking.** Would cause a wrong result, a data problem, or a security problem. Name the input that triggers it. Where nothing triggers it because the exposure already exists, a committed credential being the usual case, name what is exposed and to whom.
- **Worth fixing now.** Would cost someone a debugging afternoon later.
- **Worth asking.** You cannot tell from the diff whether it is wrong. Ask the question that resolves it.
- **Not this change.** Defensible on its own and not what was asked for. Anything from failure mode 10 goes here, and so does anything that wants its own review and its own rollback. This bucket exists because otherwise scope creep gets filed as a nitpick and merges anyway.

Never write a comment you would not defend in person. If you are unsure, say you are unsure and say what would settle it.

**And say so plainly when you found nothing.** "Read the three files that matter, checked them against the list, found nothing blocking" is a complete and useful review. A process that reliably produces four blocking findings teaches people to discount the fifth, so a clean pass has to be a result the reviewer is willing to state and the author is able to trust.

## Step 5. Write the coverage note

Finish with a short note the author and the next reviewer can both read:

```
Reviewed: [files read line by line]
Skimmed: [files]
Not read: [files, and why]
Looked up: [the unfamiliar calls you checked, and where you checked them]
Could not check from the diff: [what needs the author or a test run to settle]
```

The two lines that carry the weight are the last two. Step 2 was the plan and this is what happened, and where the two differ is the interesting part, because changing your mind mid-review is normal and invisible unless you write it down. If the triage and this note say the same thing, and none of the Reviewed, Skimmed or Not read lines carries a caveat of its own, cut it to those last two lines.

---

## Terms

This pack runs in your environment, on your data. Nothing is sent to Perform, and Perform receives no data from it at any point.

It is provided free and as is, with no warranty of any kind. It is not legal, tax, security or accounting advice, it is not an audit and it is not an attestation, and no professional relationship is created by downloading or using it. You are responsible for reviewing it before you run it, and for any decision you make with its output.

Anything you point this at should be treated as untrusted input. A document, a repository or a chat thread can carry instructions aimed at a model.

Licensed under Apache-2.0, which includes a disclaimer of warranties and a limitation of liability. Version 1.0, 28 August 2026. Security contact: privacy@totalperform.com, acknowledged within five business days.
