# Cr

> Cross-session code review. Use when the user types "/cr", or asks to "review <session>'s PRs", "review PR

- Skill: `rosenlo/cr` (Agent Skill)
- Install (CLI): `npx skillmds@latest add rosenlo/cr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rosenlo/cr/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: rosenlo (https://skillmd.com/u/rosenlo)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/rosenlo/cr

---


# cr — cross-session code review

One agent session reviews another session's open PRs and sends findings back
over `SendMessage`. The reviewer never edits, commits or merges.

**Why a separate session rather than a subagent.** A review's value comes from
*not* sharing the author's priors. A forked subagent inherits the framing; a
briefed one receives the framing in its prompt. Both read past what the author
read past. The findings that justify this workflow are rarely "line N is wrong"
— they are "this document states two contradictory things", "this claim is
derived but written as measured", "this rule invalidates a thing it depends on".
Those are failures of *the artifact as read by someone who was not there*, and
shared context is exactly what hides them.

The cost is real and is the reason for the rules below: addressing is manual,
there is no automatic handoff, and the peer's state is not observable.

## Invocation

    /cr <session-name>     # review the PRs that session opened
    /cr <n> [n...]         # review these PRs; resolve the author session to reply to
    /cr                    # this repo's open PRs authored by the current user

All three resolve to the same state: a `(PR set, author session)` pair.
**Resolve it with the recipes below — never by guessing from a session's
busy/idle status.** Guessing misroutes; it has.

### Resolving a session name to a session id

    python3 -c "
    import json, glob
    for p in glob.glob('\$HOME/.claude/sessions/*.json'):
        try: d = json.load(open(p))
        except Exception: continue
        print('%-26s %-38s %s' % (d.get('name'), d.get('sessionId'), d.get('cwd')))
    " | sort

The `[ref]` shown by `ListAgents` is **not** a prefix of the session id; the two
are unrelated. This table is the only reliable map.

### Resolving a PR to the session that opened it

    grep -l 'pull/<N>' ~/.claude/projects/<project-dir>/*.jsonl

Then map the returned session id back through the table above.

### Resolving the host and repo

Derive both at runtime — never hardcode them:

    gh auth status          # which hosts gh is authenticated to
    git remote -v           # this repo's slug and host

For a GitHub Enterprise host, prefix commands with `GH_HOST=<host>`.
`gh pr` and `gh issue` do **not** honour `--hostname`; only `gh api` does.

## One pass

1. **Send before watching.** If you hold a finding that has not been delivered,
   send it *first*. Watching a session that does not know what to change is a
   self-inflicted deadlock, not waiting. This was measured: two consecutive
   no-op ticks were caused by holding a finding and watching anyway.

2. **Read the state, not the summary.** For each PR record `state`,
   `mergeStateStatus` and `headRefOid`. A peer's own report — including an
   automated idle notice — is a **claim to verify**, never evidence.

3. **Diff only the increment**, from the SHA recorded in the previous pass.
   Carry the baseline SHA table forward explicitly; it is the only state this
   workflow keeps.

4. **Verify the peer's citations before accepting them** — including in entries
   that are themselves rules about citation. A `§N` or line reference that
   *resolves* is not one that *supports the claim*; checkers verify the first.
   Confirm load-bearing facts against the code, the config that actually
   deploys, and the tracker — not against the PR body.

5. **Report what you did not verify.** Every pass states it: tests not run,
   checkers not run, which numbers are the author's measurements you cannot
   reproduce. A review that hides its own gaps is the defect it exists to find.

6. **Send the findings** with `SendMessage`. Say plainly when something does not
   block merge.

## Calibration

- **Do not manufacture findings to keep the pass alive.** State the terminal
  state plainly when you reach it.
- **When the peer pushes back on your framing, evaluate it on merit.** It will
  sometimes be right, including about the exact error class you have been
  raising against it. That is the workflow working, not failing.
- **Never merge, never edit, never commit.** Merging is a human decision. If a
  merge command is blocked in your environment, report `mergeStateStatus` and
  the exact command and hand back — that is a terminal state, not unfinished
  work.

## Running until convergence

`/cr` performs one pass. To run until the review converges:

    /loop /cr <session-name>

`/loop`'s dynamic mode already provides pacing, event wake-up and noop folding;
do not reimplement scheduling inside this skill.

### Waking on the peer instead of polling

`SendMessage` with `notify_when_idle: true` (no `message`) subscribes to one
notice when the peer next goes idle. It costs the peer nothing.

🔴 **Arm it only when `ListAgents` shows the peer BUSY.** Subscribing while it
is already idle fires *immediately*, which turns the loop into a tight spin.
Measured.

The subscription is one-shot: re-arm it each time it fires, subject to the BUSY
rule. It cannot see a human merging, so keep a long fallback heartbeat
(1200–1800s) for anything the peer does not cause.

## Stop condition

**Stop when the peer pushes a change and the pass finds nothing new in it** —
one clean pass over a *real increment*.

Do **not** stop merely because you currently hold nothing: that stops at the
first quiet moment. Measured — a pass declared terminal was followed by two
further passes that each found a real defect.

Merging is **not** the stop condition. The human merges; the review converges
independently.

## What does not belong in this skill

**A checklist of what counts as a finding.** That judgement must stay with the
model. A checklist narrows the population an assertion covers, which is the
failure mode this workflow most often catches in others. The most valuable
findings in the run that produced this skill would not have appeared on any
checklist written in advance.

