pr-review-batch
Review one or more PRs in parallel isolated worktrees, verify every claim, save first-pass-clean reviews
Install / Use
npx skills add gke-labs/kube-agentsInstalls into whichever agent you are using.
Claude Commands
Claude Code slash commands
Quality Score
Category
Development & EngineeringSupported Platforms
Skill content
View source on GitHubdescription: Review one or more PRs in parallel isolated worktrees, verify every claim, save first-pass-clean reviews argument-hint: <pr-number> [pr-number ...]
Review these pull requests in gke-labs/kube-agents: $ARGUMENTS
PRs always live in that repo. Everything else — remote names, the base branch, the checkout location — is discovered at runtime, so this command works for any teammate in any clone regardless of what they called their remotes or where they cloned to.
Run Phase −1 in the main loop first, for every PR number, and wait for my answer. Then spawn one subagent per PR number that survives it, all in a single message so they run concurrently. Each subagent owns exactly one PR end to end and reports back a short structured result. Do not review any PR yourself in the main loop — your job is to pre-flight, fan out, then relay.
Give each subagent everything under Subagent instructions (per PR)
verbatim, with <N> replaced by its PR number, plus the PR's pre-flight verdict, the review mode I
chose for it, and the SHA that mode starts from when it is a narrowed one. Phase −1 is yours and
stops at that heading: it asks me a question, which a subagent cannot do, so handing it on would
either hang the subagent or have it answer on my behalf.
Phase −1 — Pre-flight: is this review already covered? (main loop)
Every PR here is read by kube-agents-bot on the way in, and the repo requires the author to have
run review-adversarial over their own diff and written the disposition into the body before
opening. When both of those happened and neither has gone stale, a third hostile read usually buys
nothing — so find that out before spending it.
This phase runs in the main loop, before any subagent exists, for two reasons. Its output is a question for me, and a subagent has no way to ask one. And it is pure GitHub API — no worktree, no fetch, no diff read — so it costs two calls per PR, plus one per merge sitting after the bot's review, which is nearly always none or one.
The two queries
The repo is named literally in both: Phase −1 runs before Phase 0 defines $REPO.
# Signal 1, in one call: the head SHA, the bot's reviews with the commit each one
# actually read, the commit graph, and the open threads. The filter reports the
# commits *after* the review's commit, which is what the currency test needs.
gh api graphql -f query='
query($pr:Int!){repository(owner:"gke-labs",name:"kube-agents"){pullRequest(number:$pr){
headRefOid isDraft state author{login}
reviews(last:20){nodes{author{login} state submittedAt body commit{oid}}}
commits(last:100){nodes{commit{oid messageHeadline parents(first:2){totalCount nodes{oid}}}}}
reviewThreads(first:100){nodes{isResolved path}}
}}}' -F pr=<N> --jq '.data.repository.pullRequest as $p
| ([$p.reviews.nodes[] | select(.author.login == "kube-agents-bot")] | last) as $r
| ($p.commits.nodes | map(.commit)) as $cs
| ($cs | map(.oid) | index($r.commit.oid // "")) as $i
| "head=\($p.headRefOid) state=\($p.state) draft=\($p.isDraft) commits=\($cs|length) unresolved=\([$p.reviewThreads.nodes[]|select(.isResolved|not)]|length)",
(if $r == null then "lastbot: NONE"
else "lastbot at=\($r.submittedAt) commit=\($r.commit.oid)",
($r.body | split("\n") | [.[0], (.[] | select(startswith("### Findings outside") or startswith("#### ") or startswith("_This was a")))] | join("\n")),
(if $i == null then "since: review commit is not among those \($cs|length) commits"
elif $i == ($cs|length) - 1 then "since: nothing, the review is at the tip"
else "since:\n " + ($cs[$i+1:] | map("\(.oid[0:7]) parents=\(.parents.totalCount)\(if .parents.totalCount > 1 then " p2=" + .parents.nodes[1].oid[0:7] else "" end) \(.messageHeadline)") | join("\n "))
end)
end)'
# Signal 2: the PR description. Read it yourself — see below.
gh pr view <N> --repo gke-labs/kube-agents --json body -q .body
Use GraphQL for the reviews rather than gh api repos/$REPO/pulls/<N>/reviews: the REST endpoint
has been observed returning an empty body against this repo while /pulls/<N>/comments worked.
(REST does carry the review's commit_id, so that is not the reason — availability is.)
Keep the filter's .[0] / startswith shape if you edit it. gh --jq is gojq, but the same
program gets piped through real jq often enough that it has to run in both, and jq rejects a field
access applied straight to a function call — capture("…").s compiles under gojq and is a syntax
error under jq 1.6.
All three page sizes are caps rather than promises:
reviews(last: 20)andreviewThreads(first: 100)— on a long-lived PR, say you looked at the last twenty reviews rather than reporting it clear off a truncated list.commits(last: 100)— a branch can outrun that, and a review older than the window is then indistinguishable from one whose commit was force-pushed away. Both print the samesince: … not among those N commits. That lands on stale either way, which is the safe direction, but whencommits=100say "could not confirm currency" rather than "force-pushed".
Signal 1 — a current, clean bot review
Three things must hold.
Clean. The last bot review's body opens with either of the two clean verdicts. GraphQL
reports the login as kube-agents-bot, without the [bot] suffix the REST API adds. Take the last
one: after a /review the earlier review is still sitting there, and reading it back looks exactly
like the new one.
**No findings.**— nothing raised anywhere.**No findings in the code.**— the bot cleared the diff and raised something outside it, a note on the description usually. It counts, but quote the note in the evidence rather than letting the word "clean" swallow it.
Where that note lives decides whether you have it. Sometimes it is in the first line, as on #684.
Sometimes the body carries a whole ### Findings outside this diff section — findings the bot could
not anchor to a changed line — and on #709 that section ran to a 🔴 High and fifteen hundred words.
The filter keeps the section heading and each finding's #### title line, not the argument under
them, which is enough to see that one exists and to quote it in a line. When the heading does
appear, re-run the same query with --jq '…| last | .body' and read it before you put covered in
front of me — one more call, on the rare PR that needs it. An unanchored High is a live finding on
the pull request, and it must not vanish between the review and the evidence I am shown.
Note the width too. The footer reads _This was a strict pass: only what I am certain of…_ or
_This was a wider pass: as well as what I am certain of…_. A strict-pass clean covers less ground
than a wide-pass clean, and I may want the difference.
Current. Either the review's commit is the head, or everything after it is a merge from the
base branch. Merging the base branch in is not new work to review — this is the API-only twin of
the git log <sha>..HEAD --no-merges --not "$BASE_REF" rule in Phase 2.
parents.totalCount > 1 is necessary and nowhere near sufficient. A sibling feature branch, or a
colleague's fork branch, merges with two parents exactly like main does, and it brings an entire
branch of code no review has read; messageHeadline is no backstop, since Merge branch 'main' is
a string anyone can type. The git rule catches this and the parent count does not, which is why the
filter prints the second parent as p2=. Confirm it is on the base branch before calling the review
current:
# One call per merge in the tail. "identical" or "behind" means <p2> was already on
# the base branch, so the merge brought in nothing unreviewed. "ahead" or "diverged"
# means it brought in a branch of its own: the review is stale, not current.
gh api repos/gke-labs/kube-agents/compare/<base-branch>...<p2> --jq .status
On #675 that is behind for p2=5bc8165, which is what makes its 8-commit tail a base merge. An
octopus merge has parents past the second; check each of them the same way, or call the review stale
and say why. Two ways this is still softer than the git rule, both worth saying out loud rather than
papering over:
- A conflicted merge carries hand-written resolution that no review has seen, and over the API it
looks exactly like a clean one —
comparereports on the parent, not on what the author did with it. When the tail is merges, call them presumed base merges and offer the delta pass; only a worktree can tell the two apart, and Phase −1 has no worktree by design. Phase 3 does that check; it does not usegit show --cc, for a reason worth reading before you assume it would have. - A review commit missing from the list is stale, but see the
commits(last: 100)caveat above for which kind of stale.
Nothing left open. No unresolved review thread. An open thread is outstanding work by the repo's own merge rules, however clean the latest review reads.
Signal 2 — the author reviewed and tested it themselves
Read the body. Two of its sections are what AGENTS.md's "Pull Request Hygiene" requires before a pull request is opened at all:
## Self-Review— the disposition list from the author's own pre-PR passes, merged:review-adversarialandreview-docs-drift, both on every change. What they looked for, what kind of context each pass ran in, what it found, and for each finding whether they fixed it or decided not to and why. This is the signal that matters most here, because it is the only one that says somebody already read this diff hostilely.### Live validation(and the## Testingsection around it) — that the change was actually exercised.Not live-testedwith a stated reason is a filled section.
Judge them by reading, not by measuring. A section holding only the template's HTML comment,
whitespace, or a bare - is unfilled — but so is a paragraph that says "reviewed it, looks fine",
because the bar AGENTS.md sets is that "no findings" counts only alongside what was looked for.
Length settles neither question. Watch for the section heading appearing in prose elsewhere in the
body, which is why this is a read rather than a regex: a PR that discusses the ## Self-Review
section is not a PR that filled one in.
When the section is missing or unanswered, Signal 2 fails — say so plainly, since that is the first thing the review would report anyway.
Do not reach for the author's inline comments as a substitute. Authors here do not leave top-level inline comments on their own diffs; what you see under an author's name are replies to bot threads, which is engagement with a review rather than one.
The verdicts
| Verdict | When | What you do |
| --------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| skip | Draft, closed, or merged | Report it and stop — no queries argued, no subagent |
| covered | Signal 1 in full, plus Signal 2 | Report the evidence and ask me before reviewing |
| partial | Clean bot review but stale, or Signal 2 missing, or a thread still open | Report it, name exactly what is missing, and offer a narrower or a full pass |
| review | No clean bot review — the bot found issues, or never ran | Proceed to fan-out with no prompt |
skip is the same judgement Phase 2 makes and the same one it reports; making it here as well just
saves spawning a subagent to reach it. A draft is the author s
Truncated for display — read the full file on GitHub.
Related Skills
pyspark-etl-best-practices-cursorrules-prompt-file
40.7kCursor rules for PySpark ETL development with code style, joins, window functions, map operations, and Iceberg patterns.
claude-mem
91.8kPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More
Understand-Anything
80.4kGraphs that teach > graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.
Agent-Reach
75.0kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
Security Score
Audited on Aug 24, 2026
