| name | pr-review |
| description | Intent-aware map-reduce PR review with CI/CD support, confidence gating, and intelligent comment deduplication. |
| allowed-tools | Bash(echo *), Bash(rp1 *) |
| metadata | {"category":"review","is_workflow":true,"workflow":{"run_policy":"fresh","identity_args":[]},"version":"4.1.0","tags":["review","pr","security","analysis","map-reduce","ci"],"created":"2025-10-25T00:00:00.000Z","updated":"2026-04-12T00:00:00.000Z","author":"cloud-on-prem/rp1","arguments":[{"name":"TARGET","type":"string","required":false,"description":"PR number, PR URL, branch name, or empty for current branch"},{"name":"BASE_BRANCH","type":"string","required":false,"description":"Diff base branch (default: from PR or main)"},{"name":"SKIP_VISUAL","type":"boolean","required":false,"default":false,"description":"Skip visual diagram generation","aliases":["skip-visual","no visual"]}],"sub_agents":["rp1-dev:pr-visualizer","rp1-dev:pr-review-splitter","rp1-dev:pr-sub-reviewer","rp1-dev:pr-review-synthesizer","rp1-dev:pr-review-reporter","rp1-dev:pr-comment-deduplicator","rp1-dev:pr-comment-poster"]} |
PR Review Orchestrator
§ROLE: Map-reduce PR review orchestrator. 6 phases, local + CI modes, comment deduplication.
§CTX: Use the pre-resolved projectRoot, kbRoot, and workRoot values from the generated Workflow Bootstrap section. Do not hardcode .rp1/work/ or .rp1/context/ paths.
§GUARDRAILS
- Never create git worktrees under
.rp1/work/ or anywhere inside the target project's .rp1/ directory.
- If a separate checkout is absolutely required during review, use a temporary path outside the project artifact tree.
References
| File | Purpose | When to Load |
|---|
references/link-artifacts.md | Reusable external link artifact registration pattern and PR review binding | During finalization phase (after P4 reporting) |
STATE-MACHINE
stateDiagram-v2
[*] --> reviewing
reviewing --> posting : analysis_complete
posting --> [*] : done
On each phase transition, report via:
rp1 agent-tools emit \
--workflow pr-review \
--type status_change \
--run-id {RUN_ID} \
--step {CURRENT_STATE} \
--data '{"status": "running"}'
RUN_ID comes from the generated Workflow Bootstrap section
- Derive
RUN_NAME from the resolved PR context: use "PR #{pr_number}" when a PR number is available, otherwise use "PR: {branch_name}" as fallback
- On the first emit only, include
--name "{RUN_NAME}" to label the run
On session start, emit the status change:
rp1 agent-tools emit \
--workflow pr-review \
--type status_change \
--run-id {RUN_ID} \
--name "{RUN_NAME}" \
--step reviewing \
--data '{"status": "running"}'
State Progression Protocol:
- Report each
--step with --data '{"status": "running"}' when you enter that state
- For non-terminal states: move to the NEXT state when done (entering the next state implies the previous completed)
- For terminal states (those with
→ [*] transitions): report with --data '{"status": "completed"}' and --close-run when the step's work finishes
- On error, transition to the appropriate failure state in the graph
State mapping:
reviewing covers: P-1 (config), P0 (input resolution), P0.5 (visual gen), P1 (splitting), P2 (sub-reviewers), P3 (synthesis)
posting covers: P4 (reporting), P5 (comment posting)
Example sequence:
--workflow pr-review --step reviewing --name "PR #42" --data '{"status": "running"}' # first emit includes --name
--workflow pr-review --step posting --data '{"status": "running"}' # analysis done, entering posting phase
--workflow pr-review --step posting --data '{"status": "completed"}' --close-run # posting done, workflow complete
§ARCH
P-1 (seq): Config Load -> CI Detection -> Early Exit Check
P0 (seq): Input Resolution -> Intent Model
P0.5 (bg): Visual Gen (conditional, parallel w/ P1)
P1 (seq): Splitter -> ReviewUnit[]
P2 (par): N x Sub-Reviewers -> Findings + Summaries
P3 (seq): Synthesizer -> Cross-File Issues + Judgment
P4 (seq): Reporter -> Markdown Report + Structured Data
P5 (seq): Comment Posting (CI only) -> GitHub Review
§PROC
Execute immediately. No approval prompts.
P-1: Config + CI Detection
-
Load config: Read {projectRoot}/.rp1/config/pr-review.yaml if exists:
enabled: boolean
review_drafts: boolean
ai_harness: string
add_comments: boolean
collapse_summary: boolean
verdict: string
max_comments: integer
bot_marker: string
visualize: boolean
ci_platform: string
Missing file -> use defaults.
-
Detect CI: CI=true env -> CI_MODE=true, else CI_MODE=false
Platform: CI_PLATFORM = config.ci_platform (default: github)
-
Env overrides:
| Env Var | Overrides |
|---|
| RP1_PR_REVIEW_ENABLED | enabled |
| RP1_PR_REVIEW_VERDICT | verdict |
| RP1_PR_REVIEW_ADD_COMMENTS | add_comments |
| RP1_PR_REVIEW_VISUALIZE | visualize |
-
Early exit (CI only):
if CI_MODE AND NOT config.enabled -> Output config instructions, EXIT
-
Build CI_CONTEXT (GitHub only):
- Read
GITHUB_EVENT_PATH -> pr_number, action, is_draft
- Read
GITHUB_REPOSITORY -> owner, repo
- Read
GITHUB_TOKEN, GITHUB_HEAD_REF, GITHUB_BASE_REF
Store: {"pr_number": N, "owner": "...", "repo": "...", "token": "...", "head_ref": "...", "base_ref": "...", "is_draft": false}
-
Draft check (CI only):
if CI_MODE AND CI_CONTEXT.is_draft AND NOT config.review_drafts -> Skip, EXIT
Pre-flight: Git State (Local Only)
Skip if CI_MODE=true.
git status --porcelain
- If non-empty:
rp1 agent-tools emit \
--workflow pr-review \
--type waiting_for_user \
--run-id {RUN_ID} \
--step reviewing \
--data '{"prompt": "Stash local changes and continue, or abort the review?", "context": "PR review needs a clean local git worktree before analysis"}'
{% ask_user "Stash and continue?", options: "Stash and continue", "Abort" %}
- Stash:
git stash push -m "rp1-pr-review-auto-stash", set STASHED=true
Abort:
rp1 agent-tools emit end-run \
--run-id {RUN_ID} \
--outcome cancelled \
--reason "User aborted review instead of stashing local changes"
Exit "Review cancelled."
P0: Input Resolution + Intent
CI Mode
- From CI_CONTEXT:
pr_branch, base_branch (BASE_BRANCH if provided), pr_number
gh pr view {{pr_number}} --json title,body,url 2>/dev/null -> PR_METADATA
- Set
REVIEWED_PR_URL = PR_METADATA.url when present, else REVIEWED_PR_URL=""
- Parse title ->
problem_statement, body -> expected_changes, acceptance_criteria
Parse fails -> mode="ci_minimal", problem_statement="Review PR #{{pr_number}}"; keep REVIEWED_PR_URL if url was parsed
- No user prompts (AFK)
Local Mode
-
Set REVIEWED_PR_URL=""
-
Resolve target:
| Input | Detection | Resolution |
|---|
| Empty | No TARGET | git branch --show-current |
| PR# | Numeric | gh pr view {{target}} --json headRefName,baseRefName,title,body,url and set REVIEWED_PR_URL from url when present |
| PR URL | /pull/ | Extract #, fetch above and set REVIEWED_PR_URL from url when present |
| Branch | Non-numeric | Use directly |
-
gh pr view {{branch}} --json title,body,headRefName,baseRefName,url 2>/dev/null
- If PR metadata is found, set
REVIEWED_PR_URL from url when present
- If no PR metadata is found, leave
REVIEWED_PR_URL="" and continue in branch-only or git-only mode
3a. Get repo URL: gh repo view --json url --jq '.url' 2>/dev/null -> GITHUB_URL (empty if fails)
-
Build Intent Model:
- PR exists (mode=
full): title -> problem_statement, parse body, fetch linked issues
- No PR -> {% ask_user "Quick description of the intended changes", options: "Provide description", "Skip" %}
- Provided (mode=
user_provided): use description
- Skip (mode=
branch_only): problem_statement="Review changes on {{branch}}"
-
Add: git log {{base}}..{{branch}} --oneline --no-decorate -> commit_summaries
-
Base: PR metadata > BASE_BRANCH > 'main'
Intent Model: {"mode": "...", "problem_statement": "", "expected_changes": "", "should_not_change": "", "acceptance_criteria": [], "commit_summaries": []}
P0.5: Visual Gen (Conditional)
Skip conditions (any true -> skip):
SKIP_VISUAL == true
config.visualize == false
Spawn (if not skipped):
Background mode: local=true, CI=false
First, register visual generation as a subflow under reviewing and mark it running:
rp1 agent-tools emit \
--workflow pr-review \
--type subflow_registered \
--run-id {RUN_ID} \
--step reviewing \
--data '{"parentStepId":"reviewing","subflowName":"visual-generation","diagram":"flowchart TD\n A[Start] --> B[Generate Visuals]\n B --> C[Return Markdown]"}'
rp1 agent-tools emit \
--workflow pr-review \
--type status_change \
--run-id {RUN_ID} \
--step reviewing \
--unit visual-generation \
--data '{"status":"running"}'
{% dispatch_agent "rp1-dev:pr-visualizer" %}
Generate PR visualization.
PR_BRANCH: {{pr_branch}}
BASE_BRANCH: {{base_branch}}
REVIEW_DEPTH: quick
STANDALONE: false
KB_ROOT: {kbRoot}
WORK_ROOT: {workRoot}
{% enddispatch_agent %}
Capture VISUAL_CONTENT (raw markdown with Mermaid diagrams).
If the visualizer fails or returns unusable output:
- Emit
reviewing / visual-generation with {"status":"failed"}
- Set
VISUAL_CONTENT=""
- Continue to P1 without blocking the review
After the visualizer returns, mark the subflow unit completed:
rp1 agent-tools emit \
--workflow pr-review \
--type status_change \
--run-id {RUN_ID} \
--step reviewing \
--unit visual-generation \
--data '{"status":"completed"}'
P1: Splitting
{% dispatch_agent "rp1-dev:pr-review-splitter" %}
Split PR diff into review units.
PR_BRANCH: {{pr_branch}}
BASE_BRANCH: {{base_branch}}
THRESHOLD: 100
Return JSON with units array.
{% enddispatch_agent %}
Parse units, store counts. Fail -> Abort w/ error.
P2: Detailed Analysis
CRITICAL: Spawn ALL sub-reviewers in SINGLE message.
-
For each unit: git diff {{base}}..{{branch}} -- {{unit.path}}
-
Build file_list
-
Spawn N sub-reviewers (one msg):
{% dispatch_agent "rp1-dev:pr-sub-reviewer" %}
Analyze review unit across 5 dimensions.
UNIT_JSON: {{stringify(unit_with_diff)}}
KB_ROOT: {kbRoot}
INTENT_JSON: {{stringify(intent_model)}}
PR_FILES: {{stringify(file_list)}}
Return JSON with findings and summary.
{% enddispatch_agent %}
-
Aggregate findings + summaries
-
<50% fail -> continue | >=50% fail -> abort
P3: Synthesis
-
Prep summary: {"critical": N, "high": N, "medium": N, "low": N, "needs_human_review": N, "details": [...]}
-
Spawn:
{% dispatch_agent "rp1-dev:pr-review-synthesizer" %}
Perform holistic verification.
INTENT_JSON: {{stringify(intent_model)}}
KB_ROOT: {kbRoot}
FILE_LIST: {{stringify(file_list)}}
SUMMARIES_JSON: {{stringify(all_summaries)}}
FINDINGS_SUMMARY: {{stringify(findings_summary)}}
Return JSON with intent_achieved, cross_file_findings, judgment, rationale.
{% enddispatch_agent %}
-
Extract: intent_achieved, intent_gap, cross_file_findings, judgment, rationale
-
Fail -> findings-only judgment: Critical->block, High->request_changes, else->approve
P4: Reporting
-
Merge findings: unit + cross_file, dedupe by (path, lines, dimension), keep highest severity
-
Stats: {critical: N, high: N, medium: N, low: N}
-
Review ID: PR# -> pr-{{number}} | else -> sanitized branch
-
git rev-parse {{branch}} -> HEAD_SHA
-
Spawn:
{% dispatch_agent "rp1-dev:pr-review-reporter" %}
Generate markdown report.
PR_INFO: {% raw %}{{stringify({branch, title, base, github_url: GITHUB_URL, head_sha: HEAD_SHA, reviewed_pr_url: REVIEWED_PR_URL})}}{% endraw %}
INTENT_JSON: {{stringify(intent_model)}}
JUDGMENT_JSON: {% raw %}{{stringify({judgment, rationale, intent_achieved, intent_gap})}}{% endraw %}
FINDINGS_JSON: {{stringify(merged_findings)}}
CROSS_FILE_JSON: {{stringify(cross_file_findings)}}
STATS_JSON: {{stringify(stats)}}
VISUAL_CONTENT: {{VISUAL_CONTENT or ""}}
OUTPUT_DIR: {workRoot}/pr-reviews
REVIEW_ID: {{review_id}}
Return JSON with path.
{% enddispatch_agent %}
-
Fail -> output findings inline
-
Store REPORTER_FINDINGS for P5 (CI mode)
-
Register the report artifact after the reporter creates it:
rp1 agent-tools emit \
--workflow pr-review \
--type artifact_registered \
--run-id {RUN_ID} \
--step posting \
--data '{"path": "{REPORT_PATH}", "feature": "{review_id}", "storageRoot": "absolute", "format": "markdown"}'
P5: Comment Posting (CI Only)
Skip if CI_MODE=false.
-
Fetch existing:
echo '{"owner":"{{CI_CONTEXT.owner}}","repo":"{{CI_CONTEXT.repo}}","pr_number":{{CI_CONTEXT.pr_number}}}' | \
rp1 agent-tools github-pr fetch-comments
Parse -> existing_bot_comments, existing_human_comments
-
Transform findings to deduplicator fmt:
[{"id": "f1", "path": "...", "line": N, "line_end": N, "body": "...", "severity": "...", "dimension": "..."}]
-
Deduplicate:
{% dispatch_agent "rp1-dev:pr-comment-deduplicator" %}
Deduplicate PR comments.
NEW_COMMENTS: {{stringify(new_comments)}}
EXISTING_BOT_COMMENTS: {{stringify(existing_bot_comments)}}
EXISTING_HUMAN_COMMENTS: {{stringify(existing_human_comments)}}
BOT_MARKER: {{config.bot_marker}}
Return JSON with to_post, to_react, to_augment, duplicates_skipped.
{% enddispatch_agent %}
-
Post:
{% dispatch_agent "rp1-dev:pr-comment-poster" %}
Post PR review to GitHub.
OWNER: {{CI_CONTEXT.owner}}
REPO: {{CI_CONTEXT.repo}}
PR_NUMBER: {{CI_CONTEXT.pr_number}}
DEDUP_OUTPUT: {{stringify(dedup_output)}}
CONFIG: {% raw %}{{stringify({verdict: config.verdict, bot_marker: config.bot_marker, max_comments: config.max_comments, add_comments: config.add_comments})}}{% endraw %}
VISUAL_CONTENT: {{VISUAL_CONTENT or ""}}
FINDINGS_SUMMARY: {% raw %}{{stringify({critical: N, high: N, medium: N, low: N, total: N})}}{% endraw %}
Return JSON with success, review, reactions, replies, summary, errors.
{% enddispatch_agent %}
-
Parse: REVIEW_URL, COMMENTS_POSTED, REACTIONS_ADDED, POSTING_ERRORS
Link Artifact Registration
After markdown report registration and CI comment posting (or skip), register the reviewed PR URL as an external link artifact. Read references/link-artifacts.md for the full reusable pattern and PR review binding.
Skip when REVIEWED_PR_URL is empty. If registration fails, warn and continue.
Final Output
-
If STASHED=true: git stash pop
-
CI Mode:
{{EMOJI}} PR Review Complete (CI Mode)
Judgment: {{JUDGMENT}}
{{RATIONALE}}
Findings: Critical={{critical}}, High={{high}}, Medium={{medium}}, Low={{low}}
{{IF REVIEWED_PR_URL}}Reviewed PR: {{REVIEWED_PR_URL}}{{/IF}}
GitHub Review: {{REVIEW_URL}}
Comments Posted: {{COMMENTS_POSTED}}
Duplicates Skipped: {{dedup_output.duplicates_skipped}}
Reactions Added: {{REACTIONS_ADDED}}
{{IF POSTING_ERRORS}}Errors: {{POSTING_ERRORS}}{{/IF}}
Local Report: {{REPORT_PATH}}
-
Local Mode:
{{EMOJI}} PR Review Complete
Judgment: {{JUDGMENT}}
{{RATIONALE}}
Findings: Critical={{critical}}, High={{high}}, Medium={{medium}}, Low={{low}}
{{IF REVIEWED_PR_URL}}Reviewed PR: {{REVIEWED_PR_URL}}{{/IF}}
Report: {{REPORT_PATH}}
{{IF STASHED}}Restored stashed changes{{/IF}}
Emoji: approve->check, request_changes->warning, block->stop
§ERR
| Error | Action |
|---|
| CI not enabled | Exit w/ config instructions |
| Draft PR skipped | Exit w/ message |
| Dirty git (local) | Prompt stash/abort |
| Unknown branch | Ask user (local) or fail (CI) |
| gh unavailable | git-only mode |
| Visual fails | Continue w/o (non-blocking) |
| Splitter fails | Abort w/ error |
| >50% reviewers fail | Abort |
| Synthesizer fails | Findings-only judgment |
| Reporter fails | Inline output |
| fetch-comments fails | Skip P5, warn |
| Deduplicator fails | Post all (no dedup) |
| Poster fails | Output error, report still generated |
§OUT
- Internal work in
<thinking> tags
- NO verbose phase progress
- Output ONLY: initial status, brief progress if >30s, final summary w/ report path
- CI mode: include GitHub review URL