| name | pr-status-all |
| description | Summarize all open PR statuses. |
| user-invocable | true |
| allowed-tools | ["Bash","Agent"] |
pr-status-all
Produce a one-row-per-PR status table for all open PRs. This is the
whole-queue version of pr-status: apply the same
"read the latest review and parse it for findings" discipline to every
open PR, then lay the results out as a table. It is read-only --- it reports
status, it does not push, merge, or run review loops (use
ardia for that, or
sync-pr-branch to update a branch).
Because the per-PR signals are independent and read-only, gather them
concurrently --- one subagent per PR --- then assemble the table. See
Why fan-out is safe here for why this loop parallelizes and the write-loops
don't.
When this fires
- "summarize all open PRs", "status table / dashboard of my PRs",
"what's the state of every open PR", "which PRs are ready to merge".
- Whenever you'd otherwise report on more than one PR at once.
CI green ≠ review clean
gh pr checks <N> going green is about CI state, not the review verdict. A
PR can have every check passing and still carry unaddressed review findings.
Report CI state and review verdict as separate columns --- never collapse
them into one "OK".
GitHub's mergeable / mergeStateStatus: CLEAN field is conflict existence, not a merge-readiness verdict --- don't report it as one in the status table.
A PR without a clean review verdict on the latest commit is not merge-ready.
Do not describe it as merge-ready.
Procedure
1. Enumerate the open PRs (orchestrator, one cheap call)
gh pr list --state open --json number,title,headRefName,isDraft,author \
--jq '.[] | "\(.number)\t\(.headRefName)\t\(.isDraft)\t\(.author.login)\t\(.title)"'
This is fast and sequential --- a single call to get the work units.
2. Fan out --- one subagent per PR (concurrent)
Spawn one subagent per open PR, all in a single batch (multiple Agent
calls in one message) so they run at once. The fan-out is read-only, so it
needs no worktrees --- each subagent only reads PR signals, nothing mutates,
and there is nothing to collide on.
Safety Cap: If Step 1 returns more than 10 open PRs, do not fan out per-PR subagents.
Unbounded concurrent subagents would exhaust the session's token quota.
Instead, build a condensed table straight from Step 1's own fields (title, headRefName, isDraft, author) plus one cheap orchestrator-level gh pr checks pass per PR for CI state --- skip the seven-signal subagent depth (review currency, external-reviewer check, thread counts, behind-main) entirely.
Label the table's heading "Condensed --- queue too large for full per-PR review (N open PRs)" so it reads as lower-fidelity rather than as the standard dashboard, and note that a full audit is available on a smaller subset or via ardia.
Give each subagent its PR number, headRefName, and isDraft, and have it gather the seven independent signals below and return one structured row.
Carry the disciplines into the prompt --- a subagent that doesn't follow Read the LATEST review will silently misreport:
A subagent starts fresh --- it sees only this prompt, not this skill file ---
so inline the exact commands; don't point it at a section it can't read.
Fill in <N>, <headRefName>, <isDraft>, <owner>, <repo> for each PR (resolve owner/repo once with gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"'):
Gather the status of PR # (branch <headRefName>, draft: <isDraft>) in this repo and return a single structured row.
Do not push, merge, or modify anything.
- Latest review verdict, checked for currency against the head, with hyperlinked comment URL. Read
the most recent review comment (including its URL for hyperlinking), author, requested reviewers, and the timestamp of the latest commit:
gh pr view "<N>" --json comments,commits,headRefOid,author,reviewRequests \
--jq '{
author: .author.login,
reviewRequests: [.reviewRequests[].login],
review: ([.comments[] | select(.author.login | startswith("claude"))] | last | {url: .url, body: .body, createdAt: .createdAt}),
lastCommitDate: (.commits[-1].committedDate),
headRefOid: .headRefOid
}'
This fetches more than READ_PR_COMMENTS maps to -- tool-mappings.md's entry for that token is a comments-only MCP call, which returns neither commits nor headRefOid.
In a remote/MCP session without gh, fetch those fields with separate calls rather than assuming the token mapping covers this expanded query.
The reviewer login varies by setup: gh pr view reports claude; the REST API reports claude[bot].
startswith("claude") matches both.
If .review is null, the reviewer may post as github-actions[bot] or another login -- never report "clean"; broaden the filter or say no review was found.
If .review.createdAt is earlier than .lastCommitDate, the review predates the latest push -- report [⏳ In-Flight / Stale](url) (or in-flight), not the review body's verdict, regardless of what it says (both are ISO 8601 UTC timestamps, so a plain string comparison works).
When the review body names the commit it reviewed (the @claude bot commonly writes "commit <sha>"), cross-check that mentioned SHA's prefix against .headRefOid (part of the same call above) as a corroborating signal.
Treat a mismatch as [⏳ In-Flight](url) even if the timing check alone would have said .
-- report (not ) instead, since is the commit's local committer timestamp, not when GitHub received the push, and a commit authored earlier but pushed later can pass the timing check while still being newer than the review.
Only once the review postdates the last commit a named SHA matches -- unconditionally, with no SHA named meaning , not , full stop -- apply the bar for : "Looks good" / "no findings" / "approved" with zero follow-on bullets under any heading, hyperlinked as or .
A rebuttal the reviewer still disputes is (), not clean.
3. Assemble (orchestrator)
Collect the rows the subagents return and pair each with the title, headRefName, and isDraft the orchestrator already has from step 1 (the subagent doesn't re-fetch these), then render the table + per-PR findings list (see Output) --- marking draft PRs clearly (e.g. [#<N>](url) (Draft)).
Graceful degradation to series
If subagent fan-out is unavailable (no Agent tool in the session), fall back to gathering the seven signals in series -- loop the exact same per-PR gather (items 1-7 above, including the currency check, thread resolution, behind-main check, and the human CHANGES_REQUESTED check) over each PR from step 1.
The output is the same; it is just sequential.
Don't substitute a simplified comments-only query here -- that would silently drop the current-head, thread-resolution, and human-review guarantees the rest of this skill relies on.
Output
Primary Review Summary Table
A Markdown table, one row per open PR, with these columns:
| PR | Author | AI Review Verdict | CI State | Reviewers Requested | Next Step |
|---|
| #101 | the repository owner | ✅ Approved (Round 3) | 🟢 All Green | Self-authored (GitHub prevents requesting review from author) | Ready for self-merge |
| #102 | external-dev | ✅ Clean (Round 2) | 🟢 All Green | the repository owner | Ready for human review |
| #103 | external-dev | ✅ Clean (Round 1) | 🟢 All Green | ⚠️ None (Request human review) | Request human review |
| #104 | external-dev | ❌ Needs Work (Round 1) | 🟢 All Green | - (AI review in progress) | Drive to clean (ARDI) |
| #105 (Draft) | external-dev | - | ⏳ Pending (build) | - | Draft (Work in progress) |
- PR --- markdown link
[#<N>](https://github.com/<owner>/<repo>/pull/<N>), appended with (Draft) if isDraft is true.
- Author --- author login.
- AI Review Verdict --- hyperlinked directly to the latest review comment URL (e.g.
[✅ Clean (Round N)](https://github.com/...#issuecomment-...)).
Verified current with the latest commit (.createdAt >= .lastCommitDate and matching commit SHA).
If the review predates the latest push, display [⏳ In-Flight / Stale](url).
If no SHA is named, display [⚠️ Unverified](url).
- CI State ---
🟢 All Green / ❌ Failing (<name>) / ⏳ Pending (<name>).
- Reviewers Requested --- evaluates human review status per
copilot-review-before-human.md.
If human review has requested changes, flag ❌ Changes requested by <login>.
For self-authored PRs, note *Self-authored*.
When AI review is clean and CI is green, list requested reviewers (e.g. the repository owner) or flag ⚠️ None (Request human review).
When AI review is clean but CI is failing or pending, display - (CI in progress / failing).
When AI review is in-flight or unclean, display - (AI review in progress).
- Next Step --- computed deterministically using the full state matrix:
- If
isDraft: Draft (Work in progress).
- If human
CHANGES_REQUESTED is pending: Blocked on human changes (<login>) (overrides everything below).
- If branch is behind main:
Resolve conflicts / Sync main (<N> commits behind).
- If CI is failing:
Fix CI (<failing-check>).
- If unaddressed review threads remain:
Resolve inline threads (<N> open).
- If AI review or External review has open findings:
Drive to clean (ARDI).
- If AI review is running:
In-flight AI review.
- If CI is pending:
Wait for CI (<pending-check>).
- If neither AI review nor External review has a verified clean verdict at head: .
Extended Technical Dashboard (Optional / On Request)
When detailed git/thread metrics are needed, include the extended columns:
| PR | Title | Branch | CI | Review | External | Human | Threads | Behind main | Next Step |
|---|
Below the table, list each PR's open findings briefly (or "none"), and call out anything needing action: branches behind main, failing CI, drafts, reviews that returned null, or a pending human review.
Do not label a PR "ready to merge" or "merge-ready" unless it is fully clean --- Human is none (a blocking human review overrides everything below) and at least one of Review or External is clean at the current head and neither one has open findings and all CI workflows are green and it's not behind main and every inline review thread is resolved.
Never hedge with "ready except for one nit."
Why fan-out is safe here (and the write-loops stay series)
This loop parallelizes because its units are independent and side-effect-free
--- each PR's signals are read-only and don't depend on any other PR. The
whole-queue write loops are different, and deliberately stay (mostly) series:
ardia / iterate-all --- share one working directory, compete for CI
runner capacity, and have human checkpoints. Parallelize only opt-in, with
worktree isolation + bounded concurrency --- not by default.
gii / gia --- intentionally sequential: a later issue's base branch
depends on whether the prior MR merged, and same-file issues conflict.
gip is the opt-in exception --- it fans out only the provably
independent subset (no stacking dependency, no file overlap), each subagent
in its own worktree, and sends everything else back through gii.
Rule of thumb: fan out a whole-queue loop only when its units are provably
independent and don't mutate shared state --- like this one.
Notes
- Extended operational rationale, empirical measurements, and synthetic fixture histories live in
pr-status-all.rationale.md.
- Skip draft PRs from the "ready" assessment but still show them (mark as
draft).
- One unit of work per PR: in the parallel path that's one subagent per PR; in
the series fallback it's one gather per PR. Either way, the output table and
findings list are identical.
Relationship to other skills
pr-status --- the single-PR version; this applies its latest-review-only /
null-not-clean discipline across the whole open-PR queue. (pr-status :
pr-status-all :: ardi : ardia.)
ardia / iterate-all --- the write counterpart: actually drive every
open PR to clean. This skill only reports; see Why fan-out is safe here for
why those loops stay series.
sync-pr-branch --- offered for any PR the table flags as behind main.
scripts/pr-sweep.py -- the cheap deterministic sweep that says which
PRs this dashboard should be pointed at.
It answers one narrower question ("which open PRs are stalled right now")
across several repos in one GraphQL call, with a wall-clock staleness
threshold this skill has no equivalent of.
This skill then supplies the per-PR depth it deliberately omits.
See derive-dont-enumerate.
scripts/pr-overlap.py -- the same sweep over pairs rather than over PRs: which open PRs share a file, and which share none.
Reach for it whenever this dashboard's rows are about to be merged, since "collides" is a property of the pair and no per-PR column can carry it.
It separates an identical file set (a duplicate to close) from a partial overlap (an order to pick), and reports pairs examined alongside pairs colliding.
See batch-merge-and-resolve.