AI code review is a model reading a change and commenting on it. It runs as a bot on the pull request, or in a chat you paste the diff into (the lines the change adds and removes). What it checks depends on what you give it. Anthropic’s Code Review docs (2026) say Claude’s reviewer “focuses on correctness” by default. OpenAI’s Review GitHub pull requests with Codex (2026) says Codex flags only P0 and P1 issues on GitHub, its two highest priorities.

To review design, names and tests as well, give the reviewer a standard. The checklist below has one line for each of the 51 tenets in the Prickles canon, grouped by pillar. Each line links to the tenet’s page, with the full rule, its evidence and a short note to paste into the review. The same list works whether a person or an agent does the reviewing.

Where AI code review fits

A change passes several checks before it merges, and AI review is one of them. The review note on P7 Short-Lived Branches with AI+Human Review gives the order in two sentences: “AI does the first review pass. Humans approve.”

One change, on its way to main The agent writes it on a short-lived branch Self-review pass AI6: against tests, spec and brief Linter and CI TA1, TA2: errors stop the build AI review the checklist, one line per tenet Human review P7: a person approves the merge Merge to main findings go back
Four checks stand between an agent's draft and main, and each one sends its findings back to the author.

Before the pull request opens, AI6 Self-Review Pass has the agent critique and fix its own draft against its tests, spec and brief. AI6’s rule cites the reason for that grounding. In Large Language Models Cannot Self-Correct Reasoning Yet (ICLR 2024), Jie Huang and colleagues found that models “struggle to self-correct their responses without external feedback”.

Next come the linter and CI. A linter checks code against rules without running it, and CI runs the build on every push. TA1 Linter as Law makes every quality gate an error, and TA2 Duplication Detection runs a copy-paste detector on every commit. A change that breaks either fails before anyone reads it.

Then the AI reviewer reads what’s left. A person decides whether it merges. Copilot code review and Claude Code Review both leave that decision to a person by default. GitHub’s Using GitHub Copilot code review on GitHub (2026) says Copilot’s reviews “do not count toward required approvals” unless it’s set up to approve. Anthropic’s page says Claude’s findings “don’t approve or block your PR”.

What the linter checks before review

Some tenets set a number a linter can count. The Prickles packs make each one an error, so a change over the limit fails the build. A reviewer who checks those numbers again is reading what the build already reported. Anthropic’s Code Review page says as much. Among the things to tell its reviewer to skip, it lists “anything your CI already enforces like linting or spellcheck”.

Here is what the TypeScript pack’s ESLint config checks for eight of the tenets, and what it leaves for the reviewer. The Python, Java and PHP packs cap complexity and parameters at the same numbers with their own linters.

Tenet The TypeScript pack’s linter checks Left for the reviewer
S2 Cyclomatic Caps Complexity 3, nesting 3 deep and 10 statements a function Whether the change raises a cap or adds an eslint-disable comment
S3 Parameter Object 3 parameters a function Whether the object that replaces a fourth parameter names a real group
F1 Single Responsibility Principle 15 lines a function and 150 a file Whether each function has one job
F5 Self-Documenting Code Comments at the end of a line of code, and TODO and FIXME comments A comment on a line of its own that says what the code does
S5 Inline-First Any number but -1, 0 and 1 outside a named constant Whether the constant’s name says what the number means
T2 No Escape Hatches any written in the code, and @ts-ignore comments as casts, plus an any that comes from a library call such as JSON.parse
F3 Don’t Repeat Yourself and TA2 Repeated strings and identical functions, and copied blocks through jscpd in CI Two pieces of code that do the same thing in different words

TA2’s own review note hands the last row to a model: “An LLM review pass on the change set for Type-4.” A Type-4 duplicate does the same thing as other code but is written differently, so it doesn’t look like a copy.

Everything else in a review takes judgement. Google’s reviewer guide, What to look for in a code review (2019), works through design, functionality, complexity, tests, naming and comments. One line of its summary says what F4 Simplicity says:

The developer isn’t implementing things they might need in the future but don’t know they need now.

— Google, What to look for in a code review, 2019

The AI code review checklist

Each line below quotes a tenet’s rule as the canon states it in release 4.0.0, cut to the part a reviewer checks. Each link opens the tenet’s page, with its full rule, evidence and review note. In the lines below, AC stands for acceptance criteria, E2E for end-to-end, SDLC for the software development life cycle, QA for quality assurance and PR for pull request. jscpd is a copy-paste detector.

You won’t need every pillar for every change. The Process and AI Collaboration lines concern how a change was made, so check those against the pull request itself, such as its size, tests and history.

Foundations (F1 to F6)

Architecture (A1 to A11)

Style (S2 to S5)

  • S2 Cyclomatic Caps: Cap cyclomatic complexity per function. The number is editorial. Pick one and put it in the linter.
  • S3 Parameter Object: Cap function parameters at three. The fourth parameter is the missing struct.
  • S4 Guard Clauses: When a function’s preconditions are nested as conditionals, lift each one to a guarded early return at the top of the function.
  • S5 Inline-First: Inline first. Extract when a name is needed. A binding justifies itself when its name carries information the expression does not.

Types & Schema (T1 to T4)

  • T1 Domain-Driven Types: Brand the primitives. A user ID is not a string.
  • T2 No Escape Hatches: Forbid the four escape hatches by default: any, unknown without narrowing, as casts on anything other than as const, and @ts-ignore / @ts-expect-error.
  • T3 Type Guards: Where data crosses a runtime boundary (JSON.parse, localStorage, postMessage, route input, error catch, library boundary), validate the shape with a guard before the value flows further.
  • T4 Schema Sovereignty: Give each schema exactly one owning application, and let only that application write to it.

Process (P1 to P8)

  • P1 Test-First Development: Write the failing test before the implementation.
  • P2 Spec-First Execution: Read the brief twice. Write the AC as bullets a reviewer can verify as met or unmet, not “works correctly” but “rejects an empty cart with 400 and the message cart cannot be empty”.
  • P3 Definition of Done: Don’t call work done until every row of the checklist passes: coverage, lint, type-check, format, dup-check, AC verification and accessibility, each machine-checked and signed off.
  • P4 Continuous Quality Feedback: Lint while you write. Type-check on save. Run the test against the file you just edited.
  • P5 Shift-Left Quality: Move every check earlier in the SDLC. From QA into PR. From PR into CI. From CI into the editor.
  • P6 Leave it Better Than You Found it: Tidy the line you touched. The unit is small: rename one variable, split one function, delete one dead import.
  • P7 Short-Lived Branches with AI+Human Review: Cut a branch for a single piece of work and keep it under a day where you can.
  • P8 Architecture Decision Records: Record each architecturally significant decision as the next numbered file in docs/adr/, in the same pull request as the change.

Testing (TS1 to TS8)

AI Collaboration (AI1 to AI6)

  • AI1 The Intern Pattern: Treat the agent as an over-eager intern. Run the loop in four moves: plan, approve, execute, review.
  • AI2 Persistent Brief: Keep the agent’s brief in the repo, not in the prompt.
  • AI3 Working Context: Treat the agent’s context window as a precious, finite resource: Anthropic’s own phrase.
  • AI4 Verifiable Specs: Write specs as executable contracts the agent can run and grade itself against, not passive documentation it has to interpret.
  • AI5 Verify the API: Check the current docs or source before you write any call to a framework, library or SDK.
  • AI6 Self-Review Pass: Draft, then critique what you drafted, then fix it, before you hand anything back.

Tooling (TA1 and TA2)

Patterns (PT1 and PT3)

  • PT1 Patterns Are Vocabulary: If a class implements a known pattern, name the pattern in the identifier.
  • PT3 Every Pattern Has a Failure Mode: Before introducing a pattern, read its Consequences (or Cons) section in GoF / Refactoring Guru / the canonical reference. Name the cost out loud in the same paragraph as the choice.

How to paste a tenet’s review note

  1. Open the tenet’s page from its line in the checklist.
  2. Find the box headed Copy a note and link, below the tenet’s opinion.
  3. Press Copy for paste. It copies the note, a blank line and the page’s full address.
  4. Paste it as a comment on the line it applies to, or into the AI chat with the diff.

Pick a tenet on the home page and its preview panel shows the same box. Its description on the site reads: “Grab this short comment and drop it into a PR comment or an LLM chat to prompt the right change.” Each note states the rule in plain words. Its address takes a reader to the reasoning and the evidence.

Every tenet has a note except T1 Domain-Driven Types. For T1, write the comment yourself and link the page.

A worked example of AI code review

An agent opens a pull request that adds a discount code to a shop’s cart. It adds src/orders/discount-handler.ts:

import { loadCart, saveCart } from "./cart-store";

const WELCOME_CODE = "WELCOME10";
const WELCOME_RATE = 0.1;
const BAD_REQUEST = 400;

export const discountHandler = async (request: Request): Promise<Response> => {
  const body = await request.json();
  const cart = await loadCart(body.cartId);
  if (body.code !== WELCOME_CODE) {
    return Response.json({ error: "Unknown code" }, { status: BAD_REQUEST });
  }
  const total = cart.total * (1 - WELCOME_RATE);
  await saveCart({ ...cart, total, codes: [...cart.codes, body.code] });
  return Response.json({ total });
};

It also adds this passing test:

it("applies WELCOME10 and stores the code on the cart", async () => {
  const response = await discountHandler(postJson({ cartId: "cart-1", code: "WELCOME10" }));
  expect(await response.json()).toEqual({ total: 90 });
  expect(storedCart("cart-1")?.codes).toEqual(["WELCOME10"]);
});

Both files pass the TypeScript pack’s lint rules and type-check cleanly. Reviewed against three lines of the checklist, each line finds something.

A5 Thin Handlers

Here the handler checks the code, works out the discount and saves the cart, so the only way to test the discount rule is through an HTTP request. A5’s note, pasted as a comment on the handler, says so:

Entry points dispatch, they don't decide. The handler at the framework boundary parses input, calls a single domain function, and shapes the response. Business rules, persistence, branching all belong in functions one level in. When the transport changes, the domain doesn't move: only the handler does.

https://prickles.org/tenet/thin-handlers/A5

T3 Type Guards

In TypeScript’s types, request.json() resolves to any, so nothing checks body.cartId or body.code. The linter doesn’t object either. Sent a body with no cart ID, the handler still answers with a 200 and a discounted total. T3’s note goes on the line that reads the body:

Prove the type, don't assert it. Anywhere data crosses a runtime boundary (JSON.parse, localStorage, postMessage, route input, error catch, library boundary), validate the shape with a type predicate, a discriminated-union narrowing, or a schema (Zod / Valibot / io-ts). `as` is a lie you tell the compiler.

https://prickles.org/tenet/type-guards/T3

TS8 One Concept Per Test

This test’s name has an “and” in it. It checks two outcomes, the total and the stored code, so TS8’s note goes on the test:

One concept per test. If the test name needs an 'and', split it. Multiple Acts hide multiple causes behind a single failure message. One Act, one outcome, one test. Use it.each for parametrised assertions when the same concept needs many inputs: that's one concept, many examples, not many concepts.

https://prickles.org/tenet/one-concept-per-test/TS8

What else the checklist finds

Other lines find more. T1 asks you to brand the cart ID as its own type, and here it’s a plain string. T1 has no note, so that comment comes from the reviewer.

After the fixes

Now redeemCode holds the code check and the arithmetic, isRedeemRequest guards the body and the test splits in two. The handler parses the input, dispatches one call and shapes the response:

import { redeemCode } from "./redeem-code";
import { isRedeemRequest } from "./redeem-request";

const BAD_REQUEST = 400;

const badRequest = (error: string): Response => Response.json({ error }, { status: BAD_REQUEST });

export const discountHandler = async (request: Request): Promise<Response> => {
  const body: unknown = await request.json();
  if (!isRedeemRequest(body)) {
    return badRequest("Send a cart ID and a code");
  }
  const result = await redeemCode(body);
  return result.accepted ? Response.json({ total: result.total }) : badRequest(result.reason);
};

Sent the same body with no cart ID, it now answers with a 400.

An AI code review prompt

For a review in a chat, paste the diff with whichever checklist lines apply, and ask for each finding in the same shape:

Review the diff below against these rules. For each finding, give the rule's ID, the file and line, and one sentence on what breaks the rule. Don't report anything the linter or the type checker already catches. If no rule is broken, say so.

Rules:
- A5 Thin Handlers. Keep every handler thin. At each boundary (HTTP, queue, CLI, websocket, Server Action) the handler parses input, dispatches one call and returns a response. Nothing else.
- T3 Type Guards. Where data crosses a runtime boundary (`JSON.parse`, `localStorage`, `postMessage`, route input, error catch, library boundary), validate the shape with a guard before the value flows further.
- TS8 One Concept Per Test. One concept per test. The test name is the spec for the test. If the name needs an `and`, split the test.

Diff:
<paste the diff here>

Asking for the rule’s ID ties each finding to a line you can look up. The packs’ review file opens with the same request: “Report a change that breaks any tenet below as a review finding, and name the tenet’s ID.” The prompt’s last instruction gives the model a way to report nothing.

Paste only the pillars the change touches. All 51 full rule lines come to just under 10,000 characters, the size of the packs’ review file. AI3 Working Context asks you to keep what the model reads short.

Copilot code review and the other review bots

A review bot on the pull request reads rule files from the repository, and each reads a different set. The table follows each vendor’s docs as they read on 12 October 2026.

Review bot What it reads The Prickles 3.0.0 file it gets
Copilot code review .github/copilot-instructions.md, path-specific .github/instructions files, AGENTS.md, CLAUDE.md, GEMINI.md, REVIEW.md and skills in .github/skills The Copilot pack’s .github/copilot-instructions.md, holding every rule line
Claude Code Review CLAUDE.md at every level, and REVIEW.md at the repository root The Claude Code pack’s optional REVIEW.md
Cursor Bugbot .cursor/BUGBOT.md at the root and in folders above a changed file, plus Team Rules and learned rules, but not .cursor/rules The Cursor pack’s optional .cursor/BUGBOT.md
Codex code review AGENTS.md, applying the rules under a ## Code Review Rules heading AGENTS.md in the Codex and universal packs, ending with that heading

Release 3.0.0 adds REVIEW.md and .cursor/BUGBOT.md because neither reviewer reads the packs’ rule files. Each holds every tenet’s rule line under its pillar heading, in under 10,000 characters with no frontmatter. Keep the file if you use that reviewer and delete it if you don’t.

Each bot has its own limits:

  • Claude Code Review is a research preview on Team and Enterprise plans. It flags a new breach of CLAUDE.md as a nit, and passes REVIEW.md to the agents that find and check its findings. The local /code-review command doesn’t read REVIEW.md.
  • Bugbot cuts each rule at 30,000 characters and caps all the rules in one review at 100,000. Comment bugbot run verbose=true on a pull request to see which rules it included.
  • Copilot code review reads its files from the pull request’s head branch, so a pull request can change the rules that review it. GitHub advises keeping each instruction file to about 1,000 lines. GitHub Copilot instructions covers its files in more detail.
  • Codex flags only P0 and P1 issues, so a naming or test finding may never reach the pull request.

Prickles hasn’t yet checked a Claude Code Review or Bugbot run against these files. The pack guides on the implementation page follow each vendor’s docs as they read on 12 October 2026. For a review bot that isn’t in the table, check its docs for the file it reads and put the rule lines there.