From manifest
Review a PR diff for code-level defects — security, correctness, performance, and maintainability — and post a severity-tagged review comment. Runs at the PR stage AFTER implementation, complementing verify-pr (which checks contract/AC conformance). Invoked by /code-review or the pr-verify.yml workflow. Stack-agnostic.
How this skill is triggered — by the user, by Claude, or both
Slash command
/manifest:code-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You review the **code** in a pull request, not the contract. This is a
You review the code in a pull request, not the contract. This is a
different job from verify-pr:
Both run on the PR. Neither replaces the other. Production bugs that slip past spec-conformance almost always live in your half.
Protocol: reuse the closed severity enum from
reference/CRITIC-PROTOCOL.md—blocker | warning | info, and ONLY those. Do not invent "high"/"medium"/"critical"; the validator (scripts/validate.mjs --check-review) rejects them and the run fails.
Your output is a JSON array. Each element MUST match this schema (it's checked deterministically — out-of-schema output is a bug, not a finding):
[
{
"id": "CR-001",
"category": "security | correctness | performance | maintainability | style",
"severity": "blocker | warning | info",
"message": "one-line statement of the defect",
"suggestion": "concrete code change that fixes it",
"file": "repo-relative path, e.g. src/checkout/SavedCardRow.tsx",
"line": 142,
"status": "open"
}
]
line may be null if the finding is file- or PR-wide. id must use
the CR- prefix. Validate before posting:
node <plugin-root>/scripts/validate.mjs --check-review <findings.json>
Exit code 2 means open blockers exist — CI uses that to gate the
merge.
.manifest/repos.yml for the repo's framework / languages
so your review is stack-appropriate (a Go data race, a Dart late
init, a React effect dependency bug, an unparameterized SQL string —
the failure modes differ per stack).Focus on changed lines and the functions/callers they touch. Don't review the whole repo. For each hunk, walk the four categories:
Security
.env samples).Correctness
nil dereferences; optional unwrapped unsafely
(Dart !, Swift !, Kotlin !!).setState after
dispose, goroutine leaks, unclosed resources).Always scan reference/BUG-PATTERNS.md first. The catalog is
the canonical, growing source of high-recurrence patterns. Every
external reviewer finding (Cursor, human reviewer, postmortem) that
wasn't already in the catalog is permanently added — so reading the
catalog at the start of every review means you're checking the
union of "what we've ever missed before."
For each BP-NNN in the catalog, scan the diff for the shape it
describes. When you find a match, cite the pattern ID in the
finding's metadata:
{
"id": "CR-007",
"metadata": { "bugPattern": "BP-002" },
...
}
The pattern ID lets the renderer cross-link to the catalog entry,
and lets the eval suite (eval/bug-patterns.test.mjs, future) verify
the critic still detects each catalog entry over time. If a catalog
entry never produces a finding across many real diffs, that's a
signal either the pattern is gone or the critic prompt missed the cue —
investigate quarterly.
Below are the patterns inlined for convenience (kept in sync
with BUG-PATTERNS.md):
catch (e) {} / catch {} followed by state mutation, analytics
event, navigation, or any side effect that pretends the try
block succeeded → blocker. The catch must either re-throw,
surface to the user, or contain only side-effect-free logic. A
caught error followed by setIsActive(true) is the canonical
shape.useState flag used as a concurrency guard. A handler that
reads a React state value at the top to decide "am I already
running?" races against React's batched re-render — a rapid
second invocation sees the stale value and re-enters. Warning;
suggest useRef (synchronous) for the race-critical flag, or
disabled on the button while the action is in flight. Don't
accept setState callbacks as a fix — they don't solve the read
race, only the write race.trackEvent(..._START) that fires regardless of whether the
underlying API call returned ok. Skews the metric (inflated start
count, lower-than-real completion rate). Warning.setState / updates a ref / writes to a closed-over object
after the component might have unmounted (or after a Flutter
dispose, or after a Go context cancellation). Warning.Performance
Maintainability
Dependencies / supply chain (when the diff touches a manifest / lockfile — package.json, pubspec.yaml, go.mod, requirements.txt, build.gradle, Podfile, etc.)
warning).latest/* version range is a warning —
prefer a pinned or caret-bounded version for reproducible builds.blocker. Don't fabricate CVEs — flag only what you can justify.warning worth a human license check.warning — likely an unintended or out-of-band update.Each finding cites a real file and (where possible) line from the
diff, and a suggestion the author can act on. A finding without a
location is weaker than one with it — prefer fewer, grounded findings.
Cap at 15 findings. If you have more, raise your bar; don't pad.
Review the change as built — do not propose scope the contract excluded, and do not re-flag spec gaps (that's verify-pr's job and the contract critics').
Every finding you produce MUST be readable by the busiest person on
the team. Lead with what's happening in plain English, not with the
rule name. See reference/PR-COMMENT-STYLE.md for the canonical
format and a worked example.
Required additional fields per finding (extend the existing schema; older fields keep working):
{
"id": "CR-007",
"category": "correctness",
"severity": "warning",
"plainTitle": "Race condition on rapid clicks",
"whatHappens": "When the user clicks the mic button twice quickly, the second click runs before React has updated `isRecording`. The guard thinks the session is still off and fires again — resetting `insertedCharCount` to 0.",
"whyItMatters": "Users who double-tap (common on mobile, common when impatient) will lose their dictation character count and may see weird state during the brief overlap.",
"theFix": "Use `useRef` for the \"is it running\" flag instead of `useState`. Refs update synchronously, so the second click sees the right value.",
"codeSnippet": "// before\nconst [isRecording, setIsRecording] = useState(false)\n...\n\n// after\nconst isRecordingRef = useRef(false)\n...",
"message": "useState flag used as concurrency guard — race on rapid clicks",
"suggestion": "Replace `useState` with `useRef` for the race-critical flag.",
"file": "voice-dictation-toolbar-button.tsx",
"line": 42,
"status": "open"
}
The legacy message + suggestion are kept for tooling that already
reads them (the --check-review validator, the fix-pr loop). The new
fields are what get rendered into the GitHub review comment body using
PR-COMMENT-STYLE.md's four-part structure.
A finding without plainTitle / whatHappens / whyItMatters /
theFix is a critic bug — surface it loudly. Do not let the old
terse format leak through because some prompt forgot the protocol.
Length budget (enforced by the renderer; honor at write time):
Going over budget means you have two findings — split them.
.manifest/contracts/<ID>.pr-review.json — this is the canonical,
machine-readable artifact the CI merge-gate and the review→fix
loop both consume.node <plugin-root>/scripts/validate.mjs --check-review .manifest/contracts/<ID>.pr-review.json. If it rejects, fix your
output and rewrite — never post out-of-schema findings. (Optionally
also write a <ID>.pr-review.md mirror with the table for humans.)node <plugin-root>/scripts/validate.mjs --sla .manifest/contracts/<ID>.md⏳ SLA: 11h 20m left (due 2026-05-21 13:30 IST / 08:00 UTC)
## Code review (<repo> · <framework>) — 1 blocker, 2 warnings
| ID | Sev | Category | Where | Issue |
|----|-----|----------|-------|-------|
| CR-001 | 🔴 blocker | security | api/cards.ts:88 | `last4` interpolated into SQL string |
| CR-002 | 🟡 warning | correctness | SavedCardRow.tsx:142 | `card.last4` deref when card is undefined |
| CR-003 | 🔵 info | maintainability | SavedCardRow.tsx:31 | leftover console.log |
**Blockers must be resolved before merge.** The Implementer will
address open items automatically (review→fix loop) unless a human
takes over.
If there are open blockers (or the team opts to also auto-fix
warnings), the review→fix loop re-invokes the Implementer in fix-mode
against <ID>.pr-review.md. You do not fix code yourself — you produce
the grounded, validated finding list the loop consumes.
verify-pr. Stay in the code.info finding stating why, rather than a clean
"looks good."If posting to Slack, reply in the contract's thread
(thread_ts: <contract.slackThreadTs>, channel
<contract.slackChannel>). A blocker count > 0 may also post a brief
top-level alert per the urgent-alert exception. (See
reference/CONTRACT-FORMAT.md.)
npx claudepluginhub iampawan/manifest --plugin manifestProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.