Skill Tracer
Skills › Compound Engineering › ce-code-review › scope.md

scope.md

188 lines · skills/ce-code-review/references/scope.md at 3449230

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Determining the reviewed diff and scope
2
3Read this at Stage 1. It defines how to resolve scope on every invocation path and how to compute the deterministic scope signals that Stage 3 (Select reviewers) uses.
4

Determining the reviewed diff and scope

Read this at Stage 1. It defines how to resolve scope on every invocation path and how to compute the deterministic scope signals that Stage 3 (Select reviewers) uses.

Raw lines 1 to 4
5### Stage 1: Determine scope
6
7Compute the diff range, file list, and diff. Minimize permission prompts by combining into as few commands as possible.
8
9**If `base:` argument is provided (fast path):**
11The caller already knows the diff base. Skip all base-branch detection, remote resolution, and merge-base computation. Use the provided value directly:
13```
14BASE_ARG="{base_arg}"
15BASE=$(git merge-base HEAD "$BASE_ARG" 2>/dev/null) || BASE="$BASE_ARG"
16```
18Then produce the same output as the other paths:
20```
21echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard
22```
24This path works with any ref — a SHA, `origin/main`, a branch name. Callers reviewing the current checkout should pass explicit `base:` when auto-detection is unnecessary. **Do not combine `base:` with a PR number or branch target.** If both are present, stop with an error: "Cannot use `base:` with a PR number or branch target — `base:` implies the current checkout is already the correct branch. Pass `base:` alone, or pass the target alone and let scope detection resolve the base."
26**If a PR number or GitHub URL is provided as an argument:**
28Do **not** check out the PR branch. Scope comes from GitHub read APIs plus optional local alignment when HEAD already matches the PR head branch.
30**Skip-condition pre-check.** Before scope detection, run a PR-state probe:
32```
33gh pr view <number-or-url> --json state,title,body,files
34```
36Apply skip rules in order:
38- `state` is `CLOSED` or `MERGED` -> stop with reason `PR is closed/merged; not reviewing.`
39- **Trivial-PR judgment**: spawn a lightweight sub-agent on the platform's cheapest capable model when a known override exists; otherwise omit the model override and inherit. Give it the PR title, body, and changed file paths. The agent's task: "Is this an automated or trivial PR that does not warrant a code review? Consider: dependency lock-file or manifest-only bumps, automated release commits, chore version increments with no substantive code changes. When in doubt, answer no — false negatives (skipped reviews that should have run) are more costly than false positives (unnecessary reviews)." If the judgment returns yes: stop with reason `PR appears to be a trivial automated PR; not reviewing. Run without a PR argument to review the current branch, or pass base:<ref> if review is intended.`
41If the user's or project's active instructions or the live prompt name a model for this subagent, use that model instead of the default tier above. They can name it by the subagent's tier, by its prompt-asset name, or for the subagents being dispatched. Without such an instruction, a model configured for this subagent's role takes precedence over the default tier.
43When any skip rule applies, stop without dispatching reviewers. **Default mode:** emit the reason as plain text. **`mode:agent`:** emit JSON only — `{"status":"skipped","reason":"<same message>"}` — so programmatic callers can parse the outcome. **Standalone**, **`base:`**, and **branch-remote** paths are unaffected. **Draft PRs are reviewed normally.**
45If no skip rule applies, fetch PR metadata **without checkout**:
47```
48gh pr view <number-or-url> --json title,body,baseRefName,headRefName,headRefOid,isCrossRepository,url,files,reviews,comments --jq '{title, body, baseRefName, headRefName, headRefOid, isCrossRepository, url, files: [.files[].path], hasPriorComments: ((.reviews | map(select(.state != "APPROVED" or .body != "")) | length) > 0 or (.comments | length) > 0)}'
49```
51Set `BASE:` to `pr:<number-or-url>` (logical marker — not a git SHA). Set `UNTRACKED:` from `git ls-files --others --exclude-standard` on the **current** checkout (usually empty during PR-remote review).
53**PR scope mode.** Classify as **`local-aligned`** only when **all** of these hold; otherwise use **`pr-remote`**. A matching branch name alone is not enough — a fork PR or a stale local branch can share a name with the PR head while pointing at unrelated code, and trusting the name would diff and inspect the wrong tree.
551. `git rev-parse --abbrev-ref HEAD` equals `headRefName`.
562. The PR is **not** cross-repository (`isCrossRepository` is false).
573. The PR head commit is contained in the local checkout: `git merge-base --is-ancestor <headRefOid> HEAD` exits 0. This confirms the working tree actually carries the PR head (allowing unpushed local fixes layered on top) rather than an unrelated same-named branch.
59- **`local-aligned`** — all three checks pass. Local Read/Grep/git blame against workspace files are valid for PR changed paths.
60- **`pr-remote`** — any check fails. The working tree is **not** the PR head; workspace file contents for changed paths may be stale or unrelated.
62**Diff by scope mode** (do not mix remote and local diffs — contradictory hunks cause false positives):
64- **`local-aligned`:** Resolve `<resolved-base-ref>` from `baseRefName` (fetch if needed). Compute `BASE=$(git merge-base HEAD <resolved-base-ref>)`, then set `FILES:` from `git diff --name-only $BASE` and `DIFF:` from `git diff -U10 --irreversible-delete $BASE` (includes committed, staged, and unstaged changes on the PR branch). Do **not** call `gh pr diff` or append remote hunks — when unpushed fixes exist, the local tree is canonical. Note in Coverage: `scope: local-aligned (PR; local tree diff)`.
65- **`pr-remote`:** Set `FILES:` from the PR `files` array. Set `DIFF:` from `gh pr diff <number-or-url> --color=never`. If `gh pr diff` fails, stop with an actionable error — do not fall back to checkout. Then drop deleted-file bodies from that saved output the way `--irreversible-delete` does on the local paths, with `awk '/^diff --git /{del=0} del && !/^index /{next} /^deleted file mode /{del=1} {print}'`; never pipe `gh pr diff` straight into the filter, because the pipe hides its failure.
67When **`pr-remote`**, before Stage 4:
691. Best-effort fetch PR head without checkout: `git fetch --no-tags origin <headRefName>:refs/review/pr-<number>-head` (substitute PR number from metadata).
702. Set `PR_HEAD_REF=refs/review/pr-<number>-head` for reviewers and validators only when `git rev-parse refs/review/pr-<number>-head` equals the metadata `headRefOid`. A successful fetch does not prove the ref is the PR head: on a cross-repository PR, `origin` can carry an unrelated branch with the same name, and the PR can move between the metadata call and the fetch. When the fetch fails or the OID differs, omit `PR_HEAD_REF` and note which in Coverage — reviewers must rely on diff hunks only.
713. Best-effort fetch the PR base without checkout: `git fetch --no-tags origin <baseRefName>`. When it succeeds, resolve a concrete ref with `git rev-parse FETCH_HEAD` and set `PR_BASE_REF` to that SHA — a **real git base ref** reviewers and validators use for file-level git diffs (e.g. `data-migration-reviewer` runs `git diff <PR_BASE_REF> -- db/schema.rb`/`structure.sql`). The `pr:<number-or-url>` logical marker in `BASE:` stays the scope marker; `PR_BASE_REF` is the diffable base. When the fetch fails, omit `PR_BASE_REF` and note in Coverage — schema-drift and other git-diff checks fall back to diff hunks only and must **not** assume `main`.
724. Include `<pr-scope-mode>pr-remote</pr-scope-mode>` and, when set, `<pr-head-ref>...</pr-head-ref>` and `<pr-base-ref>...</pr-base-ref>` in the Stage 4 review context bundle.
74Reviewers and Stage 5b validators in **`pr-remote`** mode must **not** Read/Grep workspace paths for files in `FILES:`. Inspect via `git show <PR_HEAD_REF>:<path>` when `PR_HEAD_REF` is set, otherwise use only the provided diff hunks. **`local-aligned`** uses normal workspace inspection.
76**If a branch name is provided as an argument:**
78Substitute the provided branch name as `<branch>`. Do **not** check out `<branch>`.
80If `git rev-parse --abbrev-ref HEAD` equals `<branch>`, use the **standalone (current branch)** path below — same tree, explicit branch name; do not use remote-only diff.
82Otherwise diff the remote/local ref **without checkout**:
841. Try `gh pr view <branch> --json baseRefName,url,headRefName` — if a PR exists, prefer the **PR number/URL path** above (same remote diff rules).
852. Else resolve `<branch>` as `origin/<branch>` or `<branch>` after `git fetch --no-tags origin <branch>` when needed.
863. Resolve default base branch (same logic as standalone). Compute `BASE=$(git merge-base <base-ref> <branch-ref>)` and `git diff -U10 --irreversible-delete $BASE <branch-ref>`.
874. If `<branch-ref>` cannot be resolved locally, stop: "Cannot diff branch `<branch>` without checkout. Check out that branch, pass its open PR URL/number, or review the current branch with `base:`."
89On success for remote branch diff, set **branch-remote scope**. The working tree is **not** `<branch>`. Include `<pr-scope-mode>branch-remote</pr-scope-mode>` and `<branch-head-ref><branch-ref></branch-head-ref>` in the Stage 4 review context bundle. Reviewers and Stage 5b validators must **not** Read/Grep workspace paths for files in `FILES:`. Inspect via `git show <branch-ref>:<path>` or diff hunks only.
91Produce:
93```
94echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE <branch-ref> && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE <branch-ref> && echo "UNTRACKED:" && git ls-files --others --exclude-standard
95```
97**If no argument (standalone on current branch):**
99Apply the same base-detection logic as branch mode above, using the current branch (i.e., `gh pr view --json baseRefName,url` with no argument defaults to the current branch).
101If no base can be resolved, **stop**. Do not fall back to `git diff HEAD` — a standalone review without the base would only show uncommitted changes and silently miss all committed work on the branch.
103On success, produce the diff:
105```
106echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard
107```
109Using `git diff $BASE` (without `..HEAD`) diffs the merge-base against the working tree, which includes committed, staged, and unstaged changes together.
111**Whole-file deletions carry no body.** `--irreversible-delete` keeps each deleted file's `deleted file mode` header and path and leaves out its old content, so reviewers do not read code the change removes. A reviewer that needs a deleted body reads it from the review base, as `references/diff-scope.md` states.
113**Untracked file handling:** Always inspect `UNTRACKED:`. Untracked paths are out of scope unless staged. When non-empty, list excluded files in Coverage and continue on tracked changes only — never stop or prompt.

Stage 1: Determine scope

Compute the diff range, file list, and diff. Minimize permission prompts by combining into as few commands as possible.

**If base: argument is provided (fast path):**

The caller already knows the diff base. Skip all base-branch detection, remote resolution, and merge-base computation. Use the provided value directly:

BASE_ARG="{base_arg}"
BASE=$(git merge-base HEAD "$BASE_ARG" 2>/dev/null) || BASE="$BASE_ARG"

Then produce the same output as the other paths:

echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard

This path works with any ref — a SHA, origin/main, a branch name. Callers reviewing the current checkout should pass explicit base: when auto-detection is unnecessary. **Do not combine base: with a PR number or branch target.** If both are present, stop with an error: "Cannot use base: with a PR number or branch target — base: implies the current checkout is already the correct branch. Pass base: alone, or pass the target alone and let scope detection resolve the base."

If a PR number or GitHub URL is provided as an argument:

Do not check out the PR branch. Scope comes from GitHub read APIs plus optional local alignment when HEAD already matches the PR head branch.

Skip-condition pre-check. Before scope detection, run a PR-state probe:

gh pr view <number-or-url> --json state,title,body,files

Apply skip rules in order:

  • state is CLOSED or MERGED -> stop with reason PR is closed/merged; not reviewing.
  • Trivial-PR judgment: spawn a lightweight sub-agent on the platform's cheapest capable model when a known override exists; otherwise omit the model override and inherit. Give it the PR title, body, and changed file paths. The agent's task: "Is this an automated or trivial PR that does not warrant a code review? Consider: dependency lock-file or manifest-only bumps, automated release commits, chore version increments with no substantive code changes. When in doubt, answer no — false negatives (skipped reviews that should have run) are more costly than false positives (unnecessary reviews)." If the judgment returns yes: stop with reason PR appears to be a trivial automated PR; not reviewing. Run without a PR argument to review the current branch, or pass base:<ref> if review is intended.

If the user's or project's active instructions or the live prompt name a model for this subagent, use that model instead of the default tier above. They can name it by the subagent's tier, by its prompt-asset name, or for the subagents being dispatched. Without such an instruction, a model configured for this subagent's role takes precedence over the default tier.

When any skip rule applies, stop without dispatching reviewers. Default mode: emit the reason as plain text. **mode:agent:** emit JSON only — {"status":"skipped","reason":"<same message>"} — so programmatic callers can parse the outcome. Standalone, **base:, and branch-remote paths are unaffected. Draft PRs are reviewed normally.**

If no skip rule applies, fetch PR metadata without checkout:

gh pr view <number-or-url> --json title,body,baseRefName,headRefName,headRefOid,isCrossRepository,url,files,reviews,comments --jq '{title, body, baseRefName, headRefName, headRefOid, isCrossRepository, url, files: [.files[].path], hasPriorComments: ((.reviews | map(select(.state != "APPROVED" or .body != "")) | length) > 0 or (.comments | length) > 0)}'

Set BASE: to pr:<number-or-url> (logical marker — not a git SHA). Set UNTRACKED: from git ls-files --others --exclude-standard on the current checkout (usually empty during PR-remote review).

PR scope mode. Classify as **local-aligned only when all of these hold; otherwise use pr-remote**. A matching branch name alone is not enough — a fork PR or a stale local branch can share a name with the PR head while pointing at unrelated code, and trusting the name would diff and inspect the wrong tree.

  1. git rev-parse --abbrev-ref HEAD equals headRefName.
  2. The PR is not cross-repository (isCrossRepository is false).
  3. The PR head commit is contained in the local checkout: git merge-base --is-ancestor <headRefOid> HEAD exits 0. This confirms the working tree actually carries the PR head (allowing unpushed local fixes layered on top) rather than an unrelated same-named branch.
  • **local-aligned** — all three checks pass. Local Read/Grep/git blame against workspace files are valid for PR changed paths.
  • **pr-remote — any check fails. The working tree is not** the PR head; workspace file contents for changed paths may be stale or unrelated.

Diff by scope mode (do not mix remote and local diffs — contradictory hunks cause false positives):

  • **local-aligned:** Resolve <resolved-base-ref> from baseRefName (fetch if needed). Compute BASE=$(git merge-base HEAD <resolved-base-ref>), then set FILES: from git diff --name-only $BASE and DIFF: from git diff -U10 --irreversible-delete $BASE (includes committed, staged, and unstaged changes on the PR branch). Do not call gh pr diff or append remote hunks — when unpushed fixes exist, the local tree is canonical. Note in Coverage: scope: local-aligned (PR; local tree diff).
  • **pr-remote:** Set FILES: from the PR files array. Set DIFF: from gh pr diff <number-or-url> --color=never. If gh pr diff fails, stop with an actionable error — do not fall back to checkout. Then drop deleted-file bodies from that saved output the way --irreversible-delete does on the local paths, with awk '/^diff --git /{del=0} del && !/^index /{next} /^deleted file mode /{del=1} {print}'; never pipe gh pr diff straight into the filter, because the pipe hides its failure.

When **pr-remote**, before Stage 4:

  1. Best-effort fetch PR head without checkout: git fetch --no-tags origin <headRefName>:refs/review/pr-<number>-head (substitute PR number from metadata).
  2. Set PR_HEAD_REF=refs/review/pr-<number>-head for reviewers and validators only when git rev-parse refs/review/pr-<number>-head equals the metadata headRefOid. A successful fetch does not prove the ref is the PR head: on a cross-repository PR, origin can carry an unrelated branch with the same name, and the PR can move between the metadata call and the fetch. When the fetch fails or the OID differs, omit PR_HEAD_REF and note which in Coverage — reviewers must rely on diff hunks only.
  3. Best-effort fetch the PR base without checkout: git fetch --no-tags origin <baseRefName>. When it succeeds, resolve a concrete ref with git rev-parse FETCH_HEAD and set PR_BASE_REF to that SHA — a real git base ref reviewers and validators use for file-level git diffs (e.g. data-migration-reviewer runs git diff <PR_BASE_REF> -- db/schema.rb/structure.sql). The pr:<number-or-url> logical marker in BASE: stays the scope marker; PR_BASE_REF is the diffable base. When the fetch fails, omit PR_BASE_REF and note in Coverage — schema-drift and other git-diff checks fall back to diff hunks only and must not assume main.
  4. Include <pr-scope-mode>pr-remote</pr-scope-mode> and, when set, <pr-head-ref>...</pr-head-ref> and <pr-base-ref>...</pr-base-ref> in the Stage 4 review context bundle.

Reviewers and Stage 5b validators in **pr-remote mode must not** Read/Grep workspace paths for files in FILES:. Inspect via git show <PR_HEAD_REF>:<path> when PR_HEAD_REF is set, otherwise use only the provided diff hunks. **local-aligned** uses normal workspace inspection.

If a branch name is provided as an argument:

Substitute the provided branch name as <branch>. Do not check out <branch>.

If git rev-parse --abbrev-ref HEAD equals <branch>, use the standalone (current branch) path below — same tree, explicit branch name; do not use remote-only diff.

Otherwise diff the remote/local ref without checkout:

  1. Try gh pr view <branch> --json baseRefName,url,headRefName — if a PR exists, prefer the PR number/URL path above (same remote diff rules).
  2. Else resolve <branch> as origin/<branch> or <branch> after git fetch --no-tags origin <branch> when needed.
  3. Resolve default base branch (same logic as standalone). Compute BASE=$(git merge-base <base-ref> <branch-ref>) and git diff -U10 --irreversible-delete $BASE <branch-ref>.
  4. If <branch-ref> cannot be resolved locally, stop: "Cannot diff branch <branch> without checkout. Check out that branch, pass its open PR URL/number, or review the current branch with base:."

On success for remote branch diff, set branch-remote scope. The working tree is not <branch>. Include <pr-scope-mode>branch-remote</pr-scope-mode> and <branch-head-ref><branch-ref></branch-head-ref> in the Stage 4 review context bundle. Reviewers and Stage 5b validators must not Read/Grep workspace paths for files in FILES:. Inspect via git show <branch-ref>:<path> or diff hunks only.

Produce:

echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE <branch-ref> && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE <branch-ref> && echo "UNTRACKED:" && git ls-files --others --exclude-standard

If no argument (standalone on current branch):

Apply the same base-detection logic as branch mode above, using the current branch (i.e., gh pr view --json baseRefName,url with no argument defaults to the current branch).

If no base can be resolved, stop. Do not fall back to git diff HEAD — a standalone review without the base would only show uncommitted changes and silently miss all committed work on the branch.

On success, produce the diff:

echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 --irreversible-delete $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard

Using git diff $BASE (without ..HEAD) diffs the merge-base against the working tree, which includes committed, staged, and unstaged changes together.

Whole-file deletions carry no body. --irreversible-delete keeps each deleted file's deleted file mode header and path and leaves out its old content, so reviewers do not read code the change removes. A reviewer that needs a deleted body reads it from the review base, as references/diff-scope.md states.

Untracked file handling: Always inspect UNTRACKED:. Untracked paths are out of scope unless staged. When non-empty, list excluded files in Coverage and continue on tracked changes only — never stop or prompt.

Raw lines 5 to 114
Starts an agent spawn :39
Starts an agent sub-agent :39
Starts an agent subagent :41
Starts an agent subagents :41
Starts an agent dispatched :41
Starts an agent dispatching :43
115### Stage 1b: Compute scope signals (cheap, deterministic)
117Derive deterministic facts once with `scripts/review-scope.py` from this skill's directory. The helper validates the endpoints, counts changed lines, derives path classes, and reports floors. It never awards lite. Do not reproduce those mechanics in prose or estimate them from diff hunks. The invocation below is the helper's contract: run it directly rather than inspecting the script or probing its `--help`, unless it actually fails with an incompatibility.
119Set `SCOPE_MODE` to the Stage 1 scope mode and set `DIFF_A`/`DIFF_B` to its two endpoints:
120- **`local-aligned` / standalone / `base:`** — `DIFF_A="$BASE"` (a real SHA/ref), `DIFF_B` empty (diffs base vs working tree).
121- **`pr-remote` / `branch-remote`** — `DIFF_A=<PR_BASE_REF>`, `DIFF_B=<PR_HEAD_REF>` (or `<branch-head-ref>`) — the fetched refs from Stage 1.
123```bash
124SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>";
125PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
126if [ "$SCOPE_MODE" = "pr-remote" ] || [ "$SCOPE_MODE" = "branch-remote" ]; then
127 "$PY" "$SKILL_DIR/scripts/review-scope.py" --base "${DIFF_A:-}" --head "${DIFF_B:-}" --docs-root "<root>";
128else
129 "$PY" "$SKILL_DIR/scripts/review-scope.py" --base "$DIFF_A" --docs-root "<root>";
130fi
131```
133Remote scope always passes both endpoint flags, even when a best-effort fetch left one value empty; the helper then refuses to compute rather than comparing the fetched base to the unrelated local worktree. Load the JSON result. `hard_block_full` is a floor for the Review depth gate in `references/modes-and-output.md`; it covers the named hard-block classes, uncounted files, and a `size_band` of `large` (executable non-test changed lines at the full floor). `silent_pass_classes` names guards the gate may never send to lite. Neither awards lite, and a count below the floor decides nothing on its own. Whole-file deletions are not in any of the line counts above: the helper lists them in `deleted_files`, counts their executable non-test lines in `deleted_exec_nontest_lines`, and bands that count in `deleted_band` (`none`, `small`, or `large`), which the gate reads. `signals` are path heuristics, not selection decisions and not a lite block. After this stage, apply that gate before reading any later reference. On the full spine, Stage 3 still judges content-based risk such as auth, payments, mutation, external I/O, concurrency, and process execution. Use `test_files_changed`, `agent_surface`, `has_learnings_corpus`, and `declared_packs` as inputs to the conditions that select generic reviewers, not as automatic spawn decisions. `declared_packs` reports whether the local CE config names any Compound Pack, read from the config alone (nothing is resolved, so `pack_roots` is always 0); the learnings selection rule in `references/persona-catalog.md` decides what that fact selects on the full spine, and `references/depth-paths.md` decides how the lite and focused paths use it. It describes the local checkout, so the helper evaluates it only in local scope: in remote scope it is `null` and no resolver runs. In local scope, `null` means the helper could not tell; read the config's `packs:` key yourself.

Stage 1b: Compute scope signals (cheap, deterministic)

Derive deterministic facts once with scripts/review-scope.py from this skill's directory. The helper validates the endpoints, counts changed lines, derives path classes, and reports floors. It never awards lite. Do not reproduce those mechanics in prose or estimate them from diff hunks. The invocation below is the helper's contract: run it directly rather than inspecting the script or probing its --help, unless it actually fails with an incompatibility.

Set SCOPE_MODE to the Stage 1 scope mode and set DIFF_A/DIFF_B to its two endpoints:

  • **local-aligned / standalone / base:** — DIFF_A="$BASE" (a real SHA/ref), DIFF_B empty (diffs base vs working tree).
  • **pr-remote / branch-remote** — DIFF_A=<PR_BASE_REF>, DIFF_B=<PR_HEAD_REF> (or <branch-head-ref>) — the fetched refs from Stage 1.
SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>";
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
if [ "$SCOPE_MODE" = "pr-remote" ] || [ "$SCOPE_MODE" = "branch-remote" ]; then
  "$PY" "$SKILL_DIR/scripts/review-scope.py" --base "${DIFF_A:-}" --head "${DIFF_B:-}" --docs-root "<root>";
else
  "$PY" "$SKILL_DIR/scripts/review-scope.py" --base "$DIFF_A" --docs-root "<root>";
fi

Remote scope always passes both endpoint flags, even when a best-effort fetch left one value empty; the helper then refuses to compute rather than comparing the fetched base to the unrelated local worktree. Load the JSON result. hard_block_full is a floor for the Review depth gate in references/modes-and-output.md; it covers the named hard-block classes, uncounted files, and a size_band of large (executable non-test changed lines at the full floor). silent_pass_classes names guards the gate may never send to lite. Neither awards lite, and a count below the floor decides nothing on its own. Whole-file deletions are not in any of the line counts above: the helper lists them in deleted_files, counts their executable non-test lines in deleted_exec_nontest_lines, and bands that count in deleted_band (none, small, or large), which the gate reads. signals are path heuristics, not selection decisions and not a lite block. After this stage, apply that gate before reading any later reference. On the full spine, Stage 3 still judges content-based risk such as auth, payments, mutation, external I/O, concurrency, and process execution. Use test_files_changed, agent_surface, has_learnings_corpus, and declared_packs as inputs to the conditions that select generic reviewers, not as automatic spawn decisions. declared_packs reports whether the local CE config names any Compound Pack, read from the config alone (nothing is resolved, so pack_roots is always 0); the learnings selection rule in references/persona-catalog.md decides what that fact selects on the full spine, and references/depth-paths.md decides how the lite and focused paths use it. It describes the local checkout, so the helper evaluates it only in local scope: in remote scope it is null and no resolver runs. In local scope, null means the helper could not tell; read the config's packs: key yourself.

Raw lines 115 to 134
Runs code scripts/review-scope.py :117
Starts an agent spawn :133
135### Stage 1c: Map criteria files to changed paths
137**Goal:** the mapping that pairs each criteria file governing this change with the changed files it governs. Paths, not contents. Both depth paths consume it: the lite path checks the diff against it in context, and Stage 3b decides the `project-standards` dispatch from it.
139Enumerate the candidates from **the tree under review**, never from whichever tree happens to be checked out: the workspace only in `local-aligned` scope, and the reviewed head ref in `pr-remote` and `branch-remote` (Stage 1 resolved which). A criteria file that exists only in the reviewed tree must appear, and one deleted there must not, or the review enforces criteria the change never had.
141Candidates are `CODING_STANDARDS.md`, `CLAUDE.md`, and `AGENTS.md` at any depth. Keep those whose directory is an ancestor of a changed file — a root-level file governs the whole checkout, `skills/AGENTS.md` only what is under `skills/`.
143`CODING_STANDARDS.md` is the designated criteria source, so an instruction file supplies criteria only for changed files that no `CODING_STANDARDS.md` governs, and no file is graded against both kinds. Every governing `CODING_STANDARDS.md` still applies together. Declared Compound Packs are not a criteria kind here. On the full spine they select `learnings-researcher`, which grades them; on the lite and focused paths they are graded in context. Either way they are graded separately from criteria files, so a line that violates a standards rule and a pack rule yields one finding per source.
145**Done** when no changed file could be graded against two kinds of criteria. A changed file that no criteria file governs is a complete result, not a gap. A search failure or uncertain scope is recorded as exactly that, never as an empty result; the Review depth gate and Stage 3b each state what it means for them.
147Create the review run directory now. Every path, lite or full, writes its artifacts there:
149```bash
150SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)";
151[ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)";
152if [ -L "$SCRATCH_ROOT" ]; then echo "unsafe scratch root symlink: $SCRATCH_ROOT" >&2; exit 1; fi;
153(umask 077; mkdir -p "$SCRATCH_ROOT") || exit 1;
154if [ -L "$SCRATCH_ROOT" ] || [ ! -O "$SCRATCH_ROOT" ]; then echo "scratch root is not owned by the current user: $SCRATCH_ROOT" >&2; exit 1; fi;
155chmod 700 "$SCRATCH_ROOT" || exit 1;
156RUN_ID=$(date +%Y%m%d-%H%M%S)-$(head -c4 /dev/urandom | od -An -tx1 | tr -d ' ');
157RUN_DIR="$SCRATCH_ROOT/ce-code-review/$RUN_ID";
158(umask 077; mkdir -p "$RUN_DIR") || exit 1; chmod 700 "$RUN_DIR" || exit 1;
159echo "$RUN_DIR";
160```

Stage 1c: Map criteria files to changed paths

Goal: the mapping that pairs each criteria file governing this change with the changed files it governs. Paths, not contents. Both depth paths consume it: the lite path checks the diff against it in context, and Stage 3b decides the project-standards dispatch from it.

Enumerate the candidates from the tree under review, never from whichever tree happens to be checked out: the workspace only in local-aligned scope, and the reviewed head ref in pr-remote and branch-remote (Stage 1 resolved which). A criteria file that exists only in the reviewed tree must appear, and one deleted there must not, or the review enforces criteria the change never had.

Candidates are CODING_STANDARDS.md, CLAUDE.md, and AGENTS.md at any depth. Keep those whose directory is an ancestor of a changed file — a root-level file governs the whole checkout, skills/AGENTS.md only what is under skills/.

CODING_STANDARDS.md is the designated criteria source, so an instruction file supplies criteria only for changed files that no CODING_STANDARDS.md governs, and no file is graded against both kinds. Every governing CODING_STANDARDS.md still applies together. Declared Compound Packs are not a criteria kind here. On the full spine they select learnings-researcher, which grades them; on the lite and focused paths they are graded in context. Either way they are graded separately from criteria files, so a line that violates a standards rule and a pack rule yields one finding per source.

Done when no changed file could be graded against two kinds of criteria. A changed file that no criteria file governs is a complete result, not a gap. A search failure or uncertain scope is recorded as exactly that, never as an empty result; the Review depth gate and Stage 3b each state what it means for them.

Create the review run directory now. Every path, lite or full, writes its artifacts there:

SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)";
[ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)";
if [ -L "$SCRATCH_ROOT" ]; then echo "unsafe scratch root symlink: $SCRATCH_ROOT" >&2; exit 1; fi;
(umask 077; mkdir -p "$SCRATCH_ROOT") || exit 1;
if [ -L "$SCRATCH_ROOT" ] || [ ! -O "$SCRATCH_ROOT" ]; then echo "scratch root is not owned by the current user: $SCRATCH_ROOT" >&2; exit 1; fi;
chmod 700 "$SCRATCH_ROOT" || exit 1;
RUN_ID=$(date +%Y%m%d-%H%M%S)-$(head -c4 /dev/urandom | od -An -tx1 | tr -d ' ');
RUN_DIR="$SCRATCH_ROOT/ce-code-review/$RUN_ID";
(umask 077; mkdir -p "$RUN_DIR") || exit 1; chmod 700 "$RUN_DIR" || exit 1;
echo "$RUN_DIR";
Raw lines 135 to 161
Starts an agent dispatch :137
Project instruction file CLAUDE.md :141
Project instruction file AGENTS.md :141
162### Stage log
164Every run records what each stage cost, so the thresholds this skill uses can be set from measured runs. The record is `<run-dir>/stages.jsonl`, written only by the bundled script below; the receipt writer folds it into `metadata.json` at the end (`summarize`, named where each path writes its receipt). Open the scope stage now, in the same shell call that printed the run directory when you can:
166```bash
167SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>";
168PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
169"$PY" "$SKILL_DIR/scripts/run-log.py" event --run-dir "$RUN_DIR" --start scope
170```
172Every later boundary is the same call with `--end <stage> --start <stage>` in one invocation, and `--reviewers`, `--candidates`, `--tokens` (only when the host handed you a count), or `--fact key=value` for what that stage learned. The stage names are fixed: `scope`, then `review` and `receipt` on lite and focused (with `peer` overlapping `review` on focused), or `select`, `dispatch`, `validate`, `merge`, and `report` on the full spine (with `peer` overlapping `dispatch`). Fold the call into a shell call the step already makes wherever one exists; a boundary is never its own turn.

Stage log

Every run records what each stage cost, so the thresholds this skill uses can be set from measured runs. The record is <run-dir>/stages.jsonl, written only by the bundled script below; the receipt writer folds it into metadata.json at the end (summarize, named where each path writes its receipt). Open the scope stage now, in the same shell call that printed the run directory when you can:

SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>";
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
"$PY" "$SKILL_DIR/scripts/run-log.py" event --run-dir "$RUN_DIR" --start scope

Every later boundary is the same call with --end <stage> --start <stage> in one invocation, and --reviewers, --candidates, --tokens (only when the host handed you a count), or --fact key=value for what that stage learned. The stage names are fixed: scope, then review and receipt on lite and focused (with peer overlapping review on focused), or select, dispatch, validate, merge, and report on the full spine (with peer overlapping dispatch). Fold the call into a shell call the step already makes wherever one exists; a boundary is never its own turn.

Raw lines 162 to 173
Runs code scripts/run-log.py :169
Starts an agent dispatch :172
174### Stage 1d: Stage the review policy
176**Goal:** the repository's review policy, read from a copy the change under review cannot have edited, staged where every later reader finds it.
178A review policy is a `REVIEW.md` file: instructions for how review behaves in this repository, such as what each severity means here, paths or classes of finding to skip, a cap on minor findings, the evidence a class of finding needs, or checks to run on every change. Claude Code Review reads the same file. It is not a criteria file: it neither supplies criteria nor displaces them, so Stage 1c's precedence ignores it.
180- **Which files.** The root `REVIEW.md` governs the whole change. A `REVIEW.md` in a directory that is an ancestor of a changed file adds to the root file for the paths under it.
181- **Which copy.** Read each file from the Stage 1 base (`BASE:`, or `PR_BASE_REF` in `pr-remote`), never from the reviewed tree, because a change must not be reviewed under policy it wrote itself. When the diff adds, edits, or deletes a policy file, the base copy applies (none for a new file), and Coverage says the change's version was not applied.
182- **Includes.** In the root file, a line holding only `@<path>` stands for that file, read from the same ref, one level deep. Skip an include of a file this review already reads as criteria, such as `@CODING_STANDARDS.md`.
184Write what you read to `<run-dir>/review-policy.md`, each file under a heading that names its path and the paths it governs. No policy file at the base is a complete result: write nothing. A policy file you found but could not read is recorded for Coverage as unread, never treated as absent.

Stage 1d: Stage the review policy

Goal: the repository's review policy, read from a copy the change under review cannot have edited, staged where every later reader finds it.

A review policy is a REVIEW.md file: instructions for how review behaves in this repository, such as what each severity means here, paths or classes of finding to skip, a cap on minor findings, the evidence a class of finding needs, or checks to run on every change. Claude Code Review reads the same file. It is not a criteria file: it neither supplies criteria nor displaces them, so Stage 1c's precedence ignores it.

  • Which files. The root REVIEW.md governs the whole change. A REVIEW.md in a directory that is an ancestor of a changed file adds to the root file for the paths under it.
  • Which copy. Read each file from the Stage 1 base (BASE:, or PR_BASE_REF in pr-remote), never from the reviewed tree, because a change must not be reviewed under policy it wrote itself. When the diff adds, edits, or deletes a policy file, the base copy applies (none for a new file), and Coverage says the change's version was not applied.
  • Includes. In the root file, a line holding only @<path> stands for that file, read from the same ref, one level deep. Skip an include of a file this review already reads as criteria, such as @CODING_STANDARDS.md.

Write what you read to <run-dir>/review-policy.md, each file under a heading that names its path and the paths it governs. No policy file at the base is a complete result: write nothing. A policy file you found but could not read is recorded for Coverage as unread, never treated as absent.

Raw lines 174 to 185
186## Task Visibility
188For the multi-agent path, once the review scope is resolved, use the platform's task-tracking capability when available to show a short user-facing view derived from the execution spine. Track review outcomes, not individual personas, setup mechanics, or tool calls; add conditional work only when its condition is met, and update the view at meaningful transitions. If no task-tracking capability is available, continue with the normal progress and final report without simulating a task list in chat.

Task Visibility

For the multi-agent path, once the review scope is resolved, use the platform's task-tracking capability when available to show a short user-facing view derived from the execution spine. Track review outcomes, not individual personas, setup mechanics, or tool calls; add conditional work only when its condition is met, and update the view at meaningful transitions. If no task-tracking capability is available, continue with the normal progress and final report without simulating a task list in chat.

Raw lines 186 to 188