Multi-agent code review of GitHub Pull Requests (Python source, documentation (Markdown/RST), and CI/CD config PRs) covering architecture, tests, performance, docs, lint, security, and API design. TRIGGER when: user provides a GitHub PR number (e.g. 42, #42) and asks to review/audit/check it, or provides a saved review-report path with --reply to draft a contributor-facing comment; phrases: 'review PR 123', 'audit this pull request', 'look at PR #42', 'draft a reply for this review report'. S...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add Borda/AI-Rig --skill review --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Review?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/borda-review-2e546cb2)More formats (shields.io, HTML) on the badges page.
---
name: review
description: "Multi-agent code review of GitHub Pull Requests (Python source, documentation (Markdown/RST), and CI/CD config PRs) covering architecture, tests, performance, docs, lint, security, and API design. TRIGGER when: user provides a GitHub PR number (e.g. 42, #42) and asks to review/audit/check it, or provides a saved review-report path with --reply to draft a contributor-facing comment; phrases: 'review PR 123', 'audit this pull request', 'look at PR #42', 'draft a reply for this review report'. SKIP: local file or current git diff review (use /develop:review (requires 'develop' plugin)); non-Python source PRs without Python files (TypeScript-only, Go-only, Rust-only); standalone issue/discussion thread analysis (use /oss:analyse)."
argument-hint: '[PR number|path/to/report.md] [--reply] [--no-challenge] [--codemap] [--semble] [--worktree] [--full] [--keep "<items>"]'
allowed-tools: Read, Write, Edit, Bash, Agent, Skill, TaskList, TaskCreate, TaskUpdate, AskUserQuestion, EnterWorktree, ExitWorktree
model: sonnet
effort: high
---
<objective>
Spawn specialized sub-agents in parallel. Consolidate findings into structured feedback with severity levels.
NOT for local file review or current git diff — use `/develop:review` (requires `develop` plugin). NOT for non-Python source PRs (TypeScript, Go, Rust, etc.) unless they include Python files — docs-only and CI/CD-only PRs in scope. NOT for standalone GitHub issue analysis or thread summarization — use `oss:analyse`. **Draft PRs** (GitHub `isDraft=true`) are work-in-progress; pass explicit PR number anyway to review draft. Note: oss:review performs inline linked-issue analysis (root-cause alignment check in Step 1) as part of PR review — within scope, no conflict.
</objective>
<inputs>
- **$ARGUMENTS**: PR number or report path.
- Number given (e.g. `42` or `#42`): review PR diff
- `--reply`: spawn oss:shepherd to draft contributor-facing PR comment. Path ending in `.md` → spawn oss:shepherd from that report, skip new review.
- **Scope**: Python source only. Non-Python file → state out of scope, suggest tool, no findings.
- **Local files**: use `/develop:review` (requires `develop` plugin) for local files or current git diff.
- `--codemap`: strict mode — stop, report if codemap not installed (on by default when installed; use `--no-codemap` to opt out; requires codemap plugin installed)
- `--semble`: enable semble semantic search companion (off by default; requires semble MCP server configured)
- `--full`: run **every** dimension the scope preselected, instead of only the `FANOUT_MAX` most relevant of them. Never widens the preselection itself — a dimension the scope ruled out stays out. **Not free**: each extra agent costs ~120,851 tok of fixed overhead however little work it does. Default stays capped; pass this when depth matters more than cost.
- **--plan handoff not supported** — skill doesn't accept plan-mode output from `/develop:plan` (requires `develop` plugin).
</inputs>
<constants>
```text
FANOUT_MAX=3 # default: top-N most relevant of the scope-preselected SPAWN UNITS
# units: sw-engineer · perf+arch (one merged spawn) · docs+lint (one
# merged spawn) · challenger · cicd-steward
# OUTSIDE the cap, never ranked out: bridge review, the issue agent,
# and qa-specialist (security-scan-every-PR contract pin)
# --full runs ALL scope-preselected units instead — no numeric cap
AGENT_CALL_BUDGET=55 # target tool-calls per agent; past ~60 they stall without returning an envelope
CHALLENGE_ENABLED=true # set to false via --no-challenge
CODEMAP_ENABLED=auto # on by default if codemap installed + index found; --no-codemap = off; --codemap = strict (stop if not installed)
SEMBLE_ENABLED=false # set to true via --semble
```
> Background agent health monitoring (CLAUDE.md §6) — applies to Step 3 parallel agent spawns
```text
MONITOR_INTERVAL=300 # 5 minutes between polls
HARD_CUTOFF=900 # 15 minutes of no file activity → declare timed out
EXTENSION=300 # one +5 min extension if output file explains delay
```
</constants>
<compaction>
- Key boundary: end of Step 2 — parallel review-agent fan-out outputs collected, before Step 5 consolidation.
- Second boundary: end of Step 5 — consolidated report written, before Step 8 --reply.
- Preserve at boundary 1: RUN_DIR, REPORT_DIR, PR# (CLEAN_ARGS), per-agent finding file paths.
- Preserve at boundary 2: final report path, PR#, reply-mode flag.
</compaction>
<workflow>
<!-- Agent resolution: see _OSS_SHARED/agent-resolution.md -->
## Agent Resolution
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# loads: oss-shared-resolver.md
# loads: review-section-taxonomy.md
# loads: compaction-contract.md
# cold-start fallback (sets $_OSS_SHARED)
_OSS_SHARED=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/resolve_shared_path.py" oss skills/_shared 2>/dev/null) # timeout: 5000
# --reply needs $_OSS_SHARED (Step8 shepherd-reply-protocol.md); else degrades gracefully
if [ ! -d "$_OSS_SHARED" ]; then
if [[ "$ARGUMENTS" == *--reply* ]]; then
echo "⛔ _OSS_SHARED resolved to '$_OSS_SHARED' but dir absent — --reply requires oss plugin shared dir; verify oss plugin installed"
exit 1
else
echo "⚠ _OSS_SHARED resolved to '$_OSS_SHARED' but dir absent — continuing with degraded functionality (oss skill-specific shared helpers unavailable; --reply mode will not work in this run)"
fi
fi
echo "$_OSS_SHARED" > "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" # cross-block (Check 41)
[ -d "$_OSS_SHARED" ] && cat "$_OSS_SHARED/agent-resolution.md" # timeout: 5000
REVIEW_SKILL_DIR="${CLAUDE_PLUGIN_ROOT:-}/skills/review"
[ -d "$REVIEW_SKILL_DIR" ] || REVIEW_SKILL_DIR=$(ls -td ~/.claude/plugins/cache/borda-ai-rig/oss/*/skills/review 2>/dev/null | head -1)
[ -z "$REVIEW_SKILL_DIR" ] && REVIEW_SKILL_DIR="plugins/cc_oss/skills/review"
echo "$REVIEW_SKILL_DIR" > "${TMPDIR:-/tmp}/review-skill-dir-${CSID}" # cross-block (Check 41)
```
Agents: `foundry:sw-engineer`, `foundry:qa-specialist`, `foundry:perf-optimizer`, `foundry:doc-scribe`, `foundry:linting-expert`, `foundry:solution-architect`, `foundry:challenger`, `oss:cicd-steward`. <!-- Inline fallback (if unreadable): all → general-purpose. -->
**`REVIEW_SKILL_DIR`** (resolved above) — substitute into every Agent spawn prompt and every `cat "$REVIEW_SKILL_DIR/..."` call below.
**Task hygiene**: Call `TaskList` first. Each found task: `completed` if work done · `deleted` if orphaned · `in_progress` if genuinely continuing. TaskCreate each major phase; mark in_progress/completed throughout.
Create these tasks **before** starting Step 1 (in order, all at once):
- **"Step 1: Scope and context detection"** — TaskUpdate(in_progress) at Step 1 start; TaskUpdate(completed) when all scope vars set (SCOPE, REPLY_MODE, mode flags)
- **"Step 2: Agent launch"** — TaskUpdate(in_progress) before spawning agents; TaskUpdate(completed) when all Agent() calls issued
- **"Step 3: Post-agent checks"** — TaskUpdate(in_progress) before post-agent checks run; TaskUpdate(completed) when all agent output files collected (or timed out); per task-lifecycle.md: TaskUpdate BEFORE long output blocks
- **"Step 4: Cross-validate critical findings"** — TaskUpdate(in_progress) before spawning verifier agents; TaskUpdate(completed) when all verdicts received; **TaskUpdate(deleted) when no critical/blocking findings exist after Step 3** (always created upfront)
- **"Step 5: Consolidate findings"** — TaskUpdate(in_progress) before spawning consolidator; TaskUpdate(completed) when consolidator returns its one-liner (Write to `review-report.md` done) — **do NOT mark completed for the terminal print, that's a separate task below**
- **"Step 5b: Print report header"** — created **blockedBy** "Step 5: Consolidate findings"; TaskUpdate(in_progress) immediately after the consolidator's one-liner returns; TaskUpdate(completed) only once the `---` header table has actually appeared in this response's output (not merely queued/intended). The consolidator's one-liner (`verdict=... | findings=N | file=<path>`) is NOT this table — it is a routing signal for the orchestrator, never a substitute for reading `$REPORT_DIR/review-report.md` and printing its header. **Step 7a's `AskUserQuestion` must not fire while this task is `pending`/`in_progress`** — a real skip incident showed the hard-enforced tool call (`AskUserQuestion`) firing correctly while this prose-only print step got silently dropped; the dedicated task exists specifically to make the print step as trackable/enforceable as the tool calls around it.
- **"Step 8: Contributor reply draft"** — create only when REPLY_MODE=true, before spawning oss:shepherd; TaskUpdate(in_progress) immediately after creation; TaskUpdate(completed) when shepherd output written
## Step 0: Parse flags and content-type pre-classification
Parse `$ARGUMENTS` flags first (via `bin/parse-skill-flags.py`, C5) — this sets `CLEAN_ARGS`, the mode flags, and `DIRECT_PATH_MODE` **before** any step below references them (the pre-classification and Step 1 both read them):
| Flag | Variable | Present | Absent |
| -- | -- | -- | -- |
| `--reply` | `REPLY_MODE` | `true` | `false` |
| `--no-challenge` | `CHALLENGE_ENABLED` | `false` | `true` |
| `--no-codemap` | `CODEMAP_FORCE_OFF` | `true` | `false` |
| `--codemap` | `CODEMAP_STRICT` | `true` | `false` |
| `--semble` | `SEMBLE_ENABLED` | `true` | `false` |
| `--worktree` | `WT_ENABLED` | `true` | `false` |
| `--full` | `FANOUT_CAP` | `0` — no cap, all preselected | `3` (`FANOUT_MAX`) |
| `--keep "<items>"` | `KEEP_ITEMS` | value string | `""` |
`CLEAN_ARGS`: `$ARGUMENTS` with matched flags removed (including `--keep "<items>"` and its quoted value), leading whitespace stripped, leading `#` stripped.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# parses --reply/--no-challenge/--semble/--worktree/--keep; codemap flags detected-only, re-derived independently below
# shared flag/--keep parser (C5; also resolve/analyse SKILL.md)
eval "$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/parse-skill-flags.py" --flags reply,no-challenge,no-codemap,codemap,semble,worktree,full "$ARGUMENTS")" # timeout: 5000
FANOUT_CAP=3; [ "$FLAG_FULL" = "true" ] && FANOUT_CAP=0 # 0 = no cap: all scope-preselected dimensions
REPLY_MODE="$FLAG_REPLY"
SEMBLE_ENABLED="$FLAG_SEMBLE"
WT_ENABLED="$FLAG_WORKTREE"
[ "$FLAG_NO_CHALLENGE" = "true" ] && CHALLENGE_ENABLED=false || CHALLENGE_ENABLED=true
# stale contract, crashed prior run (compaction-contract.md §Lifecycle)
rm -f .temp/state/skill-contract.md # timeout: 5000
# flags sentinel; CHALLENGE_ENABLED kept separate — scope-detection.md:34-38 truncates this file
{
echo "REPLY_MODE=$REPLY_MODE"
echo "WT_ENABLED=$WT_ENABLED"
echo "SEMBLE_ENABLED=$SEMBLE_ENABLED"
} > "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
echo "$CHALLENGE_ENABLED" > "${TMPDIR:-/tmp}/oss-review-challenge-enabled-${CSID}"
echo "$CLEAN_ARGS" > "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}"
echo "$KEEP_ITEMS" > "${TMPDIR:-/tmp}/oss-review-keep-items-${CSID}" # timeout: 5000
```
Then set direct-report fast-path mode (a review-report `.md` path passed instead of a PR number):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
DIRECT_PATH_MODE=false
if [[ "$CLEAN_ARGS" == *.md ]]; then
# reject plan files — no replies drafted from plan content
if [[ "$CLEAN_ARGS" == .plans/* ]] || [[ "$CLEAN_ARGS" == *todo_*.md ]]; then
echo "Error: plan files cannot be used as review report input. Pass a review report from .reports/review/<timestamp>/review-report.md or a PR number."
exit 1
fi
if [ -f "$CLEAN_ARGS" ] && grep -qE '(^## Summary|^verdict:|APPROVED|NEEDS_WORK|REQUEST_CHANGES)' "$CLEAN_ARGS" 2>/dev/null; then # timeout: 5000
DIRECT_PATH_MODE=true
REVIEW_FILE="$CLEAN_ARGS"
else
echo "⚠ $CLEAN_ARGS is a .md file but lacks review-report markers (## Summary | verdict: | APPROVED|NEEDS_WORK|REQUEST_CHANGES) — refusing direct-path fast-path; continuing with normal review path which expects a PR number."
fi
fi
{
echo "DIRECT_PATH_MODE=$DIRECT_PATH_MODE"
[ "$DIRECT_PATH_MODE" = "true" ] && echo "REVIEW_FILE=$REVIEW_FILE"
} >> "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
```
**Content-type pre-classification (PR mode only)** — skip when `DIRECT_PATH_MODE=true`.
Classify PR from changed file patterns. Default `PR_TYPE=CODE`; override only when unambiguous.
**PR snapshot — fetch once, reuse everywhere.** All later steps (pre-classification, Step 1 scope/CI, acceptance gate, codemap battery, Step 3 checks, signals script) read these files instead of re-calling `gh` — one consistent snapshot of the PR per run, ~10+ fewer network round-trips:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
[ -f "${TMPDIR:-/tmp}/oss-review-flags-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
if [ "$DIRECT_PATH_MODE" = "false" ] && [[ "$CLEAN_ARGS" =~ ^[0-9]+$ ]]; then
SNAP_DIR="${TMPDIR:-/tmp}/oss-review-snap-${CLEAN_ARGS}-${CSID}"
mkdir -p "$SNAP_DIR"
gh pr view $CLEAN_ARGS --json number,title,body,url,labels,milestone,reviews,headRefOid > "$SNAP_DIR/pr-meta.json" 2>/dev/null # timeout: 6000
gh pr diff $CLEAN_ARGS > "$SNAP_DIR/pr.diff" 2>/dev/null # timeout: 15000
gh pr diff $CLEAN_ARGS --name-only > "$SNAP_DIR/files.txt" 2>/dev/null # timeout: 6000
# two checks snapshots: --required = merge-blocking gate set (exits 1 when repo defines none — empty file is the correct signal), bare = full count base
gh pr checks $CLEAN_ARGS --json name,bucket > "$SNAP_DIR/checks.json" 2>/dev/null || : > "$SNAP_DIR/checks.json" # timeout: 15000
gh pr checks $CLEAN_ARGS --required --json name,bucket > "$SNAP_DIR/checks-required.json" 2>/dev/null || : > "$SNAP_DIR/checks-required.json" # timeout: 15000
[ -s "$SNAP_DIR/pr-meta.json" ] || { echo "! BLOCKED — gh pr view failed for PR #$CLEAN_ARGS (network, auth, or wrong number)"; exit 1; }
echo "$SNAP_DIR" > "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}"
fi
```
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
[ -f "${TMPDIR:-/tmp}/oss-review-flags-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
PR_TYPE="CODE"
DOCS_TYPING_MODE=false; TESTS_CI_MODE=false
if [ "$DIRECT_PATH_MODE" = "false" ] && [[ "$CLEAN_ARGS" =~ ^[0-9]+$ ]]; then
_CHANGED=$(cat "$SNAP_DIR/files.txt" 2>/dev/null)
# no `|| echo 0`: grep -c already prints 0 & exits 1 — fallback would double it to "0\n0", breaking `-eq 0` tests below
_PY_LOGIC_COUNT=$(echo "$_CHANGED" | grep -E '\.py$' | grep -cvE '(test_|_test\.py|conftest\.py|\.pyi$)' 2>/dev/null)
_ALL_COUNT=$(echo "$_CHANGED" | grep -c . 2>/dev/null)
_DOC_COUNT=$(echo "$_CHANGED" | grep -cE '\.(md|rst|txt|ipynb)$' 2>/dev/null)
_TEST_CI_COUNT=$(echo "$_CHANGED" | grep -cE '(test_|_test\.py|conftest\.py|\.ya?ml$|\.github/|tox\.ini|Makefile)' 2>/dev/null)
if [ "${_PY_LOGIC_COUNT:-0}" -eq 0 ] && [ "${_ALL_COUNT:-0}" -gt 0 ]; then
if [ "$_DOC_COUNT" -ge "$_ALL_COUNT" ]; then
PR_TYPE="DOCS_TYPING"; DOCS_TYPING_MODE=true
elif [ "$(( _TEST_CI_COUNT + _DOC_COUNT ))" -ge "$_ALL_COUNT" ]; then
PR_TYPE="TESTS_CI"; TESTS_CI_MODE=true
fi
fi
echo "→ PR_TYPE=$PR_TYPE (_py_logic=$_PY_LOGIC_COUNT, _all=$_ALL_COUNT)"
fi
# persist PR_TYPE/mode flags (Check 41) — reloaded by challenge-skip, Steps 2/5
{
echo "PR_TYPE=$PR_TYPE"
echo "DOCS_TYPING_MODE=$DOCS_TYPING_MODE"
echo "TESTS_CI_MODE=$TESTS_CI_MODE"
} > "${TMPDIR:-/tmp}/oss-review-mode-flags-${CLEAN_ARGS}-${CSID}"
```
**Challenge skip** — challenger adds no value for non-logic PRs:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
IFS= read -r CHALLENGE_ENABLED < "${TMPDIR:-/tmp}/oss-review-challenge-enabled-${CSID}" 2>/dev/null; [ "$CHALLENGE_ENABLED" = "false" ] || CHALLENGE_ENABLED=true
# reload PR_TYPE (Check 41)
[ -f "${TMPDIR:-/tmp}/oss-review-mode-flags-${CLEAN_ARGS}-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-mode-flags-${CLEAN_ARGS}-${CSID}"
if [ "$PR_TYPE" = "DOCS_TYPING" ] || [ "$PR_TYPE" = "TESTS_CI" ]; then
CHALLENGE_ENABLED=false
fi
echo "$CHALLENGE_ENABLED" > "${TMPDIR:-/tmp}/oss-review-challenge-enabled-${CSID}"
```
Agent lineup — `PR_TYPE != CODE` overrides scope-based rules in Step 1:
| `PR_TYPE` | Agents | Challenger | Consolidator |
| -- | -- | -- | -- |
| `DOCS_TYPING` | `foundry:linting-expert` only | skip | `foundry:linting-expert` |
| `TESTS_CI` | `foundry:qa-specialist` + `foundry:linting-expert` | skip | `foundry:qa-specialist` |
| `CODE` | full scope-based lineup | per `--no-challenge` | `foundry:sw-engineer` |
When `DOCS_TYPING_MODE=true` or `TESTS_CI_MODE=true`: skip Step 1 file-scope detection and SCOPE classification; proceed directly to Step 2 agent launch.
## Step 1: Identify scope and context (run in parallel for PR mode)
Flags, `CLEAN_ARGS`, and `DIRECT_PATH_MODE` were parsed in Step 0 — reuse those values here.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# loads: detect_codemap.py — consumers: resolve/SKILL.md, review/SKILL.md
_DETECT_CODEMAP="${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/detect_codemap.py"
# codemap flags parsed here only (resolve/SKILL.md:141-143 idiom)
CODEMAP_FORCE_OFF=false; CODEMAP_STRICT=false
[[ " $ARGUMENTS " == *" --no-codemap "* ]] && CODEMAP_FORCE_OFF=true
[[ " $ARGUMENTS " == *" --codemap "* ]] && [[ " $ARGUMENTS " != *" --no-codemap "* ]] && CODEMAP_STRICT=true
[ "$CODEMAP_FORCE_OFF" = "true" ] && _DETECT_FLAGS="--force-off" || _DETECT_FLAGS=""
[ "$CODEMAP_STRICT" = "true" ] && _DETECT_FLAGS="$_DETECT_FLAGS --strict"
python "$_DETECT_CODEMAP" --prefix review $_DETECT_FLAGS 2>&1 # timeout: 5000
[ $? -ne 0 ] && { echo "! BLOCKED — codemap strict mode requested but codemap not installed or index missing"; exit 1; }
IFS= read -r CODEMAP_ENABLED < "${TMPDIR:-/tmp}/review-codemap-enabled-${CSID}" 2>/dev/null || CODEMAP_ENABLED="false"
IFS= read -r CODEMAP_CURRENCY < "${TMPDIR:-/tmp}/review-codemap-currency-${CSID}" 2>/dev/null || CODEMAP_CURRENCY="off"
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED="" # reload (Check 41)
[ "$CODEMAP_FORCE_OFF" = "false" ] && cat "$_OSS_SHARED/codemap-gates.md" # timeout: 5000
```
**Codemap gates** — when `CODEMAP_FORCE_OFF=false`, run (from `codemap-gates.md`, loaded above): **Gate A** if `CODEMAP_ENABLED=false` (missing index → offer to build); **Gate B** if `CODEMAP_ENABLED=true` and `CODEMAP_CURRENCY=stale`. On a build choice, build with the gated `codemap-py index` binary in the foreground, then set `CODEMAP_ENABLED=true` — never model-invoke the `codemap-py:scan-codebase` skill, which is `disable-model-invocation: true` (user-slash-only). Skip both gates when `CODEMAP_FORCE_OFF=true` (`--no-codemap`).
If `SEMBLE_ENABLED=true`: proceed — semble MCP tool availability verified at first use. If `mcp__semble__search` is unavailable when called, it fails with a clear error; do not preemptively exit here.
**Unsupported flag check** — after all supported flags extracted, scan `$ARGUMENTS` for remaining `--<token>` tokens. Found: print `` ! Unknown flag(s): `--<token>`. Supported: `--reply`, `--no-challenge`, `--codemap`, `--no-codemap`, `--semble`, `--worktree`, `--keep`. `` then invoke `AskUserQuestion` — (a) **Abort** (stop, re-invoke with correct flags) · (b) **Continue ignoring** (skip unknown flags, proceed). On Abort: stop.
**Worktree isolation** — when `WT_ENABLED=true` **and** this is a PR review (not `--reply` / direct-report `.md` mode): run the review in an isolated git worktree so no dimension agent can mutate main sources. Load and follow the oss worktree protocol (§Enter now, §review deliverable routing, §Exit at the follow-up gate):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED="$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/resolve_shared_path.py" oss skills/_shared 2>/dev/null)" # timeout: 5000
[ -f "${TMPDIR:-/tmp}/oss-review-flags-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
[ "$WT_ENABLED" = "true" ] || WT_ENABLED=false
[ "$WT_ENABLED" = "true" ] && [ -f "$_OSS_SHARED/worktree-isolation.md" ] && cat "$_OSS_SHARED/worktree-isolation.md" # timeout: 5000
```
`WT_ENABLED=true` → follow §Enter (base off HEAD, `EnterWorktree(path=…)`) before Step 1; the report is routed to the main tree (§review). Else skip — run in main tree.
> `file-handoff-protocol.md`, `foundry--cross-validation-protocol.md` and `codex-delegation.md` (Steps 5/7/consolidator) ship in **this** plugin's `_shared`, kept identical to foundry's canonical by `propagate_shared.py` (the `foundry--` prefix marks a propagated copy — a plugin-local file can never collide with it). No separate resolution needed — `$_OSS_SHARED` from Step 0 covers them, and none of those steps degrade when foundry is absent.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
[ -f "${TMPDIR:-/tmp}/oss-review-flags-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-flags-${CSID}"
if [ "$DIRECT_PATH_MODE" = "false" ]; then
if [ -z "$CLEAN_ARGS" ] || ! [[ "$CLEAN_ARGS" =~ ^[0-9]+$ ]]; then
echo "Error: PR number required. Usage: /oss:review <PR number> [--reply] [--no-challenge]"
exit 1
fi
# all from the Step-0 snapshot — no network
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
[ -s "$SNAP_DIR/pr-meta.json" ] || { echo "! BLOCKED — PR snapshot missing; rerun the Step-0 snapshot block"; exit 1; }
CHANGED_FILES=$(cat "$SNAP_DIR/files.txt" 2>/dev/null) # reused by codemap block
jq '{title,body,url,labels:[.labels[].name],milestone,reviews:(.reviews|length)}' "$SNAP_DIR/pr-meta.json" # timeout: 5000
cat "$SNAP_DIR/checks.json" # timeout: 3000
# scope-detection.md/SCOPE block run in fresh shells — w/o these sentinels: empty inputs, file-scope guard aborts, FIX→REFACTOR override never fires
PR_LABELS=$(jq -r '[.labels[].name] | join(",")' "$SNAP_DIR/pr-meta.json" 2>/dev/null) # timeout: 5000
PR_TITLE=$(jq -r .title "$SNAP_DIR/pr-meta.json" 2>/dev/null) # timeout: 5000
printf '%s\n' "$CHANGED_FILES" > "${TMPDIR:-/tmp}/oss-review-changed-files-${CSID}"
printf '%s\n' "$PR_LABELS" > "${TMPDIR:-/tmp}/oss-review-pr-labels-${CSID}"
printf '%s\n' "$PR_TITLE" > "${TMPDIR:-/tmp}/oss-review-pr-title-${CSID}"
fi
```
**CI STATUS** (PR mode only): run this block verbatim — never hand-compose a checks parse. `jq` over the snapshot beats grepping the table: no tab-literal quoting, no `grep -P` (absent on BSD/macOS), and the `bucket` field is gh's own pass/fail/pending classification.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
# checks-required.json = merge-blocking set only (empty file when repo defines none)
CI_FAILING_CHECKS=$(jq -r '[.[]|select(.bucket=="fail")|.name]|join(", ")' "$SNAP_DIR/checks-required.json" 2>/dev/null) || CI_FAILING_CHECKS="" # timeout: 5000
CI_COUNTS=$(jq -r '"\([.[]|select(.bucket=="pass")]|length)/\(length)"' "$SNAP_DIR/checks.json" 2>/dev/null) || CI_COUNTS="" # timeout: 5000
if [ -n "$CI_FAILING_CHECKS" ]; then CI_RED=true; else CI_RED=false; fi
# Stage-2 gate + consolidator run in fresh shells — w/o sentinels CI_RED reads unset, red CI never blocks
printf '%s\n' "$CI_RED" > "${TMPDIR:-/tmp}/oss-review-ci-red-${CSID}"
printf '%s\n' "$CI_FAILING_CHECKS" > "${TMPDIR:-/tmp}/oss-review-ci-failing-${CSID}"
printf '%s\n' "$CI_COUNTS" > "${TMPDIR:-/tmp}/oss-review-ci-counts-${CSID}"
echo "CI_RED=$CI_RED FAILING=[$CI_FAILING_CHECKS] COUNTS=$CI_COUNTS"
```
`CI_RED=true`: print `⚠ CI is red: [list failing check names] — review proceeds; status noted in report header.` Continue to Steps 2–8 regardless. Expand `$CI_RED`, `$CI_FAILING_CHECKS` and `$CI_COUNTS` to literal values in the consolidator spawn prompt (Step 5).
### File scope detection
<!-- loads: modes/scope-detection.md -->
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload REVIEW_SKILL_DIR (Check 41: fresh shell)
IFS= read -r REVIEW_SKILL_DIR < "${TMPDIR:-/tmp}/review-skill-dir-${CSID}" 2>/dev/null || REVIEW_SKILL_DIR=""
cat "$REVIEW_SKILL_DIR/modes/scope-detection.md" # timeout: 5000
```
Follow above and execute its bash blocks inside the `DIRECT_PATH_MODE = "false"` guard. Sets `PY_FILES`, `DOC_FILES`, `CICD_FILES`, `CICD_ONLY_MODE`, `DOCS_ONLY_MODE`, `DOCS_CICD_MODE`; persists flags to `${TMPDIR:-/tmp}/oss-review-mode-flags-${CLEAN_ARGS}-${CSID}` for reload in Step 2.
### Scope pre-check
**DOCS_TYPING mode** (`DOCS_TYPING_MODE=true`): annotation-only .py changes (no logic). Spawn: `foundry:linting-expert` only; challenger disabled by Step 0; skip all other agents. Proceed directly to agent launch.
**TESTS_CI mode** (`TESTS_CI_MODE=true`): test files and CI config only. Spawn: `foundry:qa-specialist` + `foundry:linting-expert`; challenger disabled by Step 0; skip all other agents. Proceed directly to agent launch.
**CI/CD-only mode** (`CICD_ONLY_MODE=true`): no `.py`/`.md`/`.rst`. Spawn: `oss:cicd-steward` + Agent 1 + Agent 7 (if `CHALLENGE_ENABLED=true`) + Codex; skip Agents 2–6. Proceed directly to agent launch.
**Docs-only mode** (`DOCS_ONLY_MODE=true`): no `.py`. **foundry:doc-scribe (Agent 4) leads** — Agent 1 explicitly skipped (NOT for docs clause); linked-issue spawns also skip Agent 1. Spawn: Agent 4 + Agent 7 (if `CHALLENGE_ENABLED=true`) + Codex; skip Agents 1, 2, 3, 5, 6. Proceed directly to agent launch.
**Docs + CI/CD mode** (`DOCS_CICD_MODE=true`): no Python. Spawn: `oss:cicd-steward` (Agent 8) + `foundry:doc-scribe` (Agent 4) + Agent 7 (if `CHALLENGE_ENABLED=true`) + Codex; skip Agents 1, 2, 3, 5, 6. Proceed directly to agent launch.
Before spawning agents (Python mode only — all three mode flags false), classify diff:
- Count files changed, lines added/removed, new classes/modules
- Classify: **FIX** (\<3 files, \<50 lines), **REFACTOR** (internal restructure, no new public API), **FEATURE** (new public API or module), **CHORE** (deps, config, tooling — no logic changes), or **MIXED**
- **Short-diff multi-concern refactors**: FIX heuristic classifies by diff size, not intent. Override FIX → REFACTOR when PR labels include `perf`, `performance`, `optimization`, `refactor`, `architecture`, `cleanup` OR commit message keywords `refactor:`, `perf:`, `rewrite` OR diff touches different modules. Detect via `gh pr view --json labels,title`. Small-diff perf refactors are exactly the case FIX would silently mishandle.
- **Complexity smell**: 8+ files changed OR `PY_LOC_DELTA >400` → note in report header
Assign `SCOPE` shell variable so the `EXPECTED` array (Step 2 health monitor) can branch on it without comparing to an undefined value:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
# rehydrate Step1 inputs (Check 41) — PY_FILES lived in scope-detection.md's shell
CHANGED_FILES=$(cat "${TMPDIR:-/tmp}/oss-review-changed-files-${CSID}" 2>/dev/null)
PY_FILES=$(echo "$CHANGED_FILES" | grep '\.py$' || true)
IFS= read -r PR_LABELS < "${TMPDIR:-/tmp}/oss-review-pr-labels-${CSID}" 2>/dev/null || PR_LABELS=""
IFS= read -r PR_TITLE < "${TMPDIR:-/tmp}/oss-review-pr-title-${CSID}" 2>/dev/null || PR_TITLE=""
PY_FILE_COUNT=$(echo "$PY_FILES" | grep -c . 2>/dev/null)
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
# PY_LOC_DELTA = total churn, not net — renames give >0 at net 0; label/keyword override handles it
PY_LOC_DELTA=$(grep -E '^[+-][^+-]' "$SNAP_DIR/pr.diff" 2>/dev/null | grep -vE '^[+-]{3}' | wc -l | tr -d ' ') # timeout: 5000
# new API surface: added lines inside src/**/__init__.py sections of the snapshot diff
NEW_API_LINES=$(awk '/^diff --git /{f=($0 ~ /^diff --git a\/src\/.*__init__\.py /)} f && /^\+[^+]/{c++} END{print c+0}' "$SNAP_DIR/pr.diff" 2>/dev/null) # timeout: 5000
# pure config/deps changes (no .py logic changes)
NON_CONFIG_PY=$(echo "$PY_FILES" | grep -vE '(pyproject\.toml|setup\.cfg|setup\.py|requirements.*\.txt|conftest\.py)' || true)
SCOPE=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/classify_pr_scope.py" --py-files "$PY_FILE_COUNT" --loc-delta "$PY_LOC_DELTA" --new-api-lines "$NEW_API_LINES" --labels "$PR_LABELS" --title "$PR_TITLE" 2>/dev/null) # timeout: 10000
echo "→ SCOPE=$SCOPE (py_files=$PY_FILE_COUNT, py_loc=$PY_LOC_DELTA, new_api=$NEW_API_LINES)"
# persist — Step2 ranking + consolidator <SCOPE> substitution run in separate blocks
echo "$CHANGED_FILES" | grep -qE '(^|/)(requirements.*\.txt|pyproject\.toml|package.*\.json|Pipfile|poetry\.lock|setup\.cfg|.*\.lock)$' && CHORE_DEPS=true || CHORE_DEPS=false
_REVIEW_SCOPE_FILE="${TMPDIR:-/tmp}/oss-review-scope-${CLEAN_ARGS}-${CSID}"
{
echo "SCOPE=$SCOPE"
echo "CHORE_DEPS=$CHORE_DEPS"
} > "$_REVIEW_SCOPE_FILE"
```
Skip optional agents by classification:
- FIX scope → skip Agent 3 (perf-optimizer), Agent 6 (solution-architect)
- REFACTOR scope → keep all agents; perf-optimizer runs to verify new structure isn't slower
- FEATURE/MIXED → spawn all agents
- CHORE scope → spawn Agents 1, 4, 5, 7 (challenger, if `CHALLENGE_ENABLED=true`), Codex (if available); skip Agents 2, 3, 6
- **CHORE + dependency files exception**: diff includes `requirements*.txt`, `pyproject.toml`, `package*.json`, `Pipfile`, `poetry.lock`, `setup.cfg`, `*.lock` → keep Agent 2 (qa-specialist) for OWASP/CVE checks. Detect via `CHORE_DEPS` flag above. CHORE + non-deps → skip qa-specialist.
### Structural context + review pre-flight (codemap-py — only if `CODEMAP_ENABLED=true`)
**Skip entire section if `CODEMAP_ENABLED=false`** — sets `codemap_available=false` for downstream agent prompts; agents fall back to file reads.
<!-- loads: modes/codemap-context.md -->
> loads: modes/codemap-context.md
`CODEMAP_ENABLED=true`:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload REVIEW_SKILL_DIR (Check 41: fresh shell)
IFS= read -r REVIEW_SKILL_DIR < "${TMPDIR:-/tmp}/review-skill-dir-${CSID}" 2>/dev/null || REVIEW_SKILL_DIR=""
cat "$REVIEW_SKILL_DIR/modes/codemap-context.md" # timeout: 5000
```
Follow above and execute its contents — stages `codemap_available` and `$CODEMAP_CONTEXT_STAGE` to TMPDIR (Step 2 copies into `$RUN_DIR/codemap-context.md`) and defines the Step-2 spawn-prompt substitution rules + semble companion. `CODEMAP_ENABLED=false`: skip; agents fall back to file reads.
### Linked issue analysis (PR mode only)
Parse PR body (`gh pr view $CLEAN_ARGS`) for issue refs (`Closes #N`, `Fixes #N`, `Resolves #N`, `refs #N` — case-insensitive). Extract to `ISSUE_NUMS`. Cap 3.
`ISSUE_NUMS` non-empty AND `DOCS_CICD_MODE != true`: spawn ONE **foundry:doc-scribe** covering ALL linked issues in Step 2 alongside Codex (one spawn, not one per issue — each extra spawn costs ~120,851 tok fixed overhead). The issue agent, per issue N: fetch `gh issue view <N> --json title,body,comments,state,labels` + `gh issue view <N> --comments`; produce `/oss:analyse`-style output (Summary, Root Cause Hypotheses top 3, Code Evidence); write each analysis to its own `$RUN_DIR/issue-<N>.md` (per-issue files are load-bearing — consumed by Agent 1, the consolidator, and the monitor list); return only a JSON array, one element per issue: `[{"status":"done","issue":N,"root_cause":"<one-line>","file":"$RUN_DIR/issue-<N>.md","confidence":0.N}, …]`.
`ISSUE_NUMS` empty → skip issue checks downstream.
### Acceptance gate (PR mode only) — validate reject, then block
Skip if `DIRECT_PATH_MODE=true`. Two ordered stages, cheap, before Step 2's expensive fanout. **Reject is terminal** — no code change fixes the premise, pipeline stops. **Block is not** — the premise is sound, current diff state has a fixable gap (red CI, a typo, a flaky test) — full fanout still runs, the report just surfaces the fixable gap up front instead of burying it in consolidator output. Test to pick the stage: *"could revising the code, not the goal, resolve this?"* Yes → block. No → reject.
> **Why this gate exists**: Step 2's fanout costs ~120,851 tok/agent, up to ~7 spawns under `--full` (4 units + pinned qa + bridge + issue agent) — never spend that on a PR whose premise is already fatal. This gate must stay cheap (a `gh pr view` + at most one `foundry:challenger` call) — never grow it into anything resembling the full fanout it exists to avoid paying for.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
IFS= read -r PR_LABELS < "${TMPDIR:-/tmp}/oss-review-pr-labels-${CSID}" 2>/dev/null || PR_LABELS=""
IFS= read -r CHANGED_FILES < "${TMPDIR:-/tmp}/oss-review-changed-files-${CSID}" 2>/dev/null || CHANGED_FILES=""
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
PR_BODY=$(jq -r '.body // ""' "$SNAP_DIR/pr-meta.json" 2>/dev/null) # timeout: 5000
PR_HEAD_SHA=$(jq -r '.headRefOid // ""' "$SNAP_DIR/pr-meta.json" 2>/dev/null) # timeout: 5000
echo "$PR_BODY" > "${TMPDIR:-/tmp}/oss-review-pr-body-${CSID}"
echo "$PR_HEAD_SHA" > "${TMPDIR:-/tmp}/oss-review-pr-head-sha-${CSID}"
# cheap mechanical signals for grounds 3/5/6 below — reuses data already fetched, one small gh call per linked issue
SCOPE_LABEL_HIT=false
case ",${PR_LABELS}," in *,wontfix,*|*,invalid,*|*,declined,*|*,out-of-scope,*) SCOPE_LABEL_HIT=true ;; esac
DUPLICATE_HIT=false; DUPLICATE_REASON=""
for N in $ISSUE_NUMS; do
_ISTATE=$(gh issue view "$N" --json state --jq .state 2>/dev/null) # timeout: 6000
if [ "$_ISTATE" = "CLOSED" ]; then
_CLOSER=$(gh issue view "$N" --json closedByPullRequestsReferences --jq '.closedByPullRequestsReferences[0].number // empty' 2>/dev/null) # timeout: 6000
[ -n "$_CLOSER" ] && [ "$_CLOSER" != "$CLEAN_ARGS" ] && { DUPLICATE_HIT=true; DUPLICATE_REASON="issue #$N already closed by #$_CLOSER"; }
fi
done
REVERT_CANDIDATE=$(git log --all --grep='^Revert' --oneline -- $CHANGED_FILES 2>/dev/null | head -3) # timeout: 10000
echo "scope_label=$SCOPE_LABEL_HIT duplicate=$DUPLICATE_HIT revert_candidate=${REVERT_CANDIDATE:+yes}"
```
**Description drift caution** — `PR_BODY` is a snapshot written at PR-open time; it drifts from what the diff actually does as commits land (further changes, or fixes pushed in response to earlier review feedback) and nobody edits the description to match. Judge every ground below against **current diff behavior**, not the stated text alone — read `CHANGED_FILES`/diff intent (already fetched in Step 0/1) alongside `PR_BODY`. Body says one thing, diff does another → trust the diff; a stale description is not itself a reject ground, note the mismatch in `Summary:` if it's material.
**Stage 1 — Reject (terminal).** Eight grounds — aligned with close-without-merge practice in K8s/CPython/Rust/Django contributing docs. Every ground needs affirmative evidence, never suspicion alone — disagreement-with-approach is a `NEEDS_WORK`/`[blocking]` finding, stage 2 or full review territory, never a reject. Grounds 1–2 already had detail; 3–8 are the agreed expansion:
1. **REJECT_GOAL** — stated goal factually/technically wrong even if well-intentioned. Test: does the goal — read from `PR_BODY`, cross-checked per the drift caution above — contradict a known invariant, spec, or domain fact — e.g. "raise this accuracy metric above 1.0" when the metric is bounded `[0,1]` by definition, goal is unreachable no matter how the code changes. Judge from PR description + package docs/spec, not the diff's mechanics. Orchestrator judgment only — no agent spawn.
2. **REJECT_CONDUCT** — contribution by design adversarial, malicious, or a Code of Conduct violation (not an accidental bug). Never reject on suspicion alone: requires the `foundry:challenger` confirmation below.
3. **REJECT_SCOPE** — out of project scope / against roadmap, maintainers already decided against this direction. Evidence: `SCOPE_LABEL_HIT=true` (maintainer already triaged `wontfix`/`invalid`/`declined`/`out-of-scope`), or an explicit "out of scope" statement in `CONTRIBUTING.md`/an ADR that the PR's stated intent directly matches — grep for it, don't assume. No documented evidence → not a reject, at most a `NEEDS_WORK` scope concern. Orchestrator judgment only.
4. **REJECT_LICENSE** — license/provenance conflict: incompatible license copied in (e.g. GPL source pasted into a permissive-licensed project), or plagiarized/copied source the contributor has no right to submit. Not the same as a missing CLA/DCO signature — that's Stage 2 `[blocking]`, fixable by signing; this is the source itself being unlicensable. Requires the `foundry:challenger` confirmation below when suspected (explicit "ported from `<project>`" in `PR_BODY`, or a license header in the diff that conflicts with this repo's license).
5. **REJECT_DUPLICATE** — another PR already merged solving this, or the linked issue already fixed upstream. Evidence: `DUPLICATE_HIT=true` (`$DUPLICATE_REASON`). No `ISSUE_NUMS` linked or issue still open → not this ground.
6. **REJECT_REVERTED** — reintroduces a previously reverted change without addressing why it was reverted. Evidence: `REVERT_CANDIDATE` non-empty (a prior revert touched the same files) **and** `PR_BODY` doesn't reference or address that revert/its reason — a candidate alone is not enough, read the revert commit message and compare intent before rejecting. Orchestrator judgment only.
7. **REJECT_SPAM** — spam/low-effort/AI-slop: no real change, hacktoberfest-farming pattern. Evidence needs both: diff is trivially low-value (whitespace/punctuation-only across the changed lines, no logic touched) **and** `PR_BODY` is generic/templated with no specifics tying it to this repo. Either alone is not enough — a genuine one-line critical fix is low-value-looking but not spam; judge the pairing, not the diff size alone. Orchestrator judgment only.
8. **REJECT_PHILOSOPHY** — contradicts a documented design principle (not a style preference). Evidence: an explicit principle stated in `README.md`/`CONTRIBUTING.md`/an ADR that the PR's intent directly violates — e.g. adding a GUI to a project whose docs state "CLI-only by design". Cite the exact doc line in `Summary:` — no citable line, no reject. Orchestrator judgment only.
**Challenger confirmation** (grounds 2 and 4 only — the two where accusing wrongdoing carries real reputational/legal stakes, so both share one call): only spawn when the orchestrator's own read of `PR_BODY`/diff/`CHANGED_FILES` raised a concrete suspicion for either — never spawn speculatively on every PR. Prompt: "Investigate PR #<N> (body: \<PR_BODY>, diff: changed files) for two things: (1) is its intent a by-design malicious/adversarial contribution or Code of Conduct violation, vs. an accidental mistake; (2) is any changed content plagiarized or under an incompatible license the contributor has no right to submit, vs. original/properly licensed work. Read the diff and linked issue if any. Return ONLY: `{\"conduct\":{\"verdict\":\"BY_DESIGN\"|\"ACCIDENTAL\"|\"N/A\",\"confidence\":0.N},\"license\":{\"verdict\":\"CONFLICT\"|\"CLEAN\"|\"N/A\",\"confidence\":0.N},\"rationale\":\"<one sentence per flagged verdict>\"}`". `ACCIDENTAL`/`CLEAN`, `N/A`, or `confidence <0.7` on either axis → that ground is not a reject, falls through as a normal finding.
Any ground confirmed:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r PR_HEAD_SHA < "${TMPDIR:-/tmp}/oss-review-pr-head-sha-${CSID}" 2>/dev/null || PR_HEAD_SHA=""
{ echo "GATE=REJECT_GOAL"; echo "GATE_SHA=${PR_HEAD_SHA}"; echo "GATE_REASON=<one-line evidence for the ground that fired>"; } > "${TMPDIR:-/tmp}/oss-review-gate-${CSID}" # substitute the actual REJECT_<GROUND> code (GOAL/CONDUCT/SCOPE/LICENSE/DUPLICATE/REVERTED/SPAM/PHILOSOPHY)
```
<!-- policy-sibling: plugins/cc_oss/skills/review/SKILL.md, plugins/cc_oss/skills/resolve/SKILL.md — `Gate: REJECT_* @<sha>` line format, both sides must agree -->
Skip Step 2–4 entirely. Orchestrator writes `$REPORT_DIR/review-report.md` itself (Write tool, same `---` header format as `templates/review-report.md`) with `Gate: REJECT_<GROUND> @<PR_HEAD_SHA>` (the `@<sha>` suffix is load-bearing — `/oss:resolve` parses it to refuse restarting on an unchanged, rejected PR; never omit it, regardless of which of the 8 grounds fired), `Outcome: N/A — rejected at gate`, `Summary:` stating the specific evidence (factual contradiction, challenger rationale, label/issue/revert citation, doc line quoted), `Next steps:` recommends closing the PR with that rationale (drafted for user, never auto-posted — `gh pr close`/comment forbidden by public-github.md read-only policy). Then jump straight to Step 5b's print sequence and Step 7's gate — no consolidator spawn needed, nothing to consolidate.
No ground confirmed → proceed to Stage 2.
**Stage 2 — Block (non-terminal).** Reloads `CI_RED`/`CI_FAILING_CHECKS` from the CI STATUS sentinels — no new fetch. Red CI is the only mechanically-cheap block signal available pre-fanout; a typo or a flaky test can't be told apart from a real regression without actually reading the diff or a rerun, so those stay classification guidance for the full-review agents (below), not a pre-fanout check.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CI_RED < "${TMPDIR:-/tmp}/oss-review-ci-red-${CSID}" 2>/dev/null || CI_RED=false
IFS= read -r CI_FAILING_CHECKS < "${TMPDIR:-/tmp}/oss-review-ci-failing-${CSID}" 2>/dev/null || CI_FAILING_CHECKS=""
if [ "${CI_RED:-false}" = "true" ]; then
{ echo "GATE=BLOCK"; echo "GATE_REASON=ci-red: ${CI_FAILING_CHECKS}"; } > "${TMPDIR:-/tmp}/oss-review-gate-${CSID}"
else
{ echo "GATE=PASS"; echo "GATE_REASON="; } > "${TMPDIR:-/tmp}/oss-review-gate-${CSID}"
fi
```
`GATE=BLOCK` does **not** skip Step 2 — proceed to full fanout regardless, `Gate: BLOCK` is carried into the Step 5 report header alongside the normal `Outcome:` so the fixable blocker is visible immediately, not buried after N findings.
**Classification guidance for full-review agents and the consolidator** (applies once fanout runs, whichever gate state): tag a finding `[blocking]` only when it is (a) objectively fixable by more commits and (b) actually prevents merge until resolved. Design/architecture disagreements are never `[blocking]` — those are `[medium]`/`[high]` `NEEDS_WORK` findings, and a goal-level disagreement should have been caught at Stage 1, not here. Per-category default: `<notes>` §Block-tier catalogue — canonical, don't re-derive per run.
### Direct report fast-path
`DIRECT_PATH_MODE=true`:
- `REPLY_MODE=false` → use `AskUserQuestion`: "A report path was passed without `--reply`. Did you mean `/oss:review <path.md> --reply`?" Options: (a) "Yes — continue with `--reply` mode" → set `REPLY_MODE=true`; then re-check: `[ ! -f "$REVIEW_FILE" ] && echo "Error: review file not found at $REVIEW_FILE" && exit 1`; proceed; (b) "No — review a PR instead" → print usage hint (`/oss:review <N> | path/to/dir`) and stop.
- `REPLY_MODE=true` and `[ ! -f "$REVIEW_FILE" ]` → print `Error: report not found: $REVIEW_FILE` and stop.
- `REPLY_MODE=true` and file exists → print `[direct] using $REVIEW_FILE` → **skip to Step 8**. Skip Steps 2–7.
## Step 2: Codex + parallel agent launch
Set up run directory (shared by all agents) and resolve skill paths:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
TIMESTAMP=$(date -u +%Y-%m-%dT%H-%M-%SZ)
RUN_DIR=".temp/review/$TIMESTAMP"
mkdir -p "$RUN_DIR" # timeout: 5000
# persisted for cat resolve — hand-typing caused leading-dot drops → stray temp/review/ dirs
echo "$RUN_DIR" > "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}"
# deliverable → main tree (worktree-isolation.md §review): --worktree sets orig-root at §Enter, else pwd — report stays reachable outside worktree; RUN_DIR stays worktree-local
IFS= read -r _REPORT_BASE < "${TMPDIR:-/tmp}/oss-review-orig-root-${CSID}" 2>/dev/null || _REPORT_BASE="$(pwd)"
[ -n "$_REPORT_BASE" ] || _REPORT_BASE="$(pwd)"
REPORT_DIR="$_REPORT_BASE/.reports/review/$TIMESTAMP"
mkdir -p "$REPORT_DIR" # timeout: 5000
echo "$REPORT_DIR" > "${TMPDIR:-/tmp}/oss-review-report-dir-${CSID}" # persist for contract-write
```
**File-based handoff**:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload _OSS_SHARED (Check 41: fresh shell)
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED=""
cat "$_OSS_SHARED/file-handoff-protocol.md" # timeout: 5000
```
Follow above. File absent → warn and continue without it.
**IMPORTANT**: Replace `$REPORT_DIR`, `$REVIEW_SKILL_DIR`, `$BRANCH`, and `$DATE` with actual literal computed values in every Agent spawn prompt. Do NOT pass as shell variables — agents receive text, not shell context. **Exception — `$RUN_DIR`**: never hand-substitute it; agents self-resolve via `export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"; cat "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}"` per the run-dir preamble in `agent-prompts.md` (eliminates leading-dot transcription slips).
Check Codex availability:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
CODEX_STATUS=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/check_bridge.py" --status 2>/dev/null || echo "absent") # timeout: 5000
if [ "$CODEX_STATUS" = "available" ]; then CODEX_AVAILABLE=1; echo "bridge@borda-ai-rig available"; else CODEX_AVAILABLE=0; echo "⚠ bridge@borda-ai-rig is ${CODEX_STATUS} — skipping co-review"; fi
echo "$CODEX_AVAILABLE" > "${TMPDIR:-/tmp}/oss-review-codex-available-${CSID}"
```
<!-- loads: agent-prompts.md -->
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload REVIEW_SKILL_DIR (Check 41: fresh shell)
IFS= read -r REVIEW_SKILL_DIR < "${TMPDIR:-/tmp}/review-skill-dir-${CSID}" 2>/dev/null || REVIEW_SKILL_DIR=""
cat "$REVIEW_SKILL_DIR/templates/agent-prompts.md" # timeout: 5000
```
Template (loaded above). Substitute `<REVIEW_SKILL_DIR>` → `$REVIEW_SKILL_DIR` before using content in spawn prompts. Leave `$RUN_DIR` literal in the prompt text — agents resolve it themselves via the run-dir preamble (`cat "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}"`); the orchestrator must NOT retype the run-dir path.
**Codemap context propagation**: rehydrate `codemap_available` from Step 1 persist file, copy staged context into `$RUN_DIR/codemap-context.md`, substitute into every dimension-agent spawn prompt per the rules in the Structural-context block above. Block omitted when `codemap_available=false`.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || RUN_DIR=""
[ -n "$RUN_DIR" ] || { echo "! BLOCKED — run-dir sentinel empty; refusing to copy codemap context to a root-relative path"; exit 1; }
IFS= read -r _PR_TAG < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || _PR_TAG="$CLEAN_ARGS"
IFS= read -r codemap_available < "${TMPDIR:-/tmp}/oss-review-codemap-available-${_PR_TAG}-${CSID}" 2>/dev/null || codemap_available="false"
IFS= read -r CODEMAP_CONTEXT_STAGE < "${TMPDIR:-/tmp}/oss-review-codemap-context-stage-${_PR_TAG}-${CSID}" 2>/dev/null || CODEMAP_CONTEXT_STAGE=""
if [ "$codemap_available" = "true" ] && [ -n "$CODEMAP_CONTEXT_STAGE" ] && [ -f "$CODEMAP_CONTEXT_STAGE" ]; then
cp "$CODEMAP_CONTEXT_STAGE" "$RUN_DIR/codemap-context.md"
fi
```
**Health monitoring** (CLAUDE.md §6): Create checkpoint BEFORE spawning agents:
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
REVIEW_CHECKPOINT="${TMPDIR:-/tmp}/review-check-$(date +%s)-${CSID}"
touch "$REVIEW_CHECKPOINT"
# read back by poll block (separate invocation)
echo "$REVIEW_CHECKPOINT" > "${TMPDIR:-/tmp}/oss-review-checkpoint-${CSID}"
```
**Spawn-count gate — apply before spawning anything.** Each agent costs ~120,851 tok of fixed overhead regardless of how little work it does, i.e. ~73 tool-calls' worth, plus ~12.0 s/call. Measured on a real PR review: 11 agents, ~55% of the whole bill. Rules, all mandatory:
Two stages, in order — never collapse them:
1. **Scope preselection** (always): the scope/mode rules above decide which dimensions are *relevant at all*. A dimension with no changed file in its territory is out here and never comes back, at any flag. Paired dimensions (perf+arch, docs+lint — see agent-prompts.md §Merged spawn units) form one spawn unit: the unit survives when either member does, and its prompt carries only the surviving members' instructions.
2. **Relevance ranking** (default only): rank the surviving units by evidence — changed files and lines in each unit's territory, what Step 1 pre-classification found, what the structural context flagged — and spawn the top `FANOUT_MAX` (3). **qa-specialist is pinned outside the cap** — it spawns on every CODE PR its scope rules allow (security-scan-every-PR contract) and never occupies a ranked slot. With `--full` (`FANOUT_CAP=0`) skip this stage and spawn every survivor of stage 1.
- More work → give each agent more, never add agents.
- **Spawn the fewest that keep each near `AGENT_CALL_BUDGET`** — not the most the cap allows. Total work under ~73 calls → do it inline and spawn nothing.
- **Merge before you split**: two dimensions whose files overlap go to one agent, not two.
- Every spawn prompt states the budget and requires an envelope even on exhaustion — `partial: true` plus what was finished. An agent that stalls past ~60 calls without an envelope forces full disk reconstruction.
- Dimensions dropped by the cap are listed in the report; never silently skipped.
**Dimension-gated codemap supplement** — after the ranked lineup is decided and when `codemap_available=true`: run the qa block (qa-specialist in lineup) and/or docs block (doc-scribe in lineup) from codemap-context.md §Dimension-gated supplement, then re-run the copy block above so `$RUN_DIR/codemap-context.md` carries the supplement. Neither agent in lineup → skip (that is the point: those queries were 57% of battery volume feeding agents the cap usually drops).
Launch the bridge review, the issue agent (one spawn for all linked issues), and all review agents in one message batch. Call `Skill(skill="bridge:review", args="Read-only adversarial review of <REVIEW_TARGET>, using changed files and <RUN_DIR>/codemap-context.md when present. Identify bugs, missed edge cases, and inconsistencies with exact file:line evidence; write findings to <RUN_DIR>/foundry--codex.md and do not apply fixes.")` when `CODEX_AVAILABLE=1` and DOCS_TYPING_MODE/TESTS_CI_MODE are false. Then launch the selected Foundry agents and rank survivors under `FANOUT_MAX` as before.
Poll for expected output files per `$MONITOR_INTERVAL` / `$HARD_CUTOFF` until all present or each hits hard cutoff.
Persist the monitor list from the **actual launch batch** — never re-derive it from scope/mode flags (flag-derived lists include ranking-dropped dimensions; the monitor then waits ~15 min HARD_CUTOFF per never-spawned agent). In the same turn as the launch message, use the **Write tool** (the lineup is a ranking decision the shell cannot see) to create `$RUN_DIR/.expected-files`: one absolute path per line, exactly one line per output file of every agent actually spawned:
- `$RUN_DIR/foundry--codex.md` — only when the bridge review launched
- `$RUN_DIR/issue-<N>.md` — one per linked issue the issue agent covers
- one `$RUN_DIR/<agent>.md` per spawned review agent — basenames: `foundry--sw-engineer.md`, `foundry--qa-specialist.md`, `foundry--perf-optimizer.md`, `foundry--doc-scribe.md`, `foundry--linting-expert.md`, `foundry--solution-architect.md`, `foundry--challenger.md`, `oss--cicd-steward.md`
Then arm the guard (fresh shell):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || RUN_DIR=""
[ -n "$RUN_DIR" ] || { echo "! BLOCKED — run-dir sentinel empty; agent health monitoring cannot be armed"; exit 1; }
[ -s "$RUN_DIR/.expected-files" ] || { echo "! BLOCKED — $RUN_DIR/.expected-files missing or empty; write it (one path per spawned agent) before polling"; exit 1; }
POLL_START=$(date +%s)
```
Later poll blocks read paths back via `while read -r path; do [ -f "$path" ] || PENDING=1; done <"$RUN_DIR/.expected-files"` — no in-memory array required.
Every `$MONITOR_INTERVAL` seconds, in the poll bash block, rehydrate both the run dir and the checkpoint path first (fresh shell — an unbound `$RUN_DIR` makes the `find` scan `/`): `IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || RUN_DIR=""` and `IFS= read -r REVIEW_CHECKPOINT < "${TMPDIR:-/tmp}/oss-review-checkpoint-${CSID}" 2>/dev/null || REVIEW_CHECKPOINT=""` then `find "$RUN_DIR" -newer "$REVIEW_CHECKPOINT" -type f | wc -l` — non-zero = agents alive (refresh checkpoint: `touch "$REVIEW_CHECKPOINT"`); zero since last refresh for `$HARD_CUTOFF` seconds = stalled. One `$EXTENSION` if `tail -20` output file explains delay; second stall = cutoff. On timeout: read partial results from stalled agent's file; surface with ⏱ in report. Never omit timed-out agents.
After all outputs collected (or timed out):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || RUN_DIR=""
ls "$RUN_DIR/"*.md 2>/dev/null || echo "⚠ No agent output files found in $RUN_DIR — check that $RUN_DIR was expanded correctly in spawn prompts"
# boundary1: post-fanout, pre-consolidation (compaction-contract.md §Lifecycle)
IFS= read -r _RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || _RUN_DIR=""
IFS= read -r _PR_TAG < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || _PR_TAG="unknown"
IFS= read -r _REPORT_DIR < "${TMPDIR:-/tmp}/oss-review-report-dir-${CSID}" 2>/dev/null || _REPORT_DIR=""
IFS= read -r _KEEP < "${TMPDIR:-/tmp}/oss-review-keep-items-${CSID}" 2>/dev/null || _KEEP=""
_FINDING_FILES=$(ls "$_RUN_DIR/"*.md 2>/dev/null | tr '\n' ' ' | sed 's/ *$//')
_PRESERVE="run-dir=$_RUN_DIR, report-dir=$_REPORT_DIR, pr=$_PR_TAG, finding-files=$_FINDING_FILES"
[ -n "$_KEEP" ] && _PRESERVE="$_PRESERVE; user-keep: $_KEEP"
mkdir -p .temp/state # timeout: 5000
{
echo "## Active Skill Contract"
echo "- skill: oss:review · phase: consolidation (after parallel review-agent fan-out)"
echo "- run-dir: $_RUN_DIR"
echo "- preserve: $_PRESERVE"
echo "- next: consolidate findings → final report (→ draft --reply if reply-mode)"
} > .temp/state/skill-contract.md
```
## Step 3: Post-agent checks (concurrent with Step 2 — after PR_BASE available)
Step 3a/3b may run concurrently with still-executing Step 2 agents — issue in same response turn as final Step 2 polls. Do NOT issue before `PR_BASE` is bound.
```bash
TRUNK=$(git remote show origin 2>/dev/null | grep 'HEAD branch' | awk '{print $NF}') # timeout: 6000
# shallow-clone guard — merge-base fails silently on shallow clones
IS_SHALLOW=$(git rev-parse --is-shallow-repository 2>/dev/null || echo "unknown")
if [ "$IS_SHALLOW" = "true" ]; then
echo "⚠ Shallow clone detected — running: git fetch --unshallow to enable merge-base checks"
git fetch --unshallow 2>/dev/null || echo "⚠ git fetch --unshallow failed — Step 3 checks may be incomplete"
fi
PR_BASE=$(git merge-base HEAD "origin/${TRUNK:-main}" 2>/dev/null || echo "origin/${TRUNK:-main}")
```
### 3a: Ecosystem impact check (for libraries with downstream users)
> **Scope disclosure**: check searches public GitHub code globally. Results may include unrelated projects using same symbol names — treat as signal, not proof. Rate-limited responses (HTTP 429, empty results) may indicate limitation, not absence of usage.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
# rate-limit guard: on 429, retry once after 10s, else log+continue
# exported symbols only (import/assignment targets on changed __init__.py lines), portable ERE — grep -oP absent on BSD/macOS; cap 5 to bound the 30s-per-search loop
CHANGED_EXPORTS=$(awk '/^diff --git /{f=($0 ~ /^diff --git a\/src\/.*__init__\.py /)} f && /^[-+][^-+]/{print substr($0,2)}' "$SNAP_DIR/pr.diff" 2>/dev/null | grep -oE '(^|[[:space:],])[A-Za-z_][A-Za-z0-9_]*' | tr -d ' ,' | grep -vE '^(from|import|as|all|__all__)$' | sort -u | head -5)
for export in $CHANGED_EXPORTS; do
echo "=== $export ==="
gh api "search/code" --method GET --field "q=$export language:python" --jq '.items[:5] | .[].repository.full_name' 2>/dev/null # timeout: 30000
done
grep -A2 "deprecated" "$SNAP_DIR/pr.diff" 2>/dev/null # timeout: 5000
```
### 3b: OSS checks
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
IFS= read -r SNAP_DIR < "${TMPDIR:-/tmp}/oss-review-snap-dir-${CSID}" 2>/dev/null || SNAP_DIR=""
OSS_SIGNALS="${TMPDIR:-/tmp}/oss-review-signals-${CLEAN_ARGS}-${CSID}.json"
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || echo "") # timeout: 6000
python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}/bin/check_oss_pr_signals.py" --clean-args "$CLEAN_ARGS" --latest-tag "$LATEST_TAG" --diff-file "$SNAP_DIR/pr.diff" --output-file "$OSS_SIGNALS" # timeout: 30000
cat "$OSS_SIGNALS" 2>/dev/null
```
## Step 4: Cross-validate critical/blocking findings
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload _OSS_SHARED (Check 41: fresh shell)
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED=""
cat "$_OSS_SHARED/foundry--cross-validation-protocol.md" # timeout: 5000
```
Follow above. File absent → warn: "cross-validation protocol not found — verify foundry plugin installed (`claude plugin list`); skipping Step 4." Then skip Step 4.
**Independence requirement**: cross-validation must run as separate spawned agent — same type as finding's origin. Do NOT validate in orchestrator context.
**Spawn cap: max 3 verifier agents.** Critical/blocking findings > 3 → group into batches of ≤2 findings per verifier; note grouped IDs in rationale.
Spawn verifier agent per critical/blocking finding (or per batch when capped). Agent reads relevant finding file from `$RUN_DIR` and referenced code. Each verifier must write full rationale to `$RUN_DIR/verify-<finding-id>.md` using the Write tool, then return ONLY: `{"finding_id":"<id>","verdict":"CONFIRMED|REFUTED","rationale":"<one sentence>","file":"$RUN_DIR/verify-<finding-id>.md"}`. REFUTED → downgrade finding severity or remove before consolidation.
## Step 5: Consolidate findings
Before output path, extract:
```bash
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-' || echo 'main') # timeout: 3000
DATE=$(date -u +%Y-%m-%d) # timeout: 5000
```
**IMPORTANT**: expand `$RUN_DIR`, `$REPORT_DIR`, `$REVIEW_SKILL_DIR`, `$BRANCH`, `$DATE`, `$CI_RED`, `$CI_FAILING_CHECKS`, and `$CI_COUNTS` to literal values before inserting into the spawn prompt. Un-expanded variables create wrong paths. The `## Source Files` footnote `Glob(... path="<EXPANDED_RUN_DIR>")` path must also be expanded to the literal `$RUN_DIR` value.
Reload the Stage-2 gate verdict (Check 41: fresh shell — set by the acceptance gate in Step 1, must survive to here):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
[ -f "${TMPDIR:-/tmp}/oss-review-gate-${CSID}" ] && . "${TMPDIR:-/tmp}/oss-review-gate-${CSID}"
GATE="${GATE:-PASS}"; GATE_REASON="${GATE_REASON:-}"
echo "Gate: $GATE ${GATE_REASON:+($GATE_REASON)}"
```
A reject-gate run never reaches this point (Step 5 is skipped entirely) — `GATE` here is always `PASS` or `BLOCK`.
Select consolidator agent by `PR_TYPE` (lighter model for non-logic PRs):
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLEAN_ARGS < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || CLEAN_ARGS=""
_REVIEW_MODE_FILE="${TMPDIR:-/tmp}/oss-review-mode-flags-${CLEAN_ARGS}-${CSID}"
[ -f "$_REVIEW_MODE_FILE" ] && . "$_REVIEW_MODE_FILE"
case "${PR_TYPE:-CODE}" in
DOCS_TYPING) CONSOLIDATOR_AGENT="foundry:linting-expert" ;;
TESTS_CI) CONSOLIDATOR_AGENT="foundry:qa-specialist" ;;
*) CONSOLIDATOR_AGENT="foundry:sw-engineer" ;; # domain-match rule: file-handoff-protocol.md §Consolidator
esac
```
Spawn `$CONSOLIDATOR_AGENT` consolidator agent with prompt:
<!-- loads: consolidator-prompt.md -->
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload REVIEW_SKILL_DIR (Check 41: fresh shell)
IFS= read -r REVIEW_SKILL_DIR < "${TMPDIR:-/tmp}/review-skill-dir-${CSID}" 2>/dev/null || REVIEW_SKILL_DIR=""
cat "$REVIEW_SKILL_DIR/templates/consolidator-prompt.md" # timeout: 5000
```
Template (loaded above). Prepend the run-dir resolution preamble from `agent-prompts.md` so the consolidator self-resolves `$RUN_DIR` (`cat "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}"`). Substitute `<REPORT_DIR>`, `<REVIEW_SKILL_DIR>`, `<_OSS_SHARED>`, `<DATE>`, `<CHANGED_FILES>`, `<SCOPE>`, `<CI_FAILING_CHECKS>`, `<CI_COUNTS>`, `<GATE>` with literal expanded values (`<GATE>` = `$GATE` reloaded above, `PASS` or `BLOCK`); leave `$RUN_DIR` literal (agent self-resolves). Spawn: `Agent(subagent_type="$CONSOLIDATOR_AGENT", prompt=<substituted consolidator-prompt.md content>)`
Main context receives only the one-liner verdict. **Consolidator unavailable fallback** — `Agent` tool deferred/not loaded: Print: `⛔ BLOCKED — Agent tool not loaded; consolidator cannot run. Re-invoke /oss:review to retry. If persistent, run /foundry:setup (requires foundry plugin) to verify session config.` Do NOT read agent finding files inline — floods main context (~16–32K tokens per run), produces unreliable synthesis.
After parsing confidence: agent < 0.7 → prepend **⚠ LOW CONFIDENCE** to findings section, state gap explicitly. Never drop uncertain findings.
TaskUpdate "Step 5b: Print report header" → `in_progress`.
**MANDATORY, not optional narration** — the consolidator's returned one-liner is a routing signal only; it is never printed to the user and never satisfies this step. Perform, in this exact order, in this same turn, before any other Step 5/6/7 text:
1. Read `$REPORT_DIR/review-report.md` (Read tool).
2. Extract every field from the opening `---` up to and including the closing `---` — `Title:`, `Date:`, `PR Type:`, `Scope:`, `Focus:`, `Agents:`, `CI:`, `Outcome:`, `Summary:`, `Confidence:`, `Next steps:`, `Path:`.
3. Render those 12 fields as a two-column Markdown table (`Field | Value`, one row per key, file order) per quality-gates.md §Report File Format's Universal terminal-print rule — never print the raw `---`-delimited block. Append `→ saved to $REPORT_DIR/review-report.md`.
4. TaskUpdate "Step 5b: Print report header" → `completed` (only once the table has actually appeared in this response).
This table IS the reply header — print/omit-box handling per quality-gates.md §Report File Format (universal rule); omit the `╔═╗` Re:Anchor box (communication.md exempts quality-gates `---` report headers — the box would shadow the table). Never emit both a box header and this table. **Historical note**: an earlier revision of this step printed the raw `---`-delimited block verbatim inside a ```` ```text ```` fence to dodge markdown misparsing the literal `---` (leading `---` read as YAML frontmatter, closing `---` under `Path:` read as a setext heading) — that predates quality-gates.md's table rule and is superseded by it: converting to a table drops the raw `---` delimiters entirely, so the misparse risk the fence was guarding against does not arise. Render all 12 fields verbatim as table rows; use the `·`-separated one-line fallback ONLY when the `$REPORT_DIR/review-report.md` read genuinely fails — then state `⚠ could not read report header — verify $REPORT_DIR` before the fallback line rather than silently degrading, and still mark the task `completed` (the fallback line satisfies the step).
**Why this step is enforced twice over** (empirically motivated — a prior run genuinely skipped it): a prior run spawned the consolidator, received its one-liner, and jumped straight to Step 7's `AskUserQuestion` + confidence block — skipping this print entirely, even though `AskUserQuestion` itself (a hard tool call) fired correctly; do not treat "Step 5: Consolidate findings" completing as covering this step, they are separate tasks for that reason. **Runtime backstop**: `hooks/enforce-review-header.js` (PreToolUse on `AskUserQuestion`) denies Step 7a's call while `$REPORT_DIR/review-report.md` is missing or empty — a denial reading `oss:review report gate` means Step 5 never produced the report; spawn the consolidator, print the header, then re-issue the question. The hook can't see whether the print happened, only whether the report exists — the task above remains the actual check for the print itself.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# boundary2: post-consolidation, pre-reply (compaction-contract.md §Lifecycle)
IFS= read -r _PR_TAG < "${TMPDIR:-/tmp}/oss-review-pr-tag-${CSID}" 2>/dev/null || _PR_TAG="unknown"
IFS= read -r _REPORT_DIR < "${TMPDIR:-/tmp}/oss-review-report-dir-${CSID}" 2>/dev/null || _REPORT_DIR=""
IFS= read -r _RUN_DIR < "${TMPDIR:-/tmp}/oss-review-run-dir-${CSID}" 2>/dev/null || _RUN_DIR=""
{
echo "## Active Skill Contract"
echo "- skill: oss:review · phase: reply (after consolidation)"
echo "- run-dir: $_RUN_DIR"
echo "- preserve: final-report=$_REPORT_DIR/review-report.md, pr=$_PR_TAG"
echo "- next: draft contributor reply (--reply) or stop at Step 7"
} > .temp/state/skill-contract.md
```
## Step 6: Delegate implementation follow-up (optional)
Identify tasks Codex can implement — meaningful code/doc work grounded in actual implementation.
**Delegate**: public functions with no docstrings (read impl first, describe so Codex writes real 6-section docstring) · missing test coverage for concrete well-defined behavior · consistent rename across files. **Do not delegate**: architectural issues, logic errors, security vulns, or any task requiring human judgment.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# Reload _OSS_SHARED (Check 41: fresh shell)
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED=""
cat "$_OSS_SHARED/codex-delegation.md" # timeout: 5000
```
Follow above. File absent → warn: "codex-delegation criteria not found — verify foundry plugin installed (`claude plugin list`); skipping Step 6 delegation." Then skip Step 6.
Print `### Codex Delegation` only when tasks delegated — omit otherwise. Don't rewrite output file.
## Step 7: Reply gate — STOP CHECK
**Worktree exit** — if `WT_ENABLED=true` and a worktree was entered at Step 0: the report already lives in the main tree (§review). Follow `worktree-isolation.md` §Exit — capture branch, call `ExitWorktree(action="keep")`, append the `Worktree` block. Exit **before** the follow-up gate so any follow-up runs in the main tree. Never auto-merge.
**Hard gate**: check "Step 5b: Print report header" task status before anything else in this step. Not `completed` → the header table has not actually been printed yet — go back and do it now (see Step 5), then mark the task `completed`, before calling `AskUserQuestion` below.
**Confidence block ownership**: `REPLY_MODE=true` → block in Step 8. `REPLY_MODE=false` → block in Step 7b.
`REPLY_MODE=true`: proceed to Step 8 — no Confidence block here. `REPLY_MODE=false` — do NOT proceed to Step 8. Execute both sub-steps below:
### 7a — Follow-up gate
! IMPORTANT — invoke `AskUserQuestion` tool directly. Never write options as plain text. Single call — all options in one:
- question: "What next?"
- (a) label: `/oss:resolve $CLEAN_ARGS` — description: fix this PR (implement review findings, resolve conflicts, push)
- (b) label: `/oss:resolve report` — description: resolve from full review report only (no GitHub re-fetch)
- (c) label: `/oss:resolve $CLEAN_ARGS report` — description: fix PR + resolve from full review report in one pass
- (d) label: `walk through findings` — description: go through each finding interactively
- (e) label: `skip` — description: no action
`oss:resolve` has `disable-model-invocation: true` — `Skill()` invocation blocked. After AskUserQuestion returns:
- Resolve variant chosen (a/b/c when available): present chosen label as command user must run manually (e.g. `Run: /oss:resolve $CLEAN_ARGS`); no `Skill()` call
- `walk through findings` / `skip`: handle inline or stop
### 7b — Confidence block
End with `## Confidence` block per CLAUDE.md output standards.
```bash
rm -f .temp/state/skill-contract.md # skill complete (compaction-contract.md §Lifecycle) # timeout: 5000
```
<!-- Steps 5–7 defined in Step 5 (consolidate), Step 6 (Codex delegation), Step 7 (reply gate) blocks above — numbered sequentially from Step 1; Step 4 (cross-validate) precedes them; no gap: 4→5→6→7→8 -->
## Step 8: Draft contributor reply (only when --reply)
`REPLY_MODE` not set → skip.
```bash
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r _OSS_SHARED < "${TMPDIR:-/tmp}/review-oss-shared-${CSID}" 2>/dev/null || _OSS_SHARED="" # reload (Check 41)
cat "$_OSS_SHARED/shepherd-reply-protocol.md" # timeout: 5000
```
`shepherd-reply-protocol.md` (loaded above) — apply invocation pattern and terminal summary format.
Spawn with:
- Report path: review output file from Step 5
- PR number and contributor handle: from Step 1 `gh pr view` output
- Output path: `.temp/output-reply-<PR#>-$(date -u +%Y-%m-%d).md`
**Part 2 compliance gate** (after shepherd returns — do NOT trust the return line alone):
```bash
# only when findings reference file:line — true LGTM has none
REPLY_OUT=".temp/output-reply-<PR#>-$(date -u +%Y-%m-%d).md" # timeout: 5000
grep -qE '^\| *Importance *\| *Confidence *\| *File *\| *Line' "$REPLY_OUT" && echo "PART2_PRESENT=true" || echo "PART2_PRESENT=false"
```
`PART2_PRESENT=false` while the Step 5 report has ≥1 file:line finding → reply is non-compliant (findings folded into prose). Re-spawn `oss:shepherd` once with the same inputs plus: `"Part 2 table is MANDATORY — every file:line finding from the report must be its own row in the | Importance | Confidence | File | Line | Comment | table; do not fold file:line findings into Part 1 prose."` Re-check; if still absent, surface `⚠ Part 2 table missing — findings remain in prose` in the terminal summary.
End with `## Confidence` block per CLAUDE.md. Always last thing, regardless of `--reply`.
```bash
rm -f .temp/state/skill-contract.md # skill complete (compaction-contract.md §Lifecycle) # timeout: 5000
```
</workflow>
<calibration>
Scenarios:
1. FIX scope: single bug-fix PR with 1 changed file → scope=FIX drops the perf+arch unit entirely (both members out of scope). Surviving units: sw-engineer, docs+lint (merged), challenger (unless `--no-challenge`) = 3 units ≤ FANOUT_MAX, all spawn; + pinned qa-specialist = 4 spawns (+ Codex bridge if installed).
2. FEATURE scope: new feature PR with API changes → units sw-engineer, perf+arch, docs+lint, challenger = 4 survive preselection; default cap spawns top 3 ranked (dropped unit listed in report) + pinned qa-specialist = 4 spawns; `--full` spawns all 4 units + qa-specialist = 5 spawns.
3. --reply mode: existing review report + --reply flag → skip to Step 8, no agents spawned
4. DOCS_TYPING scope: PR with only annotation-type .py changes (no logic) → Step 0 sets PR_TYPE=DOCS_TYPING, CHALLENGE_ENABLED=false, CONSOLIDATOR_AGENT=foundry:linting-expert; only linting-expert spawned; Step 5 uses linting-expert consolidator.
5. TESTS_CI scope: PR with only test files + CI config → Step 0 sets PR_TYPE=TESTS_CI, CHALLENGE_ENABLED=false, CONSOLIDATOR_AGENT=foundry:qa-specialist; qa-specialist + linting-expert spawned; Step 5 uses qa-specialist consolidator.
6. REJECT_GOAL: PR body states "make recall exceed 1.0" as the goal → gate finds the metric bounded [0,1] by spec, contradicts stated goal regardless of diff quality → skip Step 2–4, orchestrator writes report with Gate=REJECT_GOAL, no agents spawned.
7. REJECT_CONDUCT: PR diff silently exfiltrates env vars to an external URL, body claims unrelated bugfix → gate spawns foundry:challenger, confirms `conduct.verdict=BY_DESIGN` at confidence ≥0.7 → skip Step 2–4, Gate=REJECT_CONDUCT. Contrast: same diff pattern but challenger returns `ACCIDENTAL` (e.g. leftover debug logging) → treat as normal `[critical]` finding, proceed to full fanout.
8. REJECT_DUPLICATE: PR body says "Closes #40", `gh issue view 40` shows `state=CLOSED` closed by a different, already-merged PR → `DUPLICATE_HIT=true` → skip Step 2–4, Gate=REJECT_DUPLICATE, no agents spawned, no challenger needed (mechanical evidence, orchestrator judgment only).
</calibration>
<notes>
- **PR review acceptance criteria — canonical here**: oss:shepherd cross-references these criteria; don't duplicate in shepherd. Shepherd defers to this file for acceptance thresholds, severity definitions. Header is two-part: `Gate:` (`PASS` / `BLOCK` / `REJECT_<GROUND>` where GROUND is one of the 8 in Stage 1 — GOAL, CONDUCT, SCOPE, LICENSE, DUPLICATE, REVERTED, SPAM, PHILOSOPHY; reject is terminal and skips fanout, block isn't) and `Outcome:` (`APPROVE` / `NEEDS_WORK` / `REQUEST_CHANGES` from the full fanout, or `N/A — rejected at gate` when `Gate` is a `REJECT_*`). See Acceptance gate (Step 1).
- **Block-tier catalogue** — per-category default for the `[blocking]` tag, applied by full-review agents once fanout runs (not automatic, judgment still required per row):
| Category | Default | Nuance |
| -- | -- | -- |
| CI red / failing check | `[blocking]` | only for a **major** failure (real required-check failure); a single flaky-looking rerun blip stays noted, not auto-blocking — see flaky-test rule above |
| Missing test coverage for new/changed logic | `[blocking]` | — |
| Accidental security bug (careless, not by-design) | `[blocking]` | by-design version is Stage 1 `REJECT_CONDUCT`, not this |
| Breaking API change, no deprecation/migration path | `[blocking]` | — |
| Missing docs for new/changed public behavior | `[blocking]` | missing CHANGELOG entry alone is **not** blocking — that can land in a follow-up (`/oss:release`), never gates merge by itself |
| Perf regression | **contextual, not automatic** | depends on scope: regressing 2× vs recent releases with no offsetting reason is bad; not blocking when the old speed only existed because of a prior correctness bug and the "regression" is the cost of doing it right — agent must state which case applies, not just report the delta |
| Merge conflicts | **not** `[blocking]` | out of review's scope — `/oss:resolve` handles it, oss:review doesn't gate on it |
| Incomplete implementation (TODOs in changed paths, missing error handling on new logic) | `[blocking]` | — |
| Missing CLA/DCO signature | `[blocking]` **only if the project requires one** | check first — CLA-assistant/DCO-check bot status, or a signing mandate in `CONTRIBUTING.md`; no such requirement in this project → not applicable, don't invent the rule |
- Critical issues always surfaced regardless of scope
- Skip sections with no issues — no padding. Isolated code without git context → skip OSS Checks and Performance Concerns unless evidence of perf issues (nested loops, I/O in tight loops) or OSS concerns (hardcoded secrets, new deps).
- **Signal-to-noise gate**: Function/class ≤50 lines with 1–2 critical/high issues → max 2 additional medium/low findings. Rest as `[nit]` in "Minor Observations". First 3 findings reader sees = most impactful.
- PR mode: CI red sets `Gate: BLOCK` (Step 1) — review still proceeds through full fanout, the block is surfaced up front in the header, never a reason to skip review
- Blocking issues need explicit `[blocking]` prefix
- Follow-up chains:
- `[blocking]` bugs or regressions → `/develop:fix` (requires `develop` plugin) to reproduce with test, apply targeted fix
- Structural or quality issues → `/develop:refactor` (requires `develop` plugin) for test-first improvements
- Security findings in auth/input/deps → run `pip-audit` for dep CVEs; address OWASP issues via `/develop:fix` (requires `develop` plugin)
- Mechanical issues beyond Step 5 → call `bridge:implement` with the exact finding, target paths, current evidence, permitted edits, required result, stop condition, and verification command.
- Docstrings, type annotations, renames → construct that complete `bridge:implement` brief separately for each finding; never pass a workflow step number or an unresolved shorthand reference.
- PR feedback for contributor → `--reply` to auto-draft via oss:shepherd, or invoke oss:shepherd manually for custom framing
</notes>
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!