PR review expert

Use when the user asks to review pull requests, analyze code changes, check for security issues in PRs, or assess code quality of diffs.

How to use it

Claude Code
  1. Run the line below. It pulls the whole folder into ~/.claude/skills/pr-review-expert.
  2. Describe your job in plain words. Claude Code follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit alirezarezvani/claude-skills/engineering/skills/pr-review-expert#main ~/.claude/skills/pr-review-expert

For one project only, change the path to .claude/skills/pr-review-expert.

Claude (web or desktop app)
  1. On this page open ⋯ → Download .md.
  2. Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
  3. Pick the file and Save. Claude shows the name and description and runs a security scan.
  4. Check the skill is switched on.
  5. Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
  1. ChatGPT: make a Project and paste it into Instructions.
  2. Neither? Paste it at the top of a new chat — it works for that chat.
Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Source of PR review expert

Show the full text398 lines
namedescription
pr-review-expertUse when the user asks to review pull requests, analyze code changes, check for security issues in PRs, or assess code quality of diffs.

PR Review Expert

Tier: POWERFUL Category: Engineering Domain: Code Review / Quality Assurance


Overview

Structured, systematic code review for GitHub PRs and GitLab MRs. Goes beyond style nits — this skill performs blast radius analysis, security scanning, breaking change detection, and test coverage delta calculation. Produces a reviewer-ready report with a 30+ item checklist and prioritized findings.


Core Capabilities

  • Blast radius analysis — trace which files, services, and downstream consumers could break
  • Security scan — SQL injection, XSS, auth bypass, secret exposure, dependency vulns
  • Test coverage delta — new code vs new tests ratio
  • Breaking change detection — API contracts, DB schema migrations, config keys
  • Ticket linking — verify Jira/Linear ticket exists and matches scope
  • Performance impact — N+1 queries, bundle size regression, memory allocations

When to Use

  • Before merging any PR/MR that touches shared libraries, APIs, or DB schema
  • When a PR is large (>200 lines changed) and needs structured review
  • Onboarding new contributors whose PRs need thorough feedback
  • Security-sensitive code paths (auth, payments, PII handling)
  • After an incident — review similar PRs proactively

Fetching the Diff

GitHub (gh CLI)
# View diff in terminal
gh pr diff <PR_NUMBER>

# Get PR metadata (title, body, labels, linked issues)
gh pr view <PR_NUMBER> --json title,body,labels,assignees,milestone

# List files changed
gh pr diff <PR_NUMBER> --name-only

# Check CI status
gh pr checks <PR_NUMBER>

# Download diff to file for analysis
gh pr diff <PR_NUMBER> > /tmp/pr-<PR_NUMBER>.diff
GitLab (glab CLI)
# View MR diff
glab mr diff <MR_IID>

# MR details as JSON
glab mr view <MR_IID> --output json

# List changed files
glab mr diff <MR_IID> --name-only

# Download diff
glab mr diff <MR_IID> > /tmp/mr-<MR_IID>.diff

Workflow

Step 1 — Fetch Context
PR=123
gh pr view $PR --json title,body,labels,milestone,assignees | jq .
gh pr diff $PR --name-only
gh pr diff $PR > /tmp/pr-$PR.diff
Step 2 — Blast Radius Analysis

For each changed file, identify:

  1. Direct dependents — who imports this file?
# Find all files importing a changed module
grep -r "from ['\"].*changed-module['\"]" src/ --include="*.ts" -l
grep -r "require(['\"].*changed-module" src/ --include="*.js" -l

# Python
grep -r "from changed_module import\|import changed_module" . --include="*.py" -l
  1. Service boundaries — does this change cross a service?
# Check if changed files span multiple services (monorepo)
gh pr diff $PR --name-only | cut -d/ -f1-2 | sort -u
  1. Shared contracts — types, interfaces, schemas
gh pr diff $PR --name-only | grep -E "types/|interfaces/|schemas/|models/"

Blast radius severity:

  • CRITICAL — shared library, DB model, auth middleware, API contract
  • HIGH — service used by >3 others, shared config, env vars
  • MEDIUM — single service internal change, utility function
  • LOW — UI component, test file, docs
Step 3 — Security Scan
DIFF=/tmp/pr-$PR.diff

# SQL Injection — raw query string interpolation
grep -n "query\|execute\|raw(" $DIFF | grep -E '\$\{|f"|%s|format\('

# Hardcoded secrets
grep -nE "(password|secret|api_key|token|private_key)\s*=\s*['\"][^'\"]{8,}" $DIFF

# AWS key pattern
grep -nE "AKIA[0-9A-Z]{16}" $DIFF

# JWT secret in code
grep -nE "jwt\.sign\(.*['\"][^'\"]{20,}['\"]" $DIFF

# XSS vectors
grep -n "dangerouslySetInnerHTML\|innerHTML\s*=" $DIFF

# Auth bypass patterns
grep -n "bypass\|skip.*auth\|noauth\|TODO.*auth" $DIFF

# Insecure hash algorithms
grep -nE "md5\(|sha1\(|createHash\(['\"]md5|createHash\(['\"]sha1" $DIFF

# eval / exec
grep -nE "\beval\(|\bexec\(|\bsubprocess\.call\(" $DIFF

# Prototype pollution
grep -n "__proto__\|constructor\[" $DIFF

# Path traversal risk
grep -nE "path\.join\(.*req\.|readFile\(.*req\." $DIFF
Step 4 — Test Coverage Delta
# Count source vs test files changed
CHANGED_SRC=$(gh pr diff $PR --name-only | grep -vE "\.test\.|\.spec\.|__tests__")
CHANGED_TESTS=$(gh pr diff $PR --name-only | grep -E "\.test\.|\.spec\.|__tests__")

echo "Source files changed: $(echo "$CHANGED_SRC" | wc -w)"
echo "Test files changed:   $(echo "$CHANGED_TESTS" | wc -w)"

# Lines of new logic vs new test lines
LOGIC_LINES=$(grep "^+" /tmp/pr-$PR.diff | grep -v "^+++" | wc -l)
echo "New lines added: $LOGIC_LINES"

# Run coverage locally
npm test -- --coverage --changedSince=main 2>/dev/null | tail -20
pytest --cov --cov-report=term-missing 2>/dev/null | tail -20

Coverage delta rules:

  • New function without tests → flag
  • Deleted tests without deleted code → flag
  • Coverage drop >5% → block merge
  • Auth/payments paths → require 100% coverage
Step 5 — Breaking Change Detection
API Contract Changes
# OpenAPI/Swagger spec changes
grep -n "openapi\|swagger" /tmp/pr-$PR.diff | head -20

# REST route removals or renames
grep "^-" /tmp/pr-$PR.diff | grep -E "router\.(get|post|put|delete|patch)\("

# GraphQL schema removals
grep "^-" /tmp/pr-$PR.diff | grep -E "^-\s*(type |field |Query |Mutation )"

# TypeScript interface removals
grep "^-" /tmp/pr-$PR.diff | grep -E "^-\s*(export\s+)?(interface|type) "
DB Schema Changes
# Migration files added
gh pr diff $PR --name-only | grep -E "migrations?/|alembic/|knex/"

# Destructive operations
grep -E "DROP TABLE|DROP COLUMN|ALTER.*NOT NULL|TRUNCATE" /tmp/pr-$PR.diff

# Index removals (perf regression risk)
grep "DROP INDEX\|remove_index" /tmp/pr-$PR.diff
Config / Env Var Changes
# New env vars referenced in code (might be missing in prod)
grep "^+" /tmp/pr-$PR.diff | grep -oE "process\.env\.[A-Z_]+" | sort -u

# Removed env vars (could break running instances)
grep "^-" /tmp/pr-$PR.diff | grep -oE "process\.env\.[A-Z_]+" | sort -u
Step 6 — Performance Impact
# N+1 query patterns (DB calls inside loops)
grep -n "\.find\|\.findOne\|\.query\|db\." /tmp/pr-$PR.diff | grep "^+" | head -20
# Then check surrounding context for forEach/map/for loops

# Heavy new dependencies
grep "^+" /tmp/pr-$PR.diff | grep -E '"[a-z@].*":\s*"[0-9^~]' | head -20

# Unbounded loops
grep -n "while (true\|while(true" /tmp/pr-$PR.diff | grep "^+"

# Missing await (accidentally sequential promises)
grep -n "await.*await" /tmp/pr-$PR.diff | grep "^+" | head -10

# Large in-memory allocations
grep -n "new Array([0-9]\{4,\}\|Buffer\.alloc" /tmp/pr-$PR.diff | grep "^+"

Ticket Linking Verification

# Extract ticket references from PR body
gh pr view $PR --json body | jq -r '.body' | \
  grep -oE "(PROJ-[0-9]+|[A-Z]+-[0-9]+|https://linear\.app/[^)\"]+)" | sort -u

# Verify Jira ticket exists (requires JIRA_API_TOKEN to be SET in the environment).
# Credentials are fed to curl via a config read from stdin (-K -) so the token
# never appears in argv — `ps aux` / /proc/*/cmdline can't see it, and nothing
# secret lands in shell history. Never paste the raw token on the command line.
TICKET="PROJ-123"
: "${JIRA_API_TOKEN:?JIRA_API_TOKEN must be set}"
curl -s -K - "https://your-org.atlassian.net/rest/api/3/issue/$TICKET" <<EOF | \
  jq '{key, summary: .fields.summary, status: .fields.status.name}'
user = "[email protected]:$JIRA_API_TOKEN"
EOF

# Linear ticket — same pattern: the Authorization header goes through the
# stdin config, not a -H flag, to keep the key out of the process list.
LINEAR_ID="abc-123"
: "${LINEAR_API_KEY:?LINEAR_API_KEY must be set}"
curl -s -K - -H "Content-Type: application/json" \
  --data "{\"query\": \"{ issue(id: \\\"$LINEAR_ID\\\") { title state { name } } }\"}" \
  https://api.linear.app/graphql <<EOF | jq .
header = "Authorization: $LINEAR_API_KEY"
EOF

Security note: for repeated Jira use, prefer a ~/.netrc entry (machine your-org.atlassian.net login [email protected] password <token>, chmod 600 ~/.netrc) and call curl -s --netrc … — no secret material in the command at all.


Complete Review Checklist (30+ Items)

## Code Review Checklist

### Scope & Context
- [ ] PR title accurately describes the change
- [ ] PR description explains WHY, not just WHAT
- [ ] Linked Jira/Linear ticket exists and matches scope
- [ ] No unrelated changes (scope creep)
- [ ] Breaking changes documented in PR body

### Blast Radius
- [ ] Identified all files importing changed modules
- [ ] Cross-service dependencies checked
- [ ] Shared types/interfaces/schemas reviewed for breakage
- [ ] New env vars documented in .env.example
- [ ] DB migrations are reversible (have down() / rollback)

### Security
- [ ] No hardcoded secrets or API keys
- [ ] SQL queries use parameterized inputs (no string interpolation)
- [ ] User inputs validated/sanitized before use
- [ ] Auth/authorization checks on all new endpoints
- [ ] No XSS vectors (innerHTML, dangerouslySetInnerHTML)
- [ ] New dependencies checked for known CVEs
- [ ] No sensitive data in logs (PII, tokens, passwords)
- [ ] File uploads validated (type, size, content-type)
- [ ] CORS configured correctly for new endpoints

### Testing
- [ ] New public functions have unit tests
- [ ] Edge cases covered (empty, null, max values)
- [ ] Error paths tested (not just happy path)
- [ ] Integration tests for API endpoint changes
- [ ] No tests deleted without clear reason
- [ ] Test names clearly describe what they verify

### Breaking Changes
- [ ] No API endpoints removed without deprecation notice
- [ ] No required fields added to existing API responses
- [ ] No DB columns removed without two-phase migration plan
- [ ] No env vars removed that may be set in production
- [ ] Backward-compatible for external API consumers

### Performance
- [ ] No N+1 query patterns introduced
- [ ] DB indexes added for new query patterns
- [ ] No unbounded loops on potentially large datasets
- [ ] No heavy new dependencies without justification
- [ ] Async operations correctly awaited
- [ ] Caching considered for expensive repeated operations

### Code Quality
- [ ] No dead code or unused imports
- [ ] Error handling present (no bare empty catch blocks)
- [ ] Consistent with existing patterns and conventions
- [ ] Complex logic has explanatory comments
- [ ] No unresolved TODOs (or tracked in ticket)

Output Format

Structure your review comment as:

## PR Review: [PR Title] (#NUMBER)

Blast Radius: HIGH — changes lib/auth used by 5 services
Security: 1 finding (medium severity)
Tests: Coverage delta +2%
Breaking Changes: None detected

--- MUST FIX (Blocking) ---

1. SQL Injection risk in src/db/users.ts:42
   Raw string interpolation in WHERE clause.
   Fix: db.query("SELECT * WHERE id = $1", [userId])

--- SHOULD FIX (Non-blocking) ---

2. Missing auth check on POST /api/admin/reset
   No role verification before destructive operation.

--- SUGGESTIONS ---

3. N+1 pattern in src/services/reports.ts:88
   findUser() called inside results.map() — batch with findManyUsers(ids)

--- LOOKS GOOD ---
- Test coverage for new auth flow is thorough
- DB migration has proper down() rollback method
- Error handling consistent with rest of codebase

Common Pitfalls

  • Reviewing style over substance — let the linter handle style; focus on logic, security, correctness
  • Missing blast radius — a 5-line change in a shared utility can break 20 services
  • Approving untested happy paths — always verify error paths have coverage
  • Ignoring migration risk — NOT NULL additions need a default or two-phase migration
  • Indirect secret exposure — secrets in error messages/logs, not just hardcoded values
  • Skipping large PRs — if a PR is too large to review properly, request it be split

Best Practices

  1. Read the linked ticket before looking at code — context prevents false positives
  2. Check CI status before reviewing — don't review code that fails to build
  3. Prioritize blast radius and security over style
  4. Reproduce locally for non-trivial auth or performance changes
  5. Label each comment clearly: "nit:", "must:", "question:", "suggestion:"
  6. Batch all comments in one review round — don't trickle feedback
  7. Acknowledge good patterns, not just problems — specific praise improves culture
1---
2name: "pr-review-expert"
3description: "Use when the user asks to review pull requests, analyze code changes, check for security issues in PRs, or assess code quality of diffs."
4---
5 
6# PR Review Expert
7 
8**Tier:** POWERFUL
9**Category:** Engineering
10**Domain:** Code Review / Quality Assurance
11 
12---
13 
14## Overview
15 
16Structured, systematic code review for GitHub PRs and GitLab MRs. Goes beyond style nits — this skill
17performs blast radius analysis, security scanning, breaking change detection, and test coverage delta
18calculation. Produces a reviewer-ready report with a 30+ item checklist and prioritized findings.
19 
20---
21 
22## Core Capabilities
23 
24- **Blast radius analysis** — trace which files, services, and downstream consumers could break
25- **Security scan** — SQL injection, XSS, auth bypass, secret exposure, dependency vulns
26- **Test coverage delta** — new code vs new tests ratio
27- **Breaking change detection** — API contracts, DB schema migrations, config keys
28- **Ticket linking** — verify Jira/Linear ticket exists and matches scope
29- **Performance impact** — N+1 queries, bundle size regression, memory allocations
30 
31---
32 
33## When to Use
34 
35- Before merging any PR/MR that touches shared libraries, APIs, or DB schema
36- When a PR is large (>200 lines changed) and needs structured review
37- Onboarding new contributors whose PRs need thorough feedback
38- Security-sensitive code paths (auth, payments, PII handling)
39- After an incident — review similar PRs proactively
40 
41---
42 
43## Fetching the Diff
44 
45### GitHub (gh CLI)
46```bash
47# View diff in terminal
48gh pr diff <PR_NUMBER>
49 
50# Get PR metadata (title, body, labels, linked issues)
51gh pr view <PR_NUMBER> --json title,body,labels,assignees,milestone
52 
53# List files changed
54gh pr diff <PR_NUMBER> --name-only
55 
56# Check CI status
57gh pr checks <PR_NUMBER>
58 
59# Download diff to file for analysis
60gh pr diff <PR_NUMBER> > /tmp/pr-<PR_NUMBER>.diff
61```
62 
63### GitLab (glab CLI)
64```bash
65# View MR diff
66glab mr diff <MR_IID>
67 
68# MR details as JSON
69glab mr view <MR_IID> --output json
70 
71# List changed files
72glab mr diff <MR_IID> --name-only
73 
74# Download diff
75glab mr diff <MR_IID> > /tmp/mr-<MR_IID>.diff
76```
77 
78---
79 
80## Workflow
81 
82### Step 1 — Fetch Context
83 
84```bash
85PR=123
86gh pr view $PR --json title,body,labels,milestone,assignees | jq .
87gh pr diff $PR --name-only
88gh pr diff $PR > /tmp/pr-$PR.diff
89```
90 
91### Step 2 — Blast Radius Analysis
92 
93For each changed file, identify:
94 
951. **Direct dependents** — who imports this file?
96```bash
97# Find all files importing a changed module
98grep -r "from ['\"].*changed-module['\"]" src/ --include="*.ts" -l
99grep -r "require(['\"].*changed-module" src/ --include="*.js" -l
100 
101# Python
102grep -r "from changed_module import\|import changed_module" . --include="*.py" -l
103```
104 
1052. **Service boundaries** — does this change cross a service?
106```bash
107# Check if changed files span multiple services (monorepo)
108gh pr diff $PR --name-only | cut -d/ -f1-2 | sort -u
109```
110 
1113. **Shared contracts** — types, interfaces, schemas
112```bash
113gh pr diff $PR --name-only | grep -E "types/|interfaces/|schemas/|models/"
114```
115 
116**Blast radius severity:**
117- CRITICAL — shared library, DB model, auth middleware, API contract
118- HIGH — service used by >3 others, shared config, env vars
119- MEDIUM — single service internal change, utility function
120- LOW — UI component, test file, docs
121 
122### Step 3 — Security Scan
123 
124```bash
125DIFF=/tmp/pr-$PR.diff
126 
127# SQL Injection — raw query string interpolation
128grep -n "query\|execute\|raw(" $DIFF | grep -E '\$\{|f"|%s|format\('
129 
130# Hardcoded secrets
131grep -nE "(password|secret|api_key|token|private_key)\s*=\s*['\"][^'\"]{8,}" $DIFF
132 
133# AWS key pattern
134grep -nE "AKIA[0-9A-Z]{16}" $DIFF
135 
136# JWT secret in code
137grep -nE "jwt\.sign\(.*['\"][^'\"]{20,}['\"]" $DIFF
138 
139# XSS vectors
140grep -n "dangerouslySetInnerHTML\|innerHTML\s*=" $DIFF
141 
142# Auth bypass patterns
143grep -n "bypass\|skip.*auth\|noauth\|TODO.*auth" $DIFF
144 
145# Insecure hash algorithms
146grep -nE "md5\(|sha1\(|createHash\(['\"]md5|createHash\(['\"]sha1" $DIFF
147 
148# eval / exec
149grep -nE "\beval\(|\bexec\(|\bsubprocess\.call\(" $DIFF
150 
151# Prototype pollution
152grep -n "__proto__\|constructor\[" $DIFF
153 
154# Path traversal risk
155grep -nE "path\.join\(.*req\.|readFile\(.*req\." $DIFF
156```
157 
158### Step 4 — Test Coverage Delta
159 
160```bash
161# Count source vs test files changed
162CHANGED_SRC=$(gh pr diff $PR --name-only | grep -vE "\.test\.|\.spec\.|__tests__")
163CHANGED_TESTS=$(gh pr diff $PR --name-only | grep -E "\.test\.|\.spec\.|__tests__")
164 
165echo "Source files changed: $(echo "$CHANGED_SRC" | wc -w)"
166echo "Test files changed: $(echo "$CHANGED_TESTS" | wc -w)"
167 
168# Lines of new logic vs new test lines
169LOGIC_LINES=$(grep "^+" /tmp/pr-$PR.diff | grep -v "^+++" | wc -l)
170echo "New lines added: $LOGIC_LINES"
171 
172# Run coverage locally
173npm test -- --coverage --changedSince=main 2>/dev/null | tail -20
174pytest --cov --cov-report=term-missing 2>/dev/null | tail -20
175```
176 
177**Coverage delta rules:**
178- New function without tests → flag
179- Deleted tests without deleted code → flag
180- Coverage drop >5% → block merge
181- Auth/payments paths → require 100% coverage
182 
183### Step 5 — Breaking Change Detection
184 
185#### API Contract Changes
186```bash
187# OpenAPI/Swagger spec changes
188grep -n "openapi\|swagger" /tmp/pr-$PR.diff | head -20
189 
190# REST route removals or renames
191grep "^-" /tmp/pr-$PR.diff | grep -E "router\.(get|post|put|delete|patch)\("
192 
193# GraphQL schema removals
194grep "^-" /tmp/pr-$PR.diff | grep -E "^-\s*(type |field |Query |Mutation )"
195 
196# TypeScript interface removals
197grep "^-" /tmp/pr-$PR.diff | grep -E "^-\s*(export\s+)?(interface|type) "
198```
199 
200#### DB Schema Changes
201```bash
202# Migration files added
203gh pr diff $PR --name-only | grep -E "migrations?/|alembic/|knex/"
204 
205# Destructive operations
206grep -E "DROP TABLE|DROP COLUMN|ALTER.*NOT NULL|TRUNCATE" /tmp/pr-$PR.diff
207 
208# Index removals (perf regression risk)
209grep "DROP INDEX\|remove_index" /tmp/pr-$PR.diff
210```
211 
212#### Config / Env Var Changes
213```bash
214# New env vars referenced in code (might be missing in prod)
215grep "^+" /tmp/pr-$PR.diff | grep -oE "process\.env\.[A-Z_]+" | sort -u
216 
217# Removed env vars (could break running instances)
218grep "^-" /tmp/pr-$PR.diff | grep -oE "process\.env\.[A-Z_]+" | sort -u
219```
220 
221### Step 6 — Performance Impact
222 
223```bash
224# N+1 query patterns (DB calls inside loops)
225grep -n "\.find\|\.findOne\|\.query\|db\." /tmp/pr-$PR.diff | grep "^+" | head -20
226# Then check surrounding context for forEach/map/for loops
227 
228# Heavy new dependencies
229grep "^+" /tmp/pr-$PR.diff | grep -E '"[a-z@].*":\s*"[0-9^~]' | head -20
230 
231# Unbounded loops
232grep -n "while (true\|while(true" /tmp/pr-$PR.diff | grep "^+"
233 
234# Missing await (accidentally sequential promises)
235grep -n "await.*await" /tmp/pr-$PR.diff | grep "^+" | head -10
236 
237# Large in-memory allocations
238grep -n "new Array([0-9]\{4,\}\|Buffer\.alloc" /tmp/pr-$PR.diff | grep "^+"
239```
240 
241---
242 
243## Ticket Linking Verification
244 
245```bash
246# Extract ticket references from PR body
247gh pr view $PR --json body | jq -r '.body' | \
248 grep -oE "(PROJ-[0-9]+|[A-Z]+-[0-9]+|https://linear\.app/[^)\"]+)" | sort -u
249 
250# Verify Jira ticket exists (requires JIRA_API_TOKEN to be SET in the environment).
251# Credentials are fed to curl via a config read from stdin (-K -) so the token
252# never appears in argv — `ps aux` / /proc/*/cmdline can't see it, and nothing
253# secret lands in shell history. Never paste the raw token on the command line.
254TICKET="PROJ-123"
255: "${JIRA_API_TOKEN:?JIRA_API_TOKEN must be set}"
256curl -s -K - "https://your-org.atlassian.net/rest/api/3/issue/$TICKET" <<EOF | \
257 jq '{key, summary: .fields.summary, status: .fields.status.name}'
258user = "[email protected]:$JIRA_API_TOKEN"
259EOF
260 
261# Linear ticket — same pattern: the Authorization header goes through the
262# stdin config, not a -H flag, to keep the key out of the process list.
263LINEAR_ID="abc-123"
264: "${LINEAR_API_KEY:?LINEAR_API_KEY must be set}"
265curl -s -K - -H "Content-Type: application/json" \
266 --data "{\"query\": \"{ issue(id: \\\"$LINEAR_ID\\\") { title state { name } } }\"}" \
267 https://api.linear.app/graphql <<EOF | jq .
268header = "Authorization: $LINEAR_API_KEY"
269EOF
270```
271 
272> **Security note:** for repeated Jira use, prefer a `~/.netrc` entry
273> (`machine your-org.atlassian.net login [email protected] password <token>`,
274> `chmod 600 ~/.netrc`) and call `curl -s --netrc …` — no secret material in
275> the command at all.
276 
277---
278 
279## Complete Review Checklist (30+ Items)
280 
281```markdown
282## Code Review Checklist
283 
284### Scope & Context
285- [ ] PR title accurately describes the change
286- [ ] PR description explains WHY, not just WHAT
287- [ ] Linked Jira/Linear ticket exists and matches scope
288- [ ] No unrelated changes (scope creep)
289- [ ] Breaking changes documented in PR body
290 
291### Blast Radius
292- [ ] Identified all files importing changed modules
293- [ ] Cross-service dependencies checked
294- [ ] Shared types/interfaces/schemas reviewed for breakage
295- [ ] New env vars documented in .env.example
296- [ ] DB migrations are reversible (have down() / rollback)
297 
298### Security
299- [ ] No hardcoded secrets or API keys
300- [ ] SQL queries use parameterized inputs (no string interpolation)
301- [ ] User inputs validated/sanitized before use
302- [ ] Auth/authorization checks on all new endpoints
303- [ ] No XSS vectors (innerHTML, dangerouslySetInnerHTML)
304- [ ] New dependencies checked for known CVEs
305- [ ] No sensitive data in logs (PII, tokens, passwords)
306- [ ] File uploads validated (type, size, content-type)
307- [ ] CORS configured correctly for new endpoints
308 
309### Testing
310- [ ] New public functions have unit tests
311- [ ] Edge cases covered (empty, null, max values)
312- [ ] Error paths tested (not just happy path)
313- [ ] Integration tests for API endpoint changes
314- [ ] No tests deleted without clear reason
315- [ ] Test names clearly describe what they verify
316 
317### Breaking Changes
318- [ ] No API endpoints removed without deprecation notice
319- [ ] No required fields added to existing API responses
320- [ ] No DB columns removed without two-phase migration plan
321- [ ] No env vars removed that may be set in production
322- [ ] Backward-compatible for external API consumers
323 
324### Performance
325- [ ] No N+1 query patterns introduced
326- [ ] DB indexes added for new query patterns
327- [ ] No unbounded loops on potentially large datasets
328- [ ] No heavy new dependencies without justification
329- [ ] Async operations correctly awaited
330- [ ] Caching considered for expensive repeated operations
331 
332### Code Quality
333- [ ] No dead code or unused imports
334- [ ] Error handling present (no bare empty catch blocks)
335- [ ] Consistent with existing patterns and conventions
336- [ ] Complex logic has explanatory comments
337- [ ] No unresolved TODOs (or tracked in ticket)
338```
339 
340---
341 
342## Output Format
343 
344Structure your review comment as:
345 
346```
347## PR Review: [PR Title] (#NUMBER)
348 
349Blast Radius: HIGH — changes lib/auth used by 5 services
350Security: 1 finding (medium severity)
351Tests: Coverage delta +2%
352Breaking Changes: None detected
353 
354--- MUST FIX (Blocking) ---
355 
3561. SQL Injection risk in src/db/users.ts:42
357 Raw string interpolation in WHERE clause.
358 Fix: db.query("SELECT * WHERE id = $1", [userId])
359 
360--- SHOULD FIX (Non-blocking) ---
361 
3622. Missing auth check on POST /api/admin/reset
363 No role verification before destructive operation.
364 
365--- SUGGESTIONS ---
366 
3673. N+1 pattern in src/services/reports.ts:88
368 findUser() called inside results.map() — batch with findManyUsers(ids)
369 
370--- LOOKS GOOD ---
371- Test coverage for new auth flow is thorough
372- DB migration has proper down() rollback method
373- Error handling consistent with rest of codebase
374```
375 
376---
377 
378## Common Pitfalls
379 
380- **Reviewing style over substance** — let the linter handle style; focus on logic, security, correctness
381- **Missing blast radius** — a 5-line change in a shared utility can break 20 services
382- **Approving untested happy paths** — always verify error paths have coverage
383- **Ignoring migration risk** — NOT NULL additions need a default or two-phase migration
384- **Indirect secret exposure** — secrets in error messages/logs, not just hardcoded values
385- **Skipping large PRs** — if a PR is too large to review properly, request it be split
386 
387---
388 
389## Best Practices
390 
3911. Read the linked ticket before looking at code — context prevents false positives
3922. Check CI status before reviewing — don't review code that fails to build
3933. Prioritize blast radius and security over style
3944. Reproduce locally for non-trivial auth or performance changes
3955. Label each comment clearly: "nit:", "must:", "question:", "suggestion:"
3966. Batch all comments in one review round — don't trickle feedback
3977. Acknowledge good patterns, not just problems — specific praise improves culture
398 

Discussion