| name | review |
| description | Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/PRD asked for?) — and run an adversarial "devil's advocate" pass (the pr-review-toolkit agents) before human review. All three run as parallel sub-agents and report side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X". This is Phase 7 of docs/WORKFLOW.md. |
Review
Three-lens review of the diff between HEAD and a fixed point the user supplies. This is the Phase 7 (Code Review) step of docs/WORKFLOW.md; it is a project-local override of the global review skill, Butler-anchored (GitHub issue tracker) and extended with the formalized adversarial sub-step from issue #93.
- Standards — does the code conform to this repo's documented coding standards?
- Spec — does the code faithfully implement the originating issue / PRD / spec?
- Adversarial — a devil's-advocate pass that actively tries to break the change, run via the
pr-review-toolkit agents before human review.
All lenses run as parallel sub-agents so they don't pollute each other's context, then this skill aggregates their findings.
Effort: The adversarial pass benefits from maximum scrutiny — run this review at xhigh effort (output_config: { effort: "xhigh" }) per the effort-level mapping in CLAUDE.md. budget_tokens is deprecated on Opus 4.8+; use thinking: { type: "adaptive" } with an effort level instead.
Process
1. Pin the fixed point
Whatever the user said is the fixed point — a commit SHA, branch name, tag, main, HEAD~5, etc. Don't be opinionated; pass it through. If they didn't specify one, ask: "Review against what — a branch, a commit, or main?" Don't proceed until you have it.
Capture the diff command once: git diff <fixed-point>...HEAD (three-dot, so the comparison is against the merge-base). Also note the list of commits via git log <fixed-point>..HEAD --oneline.
2. Identify the spec source
Look for the originating spec, in this order:
- Issue references in the commit messages (
#123, Closes #45) — fetch via gh issue view <n>.
- A path the user passed as an argument.
- A PRD/spec file under
docs/, .scratch/, or matching the branch/feature name.
- If nothing is found, ask the user where the spec is. If they say there isn't one, the Spec sub-agent will skip and report "no spec available".
3. Identify the standards sources
Anything in the repo that documents how code should be written. Common locations in Butler:
CLAUDE.md (root + ~/.claude rules), AGENTS.md
CONTEXT.md (domain glossary)
docs/adr/ (architectural decisions are standards)
docs/WORKFLOW.md
eslint.config.*, tsconfig.json, prettier config, vitest.config.* (machine-enforced — note them but don't re-check what tooling already checks)
Collect the list of files. The Standards sub-agent will read them.
4. Spawn all three lenses in parallel
Send a single message with the Agent tool calls below so they run concurrently.
4a. Standards sub-agent (general-purpose) — include:
- The full diff command and commit list.
- The list of standards-source files you found in step 3.
- The brief: "Read the standards docs. Then read the diff. Report — per file/hunk where relevant — every place the diff violates a documented standard. Cite the standard (file + the rule). Distinguish hard violations from judgement calls. Skip anything tooling enforces. Under 400 words."
4b. Spec sub-agent (general-purpose) — include:
- The diff command and commit list.
- The path or fetched contents of the spec.
- The brief: "Read the spec. Then read the diff. Report: (a) requirements the spec asked for that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. Quote the spec line for each finding. Under 400 words."
If the spec is missing, skip the Spec sub-agent and note this in the final report.
4c. Adversarial pass — spawn these three pr-review-toolkit agents, in the same parallel message as 4a/4b, each pointed at the diff (tell each the diff command and that it should focus on the unstaged/branch changes):
pr-review-toolkit:silent-failure-hunter — hunts swallowed errors, empty catches, and inappropriate fallback behaviour.
pr-review-toolkit:type-design-analyzer — reviews new/changed types for encapsulation and invariant expression.
pr-review-toolkit:code-reviewer — checks adherence to project guidelines and style (CLAUDE.md), and general correctness.
This adversarial sub-step runs before human review (Phase 8) so a human only sees what survived the devil's advocate. Domain reviewers — rls-isolation-reviewer (when the diff touches supabase/migrations/** or club-scoped data access) and a11y-reviewer (when the diff touches packages/frontend/src/**) — should be added to this same parallel batch when the diff is in their scope.
5. Aggregate
Present the reports under ## Standards, ## Spec, and ## Adversarial headings (and any domain-reviewer headings), verbatim or lightly cleaned. Do not merge or rerank findings across lenses — they are deliberately separate so the user can see them independently.
Never let a skipped lens read as a clean lens (the same discipline as the missing-spec case above):
- If a lens was applicable but did not run — an agent couldn't be spawned, errored, or returned nothing — say so under its heading (e.g.
silent-failure-hunter: did not run — agent unavailable) rather than omitting it. A missing heading must never be mistaken for "ran and found nothing."
- For each conditional domain reviewer (
rls-isolation-reviewer, a11y-reviewer), record the scope decision explicitly — e.g. rls-isolation-reviewer: skipped (no supabase/migrations or club-scoped data access in diff). The rls scope trigger is semantic (club-scoped data access), not just path-based, so state why it was or wasn't run. This keeps a silently-skipped security/a11y reviewer auditable.
End with a one-line summary: total findings per lens, the worst single issue (if any) flagged, and any lens that failed to run called out separately from a lens that ran and found nothing. Per WORKFLOW.md Phase 7, all Critical and High findings must be resolved before merge; Medium/Low are logged as follow-up issues.
Why multiple lenses
A change can pass one lens and fail another:
- Code that follows every standard but implements the wrong thing → Standards pass, Spec fail.
- Code that does exactly what the issue asked but breaks the project's conventions → Spec pass, Standards fail.
- Code that passes both but swallows an error or leaks a cross-tenant row → caught only by the Adversarial pass.
Reporting them separately stops one lens from masking another.