Code review checklist - coordinates specialist reviewers for thorough analysis
54
42%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Risky
Do not use without reviewing
Optimize this skill with Tessl
npx tessl skill review --optimize ./dot_config/opencode/skill/review-checklist/SKILL.md"Review this PR" means: analyze and draft a written review — do NOT submit to GitHub or implement fixes unless explicitly asked.
Conflict resolution on reviewed PRs: When a PR has reviews and conflicts with the base branch, use git merge (not git rebase) to resolve them. Rebasing rewrites history and invalidates existing review comments.
Submitting reviews: Show the full proposed review content and ask "Do you approve?" before submitting to GitHub — then STOP and wait for explicit approval. Load the gh-pr-inline skill when posting inline comments or responding to review feedback.
If reviewing a PR (URL or number provided): check out the PR branch locally before doing anything else.
gh pr checkout <PR_NUMBER>This ensures local files reflect the PR's code, enabling accurate diff panel display and local file reads. Save the original branch first so you can restore it if needed:
ORIGINAL_BRANCH=$(git branch --show-current)
gh pr checkout <PR_NUMBER>
# ... review ...
git checkout $ORIGINAL_BRANCH # restore when doneIf gh pr checkout fails (e.g., the repo isn't local), fall back to gh pr diff for the diff and gh pr view for metadata.
Read .opencode/context-log.md first for issue context and build history.
Extract issue IDs from branch name or PR, then fetch details:
git branch --show-current — parse for ENG-123, PROJ-456, #123, gh-123
For PRs: check PR body and linked issues via gh pr view --json body,title
Fetch: Linear → load the linear skill and query via gq, GitHub → gh issue view
Fetch project context — if the Linear issue belongs to a project, also fetch via the linear skill:
Project context reveals the why behind the issue — business goals, related features, constraints, and existing decisions that the diff should align with. Include it in the payload to correctness and maintainability agents.
Use context to verify: requirements alignment, acceptance criteria, scope creep, project goals, and consistency with related issues in the same project.
After getting the diff: read entire modified file(s) for full context.
For PR reviews, fetch prior review history:
gh pr reviews <PR> --json author,state,submittedAt,body — all submitted reviews with their verdict and top-level bodygh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments — all inline review comments; note path, line, body, in_reply_to_id (a non-null in_reply_to_id means it's a reply in a thread)in_reply_to_id), identify the last message per thread, and mark threads as:
Read project rules: Check for and read AGENTS.md, .opencode/AGENTS.md, CONVENTIONS.md, .github/copilot-instructions.md, REVIEW.md, and CONTRIBUTING.md in the repo root. Also check docs/ for development guides (e.g., docs/development.md, docs/writing-probes.md) that may contain framework-specific conventions (minimum test counts, data file conventions, naming patterns) that generic review cannot infer. REVIEW.md contains review-specific guidance (paths to skip, things to always flag, team conventions) — treat it as the highest-priority override. Include relevant rules in the payload to all sub-agents (not just maintainability) — security rules go to security, testing expectations go to correctness, etc.
Before any LLM analysis, run available project linters/checkers on modified files. Detect which tools are available and run them. Run all applicable tools in parallel.
Check project root for config files to detect tooling:
Style & quality:
Gemfile / .rubocop.yml → bundle exec rubocop --format json <files>package.json → check for eslint/biome scripts → npx eslint --format json <files> or npx biome check <files>pyproject.toml / setup.cfg → ruff check <files> --output-format json.golangci.yml → golangci-lint run <files>Type checking:
tsconfig.json → npx tsc --noEmit --pretty 2>&1 (type errors only)pyproject.toml with mypy config → mypy <files>sorbet/ or Gemfile with sorbet → bundle exec srb tc <files>Security:
Gemfile with brakeman → bundle exec brakeman --only-files <files> --format json -qGemfile with bundler-audit → bundle exec bundler-audit check --updatepackage.json → npm audit --json or yarn audit --jsonGemfile / package.json → check for importmap or yarn.lock changes and flag new dependencies for reviewOnly run tools that are already configured in the project — never install new tools.
Collect output. Include the raw linter findings in the payload to sub-agents under a ## Static Analysis Findings section. Tag each finding with its source tool so specialists know what's already been caught mechanically.
If no linters are configured or all pass clean, note "No static analysis findings" and move on.
The LLM's job is to find semantic issues that tools can't catch — logic errors, missing edge cases, architectural problems. Let tools handle syntax, style, and known vulnerability patterns. If a static analysis tool already flagged an issue, specialists should NOT re-report it (it's already in the output) — they should only add findings the tools missed.
Do not review the diff yourself — your job is coordination: gather context, build the payload, dispatch, handle escalations, merge, verify, and format output.
Before dispatching, scan the diff to determine which specialists are relevant:
| Signal | Triggers |
|---|---|
| Models, services, controllers, jobs, lib | correctness, completeness, maintainability |
| Removes/renames columns, enums, scopes, methods | completeness (propagation) |
ORM calls: update_column, delete, caching, type patterns | conventions |
| DB queries, loops over collections, views rendering lists | performance |
| Auth, params, cookies, sessions, CORS, encryption | security |
| Migrations, schema changes | performance + completeness |
| Views, templates, JS, CSS, frontend components | maintainability (UX) + performance (UI scalability) |
| Config files, routes, environment settings | security |
| Test files | maintainability (test validity) |
Always dispatch: correctness, completeness, maintainability — these three cover the highest-value issues on every diff.
Conditional:
When in doubt, dispatch — false negatives are worse than wasted tokens.
Prepare a base payload containing:
Prepare extended context:
linear skill / gh issue view — not the placeholder.Spawn all applicable specialists in parallel (all in a single message), all with subagent_type="expert". Each prompt tells the expert to load a specific review skill.
Always dispatch (every review):
Task("review-correctness", payload + Issue Context + Project Rules + Static Analysis + Prior Reviews)
Task("review-completeness", payload + Issue Context + Project Rules + Static Analysis + Prior Reviews)
Task("review-maintainability", payload + Issue Context + Project Rules + Static Analysis + Prior Reviews)Conditional (only when relevant signals detected):
Task("review-conventions", payload + Project Rules + Static Analysis + Prior Reviews)
Task("review-security", payload + Project Rules + Static Analysis + Prior Reviews)
Task("review-performance", payload + Project Rules + Static Analysis + Prior Reviews)Each Task prompt follows this template:
Load the `review-<skill>` skill and follow its instructions.
<base payload>
## Project Rules
<AGENTS.md / CONVENTIONS.md content>
## Issue Context ← only for correctness, completeness, maintainability
<issue details, acceptance criteria, project body>
## Static Analysis Findings
<linter output>
## Prior Reviews
<prior review summary or 'N/A — not a PR review'>Each specialist returns {"findings": [...], "escalations": [...]}.
Escalation routing: If a specialist was skipped but another specialist escalates to it, the escalation handler dispatches it as a follow-up.
After all specialists return, collect all escalations from each specialist's output. For each escalation:
for_reviewer (correctness / completeness / conventions / maintainability / security / performance)subagent_type="expert".Follow-up template:
Load the `review-<domain>` skill. The following areas were flagged by other reviewers:
<list of escalations with file:line and note>
Full diff: <diff>
Full file contents: <files>
## Project Rules: <content>
## Issue Context: <if applicable>
## Static Analysis: <output>
This is a follow-up pass. Put all issues in `findings`. Escalations will be discarded.Include ## Issue Context for correctness, completeness, and maintainability follow-ups. Omit for security, performance, and conventions.
Follow-up agents return additional findings. The coordinator discards all escalations from follow-ups to prevent infinite loops.
After all specialists and follow-up agents return:
Collect all findings from all agents (initial + follow-up)
Deduplicate — if two specialists flag the same line, keep the higher-severity one and note both concerns
Verification pass — for each finding, attempt to disprove it using the method appropriate to the claim:
| Claim type | How to disprove |
|---|---|
| "Unused variable/function" | rg for all usages including dynamic calls, string interpolation, metaprogramming; discard only if zero real usages found |
| "Missing null check" | Read the full call chain upstream — is there a guard, presence validation, or DB constraint that guarantees non-null? Discard only if protection is confirmed |
| "N+1 query" | Check default_scope, after_find, controller includes, and any concern that wraps the association; discard only if eager loading is confirmed for this access path |
| "Unsanitized input" | Trace the full input path — does a framework layer (Rack, Rails strong params, ORM) sanitize it before use? Discard only if sanitization is confirmed end-to-end |
| "Race condition" | Read the surrounding transaction, lock, or mutex scope; discard only if the critical section is provably atomic |
| "Side effect fires on failure" | Read the callback/hook definition and check whether it is wrapped in after_commit, after_save with a condition, or guarded by the success of the preceding operation |
| "Pre-existing" tag | Confirm with git blame — if the line was touched by this diff, reclassify to appropriate severity |
Default: keep, don't discard. Discard a finding only if you can positively disprove the claim above. A finding that is hard to verify is not the same as a false positive. When in doubt, keep it as a suggestion with a note that further investigation is needed.
While verifying, read the surrounding code actively — if you spot a new issue the specialists missed, add it to the list. Specifically look for cross-cutting concerns: security implications of correctness issues, correctness implications of performance changes. You are not only pruning — you are also a final reviewer.
Classify remaining findings:
blocker → Blockers sectionsuggestion → Suggestions sectionnit → Nits sectionpre-existing → Pre-existing Issues section (separate, never affects verdict)Determine verdict based on blockers only (pre-existing findings never trigger CHANGES REQUESTED)
The correctness specialist traces side effect chains in Phase 1. When building the payload, explicitly flag any callbacks, jobs, events, or webhooks visible in the diff so it traces them.
After merging specialist findings, the coordinator adds these checks directly (not delegated to specialists):
Missing acceptance criteria — if no linked issue with acceptance criteria was found, add a suggestion: "No linked issue with acceptance criteria found — cannot fully verify feature completeness. Consider linking an issue with specific acceptance criteria."
Runtime verification required — if the diff modifies views, templates, controllers, frontend code, or UI interactions, run QA automatically after all findings are merged:
$ORIGINAL_BRANCH) — already done if reviewing a PR (see PR Checkout section above)git checkout $BRANCH_NAME/qa — pass relevant context about which flows were changed (e.g., "login form was modified")git checkout $ORIGINAL_BRANCH## QA Results section after Pre-existing Issues.Do not add a note suggesting QA — execute it and report the results.
Be terse. Developers can read code — don't explain what the diff does.
## Verdict: [APPROVE | CHANGES REQUESTED | COMMENT]
[One sentence why, if not obvious]
## Blockers
- **file.rb:10** - [2-5 word issue]. [1 sentence context if needed]
```suggestion
# concrete replacement code# concrete replacement codeThese bugs exist in the codebase but were not introduced by this PR. They do not affect the verdict.
**Rules:**
- Skip sections with no items (don't say "None")
- Max 1-2 sentences per item. No filler.
- **Always include a `suggestion` code block** with the concrete fix, unless the fix requires architectural changes that can't be expressed as a snippet
- Use "I" statements, frame as questions not directives
- Pre-existing Issues never influence the verdict — they are informational only
**For PRs:** extend the output as follows:
```markdown
[PR URL as clickable link]
## TL;DR
[One sentence summary of what this PR does]
## Verdict: [APPROVE | CHANGES REQUESTED | COMMENT]
[One sentence why, if not obvious]
## Requirements Check
> Only include this section if issue context was found.
- [Acceptance criterion 1]: [met / not met — one sentence]
- [Acceptance criterion 2]: [met / not met — one sentence]
## Unresolved Prior Feedback
> Only include this section if prior reviews exist and any threads are still unresolved.
- **file.rb:10** (@reviewer, [date]) - [original comment summary]. [Status: author hasn't responded / author replied but issue remains]
## Blockers
...
## Pre-existing Issues
> These bugs exist in the codebase but were not introduced by this PR. They do not affect the verdict.
- **file.rb:55** - [issue title]. [1 sentence explanation]Prior review rules:
CHANGES REQUESTED items.03cec9d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.