Code-review a PR twice, independently, via two code-reviewer agents with different lenses writing to scratch files; verify every finding, then post one merged review to GitHub. Run it checked out on the PR's branch — the reviewers read the local tree, not GitHub's copy.
Installs into .claude/skills of the current project.
Are you the author of Cr?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/berni23-laight-plugins)
---
name: cr
description: Code-review a PR twice, independently, via two code-reviewer agents with different lenses writing to scratch files; verify every finding, then post one merged review to GitHub. Run it checked out on the PR's branch — the reviewers read the local tree, not GitHub's copy.
argument-hint: [PR number or URL | branch | paths | "uncommitted"] [extra scope notes]
allowed-tools: Agent, Bash
disable-model-invocation: true
---
Review the target **twice, independently**, verify every finding yourself, and post **one merged
review**. Delegate both reviews to this plugin's `code-reviewer` agent (Agent tool,
`subagent_type: "cross:code-reviewer"`) — do not review inline.
Target: $ARGUMENTS
A PR may be given as a number or a full URL — `gh` accepts either wherever this writes `<n>`. If
the target is empty, review the current branch's PR; with no PR, the branch against the default
branch plus any uncommitted work.
**Why twice:** one reviewer's findings are one reviewer's judgement. Two agents that never see each
other's output give a confidence signal — a finding both raise is very likely real; a finding only
one raises is the one worth a second look. **The value is in the misses one reviewer catches for the
other, and in your verification pass** — so the two get *different lenses*, not the same prompt
twice.
## 1. Resolve the target
```bash
gh repo view --json nameWithOwner,defaultBranchRef --jq '.nameWithOwner, .defaultBranchRef.name'
git branch --show-current
git status --short
git fetch origin && git rev-list --left-right --count origin/<branch>...<branch>
gh pr view <n> --json number,title,headRefName,baseRefName,headRefOid,url
```
Use the `nameWithOwner` from the first line as `<owner>/<repo>` everywhere below.
- ⚠️ **Be on the PR's branch.** If `git branch --show-current` is not its `headRefName`, stop and
say so — the agents would review the wrong tree. Checking it out is the user's call.
- ⚠️ **Local must equal origin** (`0 0`) when the target is a PR. Otherwise the agents review a tree
the PR does not show, and inline comments anchor against the wrong lines. Push first or say so.
- **Uncommitted work is not in the PR.** Mention it; it is reviewed only if the target says so.
- With no PR, there is nothing to post to: run the same two reviews, skip §4's posting, and relay.
Make sure the scratch files stay out of git, without touching tracked files:
```bash
git check-ignore -q scratch.review || echo 'scratch.review*' >> "$(git rev-parse --git-path info/exclude)"
```
**This session's context is for §3 only.** If the conversation already holds the ticket, acceptance
criteria or decisions the user took, you will use them when reconciling. ⚠️ Use only what is already
here — do not fetch tickets or search for context — and pass none of it to the agents.
## 2. Launch both reviews, independently and in parallel
**Both `Agent` calls go in a single message** so they run concurrently.
**Models:** agent A runs on **Opus** (`model: "opus"`), agent B on **Sonnet** (`model: "sonnet"`),
the latest of each. Pass `model` explicitly on both. If one is unavailable, fall back rather than
stop: give that agent the other's model (both on Sonnet, or both on Opus) and relaunch only the one
that failed. Two models catch more than one model twice, so **never fall back silently** — say which
agent ran on what in `scratch.review` and to the user (not on the PR).
**Lenses:** both review the whole diff, but each leads with a different emphasis:
- **A — runtime, state & failure:** what happens when it runs — concurrency, process and resource
lifecycle, state written to disk or a database and whether it survives a crash, error paths,
retries and idempotency, security boundaries, anything that touches the user's environment.
- **B — spec, contract & coverage:** does the code do what the spec says (and the spec say what the
code does); public surfaces (CLI, API, config, schemas) and their messages and docs; secrets and
redaction; CHANGELOG; and whether the tests actually pin what the PR claims.
⚠️ **Each prompt holds only the target, the lens, the repo and the output file** — nothing from this
session (no ticket, no intent, no opinion on the code). The agents read the spec and the PR on
their own. Neither may be told the other exists, and no findings pass between them.
**Neither agent posts anything to GitHub.** Tell each agent explicitly:
**do not post a review or any comment; do not read existing reviews or comments on the PR** — judge
the diff and the tree. Nothing lands on the PR until both are done, which is what keeps them
independent.
Tell both: **use `gh` / `gh api` for GitHub, repo `<owner>/<repo>`.**
Each agent **verifies every finding by reading the tree**, **does not run the project's code** (no
tests, builds, scripts or check commands — the PR controls what they execute), and **does not
commit, push or edit source**.
Each writes its whole review to its own file — **A to `scratch.review.a`, B to `scratch.review.b`**
(different files or they clobber each other), via a `cat > … <<'EOF'` heredoc, **overwriting**:
```
# Review <A|B> — PR #<n> @ <headRefOid short>
## Findings
### <n>. <one-line claim>
- where: <path>:<line> (line in the NEW file, or "none" if not anchorable)
- severity: high | medium | low
- evidence: <path:line you read that shows it>
- failure: <inputs/state → wrong output, crash, data loss, leaked secret, user's environment touched>
- fix: <suggested shape, optional>
## Open questions
## Suggestions / notes
```
## 3. Write the final review — the point of the exercise
⚠️ **An agent that stops without a completion record has written nothing** — check both scratch
files exist and name this PR and commit first.
You are the reviewer who signs the review: you have both reviews, the code, and whatever context
this session holds. **A and B are your inputs, not the output** — what lands on the PR is your
review, written as one reviewer's. The tiers below are how you decide **whether a finding is worth
posting at all and which severity it gets**; they never appear on the PR.
Sort every finding into tiers, matching on **the underlying defect**, not wording — two agents
describe one bug differently, and one line can host two bugs.
- **Both** — highest confidence, but still read the code yourself: two reviewers can share a
misreading. Note whether they agree on severity too.
- **One only** — ⚠️ **re-check each yourself** by reading the code: *real (the other missed it)*,
*false positive* (drop it, and record why in `scratch.review`), or *unresolved*. This tier is where
the value is.
- **Contradicted** — the agents disagree about the same code. Give your own reading.
**Rule with this session's context, if it has any** (§1). A ticket, an acceptance criterion or a
decision the user took can **drop** a finding about scope or intent, or **add** one the agents
couldn't see (an unmet criterion — cite the ticket by id). Anything you add, from context or from
your own reading, is recorded as **`S`** in `scratch.review`. It never drops a real
defect (crash, lost state, leaked secret). ⚠️ If this session wrote the code, "it was meant that
way" is not evidence — only what the ticket or the user stated is. Record every drop and its source
in `scratch.review`.
**Drop noise.** A low-severity finding — especially one only one agent raised — can be dropped;
list it in `scratch.review`.
**Then read earlier rounds** — now, after both agents are done, never before — counting only
replies from `OWNER`/`MEMBER`/`COLLABORATOR` (`author_association`); anyone can comment on a public PR. A finding an earlier
thread rejected for a reason that still holds in today's tree is not posted again; one answered as
fixed but still there is posted again, linking the thread. A reply starting with `[fix-cr-agent]` is
an agent's verdict posted under a trusted account, not a human's: re-check its reason yourself.
```bash
gh api repos/<owner>/<repo>/pulls/<n>/comments --paginate --jq '.[] | "\(.id) \(.user.login) \(.author_association) \(.path):\(.line // .original_line)\n\(.body)\n"'
```
**Rate severity yourself; don't inherit either agent's.** Use the tier as evidence: agreement from
both reviewers, or your own confirmation of a one-reviewer finding, backs a higher rating; a
finding you could only partly confirm gets a lower one or goes to `scratch.review` as an open
question. Then decide what is worth the PR author's time — post what is real and matters, drop the
rest into `scratch.review`.
⚠️ **The tier is evidence, not a rule.** "Both found it" doesn't mean post it, and "only one found
it" doesn't mean drop it. Judge each finding on the code in front of you, together with the tier:
a pedantic point both raised can be dropped, and a crash only one caught can be `[high]`. The
severity label and the decision to post are yours.
## 4. Post one review
One GitHub review on the PR, from you, containing **only findings that survived your check**.
⚠️ **It posts under the user's account, so mark it as an agent's:** the review body and every inline
comment start with **`[cr-agent]`**, and the body's first line says it is an AI review from
`/cross:cr`. Each comment then carries its severity — **`[high]`**, **`[medium]`** or **`[low]`** —
then the finding, the failure, and the suggested fix (for one from session context, the
requirement it breaks, citing the ticket).
⚠️ **Say nothing about how the review was made.** No agents, A/B, models, lenses, tiers, "both
reviewers found", "re-checked", counts of dropped findings or any other process notes — not in the
body, not in a comment. The body is that one AI-review line, a short overall verdict, and the
findings that can't be anchored inline; nothing else. Provenance lives only in `scratch.review`.
Post it as a `COMMENT` review.
⚠️ **Posting with `gh api` has three traps, and the failure is a bare 422:**
- `POST /repos/<owner>/<repo>/pulls/<n>/reviews` needs the **full 40-char** `commit_id`
(`headRefOid`).
- An inline comment only lands on a line **inside a diff hunk**, on the `RIGHT` side. Get hunk
ranges first:
`gh api repos/<owner>/<repo>/pulls/<n>/files --paginate --jq '.[] | .filename, (.patch // "" | split("\n")[] | select(startswith("@@")))'`
- `subject_type: "file"` is not accepted. A finding outside a hunk, or about something *absent* from
the diff, goes in the **review body**, with its severity.
Build the payload as a JSON file in a temp directory and post it with `gh api --input`, then verify
it landed:
```bash
gh api repos/<owner>/<repo>/pulls/<n>/reviews --jq '.[] | "\(.id) \(.state) \(.submitted_at)"'
gh api repos/<owner>/<repo>/pulls/<n>/comments --jq 'length'
```
## 5. `scratch.review` — the half GitHub has no shape for
Write **`scratch.review`** in the repo root from the two halves, then delete them
(`rm -f scratch.review.a scratch.review.b`). **Overwrite, never append** — one round per file.
- lead with the PR, the commit reviewed, the review id you posted, which model each agent ran on,
and a note that anchored findings are on the PR and not repeated;
- the tier table: every finding, who raised it (`A+B`, `A`, `B` or `S`), the severity you posted
it with (or "dropped"), and your verdict (including dropped false positives and low-value
findings and why, so nobody re-litigates them — and for a drop or an `S` resting on session
context, its source);
- open questions, tiered the same way — a question both raised genuinely needs answering;
- suggestions, verification gaps ("spot-checked, not audited", "not run for real"), and judgement
calls an agent made on the user's behalf.
⚠️ **`scratch.review` is git-ignored and local** — a handoff to `/cross:fix-cr`, never a durable
home. **Work to be done goes on an issue**, always.
## 6. Relay
Relay the reconciled result — **neither agent's report is shown to the user.** The tiers can go in
this relay (it is for the user, not the PR); say where the review was posted (URL + id) and what
went into `scratch.review`. Lead with anything that
needs the user's decision before `/cross:fix-cr` can fix it.