Skip to content

Commit 91236b2

Browse files
committed
Expand PR review skill with codemap phase, skill router, and code-vs-docs discipline
1 parent 397b198 commit 91236b2

2 files changed

Lines changed: 120 additions & 12 deletions

File tree

.claude/commands/review-pr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: Produce a structured local review report for a CoW services PR (read-only; user posts comments manually)
2+
description: Produce a structured local PR review report for cowprotocol/services — synthesizes context, builds a codemap, applies CoW-specific + Rust review skills, and emits severity-ranked findings with actionable questions. Use when the user says "review this PR", "/review-pr 1234", "look at PR #1234", or pastes a cowprotocol/services PR URL. Read-only; the user posts any comments manually.
33
---
44

55
Review PR: $ARGUMENTS

docs/COW_PR_REVIEW_SKILL.md

Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,23 @@ At this point you have:
1111

1212
## Core Principles (read before executing)
1313

14-
- **Signal over noise.** Report genuine concerns only. LGTM is a perfectly valid verdict and is the correct one whenever the PR is clean.
15-
- **Never post to GitHub.** Output is strictly for the user's terminal. No `gh pr review`, no `gh pr comment`, no `gh pr close`. The user decides what to say on GitHub.
14+
- **CRITICAL: Signal over noise.** Report genuine concerns only. LGTM is a perfectly valid verdict and is the correct one whenever the PR is clean. The goal is not to maximise finding count — the goal is to be worth a senior reviewer's attention.
15+
- **CRITICAL: Never post to GitHub.** Output is strictly for the user's terminal. No `gh pr review`, no `gh pr comment`, no `gh pr close`. The user decides what to say on GitHub.
16+
- **CRITICAL: Code is the primary source of truth.** `CLAUDE.md`, existing design docs, and this skill's own sibling docs can go stale. When a finding turns on *"X is called from Y"* or *"this field is read by Z"*, verify by grepping the codebase or using an LSP symbol tool — not by citing a doc. Docs give you higher-level *shape*; code gives you ground truth.
1617
- **Explain, don't just flag.** Each finding must give the reviewer enough context to understand *and defend* the point — not just forward AI-generated text.
1718
- **Actionable framing.** Every finding ends with either a concrete `Action:` or a specific `Question:`. Never both.
19+
- **Token discipline.** Don't read entire files when a grep or a targeted LSP symbol lookup suffices. Build a codemap (see [§3.5](#35-codemap-phase)) *before* reading file bodies. When you do need a file, read hunks adjacent to changed lines rather than the whole thing.
1820

1921
## Execution Flow
2022

2123
1. Fetch PR metadata and linked issue(s) — [§2. Metadata Fetch](#2-metadata-fetch)
2224
2. Classify diff paths and load sibling context docs — [§3. Classification](#3-classification)
23-
3. Synthesize the context block — [§4. Context Synthesis](#4-context-synthesis)
24-
4. Produce findings by severity — [§5. Review and Severity](#5-review-and-severity)
25-
5. Print the structured report — [§6. Report Template](#6-report-template)
26-
6. Offer verification (background) — [§7. Verification Offer](#7-verification-offer)
27-
7. Print cleanup hint — [§8. Cleanup](#8-cleanup)
25+
3. Build a targeted codemap — [§3.5. Codemap Phase](#35-codemap-phase)
26+
4. Synthesize the context block — [§4. Context Synthesis](#4-context-synthesis)
27+
5. Produce findings by severity — [§5. Review and Severity](#5-review-and-severity)
28+
6. Print the structured report — [§6. Report Template](#6-report-template)
29+
7. Offer verification (background) — [§7. Verification Offer](#7-verification-offer)
30+
8. Print cleanup hint — [§8. Cleanup](#8-cleanup)
2831

2932
Error behavior is consolidated in [§9. Error Playbook](#9-error-playbook).
3033

@@ -90,6 +93,61 @@ This list will grow. When adding a new sibling doc (e.g. `solver-engine.md`, `au
9093

9194
---
9295

96+
## 3.5 Codemap Phase
97+
98+
**Purpose:** Before reading file bodies, build a targeted map of the symbols the diff touches, their callers, and their call sites. A codemap turns a 1000-line diff into a ~20-line mental model and preempts the "I read 10 files to find the impact" failure mode. It also catches findings that only become visible at the *shape* level (API ergonomics, unused abstractions, caller-count inconsistencies).
99+
100+
### What to map
101+
102+
For each non-trivial symbol the diff adds, modifies, or deletes:
103+
104+
1. **New public types / traits / functions** — what are their fields / methods / signatures? (`rust-symbol-analyzer` or `get_symbols_overview`.)
105+
2. **Modified function signatures** — who calls them? (`rust-call-graph`, `find_referencing_symbols`, or `rg '<fn_name>\b' crates/`.) This is how you catch "this signature changed but 4 call sites weren't updated".
106+
3. **New trait impls** — which types implement the trait? Is the trait used anywhere outside the PR? (`rust-trait-explorer` or `find_referencing_symbols`.)
107+
4. **Error-type changes** — where do callers match on this error? (`rg '::<ErrorVariant>'` / `find_referencing_symbols`.)
108+
109+
### Tools (prefer the cheapest viable option)
110+
111+
In order of token cost, ascending:
112+
113+
| Tool | When |
114+
|---|---|
115+
| `Grep` with `-n` on a symbol name | Fastest. Use when you need caller counts, not structure. Example: `rg 'OrderValidator::new\b' crates/` to verify all call sites were updated. |
116+
| `mcp__plugin_serena_serena__find_symbol` / `find_referencing_symbols` | Cheap, precise. Use when you need location + kind + signature, not the whole file. |
117+
| `mcp__plugin_serena_serena__get_symbols_overview` on a single file | Use before reading the file body — gets the symbol table for free. |
118+
| LSP-backed skills (`rust-call-graph`, `rust-symbol-analyzer`, `rust-trait-explorer`, `rust-code-navigator`) | Richer analysis — full call hierarchies, trait impl trees, type relationships. Use for diffs that touch cross-crate abstractions. |
119+
| Reading full files with `Read` | Last resort. Only when the diff hunks don't give enough surrounding context and the LSP tools can't pin down what you need. |
120+
121+
### What the codemap produces
122+
123+
A short block in the report header (shown to the reviewer) that looks like:
124+
125+
```
126+
Codemap
127+
───────────────────────────────────────────────────────────
128+
New symbols (shared::order_validation):
129+
Eip1271Simulating (trait, new)
130+
Eip1271Simulator (struct, 3 pub fields, no constructor)
131+
ValidationError::SimulationFailed(String) (new variant)
132+
133+
Callers of OrderValidator::new: 16 sites total (1 real, 15 test).
134+
All updated in diff ✓
135+
136+
Config asymmetry noted:
137+
configs::Eip1271SimulationMode (3 variants, Disabled default)
138+
shared::Eip1271SimulationMode (2 variants, Shadow default)
139+
```
140+
141+
This is not filler — it's the raw material §4 (synthesis) and §5 (findings) work from. A finding like *"`Eip1271Simulator` pub fields have no constructor; 16 callers means each future field addition is a source-break"* only becomes findable once the codemap surfaces the caller count.
142+
143+
### When to skip the codemap
144+
145+
- Trivial PRs (docs-only, single-line version bump, pure test addition) — skip.
146+
- Pure refactor PRs where the diff has no added public API — skim only.
147+
- Everything else — do it.
148+
149+
---
150+
93151
## 4. Context Synthesis
94152

95153
Produce a 1-3 paragraph block combining:
@@ -117,13 +175,32 @@ Produce a 1-3 paragraph block combining:
117175

118176
## 5. Review and Severity
119177

120-
Read the diff. For non-trivial hunks, read the full changed file(s) for surrounding context. Apply, in order:
178+
With the codemap ([§3.5](#35-codemap-phase)) and context synthesis ([§4](#4-context-synthesis)) in hand, review the diff. For non-trivial hunks, read the full changed file only when the codemap + diff don't answer the question. Apply, in order:
121179

122-
1. Generic Rust review from `actionbook/rust-skills`.
123-
2. CoW services conventions from `CLAUDE.md`.
124-
3. Conditionally loaded sibling docs from [§3](#3-classification).
180+
1. CoW services conventions from `CLAUDE.md`.
181+
2. Sibling docs from [§3](#3-classification) (conditionally loaded).
182+
3. **Activate installed Rust review skills by diff content (below).**
125183
4. Soft QM skill (`ra-qm-team`), if in `loaded_context`.
126184

185+
### Skill router — activate installed Rust skills by diff content
186+
187+
These skills are installed via `actionbook/rust-skills` (hard prereq) and the related ecosystem. They're most effective when *explicitly* activated based on what the diff contains. Before writing findings, scan the diff and invoke any skill whose trigger fires:
188+
189+
| Skill | Trigger in diff | Why activate |
190+
|---|---|---|
191+
| `m06-error-handling` | Adds/modifies `Result`, `Option`, `?`, `.unwrap()`, `.expect()`, `anyhow!`, `thiserror`, or error-enum variants | Validates error taxonomy, propagation, lost context (e.g. `anyhow!("{err}")` flattening), panic-vs-Result choice. |
192+
| `m07-concurrency` | Adds `tokio::`, `async fn`, `.await`, `tokio::join!` / `try_join!`, `tokio::spawn`, `tokio::time::timeout`, `Mutex`, `RwLock`, `Arc<...>` in shared state | Validates timeout scoping, join-vs-try_join, deadlock/lock-contention, task cancellation semantics, Send/Sync bounds. |
193+
| `m04-zero-cost` | Adds new generics, `impl Trait`, `dyn Trait`, trait objects, `Box<dyn ...>` | Validates static-vs-dynamic dispatch choice, unnecessary allocation, trait-object safety, monomorphization cost on a workspace this large. |
194+
| `m05-type-driven` | Adds newtypes, `PhantomData`, marker traits, builder patterns, type-state | Validates "make invalid states unrepresentable" and whether the type design actually narrows the state space. |
195+
| `m15-anti-pattern` | Any non-trivial new code | Sanity pass for common Rust anti-patterns. Cheap; run it. |
196+
| `m10-performance` | Changes to hot paths (auction loop, settlement submission, per-order handlers, native price estimation) | Validates allocations, caching, loop invariants, lock granularity. |
197+
| `unsafe-checker` | Any `unsafe` block, FFI (`extern`), `transmute`, raw pointers, `MaybeUninit` | **Mandatory** — any finding here defaults to **High**. Soundness issues are never Small. |
198+
| `rust-trait-explorer` | Adds a new trait or a new impl of an existing trait | Maps the trait's existing impls — catches "you added a default method to a trait with 12 impls, one of them should override it". |
199+
| `rust-call-graph` / `rust-code-navigator` | Modified function signatures on cross-crate public APIs | Catches missed caller sites, breaking changes, downstream blast radius. |
200+
| `ra-qm-skills` | Soft prereq | QM checklists — supplementary if installed. |
201+
202+
**Rule of thumb:** If a skill's trigger keywords appear in the diff's **added** lines, activate it. Don't run skills on context lines (unchanged code around the diff) — that wastes tokens on things you're not actually reviewing.
203+
127204
### Severity Rubric
128205

129206
| Severity | Meaning | Example |
@@ -158,6 +235,8 @@ A senior reviewer catches things that aren't bugs. They shape the code for futur
158235

159236
7. **Description-vs-code mismatches.** If the PR body describes `Mutex<HashSet>` and the code has `DashSet`, the description is stale. Flag it — not because it changes the code, but because whoever reads the PR as history will be confused. Small finding, usually.
160237

238+
**Related: design spec referenced but not committed.** If the PR body mentions a design spec (e.g. `docs/superpowers/specs/...`) that isn't in the PR's diff, that's a **Small** finding asking the author to commit it — teammates reading the PR six months from now can't see the rationale otherwise.
239+
161240
8. **Bundled orthogonal changes.** When a PR contains a change that isn't clearly required by the main feature, ask: does this belong in its own PR? Sometimes the answer is "it's required, here's why" (fine — ask for the reason to be in the commit message or a code comment). Sometimes the answer is "you're right, let me split it" (better git history).
162241

163242
9. **Error taxonomies in web3 code.** Blockchain RPC errors have multiple categories: contract reverts, RPC transport failures, provider-side rate-limiting, timeout errors, decoding failures. Each should lead to *different* handling. If new code treats `Err(_)` as a single bucket when the branches should differ (e.g. cache a non-vault verdict on contract revert, but *not* on a network error), this is a correctness issue — Medium or High depending on whether the wrong classification can poison downstream behavior.
@@ -203,12 +282,21 @@ PR #<N> — <title>
203282
═══════════════════════════════════════════════════════════
204283
Author: @<author>
205284
Scope: +<additions> −<deletions> across <N> files
285+
(include a "(~X LOC human-written; rest generated/lockfile,
286+
filtered)" suffix when the filter materially changed the count)
206287
Labels: <labels, comma-separated; or "—">
207288
Base/Head: <baseRef> ← <headRef>
208289
Linked issue: #<N> — <issue title> (omit line if none)
209290
Loaded context: <comma-separated loaded_context list>
291+
Activated skills: <list of Rust skills fired by the skill router, e.g.
292+
m07-concurrency, m06-error-handling>
210293
Mode: full checkout | degraded static-diff
211294
295+
Codemap
296+
───────────────────────────────────────────────────────────
297+
<concise codemap from §3.5 — new symbols, modified signatures, caller counts.
298+
Omit when the diff is trivial enough that §3.5 was skipped.>
299+
212300
───────────────────────────────────────────────────────────
213301
CONTEXT
214302
───────────────────────────────────────────────────────────
@@ -341,6 +429,26 @@ Return with: git switch <prior_branch>
341429

342430
---
343431

432+
## 10. Code-vs-docs Discipline (Always Apply)
433+
434+
When a finding rests on a claim about the codebase, verify the claim by looking at the code — not by trusting a doc, a comment, or this skill's own sibling files.
435+
436+
- **Claim:** *"`OrderSimulator::encode_order` only reads `OrderData` and `Interactions`."*
437+
**Wrong:** cite a doc comment.
438+
**Right:** `rg 'fn encode_order' crates/orderbook/src/` → read the function body → verify.
439+
440+
- **Claim:** *"All `OrderValidator::new` call sites have been updated."*
441+
**Wrong:** count the test assertions in the diff.
442+
**Right:** `rg 'OrderValidator::new\b' crates/` → compare the count to the diff's modified lines.
443+
444+
- **Claim:** *"This module is only used by X."*
445+
**Wrong:** trust the module's top-level comment.
446+
**Right:** `find_referencing_symbols` on the public exports → see actual call graph.
447+
448+
Docs age. Comments lie. Grep and LSP don't. When reporting a finding that depends on such a claim, verify *before* you write the finding — not after the author pushes back.
449+
450+
---
451+
344452
## Maintenance Notes
345453

346454
- **When you find yourself adding a project-specific heuristic more than twice**, move it into a sibling context doc under `docs/review-context/` and add a trigger rule to [§3](#3-classification).

0 commit comments

Comments
 (0)