Deep multi-agent code review with algorithmic complexity analysis, data structure review, paradigm enforcement, and efficiency analysis. Complements /simplify with deeper dimensions.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Deep multi-agent code review with algorithmic complexity analysis, data structure review, paradigm enforcement, and efficiency analysis. Complements /simplify with deeper dimensions.
disable-model-invocation
false
argument-hint
[--scope=diff|full] [file or directory]
allowed-tools
Read, Grep, Glob, Bash, Agent, Write
effort
xhigh
wrought
{"version":"1.0","tools":{"capabilities":["read_file","search_content","find_files","run_command","delegate","write_file"]},"platforms":{"claude-code":{"allowed-tools":"Read, Grep, Glob, Bash, Agent, Write","disable-model-invocation":false}},"agent":{"role":"Code Review Orchestrator","expertise":["code review orchestration","finding aggregation and deduplication","severity classification"],"non_goals":["modifying source code","running tests","implementing fixes"]},"execution":{"default_mode":"self-refine","max_iterations":10,"max_refine":1,"stop_conditions":["Review report written to docs/reviews/","User instructed to stop"]},"output":{"format":"markdown","template":"docs/reviews/{YYYY-MM-DD_HHMM}_{scope}.md","required_sections":["Executive Summary","Critical Findings","Warnings","Suggestions","Subagent Reports"]},"pipeline":{"track":"proactive","standalone":true,"prerequisites":[],"produces":["docs/reviews/*.md"],"suggested_next":["finding"]}}
Code Review Orchestrator
Trigger: /forge-review [--scope=diff|full] [file or directory]
Purpose: Deep multi-agent code review that orchestrates 4 specialized subagents in parallel. Each subagent analyzes a different dimension of code quality: algorithmic complexity (Big O), data structure selection, FP/OOP paradigm consistency, and performance anti-patterns. Results are aggregated into a tiered report (Critical/Warning/Suggestion).
Complements /simplify: This skill handles deep analysis dimensions. /simplify handles code reuse, readability, and basic efficiency. They do not overlap.
Overview
You are the Review Orchestrator. You do NOT analyze anything yourself — you delegate to specialist subagents, collect their results, deduplicate findings, and produce a unified report.
Your subagents:
Complexity Analyst (.claude/agents/complexity-analyst.md) — Big O time/space, hot paths, call chains
DS&A Reviewer (.claude/agents/ds-reviewer.md) — Data structure selection vs access patterns
Flow Integrator (.claude/agents/flow-integrator.md) — End-to-end navigation-flow correctness (conditional spawn: only runs when diff touches navigation surfaces)
Prose Reviewer (.claude/agents/prose-reviewer.md) — Semantic defects in shipped methodology prose: inverted conditionals, unspecified branches, instructions naming capabilities that do not exist (conditional spawn: only runs when the diff touches src/wrought/claude/**/*.md)
Note: Agents 1–4 run only when the diff contains code; Agent 5 only when that code touches navigation surfaces; Agent 6 only when the diff contains shipped prose. See Step 4a-ter for the count.
Note: Agent 6's output is quarantined (CST-014) — advisory only, no F-numbers, never counted, never gating. Agents 1–5 produce Critical/Warning/Suggestion findings that do gate.
Step 1: Parse Arguments
Parse the user's invocation arguments:
--scope=diff (DEFAULT if not specified): Review only changed files
: Review all source files in the project
--scope=full
[file or directory] (optional): Narrow the review to a specific path
Examples:
/forge-review → diff scope, all changed files
/forge-review --scope=full → full scope, entire project
/forge-review --scope=full src/engine/ → full scope, only src/engine/
/forge-review src/cli/commands.py → diff scope, only this file (if changed)
Step 1.5: Check Loop State
Read .claude/wrought-loop-state.json to determine if this review was triggered by a completed implementation loop.
If the file exists and review_pending: true:
Clear review_pending (set to false) by writing the updated state file
Extract finding_id and tracker_path from the state for lifecycle updates in Step 8
These same two fields are Step 8's round determination inputs, and tracker_path is reachable nowhere else in this skill — a standalone invocation therefore has no round, which is why Step 8's round logic lives inside its loop-context branch
Note: this means the review is part of the implementation pipeline, not a standalone invocation
If the file does not exist, or review_pending is absent/false:
Continue normally — this is a standalone invocation
No lifecycle updates will be performed in Step 8
Store the loop context (if any) for use in Step 8.
Step 2: Get File List
For --scope=diff (default)
Run these commands to get the list of changed files:
# Get unstaged changes
git diff --name-only HEAD 2>/dev/null
# Get staged changes
git diff --name-only --cached 2>/dev/null
# Get untracked files
git ls-files --others --exclude-standard 2>/dev/null
Combine and deduplicate the results.
Committed-branch review (the loop's usual shape — the implementation was committed before the review runs, so the working tree holds no reviewable changes but the branch does): resolve the list from the committed branch diff instead — git diff --name-only "$BASE"...HEAD (BASE = the PR base, e.g. main) — and review that committed diff throughout.
For --scope=full
git ls-files 2>/dev/null
Apply Filters
From whichever file list you obtained:
Path filter: If user specified a file or directory, filter to that path only
Lane split: Sort the surviving files into TWO independent lanes. A file belongs to at most one.
PROSE_FILES — shipped methodology prose: *.md under src/wrought/claude/ only
The lanes exist because the four code specialists analyse algorithmic complexity, data
structures, paradigm consistency and performance — dimensions defined for executable code.
Handing them prose produces noise, not review. Prose goes to prose-reviewer (Agent 6) and
nowhere else; code goes to Agents 1–5 and nowhere else.
PROSE_FILES is deliberately narrow. It does NOT include docs/** (findings trackers, RCAs,
design docs — audit-trail artifacts, not shipped instructions), the repo's own README.md /
CLAUDE.md, or .claude/** (a deployed mirror of src/wrought/claude/; reviewing it would
duplicate the source review). Prose embedded in Python string literals — cli/scaffold.py's
CLAUDE.md template and enforcement rules — is out of reach of any extension filter and is
covered instead by tests/claude/test_scaffold_prose_consistency.py.
Exclusion filter: Remove these patterns from BOTH lanes:
*.pyc, *.pyo, __pycache__/
node_modules/, vendor/, .venv/, venv/, env/
*.min.js, *.min.css, *.bundle.js
*.lock, package-lock.json, uv.lock, poetry.lock
.git/, .claude/
Test fixtures: tests/fixtures/, test_data/, testdata/
Store both lists — CODE_FILES and PROSE_FILES — for passing to subagents.
Step 3: Validate
Validate per lane, not globally. A lane being empty is normal and is not an abort condition on
its own — a prose-only diff has an empty CODE_FILES, and a code-only diff has an empty
PROSE_FILES. Both are ordinary and both are reviewable.
Abort ONLY if CODE_FILES and PROSE_FILES are both empty:
No reviewable files found for scope '{scope}'.
{If diff scope}: No changed files detected. Try --scope=full for a full codebase review.
{If full scope with path filter}: No source files found at '{path}'.
{If full scope}: No source files found in the project.
STOP — do not spawn subagents.
If either lane is non-empty, continue to Step 4 and spawn only the agents that lane feeds.
Step 4: Spawn Subagents in Parallel (0–6 agents depending on diff)
4a. Detect Navigation-Surface Trigger
Before spawning, check if the diff touches navigation surfaces. Spawn flow-integrator as a 5th agent if ANY of the following match:
File-path regex (check each changed file path):
routes/ or pages/ or app/ as a directory segment
[^/]+Layout\.[jt]sx? (layout files)
[^/]+Router\.[jt]sx? (router files)
_app\.[jt]sx? (Next.js _app)
middleware\.[jt]sx?
[^/]+\.route\.[jt]sx?
Extension filter (check for any changed file with):
If ANY category matches → set SPAWN_FLOW_INTEGRATOR = true. Otherwise → SPAWN_FLOW_INTEGRATOR = false.
The nav trigger is evaluated against CODE_FILES only — prose files never touch navigation surfaces.
The reference implementation of this heuristic is src/wrought/core/flow_trigger.py::should_spawn_flow_integrator() — tested by tests/claude/test_flow_integrator_trigger.py.
4a-bis. Detect Prose Trigger
Set SPAWN_PROSE_REVIEWER = true iff PROSE_FILES is non-empty. There is no heuristic here — the
lane split in Step 2 already decided it.
4a-ter. Compute AGENT_COUNT
AGENT_COUNT is derived per lane, NOT a fixed base of 4. A diff that touches no code spawns no code
specialists:
AGENT_COUNT = 0
if CODE_FILES is non-empty:
AGENT_COUNT += 4 # Agents 1-4
if SPAWN_FLOW_INTEGRATOR: AGENT_COUNT += 1 # Agent 5
if SPAWN_PROSE_REVIEWER:
AGENT_COUNT += 1 # Agent 6
Diff shape
AGENT_COUNT
backend code only
4
code touching navigation surfaces
5
shipped prose only
1
code + shipped prose
5
code + navigation + shipped prose
6
README.md / docs/** only (neither lane)
0 → Step 3 aborts
Mirrored by tests/claude/test_forge_review_spawn_count.py.
4b. Launch Agents in Parallel
CRITICAL: All agents MUST be launched in a SINGLE message using the Agent tool. This ensures true parallel execution.
For each subagent, use:
subagent_type: "general-purpose"
model: the per-spawn model assigned to that specialist (see the launch list below) — Precheck-1-confirmed to take effect independent of subagent_type
Include in the prompt: the file list, the scope description, and instruction to return structured findings
The prompt for each subagent should follow this template:
You are the {agent_name} subagent for /forge-review.
Your system prompt is defined in `.claude/agents/{agent_filename}.md` — read it first and follow all instructions.
## Review Scope
**Scope**: {diff|full}
**Files to review**:
{file_list — one file per line; `CODE_FILES` for Agents 1-5, `PROSE_FILES` for Agent 6}
## Instructions
1. Read your system prompt at `.claude/agents/{agent_filename}.md`
2. Read your MEMORY.md for prior context about this codebase
3. Analyze each file in the list above according to your system prompt
4. Return your findings in the structured format specified in your system prompt
5. Update your MEMORY.md with new patterns discovered
Return ONLY your structured findings. Do not include explanatory prose.
Launch agents in parallel, per the AGENT_COUNT computation in Step 4a-ter. Each specialist is pinned to an explicit per-spawn model so the fan-out partially decorrelates (see the same-lab caveat below). The {file_list} each agent receives is lane-specific — Agents 1–5 get CODE_FILES, Agent 6 gets PROSE_FILES. Never pass a lane's files to the other lane's agents:
Agent 1 (conditional on CODE_FILES): Complexity Analyst → .claude/agents/complexity-analyst.md — model: opus — CODE_FILES
Agent 2 (conditional on CODE_FILES): DS&A Reviewer → .claude/agents/ds-reviewer.md — model: opus — CODE_FILES
Agent 3 (conditional on CODE_FILES): Paradigm Enforcer → .claude/agents/paradigm-enforcer.md — model: sonnet (named calibration exception — see the decorrelation-axes note below) — CODE_FILES
Agent 4 (conditional on CODE_FILES): Efficiency Sentinel → .claude/agents/efficiency-sentinel.md — model: opus — CODE_FILES
Agent 5 (conditional on SPAWN_FLOW_INTEGRATOR): Flow Integrator → .claude/agents/flow-integrator.md — model: opus — CODE_FILES
Agent 6 (conditional on SPAWN_PROSE_REVIEWER): Prose Reviewer → .claude/agents/prose-reviewer.md — model: opus — PROSE_FILES
Agent 6 is quarantined (CST-014). Its findings are advisory: they render only in their own report
section, carry no F-numbers, and never enter Step 5's C/W/S parsing, Step 6a dedup, the Step 6d
counts, or Step 8's gate arithmetic. It is a triage filter reporting to a human, not a gate — and it
must not become one: CST-014 keeps this lane quarantined regardless of rule 9's severity tiers.
Four specialists are pinned model: opus (the F4 floor); paradigm-enforcer is held at model: sonnet as the named calibration exception — empirically the strictest severity calibrator across shipped reviews and the standing same-lab partial axis while the cross-lab lane's ran-rate is being proven. Flattening it is a one-line change gated on recorded ran overseer rows in the Routing Ledger (CST-012) — a tracker task, never prose drift.
Note (decorrelation axes): pinning the specialists to heterogeneous per-spawn models gives same-lab (Claude-tier) partial decorrelation — it breaks some shared blind spots between differently-sized models from the same lab, but same-lab is not a cross-lab hedge, and the pin set is cost-neutral when the main loop already runs Opus. True cross-lab decorrelation exists ONLY when the Step 4c overseer lane below actually runs: it is conditional on codex availability + working auth, and an absent rung means zero cross-lab decorrelation axis for that review — the recorded row says exactly that, never silence. The honest sell is blind-spot decorrelation + auditability, nothing more (off-lineage monitors transfer worse in-domain — arXiv:2607.06596). Field evidence that a saturated deterministic oracle is not review: Bun's Zig→Rust port passed a >1M-assertion suite and was still publicly called "unreviewed slop" (The Register, 2026-07-14) — oracle-saturation ≠ review. (Fable stays holstered per CST-004 — never auto-pin/auto-select it as a specialist model, never an overseer.)
Step 4c: Cross-Lab Overseer Lane (conditional — quarantined)
A single-shot, non-collaborative, cross-lab review pass (CST-011): artifact-scoped, read-only, objections-never-verdicts, output-to-human. It supplements — never substitutes — the human spine (CST-005).
Trigger
Run the lane iff ANY of: the diff/finding carries decorrelation_critical, irreversible, or launch_gating (tracker tags / the CP1 gate — the same CLI-computable signals Pass-B uses), OR the owner explicitly requests it. SKIP when Step 1.5 found loop context (review_pending was true) — that review is a loop-pipeline continuation whose oracle is tests; a manual /forge-review issued while review_pending is true is treated identically. NEVER invoke the lane from inside the Ralph loops, and it is NEVER a required CI check. AGENT_COUNT is unchanged by this lane. If the trigger does not fire, still record the row: --status waived --note not-triggered. Every review gets a row.
Rule-10 pre-send gate (HARD)
The bundle leaves the machine for a second vendor. Before sending, re-read the assembled bundle against the pre-send redaction discipline (rule 10 — private names, the day-job employer, prospect identities must not ride along). The Bash permission prompt for codex exec is deliberately NOT allowlisted: the prompt IS the human pre-send gate — never allowlist this command. The prompt shows only the argv, never the payload — so assemble and inspect the payload first: run the block's binding line plus the scoped git diff … -- "$@" together in a single shell invocation ("$@" is empty in a fresh shell and silently yields the FULL unscoped working-tree diff), sweep the output against the private-name key set, then send.
Set SCHEMA to the overseer_objections_schema.json in this SKILL file's own directory (resolve it relative to wherever this skill is installed — never a repo-anchored path). List the review's resolved scope plus any non-source artifacts under review (skill/schema/doc twins the source-file list cannot carry) as the positional arguments — one single-quoted path per argument; the payload must never exceed, nor silently undershoot, what the review actually covers. Then run exactly:
set -- 'PASTE-PATH-1' 'PASTE-PATH-2' # one single-quoted path per argument: the Step-2 resolved list + any non-source artifacts under review (skill/schema/doc twins)
[ "$#" -gt 0 ] || { echo "file list is empty — refusing to send an unscoped diff" >&2; exit 1; }
# branch already committed (the loop's usual shape)? swap HEAD for "$BASE"...HEAD in BOTH git diff lines below
git diff --quiet HEAD -- "$@" && { echo "no tracked hunks in scope — refusing (all-untracked? use the --no-index append rule)" >&2; exit 1; }
{
cat <<'BUNDLE'
You are a single-shot, non-collaborative cross-lab overseer (adversarial-artifact mode).
Review ONLY the artifact bundle below. You have no repository access; do not request files.
Output MUST match the provided JSON schema: a mandatory "omissions" channel (tiered
MUST-ADDRESS / SHOULD-CONSIDER / NOTE — omissions have no file:line) and an "anchored"
channel (file + verbatim snippet, never line numbers) that is empty when nothing anchors.
Objections only — no verdicts, no scores, no approve/reject.
## Acceptance criteria / intent under review
{PASTE: the ACs or blueprint excerpt}
## Files in scope
{PASTE: the Step-2 file list}
## Diff
BUNDLE
git diff HEAD -- "$@"
} | codex exec - -s read-only --ephemeral --ignore-user-config --ignore-rules \
-c model_reasoning_effort="high" \
--output-schema "$SCHEMA"
Quote every path at paste time — single quotes keep spaces, glob metacharacters, and $/backticks inert (a pasted path is data, never code). Untracked in-scope files carry no hunks — append git diff --no-index /dev/null "$f" || true per file (--no-index exits 1 on difference by design).
Effort is capped high on this rung — the Bash tool hard-caps at 600 s and deep xhigh passes will not fit; a timeout or non-zero exit records --status failed, never silent no-findings (a recurring failed here is the demand signal for a dedicated CLI rung). Record the model-id codex reports verbatim.
Rung 1 — codex exec (above): requires the binary present AND authenticated — presence is not auth; an unauthenticated binary is absent, and a broken rung falls through to the next, never blocks.
Rung 2 — manual paste: paste the identical bundle into the other lab's current frontier model UI and request the same tiered vocabulary in prose; record --overseer-model manual/{model-id verbatim}.
Rung 3 — waived: the owner declines, with the reason in --note.
Record ALL outcomes — absence is data: ran (a rung completed; note which rung and which were skipped) · waived (owner declined or not-triggered) · absent (no rung available) · failed (a rung started and errored/timed out). Emit the row and paste it into BOTH the report's Cross-Lab Overseer section and the tracker's ### Routing Ledger:
The overseer's output renders ONLY in the report's ## Cross-Lab Objections (advisory — not tracker findings) section. It never enters Step 5 collection, Step 6a dedup, the C/W/S counts, or Step 8's gate arithmetic; it carries no F-numbers and can never block. A human promotes an objection into a C/W/S finding by hand — that promotion is the CST-005 spine. Objections only — no verdicts, no scores, no approve/reject.
Step 5: Collect Results
Wait for all agents to complete (AGENT_COUNT of them, per Step 4a-ter). Each will return structured findings in their specified format:
Field 2 of every gating result set is the reachability token — live or latent. It is defined identically in all five agent files and is the input to Step 6b's cap. A result line missing it is malformed: treat the finding as live (fail toward gating) and note the malformed line in the report rather than silently guessing latent.
Parse each result set. If a subagent returned "No X findings." (or "No flow-integration findings." for Flow Integrator), record zero findings for that agent.
Agent 6 (Prose Reviewer) is NOT one of these result sets. Collect its output separately, verbatim, into PROSE_OBJECTIONS:
Prose Reviewer (if spawned): {tier} | {file}:{line} | {defect_class} | {description} | {evidence} | {suggested_fix} — tiers are MUST-ADDRESS / SHOULD-CONSIDER / NOTE, never Critical/Warning/Suggestion. "No prose findings." means zero objections.
Never parse PROSE_OBJECTIONS as a C/W/S result set, never assign it severities from the C/W/S scale, and never merge it into the finding list. It is quarantined (CST-014) and renders only in its own report section. The tier vocabulary differs precisely so this separation cannot be lost by accident.
The Step 4c overseer output is NOT one of these result sets either — the overseer is not an agent; never parse its output as a result set here, and it never enters 6a dedup or the C/W/S counts.
Step 6: Aggregate
6a. Deduplicate
If two or more agents flag the same file:line (within 5 lines tolerance):
Merge into a single finding
List all contributing agents in the Agent field (e.g., "Complexity Analyst, Efficiency Sentinel")
Use the highest severity from any contributing agent
Merge reachability to live if any contributing agent said live; otherwise latent.
This errs toward gating on purpose: a live claim names a symptom someone can go and observe, so
keeping it costs a check, while dropping it could hide a real defect behind a second agent's
more cautious call.
Composition of the two rules above — they interact, and the interaction is not either rule alone:
a merged finding's severity is capped by the severity of the contributor whose live claim was
adopted. Without this, Suggestion | live merged with Critical | latent at the same file:line
takes Critical (highest severity) and live (any-live), passes 6b's cap untouched, and gates — a
Critical no agent filed. Suggestion | live is ordinary output, not a corner case.
Combine issue descriptions
Note on flow-integrator overlap: Flow Integrator's findings focus on behavioral navigation correctness. They may occasionally overlap with design-quality agent findings (once GitHub issue #134 ships) when both flag the same component file. The same dedup rules apply — merge and list both agents in the Agent field.
Never dedup or merge Cross-Lab Objections into C/W/S findings — they are quarantined (CST-011) and render only in their own report section.
Never dedup or merge Prose Reviewer objections (PROSE_OBJECTIONS) into C/W/S findings either — they are quarantined (CST-014). A prose objection and a code finding may name the same file:line; that is not a duplicate and must not be merged, because the two lanes answer different questions and only one of them gates.
6b. Apply the Reachability Cap, Then Assign Severity Tiers
First, cap on reachability. Every finding carrying latent is capped at Suggestion. This is a
lookup on field 2, never a judgement about how the finding is worded. Findings carrying live pass
through at whatever severity the agent assigned — the cap never raises a severity and never lowers a
live one.
A latent finding is never dropped by the cap: it renders in the Suggestions section carrying
its pre-cap severity as [capped: latent — agent assigned {severity}], it counts in N_suggestions at 6d, and
Step 8 auto-appends it as a Review Suggestion F-number. It stops gating; it does not disappear, and
a human can promote it if the agent's reachability call was wrong.
Why the cap exists. Severity was being asked to carry two independent facts at once: how bad a
defect class is, and whether it affects the artifact today. The specialist rubrics define every
severity in runtime terms — hot paths, latency, asymptotic cost on large collections, memory growth —
and none has a row for an artifact whose only consequence is detection coverage. Session 146 measured
the result: three non-converging review rounds against a pytest guard, and of the nine Critical and
Warning findings aimed at its static census, zero were live defects. Rule 9 converted them into
same-session blockers, which scheduled the next round.
Calibration table. Real findings from that session, with the rule applied. Use these to calibrate;
they are measured cases, not illustrations:
Finding
Agent severity
Reachability
Tier after cap
Round 4 C1 — the shipped hook converted a failing verifier into a silent pass
Critical
live
Critical
Round 6 C1 — _publishes_state's first union key is dead (findall returns tuples)
Critical
latent
Suggestion
Round 6 C2 — all() over a domain the extractor cannot reach
Critical
latent
Suggestion
Round 6 C3 — the directory assertion is a name-mention proxy, not a path relation
Critical
latent
Suggestion
Round 6 W4 — the behavioural half never executes the max-iteration branch, so two of the eight shipped writers run under no assertion. The hook was correct there and the suite passed correctly on it
Warning
latent
Suggestion
Round 5 W1 — the live-poll oracle is inert on the jq backend at the shipped fixture size
Warning
live
Warning
Read the two Warning rows together — they are the whole discriminator. R5 W1 is live: the oracle
was inert on that backend, so the test passed while observing nothing, and it would have passed on the
pre-fix hook. A test that passes on an artifact that is wrong is a live defect and earns its
severity. R6 W4 is latent: the hook was correct at that branch and the suite passed correctly on
it, so the finding is about coverage a future edit could exploit — not about anything wrong today.
Do not read that as the cap discarding value. R6 W4 was a real and useful finding — S146 fixed it —
and under this rule it lands as a Suggestion: reported, tracked, promotable, non-gating. That is the
rule working as designed, not an exception to it. The cap preserves the finding, not its severity,
and a coverage gap that blocks a session is the miscalibration this whole mechanism exists to remove.
The three latent Criticals are the same case at a higher pre-cap severity: each was real in the
census's own logic, and each was conditional on an edit nobody had made.
Then group the capped findings into three tiers:
Critical: All findings with severity "Critical"
Warning: All findings with severity "Warning"
Suggestion: All findings with severity "Suggestion"
6c. Number Findings
Within each tier, number sequentially:
Critical: C1, C2, C3...
Warning: W1, W2, W3...
Suggestion: S1, S2, S3...
6d. Count Totals
Record:
Total critical count
Total warning count (N_warnings)
Total suggestion count (N_suggestions)
Total files reviewed
Total files with findings
Prose Reviewer objections are excluded from every count above (CST-014). Count them separately as PROSE_OBJECTION_COUNT, broken down by tier, and never add that number into the critical/warning/suggestion totals — those totals feed Step 8's gate arithmetic, and a quarantined lane must not be able to move a gate. Total files reviewed DOES include PROSE_FILES, since those files genuinely were reviewed; only the findings are quarantined.
Step 7: Write Report
Read the report template at src/wrought/claude/skills/forge-review/report_template.md (or the report_template.md beside this SKILL file, wherever it is installed).
Generate the report by filling in the template. Determine the output filename:
docs/reviews/{YYYY-MM-DD_HHMM}_{scope}.md
Where:
{YYYY-MM-DD_HHMM} is the current timestamp
{scope} is diff or full
Example: docs/reviews/2026-03-03_1400_diff.md
Create the docs/reviews/ directory if it doesn't exist.
Write the completed report using the Write tool.
Every Critical, Warning and Suggestion block MUST render its Reachability field, and any finding the Step 6b cap moved MUST carry its [capped: latent — agent assigned {severity}] marker. A capped finding that renders as an ordinary Suggestion is indistinguishable from one the agent filed as a Suggestion, which destroys the human's ability to promote it.
The report MUST include both Cross-Lab sections (## Cross-Lab Overseer + ## Cross-Lab Objections (advisory — not tracker findings)) — fill the status even when the pass was waived or absent.
The report MUST also include ## Prose Review (advisory — not tracker findings), rendered unconditionally — including when the prose lane did not spawn, and when it spawned and found nothing. State which case applies. An absent section is indistinguishable from a clean one, and a quarantined lane that renders nothing is a lane nobody reads.
Step 8: Pipeline Handoff
When loop context is present (from Step 1.5)
If finding_id and tracker_path were extracted from the loop state:
First, determine the review ROUND. This governs blocking disposition only — severity assignment stays solely at Step 6b (CST-018), and minting stays unconditional on round (below).
Split finding_id on +. It is a set, not a scalar: docs/capsules/ holds twelve +-joined ids, F2+F3+F8+F9 among them
Resolve each id against tracker_path and read its Type column
The review is round 2+ only if, for every resolved id, the Type is exactly "Review Warning" or exactly "Review Suggestion". Equality, never containment — a later Type-value extension must fall back to round 1, and a contains predicate would silently open the gate instead of closing it
Otherwise it is round 1, and it gates. In particular an unresolvable lookup counts as round 1 and gates: a missing tracker, a missing row, an empty split, or a Type this skill does not recognise. The tracker row parser is known to drop rows, and failing closed is what absorbs that without opening a correctness hole
This tests a proxy, and says so. Rule 9's clause is written over diff content — "a diff consisting solely of remediation for earlier review findings" — while what is actually tested is the loop finding's Type. The approximation is deliberate: diff-content analysis has no oracle here, whereas the Type column is written by this very step, so the discriminator cannot drift from what Step 8 appends
If Critical findings > 0:
Set the finding's stage to "Reviewed" in the tracker (it stays at Reviewed — not Resolved)
Do NOT advance to Resolved. The finding remains at "Reviewed" until criticals are fixed.
This branch is round-independent. Criticals block at every round; the round bound never reaches them.
If Critical == 0, but Warnings or Suggestions exist:
Auto-append each Warning as a new F-number in the tracker with type "Review Warning" and severity "Medium", recommended next step /rca-bugfix. Minting is unconditional on round — the round bound changes the gate, never the ledger. At round 2+ the F-number's title MUST additionally carry the marker [round 2+ — non-blocking]: /session-end Step 6a scans trackers with no loop state in hand, so this row is the only place the round survives, and a row without the marker is read as round 1 and gates. The tracker row is the ONLY site that applies this marker; the console templates below render the stored title and never append it
Auto-append each Suggestion as a new F-number in the tracker with type "Review Suggestion" and severity "Low", recommended next step /simplify. If Step 6b capped the finding, the F-number MUST carry its [capped: latent — agent assigned {severity}] marker. The tracker is the only place findings survive the session, so a marker that stops at the dated report leaves Step 6b's promise — that a human can promote a capped finding whose reachability call was wrong — with nothing to act on.
Cross-Lab Objections are never auto-appended and never counted here — they carry no F-numbers (CST-011); a human promotes an objection into a finding by hand
Prose Reviewer objections are never auto-appended and never counted here — they carry no F-numbers (CST-014); a human promotes an objection into a finding by hand. This is the load-bearing clause: Step 8's auto-append is where findings become tracked F-numbers, so auto-appending a broad new prose lane here would multiply the review backlog on its first run
Set the original finding's stage to "Reviewed", then immediately advance to "Resolved"
Output:
Review complete. Original finding {finding_id} → Resolved.
Review report: {output_path}
{If N_warnings > 0 and round == 1}:
WARNINGS BLOCK — {N_warnings} new Review Warnings MUST be resolved before session end:
{For each new Warning F-number: " F{N}: {severity} — {title} → /rca-bugfix"}
Address warnings via /rca-bugfix NOW. Do NOT proceed to /session-end until every Review Warning is Resolved or explicitly Rejected with a rationale.
{End if}
{If N_warnings > 0 and round >= 2}:
{N_warnings} new Review Warnings logged and marked `[round 2+ — non-blocking]` — these do NOT block session end. Rule 9's in-session obligation applies to the first `/forge-review` of a session's substantive diff, and this review examined a remediation-only diff.
{For each new Warning F-number: " F{N}: {severity} — {title} → /rca-bugfix"}
{End if}
{If N_suggestions > 0}:
{N_suggestions} new Review Suggestions logged to the queue — these do NOT block session end (rule 9). Address them via /simplify when you choose to spend a session on them.
{For each new Suggestion F-number: " F{N}: {severity} — {title} → /simplify"}
{End if}
On the {End if} terminators: Step 9's guarded regions are each a single content line, so the
blank line that follows them is an unambiguous terminator and they need nothing further. The three
regions above span multiple lines, which that convention has no answer for — a blank line inside a
region would silently end it, and the reader cannot tell which reading was intended. An explicit
terminator is therefore used HERE and only here. Review report: sits above all three guards
precisely so it can never fall inside one. The two Warning regions are mutually exclusive on
round and each opens its own guard — neither shares the other's, so exactly one can print.
If clean (0 critical, 0 warnings, 0 suggestions):
Set the original finding's stage to "Reviewed", then immediately advance to "Resolved"
When a Suggestion is intentionally Rejected with a durable rationale ("don't do X / don't re-propose Y because Z") — in addition to recording "Resolved: Rejected — {rationale}" in the tracker (rule 9) — promote it to the ## Active Constraints (in force) section in CLAUDE.md (append under ### Durable invariants):
so the rejected guidance is required-read and isn't re-surfaced in a future review. Text-persistence, not compliance (see CONVENTIONS.md). Skip transient/style rejections.
Step 9: Display Summary
Output to the user:
Review complete: {N} critical, {N} warnings, {N} suggestions across {N} files ({AGENT_COUNT} agents run).
Cross-lab overseer: {status} ({mode}, {model-id}).
Prose review: {PROSE_OBJECTION_COUNT} objections, advisory ({N} files) | not run (no shipped prose in scope).
Report saved to {output_path}.
{If flow-integrator spawned}:
Flow-integration pass was performed (diff touched navigation surfaces).
{If prose objections > 0}:
{N} prose objections are ADVISORY — they carry no F-numbers and block nothing. Read them in the report; promote any you accept into a finding by hand.
{If critical > 0 or warnings > 0}:
Findings detected ({N} critical, {N} warnings). Consider running `/finding` to create a Findings Tracker for remediation.
{If suggestions > 0}:
{N} suggestions detected that may be auto-fixable. Consider running `/simplify` to address them.
{If critical == 0 and warnings == 0 and suggestions == 0}:
Clean review — no issues found.
STOP — await user instructions. Do NOT proceed with fixes or implementations.
Read-Only Guarantee
This skill and its subagents are read-only with respect to the user's source code:
Subagents: Have tools: Read, Grep, Glob, Bash — no Write/Edit. Memory writes go to each agent's declared scope — the review agents use local (.claude/agent-memory-local/<name>/); decorrelation-critic has no memory.
Orchestrator: Uses Write ONLY to create the review report in docs/reviews/. Never modifies source code, configuration files, or any file outside docs/reviews/.
Overseer lane (Step 4c): streams its bundle via stdin and writes no files; its output lands only in the report's quarantined section.
If you find yourself about to modify a source file — STOP. That is not your job. Report the finding in the review.
Flags
--scope=diff Review only changed files (default)
--scope=full Review all project source files
No --batch mode — review is always non-interactive (subagents work autonomously).
Example Invocations
/forge-review → Review changed files (diff scope)
/forge-review --scope=full → Review entire codebase
/forge-review --scope=full src/wrought/ → Review all files in src/wrought/
/forge-review src/wrought/cli/main.py → Review specific changed file
Findings Tracker Update Protocol
When loop context is present (Step 1.5 extracted finding_id and tracker_path), follow _shared/tracker_update_checklist.md with these parameters:
Parameter
Value
{STAGE_NAME}
Reviewed
{TASK_DESCRIPTION}
FN.5: Code review
{ARTIFACT_TYPE}
Review report
{ARTIFACT_PATH_PATTERN}
docs/reviews/{YYYY-MM-DD_HHMM}_{scope}.md
Lifecycle updates performed in Step 8:
Update overview table: Stage → "Reviewed", Status → "In Progress"