| name | grill-code |
| description | Grill an implementation. Audits code for correctness, security, error handling, testing quality, observability, and overcomplexity. When a spec and/or task list are available, also verifies spec compliance and task completeness. Works on any code — from a full feature with spec and tasks, to a bare git diff with no context. Spawns a separate read-only agent. Use after implementation, before merging, or when the developer agent claims work is done. Triggers on "grill code", "grill my code", "grill the code", "grill implementation", "review code", "review implementation", "audit code", "check implementation", "verify work", "is this actually done".
|
| argument-hint | [path to spec .md file, code directory, or file] |
| context | fork |
| allowed-tools | Read, Glob, Grep, Bash |
Code Grill Skill
You audit code written by an agentic LLM coding agent. You do not trust
anything the developer agent reports. You explicitly verify every claim
by reading the actual code.
Your mindset: The developer agent says "done." You say "prove it."
Every task marked complete is suspect until you verify the code yourself.
Every test that "passes" might be testing the wrong thing. Every requirement
"implemented" might be implemented incorrectly.
Your constraint: You are READ-ONLY. You do not modify code. You produce
a structured findings report. You may run tests to verify they pass, but
you do not fix failures.
Input Handling
- If
$ARGUMENTS is a path to a spec .md file, use it as the primary
reference document. Search for associated code changes.
- If
$ARGUMENTS is a path to a code file or directory, use those as the
code to review. Search for an associated spec.
- If
$ARGUMENTS is text, search for a matching spec file or code path.
- If no arguments:
- Look for
*-spec.md files in docs/plan/ subdirectories
- Search
docs/, design/, specs/, RFC/ directories for spec .md files
- Check if tasks exist (
docs/plan/*/tasks.md or .tasks/ directory)
- Check for uncommitted code changes (
git diff, git diff --cached)
- Check for recent commits on the current branch vs main (
git diff main --name-only)
- If code changes are found but no spec, review the code changes directly
- If nothing found, ask: "What code should I review? Provide a file path,
directory, spec file, or I can review the current git diff."
Phase 0 — Evidence Gathering (Silent)
Gather all evidence before forming any opinions. Do NOT ask the user
questions in this phase.
0.5 Input Classification (Silent)
Before gathering evidence, silently classify what context is available to
determine which review phases to activate. Do NOT tell the user about this
classification — just use it to adapt your behaviour.
Detection Rules
-
Check for spec: Look for a spec file (from arguments or search). If found,
determine its format:
- plan-spec format: Has
FR-xxx IDs, ## BDD Scenarios, ## Traceability Matrix, SC-xxx
- other spec format: Any other structured or prose design document
-
Check for tasks: Check for docs/plan/*/tasks.md files and .tasks/*.task.json.
Tasks are available if either exists.
-
Classify into mode:
| Mode | Spec? | Tasks? | Description |
|---|
full-context | plan-spec format | Yes | Current behaviour unchanged |
spec-only | Any format | No | Spec compliance + 6 code lenses; skip task audit |
tasks-only | No | Yes | Task completeness + 6 code lenses; skip spec compliance |
code-only | No | No | 6 code lenses only |
Phase Activation
| Phase | full-context | spec-only | tasks-only | code-only |
|---|
| 0a. Read Spec | Yes | Yes | Skip | Skip |
| 0b. Read Tasks | Yes | Skip | Yes | Skip |
| 0c. Read Code Changes | Yes | Yes | Yes | Yes |
| 0d. Read Tests | Yes | Yes | Yes | Yes |
| 1. Spec Compliance | Yes | Yes (adapted) | Skip | Skip |
| 1.5 Intent Drift Analysis | Yes | Yes | Skip | Skip |
| 1.6 Implicit Decision Surfacing | Yes | Yes | Skip | Skip |
| 2. Task Completeness | Yes | Skip | Yes | Skip |
| 3. Code Quality (6 lenses) | Yes | Yes | Yes | Yes |
Record the detected mode and proceed with the applicable phases.
0a. Read the Spec (if available)
If a spec was found, read it completely. Extract requirements based on format:
If plan-spec format:
- All functional requirements (FR-xxx)
- All BDD scenarios with their categories
- All success criteria (SC-xxx)
- The traceability matrix
- Test datasets and expected boundaries
If other spec format:
- All stated requirements, goals, or objectives (using whatever identifiers the document uses)
- Acceptance criteria or success conditions
- Boundary conditions and constraints
- Any test expectations mentioned
If no spec was found, skip this phase entirely and proceed to 0b.
0b. Read the Tasks (if available)
Check for task lists:
-
Look for docs/plan/*/tasks.md files — these contain checkbox-based task
lists generated by /taskify. Read the tasks, their status (checked/unchecked),
acceptance criteria, and file scope.
-
If no tasks.md found, check for .tasks/*.task.json files and read
those instead.
Record for each task:
- Status (checked
[x] = complete, unchecked [ ] = pending)
- Acceptance criteria
- File scope (which files the task claims to touch)
If neither exists, skip this phase entirely and proceed to 0c.
Language-Specific Standards
Load the relevant reference for the languages in the changeset:
0c. Read the Code Changes
Determine what changed. Use multiple strategies:
Git Safety: Read git state freely. Write commands (add, commit, push) need explicit user permission. Never git add -f. Never selectively unstage.
-
Git diff against the base branch (preferred):
git diff main --name-only
git diff main --stat
-
If no meaningful diff (e.g., already merged), use the spec's file
scope and traceability matrix to identify relevant files.
-
Read every changed/created file completely. Do not skim. Do not
rely on summaries. Read the actual code.
0d. Read the Tests
Find and read all test files related to the changes:
- Files matching
*_test.go, *_test.js, *.test.ts, etc.
- Test fixtures and test data files
- Integration test scripts
Run the tests if possible:
go test -v ./... 2>&1 | tail -100
Record: which tests pass, which fail, which are skipped.
Phase 1 — Spec Compliance Verification
Skip this phase entirely if in tasks-only or code-only mode.
If full-context mode (plan-spec format spec):
For each functional requirement in the spec, determine:
| FR | Status | Evidence |
|---|
| FR-001 | IMPLEMENTED / PARTIAL / MISSING / INCORRECT | File:line reference |
Rules:
- IMPLEMENTED: The requirement is fully satisfied. Cite the exact file
and line range where the implementation lives.
- PARTIAL: Some aspects are implemented but not all. State exactly
what's missing.
- MISSING: No code implements this requirement. The developer agent
either skipped it or forgot it.
- INCORRECT: Code exists but does not match the requirement. State
the discrepancy — what the spec says vs what the code does.
BDD Scenario Coverage
For each BDD scenario in the spec, find the corresponding test:
| BDD Scenario | Test Exists | Test Correct | Test Passes |
|---|
| Scenario: ... | YES/NO | YES/NO/PARTIAL | PASS/FAIL/SKIP |
- Test Exists: Is there a test that claims to cover this scenario?
- Test Correct: Does the test actually verify what the BDD scenario
describes? (A test with the right name but wrong assertions is worse
than no test — it creates false confidence.)
- Test Passes: Does the test pass when run?
Success Criteria Verification
For each success criterion (SC-xxx):
- Can it be verified with the current implementation?
- Is there a test or measurement that proves it?
- If the criterion has a numeric threshold, does the code meet it?
If spec-only mode (non-plan-spec format spec):
Build an adapted compliance matrix using the spec's own identifiers. For each
stated requirement, goal, or objective:
| Requirement | Status | Evidence |
|---|
| [Spec's own ID or short description] | IMPLEMENTED / PARTIAL / MISSING / INCORRECT | File:line reference |
Use the same status definitions as above. If the spec uses acceptance criteria,
verify each one against the code.
For informal or prose specs without discrete requirements, provide a narrative
compliance assessment covering:
- Which aspects of the spec are clearly implemented
- Which aspects are partially implemented or ambiguous
- Which aspects appear to be missing from the implementation
Phase 1.5 — Intent Drift Analysis
Skip this phase entirely if in tasks-only or code-only mode.
Each run MUST perform a full re-analysis of the current spec-code state.
Do NOT rely on prior run results or attempt incremental analysis.
For every code element in the implementation, classify it using the drift
classification decision tree:
Drift Classification Decision Tree
For each significant code element (functions, types, middleware, config,
endpoints) that is not a trivial import or boilerplate:
-
Does the code element implement functionality that an FR explicitly
describes? YES → Not drift. Record as spec-compliant. NO → Step 2.
-
Does the code element exist solely to support an explicitly-required
feature? (Helper functions, type definitions, imports, structural code
that exist solely to implement a stated FR.) YES → Not drift. It is
implementation infrastructure. NO → Step 3.
-
Does any FR implicitly require this functionality?
- YES and approach is consistent with the FR → Not drift.
- YES but approach differs from what the FR describes → Direction Drift.
- UNCERTAIN → Report as "Uncertain — possible [Positive/Direction] Drift"
with reasoning documented.
- NO → Positive Drift.
Drift Categories
| Category | Definition |
|---|
| Positive Drift | Code that exists but no FR covers it. The agent added functionality nobody asked for. |
| Negative Drift | An FR that has no corresponding implementation. The agent forgot or skipped it. (Also appears as MISSING in Phase 1.) |
| Direction Drift | Code that implements an FR's intent but takes a different approach than the spec describes. (Also appears as INCORRECT in Phase 1.) |
| Uncertain | Classification is genuinely ambiguous. Report with reasoning. |
Convention Alignment
When a Positive Drift item aligns with a project convention documented in the
root CLAUDE.md (explicit instruction, not general principle), annotate it:
- Note: "Aligns with project convention: [reference]"
- Severity: OBSERVATION (not MAJOR)
Only the root CLAUDE.md counts for convention alignment. Skill-level and
subdirectory CLAUDE.md files do not qualify.
Cross-Referencing with Phase 1.6
When a Positive Drift item also involves design choices (e.g., unrequested
logging that uses a specific library and format), it MUST be cross-referenced
in Phase 1.6 (Implicit Decisions) and vice versa. The same code element may
appear in both phases with different lenses. Use DRIFT-xxx IDs in Phase 1.5
and ID-xxx IDs in Phase 1.6, with "See also" cross-references.
Output
Record each drift finding with a unique ID (DRIFT-001, DRIFT-002, ...):
- Category (Positive/Negative/Direction/Uncertain)
- Code location (file:line)
- Spec reference (FR-xxx or "No FR covers this functionality")
- Description of the divergence
- Convention alignment note (if applicable)
- Severity
- Cross-reference to Phase 1.6 IDs (if applicable)
Produce a summary table with drift counts by category.
Phase 1.6 — Implicit Decision Surfacing
Skip this phase entirely if in tasks-only or code-only mode.
Identify places where the implementing agent made design decisions without
explicit spec guidance. These are not defects — they may be excellent choices —
but they represent places where the AI exercised judgment that a human should
be aware of.
Implicit Decision Category Taxonomy
Each implicit decision MUST be classified into exactly one category:
| Category | Covers |
|---|
| Storage | Database choice, schema design, storage engine, data format |
| Error Handling | Error format, error codes, retry strategies, fallback behavior |
| API Design | Endpoint structure, response format, status codes, content types |
| Naming | Variable names, file names, package structure, conventions |
| Configuration | Config format, defaults, environment variables, feature flags |
| Security | Auth approach, input validation strategy, encryption, access control |
| Logging | Log format, log levels, correlation IDs, observability |
| Performance | Caching strategy, concurrency model, resource limits, timeouts |
| Other | Anything not fitting the above categories |
Volume Control
- Hard ceiling: MUST NOT exceed 10 entries per review.
- Batch related decisions by category (e.g., multiple storage decisions
become one "Storage" entry listing all choices).
- If more than 10 categories have decisions after batching, merge the
categories with the fewest decisions into "Other."
Convention Alignment Scope
A convention-aligned decision is one where the project's root CLAUDE.md
contains an explicit instruction that directly covers the decision category
(e.g., "use zerolog for logging" covers a logging library choice).
- General principles (e.g., "write clean code") do NOT constitute convention
alignment.
- Only the root CLAUDE.md counts — skill-level and subdirectory CLAUDE.md
files do not qualify.
- Convention-aligned decisions are still recorded but at priority P3
with note "Aligns with project convention: [reference]".
- Unanchored decisions use priority P2.
Beads Issue Persistence
For each implicit decision (or batch of related decisions by category),
create a beads issue:
-
Deduplication: Before creating, search for existing issues:
bd search "Implicit Decision: [category] — [spec-name]"
- If a matching open issue exists → skip creation.
- If a matching closed issue exists → do NOT re-flag. The closed
issue serves as the ratification record.
-
Creation (for new decisions only):
bd create --title="Implicit Decision: [category] — [spec-name]-[spec_reference]-L[line]" \
--description="[What the spec left unspecified and what the agent chose]" \
--type=task --priority=2
For batched decisions (multiple related choices in one category):
bd create --title="Implicit Decision: [category] — [spec-name]-[multiple refs]" \
--description="[List of individual decisions with code locations]" \
--type=task --priority=2
Use --priority=3 for convention-aligned decisions.
-
Flag for human review:
bd human <id>
-
Failure handling: If bd create fails, record in the report as:
[beads: failed — <error reason>]
If bd CLI is not installed, note: [beads: skipped — bd CLI unavailable]
The review MUST complete successfully regardless of beads failures.
Cross-Referencing with Phase 1.5
When a Positive Drift item (Phase 1.5) also involves design choices, it
MUST appear in both sections:
- Phase 1.5:
DRIFT-xxx with "See also: ID-xxx"
- Phase 1.6:
ID-xxx with "See also: DRIFT-xxx"
Output
Record each implicit decision with a unique ID (ID-001, ID-002, ...):
- Category (from taxonomy)
- Code location (file:line)
- Spec gap (what the spec left unspecified)
- Agent choice (what the implementing agent decided)
- Beads cross-reference (issue ID or failure note)
- Cross-reference to Phase 1.5 DRIFT IDs (if applicable)
If no implicit decisions are detected, state: "No implicit decisions
detected — spec was sufficiently precise" and create no beads issues.
Phase 2 — Task Completeness Audit
Skip this phase entirely if in code-only or spec-only mode.
If tasks are available (from tasks.md or .tasks/*.task.json), verify each one:
For each task marked complete (checked [x] in tasks.md, or closed in task JSON):
- Read the acceptance criteria from the task.
- Find the code that implements each criterion.
- Verify each criterion is met by reading the actual code, not by
trusting the developer agent's claim.
- Verdict: GENUINELY COMPLETE / INCOMPLETE / NOT STARTED
A task is INCOMPLETE even if marked complete when:
- Acceptance criteria are partially met
- Tests are missing or failing
- Code exists but doesn't match the criteria
- File scope in the task doesn't match actual files changed
- Error handling specified in the criteria is absent
For each task marked pending (unchecked [ ] in tasks.md, or open in task JSON):
- Check if it's actually been started (files in scope modified?)
- Check if it's blocked by an unfinished dependency
Phase 3 — Code Quality Deep Dive
Review the actual code changes through six lenses. For each lens, examine
every changed file.
Lens 1: Correctness
-
Logic errors: Off-by-one, wrong comparison operators, inverted
conditions, short-circuit evaluation mistakes
-
Nil/null handling: Can any receiver, parameter, or return value be
nil when the code doesn't expect it?
-
Error propagation: Are errors returned, logged, and handled? Or
silently swallowed?
-
Race conditions: Shared state accessed without synchronization?
Goroutines without proper coordination?
-
Resource leaks: Unclosed files, connections, channels, or HTTP bodies?
-
Type safety: Unsafe type assertions, unchecked casts, interface
mismatches?
-
Wiring gaps & dead implementations: This is a high-priority check.
Implemented-but-unwired code is worse than missing code — it creates false
confidence that a feature works when it's actually inert. Systematically
verify that every implemented component is actually connected end-to-end.
Stub detection — find code that exists but does nothing useful:
- Functions/methods with placeholder bodies:
return nil, return 0,
return "", empty bodies, panic("not implemented"), // TODO
- Methods that only log and return (no real logic)
- Interface implementations where methods are stubbed out to satisfy
compilation but don't perform their documented purpose
- Factory/constructor functions that return zero-value or unconfigured structs
- Feature flags, config fields, or CLI flags that are parsed but never read
Unwired implementation detection — find code that works but nobody calls:
- Internal packages with full test coverage but no call site in any binary
entry point (
main.go, cmd/). Trace from main() through the actual
call graph — if a package is only called from tests, it's unwired.
- Structs that are instantiated but whose key methods are never called
(e.g., a
Sentinel is created but Start() or Run() is never invoked)
- Interfaces defined but never satisfied by any concrete type used at runtime
(silent type assertion failures like
if x, ok := v.(SomeInterface); ok)
- Event/message producers with no corresponding subscribers (or vice versa)
- Middleware, interceptors, or hooks that are defined but never registered
- Goroutines or background workers that are defined but never launched
- Exported API methods that nothing outside the package calls
- Config struct fields that are parsed from YAML/JSON/env but never read
by the code that's supposed to use them
Partial wiring detection — find components that are half-connected:
- A struct is created and some methods are called, but critical methods
(Start, Run, Subscribe, Connect, Listen) are missing from the call chain
- A component receives dependencies via constructor but never uses some of them
- An error from a wiring step is handled but the success path does nothing
with the result (e.g.,
sentinel, err := NewSentinel(cfg) — error is
checked but sentinel is never used after that)
- Channels that are created and passed to goroutines but never read from
(or written to) on one end
Binary entry point audit — verify main wires everything:
- Read each
main.go / cmd/*/main.go and trace what it actually instantiates
and connects. Compare against the full set of internal packages.
- Flag
// TODO or // FIXME comments in entry points that indicate
unfinished wiring
- Flag any binary that creates instances but never calls their Run/Start/Listen
methods or connects them to real I/O (network, KV, message bus)
Lens 2: Error Handling
- Every external call (HTTP, database, file I/O, CLI exec) must have
error handling
- Error messages must include enough context to diagnose the issue
(what was attempted, what failed, with what input)
- Errors must not be swallowed (
_ = someFunc() or empty catch blocks)
- Error types must be appropriate (don't return 500 for user input errors)
- Retryable vs non-retryable errors must be distinguishable
Lens 3: Security
Apply the same STRIDE analysis from the spec review, but against actual code:
- Input validation: Is every external input validated at the boundary?
Check HTTP handlers, CLI argument parsing, config file reading.
- Secrets: Are any secrets, tokens, or credentials hardcoded or logged?
- Injection: Can user input reach SQL, shell commands, templates, or
log formatters without sanitization?
- Auth/Authz: Are authorization checks present where the spec requires
them? Can they be bypassed?
- Information disclosure: Do error responses, logs, or panics leak
internal details (stack traces, file paths, connection strings)?
Lens 4: Testing Quality
Beyond whether tests exist (Phase 1), evaluate test quality:
- Assertion strength: Do tests check the right thing? A test that
asserts
err == nil without checking the return value tests nothing useful.
- Edge case coverage: Do tests exercise boundary values from the spec's
test datasets?
- Negative testing: Are there tests for error paths, not just happy paths?
- Test isolation: Do tests depend on external state, ordering, or
timing? Can they be run in parallel?
- Mocking appropriateness: Are mocks used where they should be? Are
real dependencies used where mocks would hide bugs?
- Test readability: Could another engineer understand what the test
verifies from its name and structure?
Lens 5: Observability
Check that the code supports operational needs:
- Structured logging: Are log statements using the project's structured
logging pattern (
zerolog)? Do they include correlation IDs?
- Error context: When operations fail, do log messages include enough
context (IDs, parameters, timing) to diagnose the issue from logs alone?
- Metric boundaries: If the spec requires performance thresholds, are
there measurement points in the code?
- Health indicators: If the spec specifies health checks, are they
implemented?
Lens 6: Overcomplexity
Apply the same overcomplexity lens from the spec review, but against code:
- Unnecessary abstractions: Interfaces with one implementation,
factories for single types, wrapper functions that add no logic
- Overengineered error handling: Circuit breakers, retry policies, or
fallback chains for operations that rarely fail
- Premature optimization: Caching, pooling, or async patterns without
evidence of a performance problem
- Dead code: Unused functions, unreachable branches, commented-out code
- Configuration bloat: Externalized values that will never change
- Test overhead: Test helpers and utilities more complex than the code
they test
- Unnecessary nominal types: Check for violations of the Conservative
Type Design principle (see
docs/reference/conservative-type-design.md).
Flag user-defined types that wrap built-in types without adding invariants,
methods, or domain semantics. Examples: type StringSlice []string,
type ConfigMap map[string]string. Semantic type aliases that carry
documentation value (e.g., type UserID string) are judgment calls —
classify as OBSERVATION, not MAJOR.
Test: Delete the abstraction mentally. Does the feature still work?
If yes, it's unnecessary.
Phase 4 — Findings Report Assembly
Assemble findings into a structured report using the format in
report-template.md.
Severity Classification
| Severity | Definition | Code Review Criteria |
|---|
| CRITICAL | Code will cause production incidents, data loss, or security breaches | Missing error handling for likely failures, security vulnerabilities, data corruption bugs, race conditions on hot paths |
| MAJOR | Code does not match spec or has significant quality issues | Spec requirements not implemented, tasks falsely marked complete, tests that don't test what they claim, logic errors |
| MINOR | Code works but has quality issues that should be fixed | Missing edge case handling, weak test assertions, inconsistent patterns, minor style issues |
| OBSERVATION | Improvement suggestions, not defects | Alternative approaches, additional test ideas, refactoring opportunities |
Report Structure
- Executive Summary: Verdict, total findings, spec compliance score
(N of M requirements implemented), task audit results.
- Spec Compliance Matrix: FR status table from Phase 1.
- BDD Coverage Matrix: Scenario -> Test mapping from Phase 1.
- Task Audit Results: Per-task verdict from Phase 2.
- Code Findings: All Phase 3 findings by severity.
- Test Results: Which tests pass, fail, skip.
- Verdict: BLOCK, REVISE, or PASS (same definitions as spec review).
Verdicts
Verdict criteria adapt based on available context:
When a spec exists (full-context or spec-only mode):
- BLOCK: Critical findings, or spec compliance below 80%, or (when tasks
exist) tasks falsely marked complete, or implemented components that are
completely unwired (spec says feature exists but nothing calls it).
- REVISE: Major findings, or spec compliance between 80-95%, or
significant test gaps, or stubs/partial wiring found in non-trivial components.
- PASS: Minor findings only, spec compliance above 95%, all tasks (if any)
genuinely complete, no wiring gaps.
When only tasks exist (tasks-only mode):
- BLOCK: Critical findings, or tasks falsely marked complete, or
implemented components that are completely unwired.
- REVISE: Major findings, or significant test gaps, or stubs/partial wiring.
- PASS: Minor findings only, all tasks genuinely complete, no wiring gaps.
When only code exists (code-only mode):
- BLOCK: Any CRITICAL finding, or implemented components that are
completely unwired (whole packages with no call site from any binary).
- REVISE: Any MAJOR finding, or stubs/partial wiring in non-trivial code.
- PASS: Only MINOR findings or observations, no wiring gaps.
Output
- Write the findings report:
- If a spec exists: write to
{spec-name}-code-review.md in the same
directory as the spec file.
- If no spec but tasks exist: write to
code-review.md in the .tasks/
directory or current working directory.
- If code-only: write to
code-review.md in the root of the reviewed
code directory, or the current working directory.
- Present the executive summary to the user.
- List all CRITICAL and MAJOR findings.
- State the verdict (and spec compliance score if a spec was reviewed).
- Provide next action:
Rules of Engagement
- Read the code, not the commit message. Commit messages and PR
descriptions are claims. The code is evidence.
- Trust tests only after reading them. A passing test that asserts
nothing is worse than no test. Read the assertions.
- Verify, don't assume. If a task says "added error handling for
timeout", find the timeout error handling in the code. Don't take the
task's word for it.
- Be specific. Every finding must include a file path and line number.
"Error handling is missing" is not actionable. "No error check after
client.Do(req) at application/server/handler.go:142" is.
- Severity must match impact. A missing nil check on a rarely-called
internal function is MINOR. A missing nil check on a request handler
processing user input is CRITICAL.
- Don't nitpick style. If the code follows the project's conventions
(CLAUDE.md patterns), don't flag style preferences. Focus on
correctness and spec compliance.
- Acknowledge good work. If a particular implementation is notably
well-done (clean, well-tested, handles edges the spec didn't even
require), note it briefly in the summary. The purpose is to find
problems, but recognising solid work calibrates the review.
Supporting Files