code-critic
Code review checklist and coding standards, extended per project by whatever file AGENTS.md's Review & Planning Guidance section names (defaulting to docs/agent-rules/code-critic.md). Invoked as /code
SKILL.mdplugins/ai-workflow/skills/code-critic/SKILL.md
GitHub/code-critic
Review code changes against project standards.
Stance: the implementation is a hypothesis under attack. Assume something is wrong and try to disconfirm it; don't verify that it "looks reasonable". No fault found after genuine effort → state that explicitly (Disconfirmation Attempt). Apply the same stance to the tests (see Tests).
Input
- Plan text (optional; inline or file path)
- Test evidence: summary of the latest full test run (optional)
- The diff is not an input: step 1 selects it
Steps
1. Select the diff
- Work committed:
git diff <default-branch>...HEADorgit log -p <default-branch>..HEAD; default branch fromAGENTS.md→ Commands - Work uncommitted: working-tree diff
- Unsure →
git status,git log --oneline - You may read any file in the repo; the diff is the unit under review
2. ADRs
docs/adr/exists → list the ADRs read by number in the output; none relevant → say so explicitly (silence reads as skipped)- Diff contradicts an ADR →
FAIL; quote the ADR clause and the diff line
3. Plan compliance (skip entirely if no plan)
Per plan section:
- Approval Summary / Acceptance Criteria: every
AC-<slug>-nhas a committed test tagged[AC-<slug>-n]that would fail if the criterion broke. Missing →FAIL - Edge Cases: same for every
EDGE-<slug>-n/[EDGE-<slug>-n]. Missing →FAIL - Contract (skip if "None"): diff's routes, fields/params, response
shapes, error rendering, schema match. Undiscussed deviation →
FAIL - Types anywhere in the plan (Contract, Approach, Requirements, even
when Contract is "None"): nullable/non-null and optional/required are part
of the contract
- Diff every parameter/field type the plan states against the actual signature; don't infer from the tests' inputs
- Mismatch either way →
FAIL, even if tests pass, unless the review input documents an approved deviation
- Requirements: all addressed (source of truth for what to build)
- Approach: code follows it; no undiscussed alternative design; all plan steps accounted for
- Files: diff matches the manifest. File in diff but not listed (or listed but untouched) → undiscussed change; flag, judge scope
- Out of Scope: don't flag listed items as missing; flag code straying into it as scope creep
- Test Strategy: cross-reference against actual tests
- Quote the exact plan line and the exact violating diff line; never paraphrase the plan
4. Test evidence
- Judge test coverage by reading the code and tests; never run the suite
- Test run summary provided → treat it as the implementer's record that the suite ran and passed, not as independent proof
- No summary provided → raise Open Question: "no evidence the test suite ran on the reviewed state"; never assume green
5. Apply standards
Single responsibility
- Functions do one thing. Branch for a different concern (admin path in a
user path, parsing inside persistence):
- New function with mixed concerns →
FAIL - Existing function extended with an unrelated branch →
FAIL - Borderline, extraction hurts readability →
NEEDS_DECISION
- New function with mixed concerns →
- Classes: too many responsibilities = more than one independent reason to
change
- New class with too many →
FAIL - Change significantly worsens an existing violation →
FAIL - Change extends an oversized class without making it meaningfully worse →
NEEDS_DECISION(flag the debt; developer chooses refactor / tech debt / accept)
- New class with too many →
Error handling
- No generic catch-all in business logic. At process boundaries (top-level controllers, scheduled jobs, async entry points) a catch-all that logs and translates to a domain error is fine; judge by where it lives
- Every error carries context: what operation failed and why
- Critical-path errors logged with structured fields
Naming
- Functions say what they do (
calculateShippingCost, notcalc/process) - Booleans read as questions (
isValid,hasPermission)
Tests (you own quality AND completeness)
- Quality:
- Every public function with non-trivial behaviour has happy-path and edge tests. Exempt: trivial delegators, generated code, plain data classes, pure getters. State each exemption and why
- Names describe the scenario (
test_order_fails_when_inventory_insufficient) - Given-When-Then; test behaviour, not implementation details
- Check what each assertion actually compares, not its name/comment. A test that passes regardless of correctness (expression compared to itself) proves nothing
- Diff weakens, loosens, or deletes a plan-derived test (esp.
[AC-…]) to pass the suite →FAIL, unless the input documents an approved plan deviation
- Completeness, against the plan: every Requirement and Edge Case has a test that would fail if it broke; every branch the implementation introduces (error paths, validation failures, authorization denials, empty/null) is exercised
- Completeness, beyond the plan: statically, per branch in the diff, build an
input or event sequence that exercises it and ask whether a test covers it.
For each category ask "what would I do to make this fail?":
- Implicit branches (
if,when/switch,?:, early returns, exception handlers) not in the plan - Boundary values: 0, 1, max, min, off-by-one, empty, single-element, at-limit vs just-over
- Input shapes: null, blank, whitespace, unicode, very long, leading/trailing spaces, mixed case, duplicates, unsorted
- State/concurrency: stale reads, double submit, retries, partial writes, invoked twice
- External dependency failure: timeouts, 4xx vs 5xx, malformed payloads, slow responses
- Security-adjacent: authz on every entry point (not just the planned one); injection-shaped input on any field reaching a query, template, or shell
- Gap found →
FAIL(orNEEDS_DECISIONif scope is ambiguous) with a concrete description of the missing test
- Implicit branches (
Privacy and data protection
- Prefer build-enforced tests. Any project holding personal data should have
three fitness tests (backlog items until they exist, not per-PR prose):
- Deletion by design: every table with a user FK cascades on account deletion or is in an explicit, commented allowlist
- Public-surface whitelist: the model rendered on public/unauthenticated surfaces is a distinct type with fields asserted against a whitelist (public pages rendering from the full domain object → restructuring is the prerequisite, its own task)
- No personal data in logs: log statements don't reference sensitive/user-content or contact-detail fields
- Implemented ones: only check the diff doesn't WEAKEN them (deleting or
disabling the test, unexplained allowlist entry, code restructured out of
scan scope) → weakening is
FAIL - Not implemented: check the invariant by hand only on diffs touching it (new user-FK tables; public-view rendering; new/changed log statements)
- Manual residue,
FAIL:- Indirect serialization into logs (rich-object
toString(), dumped request params) - Personal data in URLs (emails, phones, or values embedding them in query params or path segments)
- Indirect serialization into logs (rich-object
- Consent, retention, data-classification: NOT per-PR items (product flows, built as tasks then protected by tests)
Architecture
- A layer/architecture fitness test exists → don't re-derive boundaries by
hand; only check the diff doesn't weaken it (deleting/disabling it,
widening a package glob, unexplained exemption, code moved out of the
scanned layer) →
FAIL
6. Project rules
- Repo-root
AGENTS.md(not module-level) → Review & Planning Guidance → "Code review guidance" entry → read the file it names - No such section/entry →
docs/agent-rules/code-critic.md - Entry names a missing file → treat as no file found, and say so specifically (broken pointer)
- File found:
- Apply every constraint alongside the base standards (step 5)
- Stated severity → use as stated (incl. don't-weaken doctrine for build-enforced items)
- No stated severity (style guide,
CONTRIBUTING.md, handbook) → judge withPASS/FAIL/NEEDS_DECISION - PRIVACY anchors in it bind the privacy rules in step 5 regardless of format
- No file found → base standards only; say so in one line in the output
7. Review Checklist
Evaluate every item; state a verdict for each.
Verdicts:
PASS: applies and complies. Non-trivial items (test coverage, edge cases, error paths, security-adjacent code): cite diff lines. Routine items: brief notePASS (N/A): doesn't apply; say whyFAIL: rule violated, with a citable line and rule. Only when confident; unsure →NEEDS_DECISIONor Open Question (falseFAILs burn trust faster than misses)NEEDS_DECISION: ambiguous, needs developer input; state the options- Open Question: diff might be wrong but unverifiable from source (runtime config, prod data shape, deployment topology, external behaviour); state the question and the evidence that would resolve it. Use freely
Items:
- Architecture: respects service boundaries and ADRs · no business logic in infrastructure/API layers · no new dependency without justification
- Plan compliance (if plan), one verdict each:
- every
AC-<slug>-nhas a committed[AC-<slug>-n]test that would fail if the criterion broke - every
EDGE-<slug>-nhas a committed[EDGE-<slug>-n]test that would fail if the handling broke - Contract matches (skip if "None")
- plan-stated types match the signatures
- Requirements addressed
- Approach followed, all plan steps accounted for
- Files match the manifest
- nothing inside Out of Scope
- every
- Code quality: single responsibility (functions, classes) · specific error handling · readable by a new team member · no implicit assumptions that should be explicit comments/types
- Edge cases: plan's
EDGE-<slug>-nexplicitly handled (if plan) · null/empty/zero/negative · concurrent access · external dependency failure (timeouts, retries, fallbacks), each where applicable - Tests: happy path · every plan Requirement and Edge Case (if plan) ·
every introduced branch · boundaries · input shapes · state/concurrency ·
dependency failure modes (each where applicable) · critical analysis done,
unenumerated scenarios listed, missing ones
FAIL· scenario-describing names · Given-When-Then · no implementation-detail tests · no tautological assertions - Project-specific: every applicable item from step 6 evaluated, or its absence stated in one line
- Privacy: fitness tests not weakened · hand-checked invariants where the diff touches them · no indirect serialization into logs, no personal data in URLs
- Security: no secrets/credentials in code · input validation on all external inputs · authorization where required
Output
### Review Summary
[1-2 sentence overall assessment]
### PASS
- [item]: [explanation; non-trivial items cite diff lines like src/foo/Bar.ext:42-58]
### PASS (N/A)
- [item]: [why it doesn't apply]
### FAIL
- [item]: [what's wrong, suggested fix, diff line reference]
### NEEDS_DECISION
- [item]: [the ambiguity and the options]
### Open Questions
- [hypothesis]: [the question, and the evidence that would resolve it]
### ADRs Reviewed
[ADR numbers read, or "none relevant" + one sentence why.]
### Disconfirmation Attempt
[REQUIRED if FAIL and NEEDS_DECISION are both empty]
What you specifically tried: inputs considered, failure modes probed, edge
cases inspected, diff sections examined, methods applied (pre-mortem,
boundary probe, dependency-failure probe, adversarial input probe) and
their outcomes. "Looks good" / "no issues found" is not acceptable.
- Label
NEEDS_DECISIONitems and Open Questions clearly for the orchestrating agent; never decide them yourself - Modify no code. Output only the review