Status: Draft (proposed shape; not yet implemented) Tracking PR: this PR (docs-only — locks the shape before any command-file changes land)
Today the plugin ships two review commands and both review the diff — different diff sources, but both delta-scoped:
| Command | Scope today |
|---|---|
/coding:pr-review |
git diff origin/<target>...HEAD — branch vs target (PR-shaped) |
/coding:code-review |
git diff HEAD~1 — uncommitted / most-recent local change |
The name code-review implies "review the code" — operator-readable expectation is a whole-codebase audit, not a delta against HEAD~1. The current behavior is closer to a pre-commit local check. Two commands, two diff-scopes, one missing scope (whole codebase). Operators reaching for /coding:code-review to audit an inherited codebase or do a periodic health-check get a 1-commit diff instead.
Three commands, three distinct scopes; names match scopes:
| Command | Scope | What's at this scope | Status |
|---|---|---|---|
/coding:pr-review |
remote, branch diff vs target | the PR you're about to merge | unchanged |
/coding:local-review |
local, uncommitted / most-recent | what you just typed, pre-commit | renamed from current /coding:code-review |
/coding:code-review |
whole codebase | total health; baseline-aware | new, built ground-up |
The contrast pair pr-review (remote, against target branch on GitHub) / local-review (local, on this machine, pre-push) reads cleanly. Scope is in the name. changes-review is also valid but less crisp — pr-review is also "changes", just remote-scoped changes.
The operator's mental model when typing /coding:code-review is "review the code", not "review this 1-commit delta". Aligning behavior with name reduces "wait, which one does what?" confusion. The trade-off — existing users running /coding:code-review for pre-commit checks see behavior change — is mitigated by the deprecation stub (below).
A naive "run all rules against every file" implementation is worse than nothing on any non-trivial codebase: hundreds of pre-existing tech-debt findings drown the signal. Three mechanisms make it work:
Default scope = Must Fix + Should Fix only. Nice-to-Have suppressed unless explicitly requested via flag. On a mature codebase, Nice-to-Have can dominate output; filtering it default-on keeps the report scannable. Flag: /coding:code-review --include-optional to opt in.
When a single rule fires N times across the codebase (e.g., 47 no-fmt-errorf violations), the report shows one entry per rule_id with a sample of 3-5 file:line locations + a total count, not 47 separate findings. Dedup happens at the report-consolidation stage (Step 5).
Example:
### Should Fix
- **go-errors/no-fmt-errorf** — 47 occurrences. Replace `fmt.Errorf` with `errors.Wrapf` / `errors.Errorf` from `github.com/bborbe/errors`.
Sample sites:
- pkg/storage/base.go:144
- pkg/ops/update.go:91
- pkg/handler/foo.go:203
- …and 44 more
The operator can commit a baseline that says "these N pre-existing findings are accepted; only flag NEW ones." This turns full-audit from "all tech debt" into "what's drifted since last sweep" — the actually useful mode.
Format (sketch):
# .code-review-baseline.yaml — accepted pre-existing findings
# Regenerate with: /coding:code-review --refresh-baseline
generated_at: "2026-06-28T20:00:00Z"
generated_at_sha: a7ef2bd
accepted:
go-errors/no-fmt-errorf:
count: 47
sample:
- pkg/storage/base.go:144
- pkg/ops/update.go:91
go-time/no-time-now-direct:
count: 12
# …Subsequent /coding:code-review runs compare the current finding set against accepted:
- NEW findings (rule_id × file:line not in baseline) → reported normally
- CARRIED findings (already in baseline) → suppressed; mentioned in traceability section as count
- REMOVED findings (in baseline but no longer present) → reported as "fixed since baseline" (positive signal)
For Go projects, run golangci-lint run ./... first and incorporate its findings into the mechanical-tier funnel. Avoids re-implementing what the toolchain already does well at the AST level. LLM adjudication then focuses on judgment-tier rules (architecture, layering, abstractions) where it adds real value.
- PR 1 (this doc) — design note merged. Locks the shape.
- PR 2 — rename existing
commands/code-review.md→commands/local-review.md. Single-file move + grep-and-replace internal refs. Bot review approves quickly. Plugin version bump (e.g., 0.25.0 → 0.26.0 — minor bump signals renamed surface). - PR 3 — deprecation stub:
commands/code-review.mdbecomes a thin compatibility shim that prints "did you mean/coding:local-review? whole-codebase audit is moving to this name in v0.27.0." Plus opens an issue tracking the new behavior land date. - PR 4 — implement the new whole-codebase
/coding:code-review: severity filter, rule-id dedup, baseline-file logic. The largest PR; gets its own design review. - PR 5 — flip the deprecation:
commands/code-review.mdnow invokes the whole-codebase logic; deprecation message removed.
PRs 2 + 3 + 5 ship within a single minor version cycle so the deprecation window is short (operator sees the new name for ~1-2 releases, then the new behavior). PR 4 can sit between 3 and 5 without timing pressure.
- Exact CLI flag names beyond
--include-optionaland--refresh-baseline(defer to PR 4) - Whether judgment rules need to be re-tagged with
applies_to: diff | codebase | both(likely yes, but the inventory belongs in PR 4) - How
golangci-lintfailure interacts with the report (block, warn, ignore?) - Whether the baseline format should be shared with
golangci-lint's own--newflag mechanism (it does the same thing for its rules)
Pinning defaults before PR 4 lands so the implementation has no design-time ambiguity left to resolve.
--refresh-baselinerequires a clean working tree. Generating a baseline from a tree with uncommitted work bakes accidental local cruft into the accepted set — exactly the failure mode the baseline guards against. The command refuses with a clear error ifgit status --porcelainis non-empty. (Originally posed as open question; rationale resolves it.)- Baseline file location: repo root, default
.code-review-baseline.yaml, override via--baseline-path=<path>. Repo root is discoverable (operators see it onls,git statusflags it after--refresh-baseline); the override exists for monorepo cases. - Multi-project monorepos: one baseline per repo by default;
--baseline-pathallows per-subproject baselines. Aservices/foo/.code-review-baseline.yamlis supported via/coding:code-review services/foo --baseline-path=services/foo/.code-review-baseline.yaml. Most callers run the default. .gitignoreis respected,.claude-ignoreis not introduced.git ls-filesalready excludes.gitignore'd paths — the audit operates on tracked files only. No new ignore file needed; if an operator wants to exclude a tracked file from audit, the unit of exclusion is the baseline file (accepted finding) or a rule-specific override, not a separate ignore list.
commands/pr-review.md— current diff-scoped reviewer for branch vs targetcommands/code-review.md— current diff-scoped reviewer for local changes (to be renamed tolocal-review.mdper PR 2)docs/selector-mode-guide.md— adjudication mechanics; works either scope (file-set source–agnostic), no changes neededscripts/ast-grep-runner.sh— mechanical funnel; takes a file list, scope-agnosticrules/index.json— rule catalog; potentially needsapplies_totagging in PR 4