Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ Please choose versions by [Semantic Versioning](http://semver.org/).
* MINOR version when you add functionality in a backwards-compatible manner, and
* PATCH version when you make backwards-compatible bug fixes.

## Unreleased

- feat!: selector mode is now the DEFAULT for `/coding:pr-review` and `/coding:code-review` (was opt-in via `--selector`/`selector` token); callers passing `standard` now get selector behavior — BREAKING CHANGE for any tooling that relied on `standard` triggering per-owner Task dispatch
- feat!: standard-mode per-owner Task dispatch removed — `owners_to_spawn` computation, per-owner Task prompt template, `funnel clean — no adjudication needed` standard-mode short-circuit, and `REVIEW_TIMING` per-owner instrumentation block deleted from both commands; full mode remains the per-owner deep sweep escape hatch
- docs: scenarios/002 and scenarios/004 retired (status: outdated) — superseded by scenarios/005 and scenarios/006 respectively; footer updated with retirement reason
- chore: acceptance.sh updated to assert the new dispatch reality: Selector mode (default) routing check added (section 1), per-owner block check tightened to full-mode-only assertions (section 2)

## v0.21.0

- fix: ast-grep-runner.sh excludes `.git/` paths from both input file list and findings output — incident 2026-06-11 surfaced 274 stale agent-auditor findings from `.git/COMMIT_EDITMSG` during selector validation
Expand Down
102 changes: 41 additions & 61 deletions commands/code-review.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
allowed-tools: Task, Bash(git diff:+), Bash(git log:+), Bash(git status:+), Bash(git ls-files:+)
argument-hint: "[short|standard|full|selector] [directory]"
argument-hint: "[short|full|selector] [directory]"
description: Perform a comprehensive code review of recent changes
---

Expand All @@ -19,9 +19,8 @@ Perform a code review with configurable depth based on mode.

Parse the first argument to determine mode:
- If first arg is `short|quick|fast` → **Short mode** (manual review only)
- If first arg is `full|comprehensive|complete` → **Full mode** (all 13 agents)
- If first arg is `--selector` or `selector` → **Selector mode** (in-session classify + adjudicate, zero sub-agent spawns)
- Otherwise → **Standard mode** (4 core agents, default)
- If first arg is `full|comprehensive|complete` → **Full mode** (all agents, per-owner dispatch)
- Otherwise (including `standard`, `selector`, `--selector`, or no token) → **Selector mode (default)** (in-session classify + adjudicate, zero sub-agent spawns)

Any remaining arguments are treated as the directory path.

Expand Down Expand Up @@ -53,7 +52,7 @@ Store result for later:

**3b. Run make precommit (Full mode only)**

Running the full test suite is CI's job; the review needs the result, not a re-run. In **Standard** and **Short** mode, skip this step entirely and include in the Step 5 report: "precommit skipped (standard mode) — CI covers lint+test".
Running the full test suite is CI's job; the review needs the result, not a re-run. In **Selector** and **Short** mode, skip this step entirely and include in the Step 5 report: "precommit skipped (selector mode) — CI covers lint+test".

**Full mode only**: Check if Makefile exists and has a `precommit` target:
1. Use Read tool to check if Makefile exists (will error if missing)
Expand All @@ -70,11 +69,11 @@ If Makefile doesn't exist or lacks `precommit` target, skip this step. If `make

### Step 4: Dispatcher — ast-grep funnel → findings-scoped LLM adjudication

Mirrors `commands/pr-review.md` Step 4. The funnel runs first (diff-scoped), then adjudication Tasks are spawned only for owners with findings or active judgment rules. **Standard mode**: zero LLM spawns when the funnel is clean and no judgment rules are active. **Full mode**: keeps today's behavior (all relevant owners + conditional agents).
Mirrors `commands/pr-review.md` Step 4. The funnel runs first (diff-scoped), then adjudicates findings in-session. **Selector mode (the default)**: in-session classify + adjudicate, zero sub-agent spawns. **Full mode**: keeps per-owner dispatch (all relevant owners + conditional agents). **Short mode**: skips Step 4 entirely.

**Short Mode**: No agents — skip to Step 5.

**Early exit (standard mode)**: if NO changed file has extension `.go` or `.py` AND none matches `CHANGELOG.md`, `go.mod`, `LICENSE*`, `README.md`, `Makefile`, `pyproject.toml`, `k8s/**`, `agents/**`, `commands/**`, `skills/**`, `docs/**` — the diff cannot match any rule. Skip Step 4 entirely; note "Step 4 skipped: no rule-relevant files changed" in the report. One glance at the diff stat decides this — no tool calls needed.
**Early exit**: if NO changed file has extension `.go` or `.py` AND none matches `CHANGELOG.md`, `go.mod`, `LICENSE*`, `README.md`, `Makefile`, `pyproject.toml`, `k8s/**`, `agents/**`, `commands/**`, `skills/**`, `docs/**` — the diff cannot match any rule. Skip Step 4 entirely; note "Step 4 skipped: no rule-relevant files changed" in the report. One glance at the diff stat decides this — no tool calls needed.
- BUT: if LICENSE missing AND repo is public, add to "Should Fix":
- "Missing LICENSE file"
- "README missing license section" (check with Grep for `## License` in README.md)
Expand Down Expand Up @@ -103,9 +102,7 @@ RUNNER="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/coding}/scripts

Run exactly this one Bash call, once. Emits `{stats, findings_by_owner: {<agent-name>: [...findings]}, errors}` — read it from `/tmp/code-review-findings.json`. Do NOT spawn an agent for this step (the former `coding:ast-grep-runner` agent is deprecated). If the runner is missing or fails: note "mechanical funnel unavailable" for the Step 5 report and continue with Step 4b using judgment-rule triggers only — do NOT investigate (no `find`, no `which`, no path probing).

#### 4b: Findings-scoped adjudication (standard mode)

Compute the active judgment-rule set and dispatch owners selectively:
#### 4b: Findings-scoped candidate computation

**Step 4b-i: Active judgment rules** — compute which judgment-tier rules are triggered by the diff. Run:

Expand All @@ -129,81 +126,62 @@ jq -r --arg files "$CHANGED_FILES" '
' rules/index.json
```

This produces a list of `<rule-id> <owner>` pairs whose trigger globs match at least one changed file. Rules with `trigger: ["@commits"]` are always included.

**Step 4b-ii: Dispatch set** — the set of owners to spawn is:
This produces a list of `<rule-id> <owner>` pairs whose trigger globs match at least one changed file. Rules with `trigger: ["@commits"]` are always included. This output feeds both Selector mode (Steps 4c-sel/4d-sel) and Full mode (per-owner dispatch).

```
owners_to_spawn = (keys of findings_by_owner) ∪ (owners from active judgment rules)
```
#### Selector mode (the default): Steps 4c-sel and 4d-sel

If `owners_to_spawn` is empty AND `findings_by_owner` is empty, report "funnel clean — no adjudication needed" and proceed to Step 5. ZERO LLM spawns.
Steps 4.0, 4a, and 4b-i run unchanged. Resolve the guide and execute Steps 4c-sel and 4d-sel from it — zero sub-agent spawns.

Otherwise, spawn ONE Task per owner in `owners_to_spawn` **concurrently**. Owners NOT in the set are NOT spawned — no exceptions in standard mode. Each Task prompt:
Run exactly this one command, once:

```bash
GUIDE="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/coding}/docs/selector-mode-guide.md"
[ -f "$GUIDE" ] || GUIDE="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/marketplaces/coding/docs/selector-mode-guide.md"
[ -f "$GUIDE" ] && echo "GUIDE_OK: $GUIDE" || echo "GUIDE_MISSING"
```
coding:<owner> agent: "TARGET_DIR=<directory>.

Pre-filtered mechanical findings for you (from ast-grep-runner):
<findings_by_owner[<owner>] JSON, or empty array if none>

Active judgment rules you own (from rules/index.json, triggered by this diff):
<list of rule blocks — id + doc_path + applies_when — for this owner only>
If it prints `GUIDE_MISSING`: report "selector guide unavailable" as a **Must Fix toolchain failure** in Step 5 and STOP the selector path — do NOT continue with a mechanical-findings-only review presented as a complete selector review (a review without the judgment tier silently misses every judgment-tier rule; same fail-fast discipline as Step 4.0). Do NOT investigate further (no `find`, no `ls`, no path probing).

Adjudicate: for each mechanical finding, assign severity (Critical / Important / Optional), add a fix suggestion that cites the rule by ID. Drop any finding whose rule_id is not in the index — stale-walker bug, not your concern.
If it prints `GUIDE_OK`: Read the file at that path, then execute its **Step 4c-sel CLASSIFY** and **Step 4d-sel ADJUDICATE** with:

Also scan the diff for each active judgment rule listed above and report violations you find. Read only changed files relevant to those rules.
- **DIFF** = `git diff HEAD~1` (or directory diff as parsed in Step 1)
- **CANDIDATES** = the Step 4b-i `<rule-id> <owner>` output
- **MECHANICAL_FINDINGS** = `/tmp/code-review-findings.json`
- **Working directory** = the reviewed directory

Review changed code only."
```
On the guide's short-circuit condition the report line is `selector clean — no adjudication needed`. Skip this section for short/full mode.

Run per-Owner dispatches **concurrently** — they're independent.
Include the traceability section per `docs/selector-mode-guide.md` § Traceability Report Section.

**Timing instrumentation** (mirror of `commands/pr-review.md` Step 4b): **Only when `REVIEW_TIMING=1` is set in the environment** — otherwise skip this instrumentation entirely. Record wall-time of each per-Owner dispatch as a structured event so the funnel's per-Owner ROI is measurable, not anecdotal:
#### Full mode: per-owner dispatch

```bash
ts_start=$(date +%s%3N)
# ... invoke coding:<owner> agent ...
ts_end=$(date +%s%3N)
echo "{\"event\":\"per_owner_adjudication\",\"owner\":\"<owner>\",\"findings_in\":<count>,\"wall_ms\":$((ts_end - ts_start))}" >> /tmp/code-review-timing.jsonl
```
**Full mode only** — skip this section in selector and short mode.

Roll-up summary after all owners return:
Compute the dispatch set from Step 4b-i and Step 4a findings:

```bash
# Filter to per_owner_adjudication events only — wc -l over-counts when the
# file has stale lines from a prior unclean run or the summary line itself.
total_ms=$(jq -s 'map(select(.event == "per_owner_adjudication") | .wall_ms) | add' /tmp/code-review-timing.jsonl)
owners_invoked=$(grep -c '"event":"per_owner_adjudication"' /tmp/code-review-timing.jsonl)
echo "{\"event\":\"per_owner_summary\",\"owners_invoked\":$owners_invoked,\"total_ms\":$total_ms}" >> /tmp/code-review-timing.jsonl
```
owners_to_spawn = (keys of findings_by_owner) ∪ (owners from active judgment rules)
```

Diagnostic only — operators read it to answer "is Owner X worth dispatching?" with data. Not part of the Step 5 user-facing report. `code-review.md` has no formal cleanup step (the command works against the current branch in-place; there is no review-worktree to remove), so end the run with `rm -f /tmp/code-review-timing.jsonl` to keep the file from accumulating stale lines across reviews.

#### Selector mode: Steps 4c-sel and 4d-sel

When the mode argument is `--selector` or `selector`: Steps 4.0, 4a, and 4b-i run unchanged. Skip Step 4b-ii. Resolve the guide and execute Steps 4c-sel and 4d-sel from it — zero sub-agent spawns.
If `owners_to_spawn` is empty AND `findings_by_owner` is empty, report "funnel clean — no adjudication needed" and proceed to Step 5. ZERO LLM spawns.

Run exactly this one command, once:
Otherwise, spawn ONE Task per owner in `owners_to_spawn` **concurrently** — they're independent. Each Task prompt:

```bash
GUIDE="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/coding}/docs/selector-mode-guide.md"
[ -f "$GUIDE" ] || GUIDE="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/marketplaces/coding/docs/selector-mode-guide.md"
[ -f "$GUIDE" ] && echo "GUIDE_OK: $GUIDE" || echo "GUIDE_MISSING"
```
coding:<owner> agent: "TARGET_DIR=<directory>.

If it prints `GUIDE_MISSING`: report "selector guide unavailable" as a **Must Fix toolchain failure** in Step 5 and STOP the selector path — do NOT continue with a mechanical-findings-only review presented as a complete selector review (a review without the judgment tier silently misses every judgment-tier rule; same fail-fast discipline as Step 4.0). Do NOT investigate further (no `find`, no `ls`, no path probing).
Pre-filtered mechanical findings for you (from ast-grep-runner):
<findings_by_owner[<owner>] JSON, or empty array if none>

If it prints `GUIDE_OK`: Read the file at that path, then execute its **Step 4c-sel CLASSIFY** and **Step 4d-sel ADJUDICATE** with:
Active judgment rules you own (from rules/index.json, triggered by this diff):
<list of rule blocks — id + doc_path + applies_when — for this owner only>

- **DIFF** = `git diff HEAD~1` (or directory diff as parsed in Step 1)
- **CANDIDATES** = the Step 4b-i `<rule-id> <owner>` output
- **MECHANICAL_FINDINGS** = `/tmp/code-review-findings.json`
- **Working directory** = the reviewed directory
Adjudicate: for each mechanical finding, assign severity (Critical / Important / Optional), add a fix suggestion that cites the rule by ID. Drop any finding whose rule_id is not in the index — stale-walker bug, not your concern.

On the guide's short-circuit condition the report line is `selector clean — no adjudication needed`. When the mode is anything else (short/standard/full), skip this section entirely.
Also scan the diff for each active judgment rule listed above and report violations you find. Read only changed files relevant to those rules.

Include the traceability section per `docs/selector-mode-guide.md` § Traceability Report Section.
Review changed code only."
```

#### 4c: Context-specific conventions

Expand All @@ -218,6 +196,8 @@ Load these conventionally when the diff matches:

#### 4d: Citation validation

**Full mode only** (selector mode's own citation call lives in the guide). Walk every finding from the per-owner agent reports and verify its `rule_id` field exists in `rules/index.json`. Drop findings citing missing IDs — they're hallucinations or stale-walker references. Log dropped findings to stderr.

```bash
coding:simple-bash-runner agent: "bash scripts/validate-citations.sh <findings.json>"
```
Expand Down
Loading
Loading