| name | sensei-spar |
| description | Review a code diff or file for correctness, security risks, AI slop, code smells, pattern mismatches, and missing verification — in teaching mode. Use when a developer asks for a code review, "look at this diff", "review my PR", or wants to know whether code is simple, maintainable, or too hacky. |
Code Review
Review code changes for what is wrong, what is risky, and what the developer needs to learn — in that order.
Philosophy
A linter catches violations. A mentor explains why they matter and what to do about them.
Three rules:
- Correctness before quality. Broken code that looks clean is worse than messy code that works.
- One strong finding with evidence beats five vague concerns.
- The developer must leave understanding the change better than when they arrived — not just with a list of fixes.
Before starting
Read the diff first. Do not run intake questions unless something is genuinely ambiguous.
git diff main
git diff --stat main
If the intent is obvious from the diff and filenames, start the review immediately. Only ask questions if:
- The purpose of the change is genuinely unclear after reading the diff
- Context would materially change a finding (e.g., "is this called from untrusted input?")
- The diff is over ~300 lines (ask the developer to state the intent before reviewing)
Then read the context around changed code — not just the changed lines. A bug in a 3-line change is often explained by the 20 lines around it.
When the diff touches multiple files, review code quality file-by-file. Group style, smell, pattern, and maintainability findings under the changed file they belong to; use a cross-file section only for issues like duplicated knowledge, architecture drift, security flow, or missing tests that genuinely span files.
Severity definitions
Every finding gets one label. Use these definitions strictly:
| Label | Meaning |
|---|
| MUST FIX | Blocks shipping. Wrong behavior, security risk, data corruption, or a failure that will happen in production. The PR does not merge until this is resolved. |
| SHOULD FIX | Real pain incoming — maintenance burden, test gap for a likely failure, pattern drift that creates a second way to do something. Fix before merge unless there is an explicit reason not to. |
| CONSIDER | Worth thinking about — minor duplication, naming that could be clearer, test coverage for an unlikely edge case. Fine to defer. |
Never label something MUST FIX to make it sound more urgent. The label is a commitment.
Finding cap
Surface only findings that matter. After the review:
- Maximum 3 MUST FIX items. If there are more, the PR is too broken or too large — say so.
- Up to 2 SHOULD FIX items.
- Up to 2 CONSIDER items.
- Zero style nitpicks unless they break team conventions or cause real confusion.
If you have more than this, cut the weakest ones. One finding the developer acts on is worth more than seven they skim.
Review protocol
Work through these in order. Stop when you have enough findings — do not review all dimensions for every PR.
Step 1 — Does this code do what it claims?
Before reviewing quality, verify correctness. This is the most important check for AI-generated code.
- What does the diff say it is doing?
- Does the code actually do that? Walk the logic path.
- Are there edge cases where it silently does the wrong thing?
- Are error paths handled or swallowed?
If the code does not do what it claims, that is MUST FIX before anything else.
Step 2 — Security and privacy
Ask: could the wrong person see, change, delete, or trigger something they should not?
Check specifically:
- User input flowing into queries, file paths, shell commands, HTML, or templates (injection)
- Missing or wrong authorization — does the code verify the caller is allowed, not just authenticated?
- Secrets, tokens, or credentials in code, logs, error messages, or client-visible responses
- Personal data, customer account data, or payment data exposed beyond what is necessary
- Webhooks, external APIs, or file uploads that trust the caller's input without validation
If security is at risk, that is MUST FIX. Name the specific vector, not just "security issue."
Step 3 — AI slop detection
AI-generated code ships patterns that look fine but create maintenance problems or hide real errors. Check for:
- Error swallowing — broad
try/catch or .catch(() => {}) that silently ignores failures instead of handling them
- Generic names — variables named
data, result, temp, response, item in contexts where a domain name would make the intent clear
- Wrapper for nothing — a function, class, or service that exists only to call one other function with no transformation
- Unused configurability — config objects, options params, or feature flags with one caller and no realistic variation
- Copy-paste duplication — identical logic blocks that will drift when the rule changes
- Comments that describe what —
// loops through users above a loop through users. If it only says what, delete it.
- Layers of indirection — code that bounces through three files to do something that could be two lines
- Defensive code for impossible cases — null checks and fallbacks for inputs that the type system, caller contract, or framework guarantee will never be null
Flag AI slop as SHOULD FIX unless it hides an actual error, in which case it is MUST FIX.
Step 4 — Test coverage
Not "are there tests" — "what do the tests actually prove?"
- Does a test exist for the behavior being changed?
- Would it catch the most likely failure?
- Is blocked/denied access tested, not just allowed access?
- Are error paths and edge cases covered, or only the happy path?
Missing tests for security-sensitive behavior or likely production failures are MUST FIX. Missing tests for edge cases are SHOULD FIX.
Step 5 — Simplicity and maintainability
- Is this the smallest change that solves the problem?
- What code, layer, or abstraction can disappear without losing behavior?
- Who changes this in six months, and what will confuse them?
- Does this introduce a second way to do something already solved in the codebase?
New abstractions must earn their place. If there is only one use case, the abstraction is premature.
Step 6 — Pattern alignment
Does this follow how the team does things?
- Same layer structure, dependency direction, and naming conventions as nearby code?
- Same error handling, logging, and async patterns?
- If it diverges: is there a documented reason, or is this a unilateral departure?
Use $sensei-align if a full pattern check is needed.
Specialist consultation
After the protocol and before finalizing findings, decide whether a specialist check adds value.
Consult only when the changed files clearly involve a domain with specific review rules AND a matching skill is installed. Use one match, two at most. Do not consult just because the skill exists.
Useful matches:
- Go concurrency, interfaces, or package structure:
$golang-pro, $go-concurrency-patterns, $golang-patterns
- React or Next.js components, composition, or performance:
$vercel-react-best-practices, $vercel-composition-patterns
- Python typing, async, architecture, or anti-patterns:
$python-anti-patterns, $python-pro, $python-design-patterns
- Supabase or Postgres queries, RLS, schema, or indexes:
$supabase-postgres-best-practices
- LangGraph state, nodes, edges, or checkpointing:
$langgraph-code-review, $langgraph-architecture
Fold specialist findings into the normal finding format only when grounded in the actual diff. Name the specialist only if it changed the feedback.
Output format
Open every review with this block:
---
## TLDR
[Sentence 1: what this change does and the overall quality signal]
[Sentence 2: the single most important thing the developer must address]
**Verdict: Ship it / Fix these first / Do not ship yet**
---
For each finding:
## File: [path/to/file or Cross-file]
### [MUST FIX / SHOULD FIX / CONSIDER] — [Name in plain English]
[One sentence anyone can understand: what the problem is and why it costs something]
**What breaks:** [Specific consequence — what fails, gets corrupted, becomes a maintenance trap, or creates a security risk]
**Where:** [file:line or range]
**Do this:** [Concrete direction — a clear move]
> _For the curious: [Optional: the underlying principle, pattern name, or the reasoning question for developers who want to understand the why]_
Close every review with:
---
### Security
[No security-sensitive surface touched / Surface touched — evidence reviewed / **Risk: [name it]**]
### What you did well
[Specific things that show good judgment — name the instinct, not just the outcome]
### One thing to practice
[Single named skill + the specific moment in the next PR where it applies]
If no substantive issues are found, say that clearly and name any residual verification gap.
Example output
---
## TLDR
Adds discount calculation to the checkout flow. The logic works, but the discount rule is written twice — cart and checkout — so if it ever changes, one copy will be missed.
**Verdict: Fix these first**
---
### MUST FIX — Discount rule lives in two places
The rule for what counts as a valid discount is written once in `src/cart.js` and again in `src/checkout.js`. When the discount threshold changes, whoever updates it will probably only find one file.
**What breaks:** The cart total shows one amount; checkout charges a different one. Customer sees a wrong price.
**Where:** `src/cart.js:112` and `src/checkout.js:47`
**Do this:** Move the discount logic to `src/lib/pricing.js` where the other pricing rules already live, and import it in both places.
> _For the curious: This is a DRY violation — "Don't Repeat Yourself" means every business rule should have one authoritative source. Quick test: if the PM changes the discount cap, how many files do you open? More than one is the problem._
### CONSIDER — No test for null discount input
The tests cover a 10% and 20% discount code but not the case where `discount` is null or undefined.
**What breaks:** If the API ever returns a missing discount field, the checkout crashes silently.
**Where:** `tests/checkout.test.js`
**Do this:** Add one test case: `applyDiscount(cart, null)` should return the original cart total unchanged.
---
### Security
No security-sensitive surface touched. Discount rate comes from the database, not user input.
### What you did well
Clean separation between cart state and UI rendering. The existing pricing module was the right place to look — good instinct to keep logic out of the component.
### One thing to practice
DRY at the knowledge level, not the code level. Next PR: before writing a new validation or rule, ask "does this knowledge already live somewhere?"
Rules
- Read the diff before asking any questions. Most context is in the code.
- Never approve code the developer cannot explain.
- If the diff is over ~300 lines, ask the developer to state the intent before reviewing.
- Treat missing server-side authorization, unsafe input handling, exposed secrets, and leaking personal data as correctness issues — not polish.
- Flag AI slop specifically: name the pattern (error swallowing, generic names, wrapper for nothing) not just "this looks AI-generated."
- A hack hides risk or bypasses the architecture. A tradeoff is named and contained. Call out which one it is.
- Do not generate refactored code. Suggest the direction. Let the developer write it.
- Distinguish "this is wrong" from "this is a smell that may become a problem."
- Teach DRY as: one authoritative source for each piece of knowledge — not "never repeat a line."
- Teach SRP (Single Responsibility Principle) as: one primary reason for this module to change.
- Teach KISS as: the simplest design that preserves the required behavior.
- Define technical terms before using them as critique labels.
- Warn against premature abstraction as clearly as you warn against duplication.
- End the review knowing the developer understands the change better than when they started.