Select relevant reviewer personas based on the PR diff, launch parallel reviewer subagents, merge their findings (with signal-strength and dissent handling), and post one unified review.
No active persona required — this skill selects its own reviewers.
-
Platform commands — platform-specific commands are inlined via ! preprocessing. No detection needed.
-
Resolve the request and detect mode — resolve the request number from $ARGUMENTS (URL → extract number, number → use directly, empty → detect from current branch).
Consolidated Fetch:
!`cat ~/.claude/platform-commands/consolidated-fetch.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
Parse JSON response. Store REQUEST_NUMBER, REQUEST_TITLE, HEAD_BRANCH, BASE_BRANCH.
Terminal State Handling: Check state first. If terminal (merged or closed): load deferred tool schemas via ToolSearch("select:CronList,CronDelete"), then use CronList to find any cron job whose prompt contains the skill name and <REQUEST_NUMBER>, cancel it with CronDelete if found, announce and stop.
Check for previous team reviews using the reviews data already fetched. Filter for *Role:* Team-Reviewer in review bodies. If found, set MODE=re-review, store LAST_REVIEW_ID and LAST_REVIEW_TS, and read re-review-mode.md from this skill's directory. Otherwise, set MODE=first-review.
Announce: 🔍 Mode: first review or 🔄 Mode: re-review (previous team review from <LAST_REVIEW_TS>)
-
Quick-exit check (re-review only) — follow the two-phase check in re-review-mode.md. If nothing has changed, emit a single line and stop:
PR #<REQUEST_NUMBER>: no changes since last team review (<LAST_REVIEW_TS>). Skipping. 🔄
-
Fetch PR metadata and diff — run these in parallel:
Fetch Diff → store as FULL_DIFF:
!`cat ~/.claude/platform-commands/fetch-review-diff.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
Fetch Files Changed → store as CHANGED_FILES:
!`cat ~/.claude/platform-commands/fetch-review-files.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
Fetch Review Details → store REQUEST_BODY:
!`cat ~/.claude/platform-commands/fetch-review-details.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
Fetch Commits → store as COMMITS:
!`cat ~/.claude/platform-commands/fetch-review-commits.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
For large diffs, read the full diff — thorough review requires seeing all changes.
Re-review only: Also identify NEW_COMMITS — commits after LAST_REVIEW_TS.
-
Select reviewer personas — read persona-routing.md from this skill's directory, then:
- Glob
~/.claude/commands/set-persona/*.md to get all persona files
- Read the first 5-10 lines of each (name + description + domain priorities heading)
- Derive domain terms from
CHANGED_FILES paths using judgment (see persona-routing.md)
- Match terms against persona descriptions using the matching heuristic
- Apply constraints: min 1, max 3. Don't select both a child and its parent persona.
- If no domain persona matches →
reviewer alone (N=1)
Announce: 🎭 Team: <persona-1>, <persona-2> (matched from <domains>) or 🎭 Single reviewer: <persona> (only domain match)
If N=1: Read single-reviewer-mode.md and follow its instructions. Skip steps 6 and 8-11 — the orchestrator reviews directly. Still execute step 7 (system context) — it's cheap and valuable for single-reviewer mode. Then resume at step 13.
-
Front-load persona content — for each selected persona:
- Read the full persona file
- If it has
## Extends:, read parent persona(s) in declaration order
- If it (or its parents) has
## Proactive Cross-Refs or ## Proactive loads, resolve provider: paths before loading: read ~/.claude/learnings-providers.json, expand provider:default/path via the defaultWriteTarget provider's localPath, and provider:<name>/path via the named provider's localPath. Skip references whose provider isn't in the config.
- Read each resolved proactive file
- Store the combined content as
PERSONA_CONTENT[persona_name]
-
Identify system context for cross-cutting code — scan CHANGED_FILES for cross-cutting patterns: AOP aspects (@Aspect), interceptors, shared utilities, SPI implementations, filters, or middleware. If found:
- Grep the codebase for callers: annotation usages (
@ExternalService, @Timed), injection sites, pointcut targets
- For each caller, note: class name, threading model (web request,
@Scheduled, @Async, message listener), and invocation frequency if discoverable (e.g., fixedDelay value)
- Summarize as
SYSTEM_CONTEXT: "Changed code is called by X (single-threaded scheduler, 30s interval), Y (web request handler, pooled threads)."
If no cross-cutting patterns detected, set SYSTEM_CONTEXT to empty. This step is lightweight — a few targeted greps, not a full codebase scan.
-
Launch parallel reviewer subagents — read reviewer-prompt-template.md from this skill's directory. For each selected persona, launch a foreground Agent (all in one message for parallel execution). Each subagent prompt includes:
- The reviewer prompt template with placeholders filled:
{{PERSONA_NAME}} → persona name
{{PERSONA_CONTENT}} → front-loaded content from step 6
{{SYSTEM_CONTEXT}} → caller/threading context from step 7 (or empty)
{{REQUEST_TITLE}}, {{REQUEST_BODY}}, {{COMMITS}} → PR metadata
{{FULL_DIFF}} → the full diff
{{OUTPUT_FILE}} → tmp/claude-artifacts/change-request-replies/team-review-<REQUEST_NUMBER>-<persona>-findings.json
Wait for all subagents to complete before proceeding.
-
Collect and verify findings — read each subagent's output file. Verify per ~/.claude/skill-references/subagent-patterns.md: spot-check that findings reference real files from the diff. Parse the structured JSON.
Line number correction (MANDATORY — DO NOT SKIP): Subagents derive line numbers from the diff, which is error-prone — off-by-1-3 lines is common. Inline comments posted on wrong lines undermine review credibility. For EVERY finding, verify and correct line_start before merging:
a. Extract a recognizable code token from the finding's summary or inline_comment (e.g., a function name, variable, keyword — the most specific identifier mentioned).
a-prime. Detect diff-artifact-offset reporting first. When the diff is provided as a single concatenated text artifact, subagents sometimes return positions counting from the artifact start instead of source-file lines (e.g., line_start: 336 for the 92nd line of the 8th file in a 765-line artifact). If line_start exceeds the source file's line count at head SHA (git show <head_sha>:<file> | wc -l), this is the failure mode — the ±5/±10 windows in (b)-(e) cannot recover it because the line doesn't exist in the file. Re-anchor immediately: git show <head_sha>:<file> + grep anchor_token, then skip (b)-(e). This is distinct from the <= 5 placeholder case in (f), which catches the opposite extreme.
b. Read the actual file content around the reported line_start (±5 lines): Read(file, offset=line_start-5, limit=11).
c. Check if the code token appears at line_start. If yes, the line number is correct — move on.
d. If not, scan the ±5 line window for the token. If found, update line_start (and line_end by the same delta) to the correct line.
e. If the token isn't found within ±5 lines, widen to ±10. If still not found, keep the original and flag: ⚠️ Could not verify line number for <file>:<line_start> (<token>).
f. New-file placeholder fix. Subagents commonly emit line_start: 1, line_end: 1 (or 0, 0) for findings on wholly-new files in the diff — the ±5/±10 windows don't help. When line_start <= 5 AND the file is fully + in the diff: fetch the MR-branch source via git show origin/<HEAD_BRANCH>:<file> and grep the anchor_token across the full file. Update line_start/line_end to the matched line. If the anchor is non-unique, pick the occurrence closest to the finding's described context.
This step catches the most common subagent error (LLM line-counting imprecision) before it reaches GitLab. Correct lines before merging — merged findings inherit the corrected positions.
-
Merge findings — follow the Merge Algorithm in persona-routing.md:
- Index findings by
(file, overlapping line range, category)
- Agreement (2+ personas, compatible): merge into one finding, tag
[persona-1, persona-2]
- Unique (1 persona): pass through with single-persona attribution
[persona-1]
- Disagreement (conflicting severity or contradictory recommendations): flag as
DISSENT_CANDIDATE
Not a disagreement: both agree on the problem but suggest different fixes — merge with complementary recommendations.
-
Deliberation (only if DISSENT_CANDIDATES exist) — for each disagreement between 2 personas:
- SendMessage to Persona A's subagent: "Persona B disagrees with your finding on
<file>:<line>. Their position: [B's summary and reasoning]. Does this change your recommendation? Respond with MAINTAIN or REVISE and brief reasoning."
- Simultaneously SendMessage to Persona B's subagent with the symmetric prompt.
- Both sent in parallel — neither needs the other's rebuttal.
- If either revises: resolved. Use the consensus position. Tag as agreement with both personas.
- If both hold: unresolved. Surface as
⚖️ DISSENT block in the review with both positions and their rebuttals.
For 3-way dissents (rare with max 3 reviewers): the orchestrator summarizes all positions as team lead and presents the tradeoff — no SendMessage round-trips.
-
Compose the merged review — build the review body:
## Team Review: <REQUEST_TITLE>
<2-3 sentence overview of the change and overall assessment>
Reviewed by: <persona-1>, <persona-2>, <persona-3>
### Findings
N finding(s) — see inline comments.
### ⚖️ Dissent
<Only if unresolved dissents exist. For each:>
**<topic>** — <persona-A>: <position and reasoning>. <persona-B>: <position and reasoning>. Both held after deliberation.
### Positive Signals
<Themes done well, with persona attribution where relevant>
Append the Footnote Format from the base reference with Role: Team-Reviewer. For the *Persona:* field, list all reviewer personas used (e.g., *Persona:* react-frontend, financial-reviewer), not the orchestrator's persona. The base reference's persona detection precedence (formal → ad-hoc → none) applies to single-persona skills; team-review always has explicit reviewer personas from step 5.
Inline comments: for each merged finding, compose one inline comment with combined attribution. Use the most detailed inline_comment from the contributing subagents, prefixed with the signal-strength tag. Never post duplicate comments on the same line range.
Body discipline. The body is a count + pointer, not a finding list. The ### Findings section is exactly one line: N finding(s) — see inline comments. No bullets, no per-finding summaries, no file paths, no rationale. All specifics live in inline comments exclusively. Summary-only findings (no inline target) get appended as N summary-only finding(s): <one-sentence theme>. Allowed sections only: overview, reviewer roster, Findings, Dissent, Positive Signals.
-
Post the review — write the review payload to tmp/claude-artifacts/change-request-replies/review-<REQUEST_NUMBER>-team-reviewer.json. Event: COMMENT.
Inline target must be in the diff. GitLab createDiffNote (and GitHub's equivalent) require newLine to be a line that appears in the MR/PR diff (added or context). Findings whose target file is unchanged in the diff — e.g., a pre-existing helper class called from new code — cannot post inline. Re-anchor on a related changed line (the call site in a changed file) and reference the unchanged file in the body, OR demote to summary-only. Don't try to post and discover the rejection at request time.
!`cat ~/.claude/platform-commands/post-code-review.sh 2>/dev/null || echo "UNCONFIGURED: run setup-claude.sh to set up platform-commands"`
Re-review only: Also execute reactions and follow-ups per re-review-mode.md.
-
Clean up and report — remove temp files (review payload JSON). Preserve subagent findings JSONs (team-review-<N>-<persona>-findings.json) — they contain the raw line numbers and reasoning that directors need for debugging when comments land on wrong lines. The findings are small and uniquely named; they'll be cleaned up when the operator clears tmp/claude-artifacts/change-request-replies/. Then confirm:
✅ Team review posted on <REVIEW_UNIT> #<REQUEST_NUMBER> (<N> inline comments, <M> personas)
<REQUEST_URL>
Re-review report format is in re-review-mode.md.