Skip to content
Back to skills

Review

ASecurity

Use when a ClaudeHut light- or full-route task is implemented and about to be called done - dispatches the applicable ClaudeHut auditors, gets fresh test evidence, allows at most two fix rounds, and records the verdict. Not for reviewing an arbitrary PR or diff.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 1, 2026
ai-agentsgojavaspringgitsecurity

Works with

  • mcp

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add taipt1504/claudehut --skill review --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Review?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Review
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/taipt1504-review/badge)](https://www.skillsdirectory.com/skills/taipt1504-review)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: review
description: Use when a ClaudeHut light- or full-route task is implemented and about to be called done - dispatches the applicable ClaudeHut auditors, gets fresh test evidence, allows at most two fix rounds, and records the verdict. Not for reviewing an arbitrary PR or diff.
---

# Review (phase 6 of 7)

Prove the change is done against the enforcement set, the project rules and fresh test evidence. Runs on the
main thread: it picks the lanes, verifies findings and owns the `set-review` write. A pass needs auditors that
ran this turn and a `review.md` on disk; "should pass" is not evidence.

## Flow

```mermaid
flowchart TB
  s(["Review"]) --> pk["review-pack.sh --json<br/>lanes.json + one SHA-pinned pack per lane"]
  pk --> big{"ask_user?"}
  big -- yes --> ask[/"AskUserQuestion with ask_reason"/]
  big -- no --> sel["accept or override lanes<br/>one reason line per change"]
  ask --> sel
  sel --> fan["ONE message: dispatch lanes in parallel<br/>prompt = pack path + depth"]
  fan --> esc{"reviewer escalates a lane not run?"}
  esc -- yes --> add["dispatch that lane once"] --> ded
  esc -- no --> ded["dedup: file, ±3 lines, defect class"]
  ded --> ver["open file:line of each CRITICAL/HIGH<br/>run read-only checks for Suspected"]
  ver --> ok{"outstanding empty and tests green?"}
  ok -- "no, round 1" --> fix["write review.md r1 → fix via claudehut:implement<br/>→ review-pack.sh --round 2 --prev review.md"] --> fan
  ok -- "no, round 2" --> cap(["set-review capped"])
  ok -- yes --> rec["review.md"] --> pass(["set-review pass"])
```

## 1. Build the packs

Read the task first; the profile picks the branch below:

```
{ claudehut-state --session ${CLAUDE_SESSION_ID} status 2>/dev/null || echo '{}'; } | jq -c '.task // {} | {profile, enforcement_set}'
```

```
"${CLAUDE_PLUGIN_ROOT}/scripts/review-pack.sh" --json [--route light|full] [--task <id>]
```

The script computes the diff base (active task: `task.base[repo]` minus `pre_dirty`; no task: merge-base),
snapshots the tree without touching the index or worktree, and writes `lanes.json` plus one pack per lane
(≤1,500 lines) under `tasks/<id>/review/`, or a temp dir outside a workflow. Each pack's `## Rigor` section is
`references/review-rigor.md` copied verbatim; the pack also carries the lane's enforcement items, known
pitfalls, `## Summer KB` when the diff touches Summer wiring, and for the reviewer the vocabulary, reuse
suspects and (light route) the test command.

`lanes.json`: `{base, head_tree, lanes:[{lane, agent, reasons, depth, pack, lines}], skipped:[{lane, reason}], hints, ask_user, ask_reason}`.

Lane defaults by route: **light** → reviewer (tests folded in); **full** → reviewer + test; security, db
(incl. perf) and contract (incl. observability) only when a path or hunk signals them; enforcement items only ride in packs and `hints`.
**direct** → no review unless the user asks; then run it as an out-of-workflow review (below).

If the script is missing or prints no JSON, dispatch the reviewer (plus test-runner on full) with the changed
files from `git diff --name-only $(git merge-base HEAD @{u} 2>/dev/null || git merge-base HEAD origin/HEAD 2>/dev/null || git merge-base HEAD origin/main 2>/dev/null || echo HEAD~1)`,
and note `degraded` in `review.md`; likewise per lane on `degraded: true` or `pack: ""` (fallback file list, no pack).

**Profile branch:** on `audit`/`investigation` the deliverable is `findings.md`: dispatch the reviewer and the
security-auditor with the pack path plus the `findings.md` path, and skip the test lane unless the pack's files
include production code (`src/main/**`).

## 2. Select and dispatch

- Accept `lanes[]` as the default. Adding or dropping a lane is allowed; write one reason line per change.
- **`$ARGUMENTS` NARROWS, never widens:** named aspects (`security`, `db`, `contract`, `tests`; aliases
  `perf`→db, `observability`→contract) keep only those lanes plus the reviewer; with no argument the rule-driven selection above is unchanged.
- `ask_user: true` → AskUserQuestion once with `ask_reason` (typical options: split by paths | all lanes |
  reviewer + security). Also ask for a CRITICAL `pre-existing` finding (fix in this task or record
  separately) and for an `uncovered` file that an external plugin could review at real cost.
- Dispatch every selected lane in ONE message by qualified type (`claudehut:<agent>`), no `model` parameter.
  The prompt is the pack path, `depth: standard|deep` (deep when the lane carries an enforcement item or
  security touches auth) and the session's language line verbatim. Do not paste the diff; the pack holds it.
- **One test source per route:** full → the test lane (`claudehut-test-runner`), and the reviewer prompt adds
  "Do not run build/test". Light → no test-runner; the reviewer runs the pack's `## Test command`.
- **External lane (opt-in):** only for `uncovered` files or on user request, dispatch another plugin's
  agent/skill by its namespaced name; its findings enter the table as lane `ext:<name>` and go through the
  same dedup and verification.
- An auditor output without a Verdict (for example maxTurns reached) → mark the lane `incomplete`; do not
  re-dispatch it. Bounce only an output missing a Coverage row for an enforcement item of its own lane.
- A reviewer line `escalate: <lane> — File:NN` for a lane not run, or `partial` on a file in its `uncovered`
  (`lanes.json`) → dispatch that lane's agent once, the reviewer's pack path and escalate line as its focus.

## 3. Dedup and verify

1. **Dedup** on (file, ±3 lines, defect class); keep the highest severity and count reporters.
2. **Verify** on the main thread: open `file:line` for every CRITICAL/HIGH and confirm it before it enters
   outstanding.
3. **Pre-existing** is a verdict: tag it only when `git show <base>:<path>` has the same defect. A guard the diff
   removes or loosens stays a finding. Pre-existing does not block.
4. **Suspected:** run the read-only check yourself when the session has a matching MCP (DB, Kafka); otherwise
   record the item as inferred.
5. **Tie-break** only when you cannot decide: one `claudehut:claudehut-reviewer` with `mode: verify` and the
   whole candidate list, once. It counts against the 2-round cap.
6. **Reuse suspects:** each suspect in the reviewer's pack needs a resolution in its Coverage row (confirm as
   `✗`, or `resolved` / `false-positive: <reason>`); `set-review pass` refuses an unresolved one.
7. Merge surviving outstanding (every `✗` at MED+ not justified-and-deferred):

   ```
   claudehut-state --session ${CLAUDE_SESSION_ID} set-outstanding '["framework/jpa.md: N+1 in OrderService — OrderService.java:42"]'
   ```

## 4. review.md

Write `.claude/claudehut/tasks/NNNN-<slug>/review.md` with these sections:

- **Header line** — `round: N · base: <sha> · head_tree: <sha>` from `lanes.json`.
- **Lanes** — `| Lane | Agent | Run | Result | Reason |`; Run ∈ `ran | skipped | added | dropped | incomplete`,
  Result ∈ `PASS | OUTSTANDING (n) | —`. `review-pack.sh --prev` reads this table: lanes with
  `OUTSTANDING` carry into round 2. Write words here, not `✓`.
- **Findings** — `| Severity | file:line | Quote | Lane | Reporters | Status |`.
- **Coverage** — the merged rows: `| item | ✓ satisfied / ✗ violated | File.java:NN "quote" |`. Every `✓` row
  names a locus; the reviewer's floor rows keep this section non-empty on every route.
- **Pre-existing** · **Tests** (exact command and counts, e.g. `./gradlew test — 42 passed`) ·
  **Deferrals** (each MED with its justification) · **Verdict**.

## 5. Round 2 and exit

- Round 1 not clean: write `review.md` for round 1, fix through `claudehut:implement`, then
  `review-pack.sh --json --round 2 --prev <review.md> --base <round-1 head_tree>`. Its lanes are the lanes
  still `OUTSTANDING` plus the lanes the fix diff triggers (including new untracked `src` files); the test lane
  re-runs when the fix touches source. Dispatch those and rewrite `review.md` as round 2. Judge `pre-existing`
  against the round-1 header `base:`; a round-1 finding that still reproduces stays open.
- **Round cap — 2 rounds.** No third round: `set-review capped`, surface the surviving items and what was tried.

## Exit

`outstanding == []` and tests green →

```
claudehut-state --session ${CLAUDE_SESSION_ID} set-review pass --evidence .claude/claudehut/tasks/NNNN-<slug>/review.md
```

`set-review pass` refuses a `review.md` without a coverage table, a test line with counts, a locus on each `✓`
row, or a resolution for each reuse suspect. A task that skips Learn (a light task with nothing novel, or an
audit/investigation stopping at `set-findings`) ends here: `claudehut-state --session ${CLAUDE_SESSION_ID} end --status done`.

**Out-of-workflow review** (no active task, or a direct-route request the user asked to review): run
`review-pack.sh --json` without `--task`, dispatch as above, and return the findings in chat. No `review.md`,
no `set-review`.

## Test evidence

Judge the test choice against the **cheapest test that proves the behavior**, and reject a test that proves less
than it claims: Testcontainers rather than an embedded fake, `@SpringBootTest` only as a last resort, and no
`Thread.sleep` for async (Awaitility / `StepVerifier`).
**`references/test-matrix.md` is the ladder — read it before judging a test choice, and read the SLICE, not the whole file:** `references/test-matrix.md#web-slice-mvc`, `references/test-matrix.md#web-slice-webflux`, `references/test-matrix.md#async-without-sleep`.

**Java symbol lookups:** use the LSP tool (`findReferences`, `goToDefinition`), not grep — it finds the *symbol*, so it catches an implementation reached through an interface and ignores the name in a comment. Diagnostics are off here: build and tests stay the only signal for type errors.

## Red flags

- A completion claim before the lanes ran this turn, or with a non-empty outstanding set
- A `✓` row without a `file:line` and quote, a missing in-lane row, or a claim inferred from a name
- The diff pasted into a dispatch prompt, or two test sources on the full route
- Downgrading a plausible correctness or perf defect to LOW to avoid blocking

**Next:** `claudehut:capture-learnings` — unless the task ended at *Exit*.

Files in this skill

  • SKILL.md10.1 KB
  • references/review-rigor.md3.5 KB
  • references/test-matrix.md3.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.