Review Verification Protocol
This protocol MUST be followed before reporting any code review finding. Skipping these steps leads to false positives that waste developer time and erode trust in reviews.
For Elixir/OTP/Phoenix/LiveView files, apply the gates below first; the issue-type and cross-stack sections apply when the reviewed code uses those stacks or patterns.
Hard gates (execute in order)
Do not report a finding until each relevant gate passes for that finding. A gate passes only when the pass condition is objectively satisfied (tool output, cited path:line), not when it “feels” verified.
- Read gate — Pass if: you opened the full defining function, module section, or template region (or equivalent scoped read), not only the PR diff hunk for that symbol.
- Evidence gate — Pass if: the finding cites
path:line (or line range) that you can tie to actual file content from a read/search tool in this session.
- Usage gate (before “unused”, “dead code”, “unreachable”) — Pass if: you ran a repo-wide reference search and can state the result (e.g. zero matches vs matches at listed paths); if the symbol may be invoked dynamically, Pass if: you checked reflection-like mechanisms (macros,
apply, MFA strings, config) or explicitly mark uncertainty as a question, not a defect.
- Cross-cutting gate (before “missing validation/error handling”) — Pass if: you checked at least one of caller, plug/pipeline, context, supervision, or framework guarantees, or you document that none apply.
- Severity gate (before Critical/Major) — Pass if: you can name a concrete failure mode (what breaks, who is affected), not a style preference or hypothetical edge case.
If you cannot pass a gate, omit the finding, downgrade per Severity Calibration, or ask a question instead of asserting a defect.
Pre-Report Verification Checklist
Before flagging ANY issue, verify (maps to Hard gates):
Verification by Issue Type
"Unused Variable/Function"
Before flagging, you MUST:
- Search for ALL references in the codebase (grep/find)
- Check if it's exported and used by external consumers
- Check if it's used via reflection, decorators, or dynamic dispatch
- Verify it's not a callback passed to a framework
Common false positives:
- State setters in React (may trigger re-renders even if value appears unused)
- Variables used in templates/JSX
- Exports used by consuming packages
"Missing Validation/Error Handling"
Before flagging, you MUST:
- Check if validation exists at a higher level (caller, middleware, route handler)
- Check if the framework provides validation (Pydantic, Zod, TypeScript)
- Verify the "missing" check isn't present in a different form
Common false positives:
- Framework already validates (FastAPI + Pydantic, React Hook Form)
- Parent component validates before passing props
- Error boundary catches at higher level
"Type Assertion/Unsafe Cast"
Before flagging, you MUST:
- Confirm it's actually an assertion, not an annotation
- Check if the type is narrowed by runtime checks before the point
- Verify if framework guarantees the type (loader data, form data)
Valid patterns often flagged incorrectly:
# Pattern matching, NOT type casting
%UserData{} = data = load_user()
# Guard clauses narrow the type safely
def process(%User{name: name} = user) do
name # Elixir knows this is a User struct
end
"Potential Memory Leak/Race Condition"
Before flagging, you MUST:
- Verify cleanup function is actually missing (not just in a different location)
- Check if AbortController signal is checked after awaits
- Confirm the component can actually unmount during the async operation
Common false positives:
- Cleanup exists in useEffect return
- Signal is checked (code reviewer missed it)
- Operation completes before unmount is possible
"Performance Issue"
Before flagging, you MUST:
- Confirm the code runs frequently enough to matter (render vs click handler)
- Verify the optimization would have measurable impact
- Check if the framework already optimizes this (React compiler, memoization)
Do NOT flag:
- Functions created in click handlers (runs once per click)
- Array methods on small arrays (< 100 items)
- Object creation in event handlers
Severity Calibration
Critical (Block Merge)
ONLY use for:
- Security vulnerabilities (injection, auth bypass, data exposure)
- Data corruption bugs
- Crash-causing bugs in happy path
- Breaking changes to public APIs
Major (Should Fix)
Use for:
- Logic bugs that affect functionality
- Missing error handling that causes poor UX
- Performance issues with measurable impact
- Accessibility violations
Minor (Consider Fixing)
Use for:
- Code clarity improvements
- Documentation gaps
- Inconsistent style (within reason)
- Non-critical test coverage gaps
Informational (No Action Required)
Use for:
- Improvements that require adding new dependencies or modules
- Suggestions for net-new code that didn't exist in the codebase before (new modules, test suites, abstractions)
- Architectural ideas for future consideration
- Test infrastructure suggestions (new mock libraries, behaviour extraction)
- Optimizations without measurable impact in the current context
These are NOT review blockers. They should be noted for the author's awareness but must not appear in the actionable issue count. The Verdict should ignore informational items entirely.
Do NOT Flag At All
- Style preferences where both approaches are valid
- Optimizations with no measurable benefit
- Test code not meeting production standards (intentionally simpler)
- Library/framework internal code (shadcn components, generated code)
- Hypothetical issues that require unlikely conditions
Valid Patterns (Do NOT Flag)
Elixir
| Pattern |
Why It's Valid |
case with multiple clauses |
Standard pattern matching, not excessive branching |
with chains |
Idiomatic for sequential operations that may fail |
Pipe operator (|>) chains |
Elixir's core composition pattern |
@spec without Dialyzer enforcement |
Documentation value even without static analysis |
defp private functions |
Proper encapsulation, not hidden complexity |
Phoenix/LiveView
| Pattern |
Why It's Valid |
assign/2 in mount/3 |
Standard LiveView state initialization |
handle_event/3 returning {:noreply, socket} |
Correct for UI-triggered state updates |
~H sigil for inline templates |
Valid for small components |
on_mount hooks |
Correct lifecycle pattern for auth/setup |
| PubSub broadcasts in handle_info |
Standard real-time communication pattern |
Testing
| Pattern |
Why It's Valid |
assert without message |
ExUnit provides clear diff output |
setup block for test context |
Standard ExUnit fixture pattern |
describe blocks for grouping |
Idiomatic test organization |
conn pipeline in controller tests |
Phoenix test helper convention |
General
| Pattern |
Why It's Valid |
+? lazy quantifier in regex |
Prevents over-matching, correct for many patterns |
| Direct string concatenation |
Simpler than template literals for simple cases |
| Multiple returns in function |
Can improve readability |
| Comments explaining "why" |
Better than no comments |
Context-Sensitive Rules
Pattern Matching
Flag missing pattern match ONLY IF ALL of these are true:
Process Architecture
Flag missing supervision ONLY IF:
Error Handling
Flag missing error handling ONLY IF:
Before Submitting Review
Final verification:
0. For each finding, confirm the Hard gates that apply to its type were passed (or the finding was downgraded/removed).
- Re-read each finding and ask: "Did I verify this is actually an issue?"
- For each finding, can you point to the specific line that proves the issue exists?
- Would a domain expert agree this is a problem, or is it a style preference?
- Does fixing this provide real value, or is it busywork?
- Format every finding as:
[FILE:LINE] ISSUE_TITLE
- For each finding, ask: "Does this fix existing code, or does it request entirely new code that didn't exist before?" If the latter, downgrade to Informational.
- If this is a re-review: ONLY verify previous fixes. Do not introduce new findings.
If uncertain about any finding, either:
- Remove it from the review
- Mark it as a question rather than an issue
- Verify by reading more code context
1---2name: review-verification-protocol-53description: Mandatory verification steps for all code reviews to reduce false positives. Load this skill before reporting ANY code review findings.4---5
6# Review Verification Protocol
7
8This protocol MUST be followed before reporting any code review finding. Skipping these steps leads to false positives that waste developer time and erode trust in reviews.
9
10For Elixir/OTP/Phoenix/LiveView files, apply the gates below first; the issue-type and cross-stack sections apply when the reviewed code uses those stacks or patterns.
11
12## Hard gates (execute in order)
13
14Do not report a finding until each relevant gate passes for **that** finding. A gate passes only when the pass condition is objectively satisfied (tool output, cited path:line), not when it “feels” verified.
15
161. **Read gate** — *Pass if:* you opened the full defining function, module section, or template region (or equivalent scoped read), not only the PR diff hunk for that symbol.
172. **Evidence gate** — *Pass if:* the finding cites `path:line` (or line range) that you can tie to actual file content from a read/search tool in this session.
183. **Usage gate** (before “unused”, “dead code”, “unreachable”) — *Pass if:* you ran a repo-wide reference search and can state the result (e.g. zero matches vs matches at listed paths); if the symbol may be invoked dynamically, *Pass if:* you checked reflection-like mechanisms (macros, `apply`, MFA strings, config) or explicitly mark uncertainty as a question, not a defect.
194. **Cross-cutting gate** (before “missing validation/error handling”) — *Pass if:* you checked at least one of caller, plug/pipeline, context, supervision, or framework guarantees, or you document that none apply.
205. **Severity gate** (before Critical/Major) — *Pass if:* you can name a concrete failure mode (what breaks, who is affected), not a style preference or hypothetical edge case.
21
22If you cannot pass a gate, **omit the finding**, **downgrade** per [Severity Calibration](#severity-calibration), or **ask a question** instead of asserting a defect.
23
24## Pre-Report Verification Checklist
25
26Before flagging ANY issue, verify (maps to [Hard gates](#hard-gates-execute-in-order)):
27
28- [ ] **Read gate** — Full symbol/region read, not diff-only
29- [ ] **Evidence gate** — Citable `path:line` from tool-backed content
30- [ ] **Usage gate** — Reference search (or dynamic-call check) before “unused”
31- [ ] **Cross-cutting gate** — Validation/handling checked at other layers where relevant
32- [ ] **Docs/syntax** — Verified against current framework/docs for the file’s stack (e.g. Tailwind v4, TS 5.x, React 19 when reviewing those files)
33- [ ] **Style vs wrong** — Both approaches may be valid; distinguish
34- [ ] **Intentional design** — Comments, CLAUDE.md, AGENTS.md, architectural context considered
35
36## Verification by Issue Type
37
38### "Unused Variable/Function"
39
40**Before flagging**, you MUST:
411. Search for ALL references in the codebase (grep/find)
422. Check if it's exported and used by external consumers
433. Check if it's used via reflection, decorators, or dynamic dispatch
444. Verify it's not a callback passed to a framework
45
46**Common false positives:**
47- State setters in React (may trigger re-renders even if value appears unused)
48- Variables used in templates/JSX
49- Exports used by consuming packages
50
51### "Missing Validation/Error Handling"
52
53**Before flagging**, you MUST:
541. Check if validation exists at a higher level (caller, middleware, route handler)
552. Check if the framework provides validation (Pydantic, Zod, TypeScript)
563. Verify the "missing" check isn't present in a different form
57
58**Common false positives:**
59- Framework already validates (FastAPI + Pydantic, React Hook Form)
60- Parent component validates before passing props
61- Error boundary catches at higher level
62
63### "Type Assertion/Unsafe Cast"
64
65**Before flagging**, you MUST:
661. Confirm it's actually an assertion, not an annotation
672. Check if the type is narrowed by runtime checks before the point
683. Verify if framework guarantees the type (loader data, form data)
69
70**Valid patterns often flagged incorrectly:**
71```elixir
72# Pattern matching, NOT type casting
73%UserData{} = data = load_user()
74
75# Guard clauses narrow the type safely
76def process(%User{name: name} = user) do
77 name # Elixir knows this is a User struct
78end
79```
80
81### "Potential Memory Leak/Race Condition"
82
83**Before flagging**, you MUST:
841. Verify cleanup function is actually missing (not just in a different location)
852. Check if AbortController signal is checked after awaits
863. Confirm the component can actually unmount during the async operation
87
88**Common false positives:**
89- Cleanup exists in useEffect return
90- Signal is checked (code reviewer missed it)
91- Operation completes before unmount is possible
92
93### "Performance Issue"
94
95**Before flagging**, you MUST:
961. Confirm the code runs frequently enough to matter (render vs click handler)
972. Verify the optimization would have measurable impact
983. Check if the framework already optimizes this (React compiler, memoization)
99
100**Do NOT flag:**
101- Functions created in click handlers (runs once per click)
102- Array methods on small arrays (< 100 items)
103- Object creation in event handlers
104
105## Severity Calibration
106
107### Critical (Block Merge)
108
109**ONLY use for:**
110- Security vulnerabilities (injection, auth bypass, data exposure)
111- Data corruption bugs
112- Crash-causing bugs in happy path
113- Breaking changes to public APIs
114
115### Major (Should Fix)
116
117**Use for:**
118- Logic bugs that affect functionality
119- Missing error handling that causes poor UX
120- Performance issues with measurable impact
121- Accessibility violations
122
123### Minor (Consider Fixing)
124
125**Use for:**
126- Code clarity improvements
127- Documentation gaps
128- Inconsistent style (within reason)
129- Non-critical test coverage gaps
130
131### Informational (No Action Required)
132
133**Use for:**
134- Improvements that require adding new dependencies or modules
135- Suggestions for net-new code that didn't exist in the codebase before (new modules, test suites, abstractions)
136- Architectural ideas for future consideration
137- Test infrastructure suggestions (new mock libraries, behaviour extraction)
138- Optimizations without measurable impact in the current context
139
140**These are NOT review blockers.** They should be noted for the author's awareness but must not appear in the actionable issue count. The Verdict should ignore informational items entirely.
141
142### Do NOT Flag At All
143
144- Style preferences where both approaches are valid
145- Optimizations with no measurable benefit
146- Test code not meeting production standards (intentionally simpler)
147- Library/framework internal code (shadcn components, generated code)
148- Hypothetical issues that require unlikely conditions
149
150## Valid Patterns (Do NOT Flag)
151
152### Elixir
153
154| Pattern | Why It's Valid |
155|---------|----------------|
156| `case` with multiple clauses | Standard pattern matching, not excessive branching |
157| `with` chains | Idiomatic for sequential operations that may fail |
158| Pipe operator (`\|>`) chains | Elixir's core composition pattern |
159| `@spec` without Dialyzer enforcement | Documentation value even without static analysis |
160| `defp` private functions | Proper encapsulation, not hidden complexity |
161
162### Phoenix/LiveView
163
164| Pattern | Why It's Valid |
165|---------|----------------|
166| `assign/2` in `mount/3` | Standard LiveView state initialization |
167| `handle_event/3` returning `{:noreply, socket}` | Correct for UI-triggered state updates |
168| `~H` sigil for inline templates | Valid for small components |
169| `on_mount` hooks | Correct lifecycle pattern for auth/setup |
170| PubSub broadcasts in handle_info | Standard real-time communication pattern |
171
172### Testing
173
174| Pattern | Why It's Valid |
175|---------|----------------|
176| `assert` without message | ExUnit provides clear diff output |
177| `setup` block for test context | Standard ExUnit fixture pattern |
178| `describe` blocks for grouping | Idiomatic test organization |
179| `conn` pipeline in controller tests | Phoenix test helper convention |
180
181### General
182
183| Pattern | Why It's Valid |
184|---------|----------------|
185| `+?` lazy quantifier in regex | Prevents over-matching, correct for many patterns |
186| Direct string concatenation | Simpler than template literals for simple cases |
187| Multiple returns in function | Can improve readability |
188| Comments explaining "why" | Better than no comments |
189
190## Context-Sensitive Rules
191
192### Pattern Matching
193
194Flag missing pattern match **ONLY IF ALL** of these are true:
195- [ ] Function receives structured data that should be destructured
196- [ ] Not a pass-through function that forwards data unchanged
197- [ ] Pattern match would prevent actual runtime errors
198- [ ] Not a GenServer callback with standard signature
199
200### Process Architecture
201
202Flag missing supervision **ONLY IF**:
203- [ ] Process is long-lived (not a Task)
204- [ ] Crash would affect system stability
205- [ ] No supervisor already manages this process
206- [ ] Not a test-only process
207
208### Error Handling
209
210Flag missing error handling **ONLY IF**:
211- [ ] No `with` clause handles the error case
212- [ ] No supervision tree restarts the process
213- [ ] The error would cascade beyond the current process
214- [ ] User needs specific feedback for this error type
215
216## Before Submitting Review
217
218Final verification:
2190. For each finding, confirm the [Hard gates](#hard-gates-execute-in-order) that apply to its type were passed (or the finding was downgraded/removed).
2201. Re-read each finding and ask: "Did I verify this is actually an issue?"
2212. For each finding, can you point to the specific line that proves the issue exists?
2223. Would a domain expert agree this is a problem, or is it a style preference?
2234. Does fixing this provide real value, or is it busywork?
2245. Format every finding as: `[FILE:LINE] ISSUE_TITLE`
2256. For each finding, ask: "Does this fix existing code, or does it request entirely new code that didn't exist before?" If the latter, downgrade to Informational.
2267. If this is a re-review: ONLY verify previous fixes. Do not introduce new findings.
227
228If uncertain about any finding, either:
229- Remove it from the review
230- Mark it as a question rather than an issue
231- Verify by reading more code context