Code review automation
Hand repeatable rules to CI and bots (format, types, security scans, coverage thresholds) so humans and agents focus on architecture and business semantics; this skill lays out layering, noise reduction, and merge-gate patterns.
A SKILL should list what must be automated (e.g. secret scanning, dependency vulns, breaking API checks) versus what is advisory only, and whether failures block merge or can be bypassed with an exception label process.
Agents can help generate or update workflows, CODEOWNERS, and review templates, but align with skills like “code review checklist”: automation covers known patterns and policy; the checklist covers context and product trade-offs.
Review pipeline (PR → CI → bot → human)
[ Open / update PR ]
│
▼
┌─────────────┐ Gates: lint / test / types / security scan
│ CI workflow │──── Artifacts: logs, coverage, SBOM (as needed)
└─────────────┘
│
▼
┌─────────────┐ Summarize: one thread per check; link docs & local commands
│ PR bot │──── Reproducible failures: config paths, suppression syntax, issue template
└─────────────┘
│
▼
┌─────────────┐ Semantics: design, edge cases, naming, evolution, whether exceptions are OK
│ Human/agent │──── Do not repeat formatting issues the bot already flagged
└─────────────┘
Merge order: let CI and the bot clear or explicitly waive machine-decidable issues before deep human review; avoid humans restating what the linter already said in comments.
Enforce vs advisory
Prefer report-only (or a separate workflow) for new checks to collect a baseline before flipping to enforce; do not oscillate the same rule between “comment only” and “block merge” without data.
Enforce (block or must-fix)
- Secret / credential leaks, known high-severity dependencies
- Breaking public API contract detection (within team policy)
- Format and type gates the team has voted in
- Checks required by branch protection before merge
Advisory (hints, dashboards)
- Complexity, duplication, optional style rules
- Experimental rules, static rules with high false-positive rates
- “Code smells” that need business context to judge
- Coverage breakdowns for trends only (do not gate every PR)
Complete GitHub Actions workflow for AI-assisted code review (paste and replace secret names):
name: AI Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- "**/*.md"
- "dist/**"
- "**/package-lock.json"
jobs:
ai-review:
runs-on: ubuntu-latest
if: >
!github.event.pull_request.draft &&
!contains(github.event.pull_request.title, '[skip-review]')
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed
run: |
git diff --name-only origin/${{ github.base_ref }}...HEAD \
> changed_files.txt
cat changed_files.txt
- name: Run AI review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
# Output review comments as structured JSON
node .github/scripts/ai-review.js \
--changed-files changed_files.txt \
--output review-output.json
- name: Post review comments
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node .github/scripts/post-comments.js \
--review review-output.json \
--pr ${{ github.event.pull_request.number }}
Structured JSON output format for review comments (review-output.json):
{
"summary": "Found 2 P0 issues, 3 P1 issues, 1 P2 issue",
"comments": [
{
"file": "src/api/handlers.ts",
"line": 42,
"severity": "P0",
"message": "User ID concatenated directly into SQL string: SQL injection risk",
"suggestion": "Use a parameterized query: db.query('SELECT * FROM users WHERE id = $1', [userId])",
"rule": "security/no-sql-injection"
},
{
"file": "src/services/payment.ts",
"line": 87,
"severity": "P1",
"message": "catch block swallows StripeError; caller cannot distinguish error types",
"suggestion": "Map StripeError to a business error code and re-throw",
"rule": "error-handling/no-swallow"
}
]
}
Differentiation strategy: apply different rule sets based on file path:
// Differentiation strategy in .github/scripts/ai-review.js
function getRulesForFile(filePath) {
if (filePath.endsWith('.env') || filePath.includes('config/secrets')) {
return ['security/no-plaintext-secrets', 'security/no-credentials'];
}
if (filePath.endsWith('.sql') || filePath.includes('migrations/')) {
return ['db/reversible-migration', 'db/no-drop-without-backup'];
}
if (filePath.endsWith('.tsx') || filePath.endsWith('.jsx')) {
return ['react/no-direct-mutation', 'a11y/interactive-aria-roles', 'security/no-dangerouslySetInnerHTML'];
}
return ['general/no-hardcoded-secrets', 'general/error-handling'];
}
reviewdog integration configuration (.reviewdog.yml):
runner:
ai-review:
cmd: node .github/scripts/ai-review.js --format rdjson
format: rdjson
reporter: github-pr-review
level: warning
fail_on_error: true
filter_mode: added
False-positive marking format (inline comment in source; the AI review script recognizes and skips the line):
// ai-review-ignore: hardcoded here is a test-env mock, not a production secret
const MOCK_API_KEY = "test-key-not-real";
// ai-review-ignore: legacy code, migration plan tracked in JIRA-1234
const result = db.query(`SELECT * FROM users WHERE id = ${userId}`);
PR bot comment etiquette
- One thread per check: fold or summarize multiple alerts from the same tool to avoid spam.
- Actionable: link docs, one local command to reproduce, official fix or suppression syntax (with file paths).
- False positives: explain how to file an issue or add an allowlist — not “please ignore” in a comment thread.
- Respect drafts: default Draft PRs to lightweight checks or advisory only; run full enforce before merge (match conditions in the workflow lab).
Noise and metrics
Drive noise down with data: track bot comments per PR, share of comments marked invalid or false positive, repeat triggers per rule, and median time from PR open to all green.
- Post-merge review: if a rule is routinely ignored or heavily suppressed, move it to advisory or narrow scope.
- Use
paths-ignoreor a separate job for generated dirs, lockfiles, and large assets to avoid pointless runs. - Include “comment flood” and “merge delay” in team retros, alongside the enforce list.
Human review vs automation
Automation: rules you can enumerate in syntax and policy, consistent style across repos, security and license baselines, reproducible builds and test results.
Humans / agents: whether requirements are implemented correctly, whether abstractions fit, whether error handling and observability match the scenario, whether hidden coupling or over-engineering crept in.
Division of labor: the bot states which rule was violated; humans judge whether the rule or waiver should change under business constraints. Do not repeat the same class of issue at both layers.
Workflow snippet lab
Toggle options below to generate a pasteable GitHub Actions sketch: a paths-ignore list and jobs.<id>.if conditions (tune expressions for your trigger events).
Nest paths-ignore under the relevant on.push / on.pull_request; place if: beside jobs.<id> and runs-on. Fork PRs without head_commit may need fields on github.event.pull_request or split jobs.
---
name: code-review-automation
description: CI gates and layered PR bot strategy
---
# Step 1: Classify checks (enforce vs advisory)
enforce (block merge):
- Secret/credential scan (gitleaks or trufflehog)
- Dependency CVE ≥ HIGH (dependabot or trivy)
- Type errors (tsc --noEmit)
- Coverage below threshold (nyc / c8)
advisory (report, do not block):
- Cyclomatic complexity > 20 (eslint complexity rule)
- Duplicate code (jscpd)
- AI review comments with severity < P1
# Step 2: AI review workflow
trigger: pull_request [opened, synchronize]
skip: Draft PR / title contains [skip-review]
permissions: pull-requests:write + contents:read
output: JSON with file/line/severity/message/suggestion
# Step 3: Differentiation rules
.env / secrets: security rule set only
.sql / migrations: db/reversible-migration rule set
.tsx / .jsx: react + a11y + security/xss rule sets
other: general rule set
# Step 4: reviewdog integration
format: rdjson
reporter: github-pr-review
filter_mode: added # report only new issues within the diff
# Step 5: False-positive handling
// ai-review-ignore: <reason> # inline comment skips the line
audit frequency: quarterly review of ignore list; remove stale exemptions
# Step 6: Noise metrics
track:
- Bot comments / PR (target: < 5 enforce issues)
- False-positive rate (target: < 10%)
- Median time from PR open to all-green