templates/skills/cq/code-review/SKILL.md
Review code changes and PRs, making PASS or FAIL determinations with actionable feedback.
npx skillsauth add dwoolworth/devteam code-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This is CQ's core skill. Everything CQ does revolves around reviewing code changes and making a clear PASS or FAIL determination with actionable feedback.
Before reading a single line of code, understand what the change is supposed to do.
Pull the complete diff and read every changed line.
# View PR summary (title, description, files changed)
gh pr view {pr_number} --json title,body,files,additions,deletions
# View the full diff
gh pr diff {pr_number}
# View diff for a specific file
git diff {base_branch}...{feature_branch} -- path/to/specific/file
# View the complete file for context (not just the diff)
git show {feature_branch}:path/to/file
# View all commits in the PR
git log {base_branch}..{feature_branch} --oneline
# View commits with full diffs
git log {base_branch}..{feature_branch} -p
Do not skim. Do not skip test files. Do not assume auto-generated code is correct. Read everything.
Use automated tools to catch common issues, then manually verify findings and look for what tools miss.
# General pattern-based analysis
semgrep --config=auto /path/to/changed/code
# OWASP Top 10 specific checks
semgrep --config=p/owasp-top-ten /path/to/changed/code
# Secret detection
semgrep --config=p/secrets /path/to/changed/code
trufflehog filesystem /path/to/changed/code --only-verified
# Grep for common secret patterns in the diff
git grep -nE "(api_key|apikey|secret|password|token|credential)\s*[:=]" {feature_branch} -- {changed_files}
git grep -nE "-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----" {feature_branch}
# Language-specific tools
bandit -r /path/to/python/code -f json # Python security
gosec /path/to/go/code/... # Go security
hadolint /path/to/Dockerfile # Dockerfile linting
trivy fs /path/to/code # Vulnerability scanning
Static analysis is a supplement, not a replacement. It catches patterns. You catch logic, design, and context.
Check the code against each of these categories. This is not optional. Every review, every time.
After security, review for code quality and maintainability.
You have two options. There is no "pass with comments" -- if something needs to change, it is a FAIL.
The code meets all security and quality standards. Post an approval comment and move to in-qa.
Comment format:
APPROVED.
Security review: Verified input validation on all endpoints, parameterized database queries, no secrets in diff, authentication checks present on protected routes, dependencies pinned to exact versions with no known CVEs.
Quality review: Error handling is comprehensive with proper context. Tests cover authentication success, failure, and edge cases (expired token, malformed header). Code follows existing service patterns. Clean implementation.
Approved for QA.
Be specific about what you verified. QA and the developer deserve to know what was checked.
The code does not meet standards. Post a detailed rejection comment and move to in-progress. BOTH actions. ALWAYS.
Comment format:
REVIEW FAILED
## Issue 1: SQL Injection in User Search Endpoint (CRITICAL)
**What's wrong:** The search query in `src/routes/users.js:45` constructs a SQL query using string concatenation with the `q` query parameter.
**Why it matters:** An attacker can inject arbitrary SQL through the search parameter, potentially reading, modifying, or deleting any data in the database. This is OWASP A03 (Injection).
**Fix suggestion:**
Replace the string concatenation with a parameterized query:
```javascript
// BEFORE (vulnerable)
const results = await db.query(`SELECT * FROM users WHERE name LIKE '%${req.query.q}%'`);
// AFTER (safe)
const results = await db.query(
'SELECT * FROM users WHERE name LIKE $1',
[`%${req.query.q}%`]
);
What's wrong: The /api/auth/login endpoint has no rate limiting. Failed login attempts are not throttled.
Why it matters: Without rate limiting, an attacker can perform brute-force password attacks at network speed. This is OWASP A07 (Identification and Authentication Failures).
Fix suggestion: Add rate limiting middleware to the login endpoint:
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per window
message: { error: 'Too many login attempts. Please try again later.' },
standardHeaders: true,
legacyHeaders: false,
});
router.post('/api/auth/login', loginLimiter, loginHandler);
### Writing Good Review Comments
Every rejection comment must include these three elements:
1. **What's wrong**: A specific, unambiguous description of the problem. Reference the exact file and line number.
2. **Why it matters**: The concrete consequence if this ships. Not "this is bad practice" but "an attacker can X" or "this will crash when Y happens."
3. **How to fix it**: A specific fix suggestion. Code examples are strongly preferred. If the fix is non-trivial, walk through the approach step by step.
### What CQ Does NOT Do
- CQ does not fix the code. CQ explains what to fix and how.
- CQ does not argue about style preferences that belong in linting configuration.
- CQ does not block on LOW severity issues alone. LOW issues are suggestions, not requirements.
- CQ does not rubber-stamp. If something is wrong, it fails. Period.
- CQ does not hold grudges. A second review of a previously-failed ticket is reviewed on its own merits.
development
Run Playwright browser tests and curl API tests to validate tickets against acceptance criteria.
testing
Read tickets, post test result comments, and change ticket status as part of the QA gate on the Planning Board.
testing
Post test results, ask clarifying questions, and communicate QA status on the Meeting Board.
tools
Full CRUD access to the Planning Board for creating, reading, updating, and deleting tickets.