| name | process-prs |
| description | Run one PR-processing tick on the current repo's open pull requests, then stop. Finalize your own / agent-authored PRs (rebase-if-needed, /code-review then /review-fix if needed, /recommit, green CI), review community and bot PRs, and emit exactly one canonical recommend-* verdict label per PR. File follow-up and idea issues without blocking any merge. Does one tick then stops, so it composes with /loop for a recurring PR loop (e.g. `/loop /process-prs`, `/loop 1h /process-prs`). Never merges, never closes; the verdict is a recommendation, not an action. GitHub-only (uses `gh`). Use when the user wants to process, finalize, triage, or review open pull requests, set up a recurring PR-handling agent, or run one PR-processing pass. |
Process PRs (one tick)
One pass over the repo's open PRs per invocation, then stop. Compose with
/loop for recurrence (/loop /process-prs, /loop 1h /process-prs); /loop
owns pacing and is the new-PR discovery mechanism. Never schedule yourself.
State derives entirely from GitHub (labels, draft flag, commit/comment
timestamps), so a tick is idempotent and resumable: an empty queue costs one
gh pr list and exits.
Rails
- Never merge, never close. The verdict is a recommendation; merging and
closing stay the maintainer's hand.
- Never push to a fork. Only branches in the repo itself are editable.
- Every PR comment and every issue starts with a disclaimer. PR comments:
> *This was generated by AI during PR processing.*. Issues:
> *This was generated by AI during triage.*.
- Skip PRs owned by another active loop. If another dispatcher already
drives a PR (its branch is in that loop's queue), leave it untouched. This
pass handles everything else.
0. Preflight
-
Abort if not a git repo, or if there is no GitHub remote. This skill is
GitHub-only (gh, authorAssociation, pull requests). GitLab merge
requests are a separate concern, not handled here.
-
git fetch. Identify the default branch, do not assume main:
gh repo view --json defaultBranchRef -q .defaultBranchRef.name
-
Detect optional substrate; use what exists, skip what doesn't:
- Verdict labels (
recommend-merge / recommend-close /
recommend-triage, plus the shared needs-info). If absent, offer to run
/setup-github-labels; without them the tick can review but cannot record a
verdict, so stop and say so.
- Triage wiring (
docs/agents/triage-labels.md, an issue type: axis,
ready-for-agent / needs-triage) and an issue-tracker.md. Used for
spin-off issues. If absent, fall back to plain gh issue create and skip
the triage-state labels. A repo with none of this is fine.
Labels
The three PR verdicts are mutually exclusive; a PR carries exactly one once
finalized:
recommend-merge: finalized and endorsed, for the maintainer to review and merge.
recommend-close: active reject (broken, abandoned, superseded, out of
scope, or a technical blocker the agent can name).
recommend-triage: code is sound but the merge/close call is a product or
scope judgment the agent can't make.
A PR parked on an outside human reuses the shared needs-info label. A PR still
being iterated stays a draft (no verdict label).
Tick algorithm
-
Build the queue. Open, non-draft PRs with no recommend-* verdict, minus
branches owned by another active loop:
gh pr list --state open --json number,headRefName,isDraft,author,authorAssociation,labels \
--jq '[.[] | select(.isDraft|not)]'
Also pull verdicted PRs to check for staleness (next step).
-
Re-queue stale verdicts. For each PR wearing a recommend-* label,
compare its latest commit time to the agent's last verdict comment. If the
commit is newer, the verdict is stale: remove the label and put the PR back
in the queue for this tick.
-
Classify each queued PR by author:
- Own:
authorAssociation is OWNER or MEMBER. Full finalize.
- Bot: author is a bot (Dependabot et al.). Mechanical review.
- Community: everyone else. Review-only.
-
Process, respecting throughput. Review all queued community and bot PRs
(cheap). Full-finalize at most one own PR per tick, to avoid two rebase
chains conflicting. Apply the per-tier pipeline below.
-
Handle in-flight PRs. A PR with no verdict whose checks are still pending
and which the agent already pushed/commented on is "awaiting CI", so skip it;
the next tick re-checks. This state is derived from GitHub, not stored.
-
Report. End with a short dashboard: each queued PR and the action taken,
verdicts emitted, PRs awaiting CI, spin-off issues filed, anything needing
the maintainer. /loop reads this to pace the next run.
Per-tier pipeline
Own PRs: full finalize
Skip the PR this tick if it is already awaiting CI from a prior tick (step 5) —
do not re-finalize while checks are pending. Otherwise:
-
Rebase only if a fresh CI run against current main is actually needed.
A rebase forces a push, which re-triggers CI and costs a whole extra tick of
waiting, so don't pay it reflexively. When CI runs on pull_request with a
bare actions/checkout (the common case; confirm in the workflow), a green
check already tested the PR merged into main, but only as main stood
when CI last ran, since GitHub does not re-run CI when main advances. So the
real question is whether that green has gone stale in a way that matters.
Compute the divergence (use the default branch and the PR's head ref, not
literal main):
gh pr view <N> --json mergeStateStatus,files \
-q '{state: .mergeStateStatus, files: [.files[].path]}'
base=$(git merge-base origin/<defaultBranch> origin/<headRef>)
git diff --name-only "$base"..origin/<defaultBranch>
git rev-list --count "$base"..origin/<defaultBranch>
Rebase (then let the pipeline force-push, re-triggering CI) iff any holds:
- merge state is
DIRTY/conflicting (a textual conflict to resolve), or
BEHIND (branch protection requires up-to-date before merge);
main's post-base files intersect the PR's changed files, a direct overlap,
so the merged result the PR's tests cover may actually differ;
main gained more than 20 commits since the merge-base, a catch-all:
a large disjoint advance can still carry a transitive/semantic break (a
renamed or deleted symbol the PR calls from another file) that the stale
green never exercised.
Otherwise skip the rebase entirely. main moved only in files disjoint
from the PR and within the cap, so the existing green still reflects the
merged result; leave the branch untouched and let the PR be verdicted this
same tick (step 5). (If merge state is UNKNOWN, GitHub is still computing
mergeability; re-check next tick rather than guessing.)
-
Gate /review-fix behind a cheap /code-review pass. Don't pay the full
loop reflexively. An own PR that is already non-draft has, by convention,
been through /review-fix before it was un-drafted, so re-running the full
loop (five reviewers, up to five rounds, plus a force-push that re-triggers CI
and costs another whole tick) is usually wasted motion. So first run a single
report-only /code-review at medium effort (no --fix) over the diff as a
safety net (the same cheap gate the community tier uses, and one of the five
reviewers /review-fix would run), then branch on what it finds:
- No meaningful findings (clean, or only nits already matching repo
conventions) → skip
/review-fix entirely; the PR is already settled. Go to
step 3.
- Meaningful findings (a real correctness or design fix, or several
concerns at once) → escalate to
/review-fix, which runs the bundled
reviewers, applies the meaningful fixes, folds them into clean commits, and
force-pushes with lease.
-
/recommit: reshape into a clean, logical commit sequence.
-
/verify: only if the PR changes runtime behavior and a smoke is practical.
/verify picks the method per project type (CLI invocation, server boot,
library import, GUI launch); skip for docs/refactor/test-only PRs.
-
Resolve CI non-blocking. If no push happened this tick (no rebase per
step 1, and /review-fix and /recommit each left the branch untouched), the
existing checks are authoritative: read them directly and verdict now without
waiting, applying the outcome rules below. Only a push made this tick puts the
PR into the awaiting-CI state; when its checks are pending, leave the PR
awaiting CI and move on, and a later tick applies the same rules. The outcome
rules:
- green →
recommend-merge.
- red, no prior agent fix-attempt on the PR → one fix-and-repush attempt, then
leave it awaiting CI again.
- red, a prior agent fix-attempt already exists →
recommend-close (a red
build is a nameable technical blocker, not a sound-code scope call) with the
failure quoted.
The one-attempt cap is derived from the PR's history, not stored: a prior
agent fix-attempt = a commit the git user pushed to the branch after the
first red CI, paired with its disclaimer-prefixed comment. Check the current
CI outcome before the cap — a now-green PR takes the green branch regardless.
-
Emit the verdict with a disclaimer-prefixed comment.
Community PRs: review-only, no pushing
/code-review at medium effort, without --fix.
- Post one consolidated, disclaimer-prefixed review comment (never inline
spam).
- Verdict:
- mergeable as-is, CI green →
recommend-merge.
- sound but a product/scope call →
recommend-triage.
- broken, abandoned, superseded, out of scope, or red CI with a technical
blocker →
recommend-close, reason quoted.
- needs changes the author must make (incl. a trivial CI fix, or checks not
yet authorized) → leave the review comment and park on
needs-info.
Bot PRs: mechanical
Review-only; the verdict is mechanical and needs-info never applies:
- patch/minor bump, CI green →
recommend-merge.
- red CI →
recommend-close (a failing bump is a nameable technical blocker),
failure quoted.
- major version or breaking changelog, CI green →
recommend-triage (a sound
bump, but taking it is a scope call).
Spin-off issues
While processing a PR, this pass captures two kinds of work as GitHub issues,
autonomously but guarded:
- Follow-up: a loose end from this PR that should not block its merge (a
TODO left behind, a test-coverage gap, a deferred refactor). The PR still gets
its verdict; note the filed issues in the verdict comment
(
follow-ups: #N, #M). A follow-up never blocks the parent's merge.
- Idea: a standalone improvement noticed while reading the code.
For every spin-off issue:
- Dedup-search open issues first; skip if one already covers it.
- Create it with
gh issue create (or the repo's issue-tracker.md
conventions), including the triage disclaimer and a link back to the
originating PR. Add a type: label if the substrate exists, but leave the
triage state to /process-issues: file it without a triage-state label so
the next /process-issues tick picks it up and routes it.
- List it in the tick report.
Composition
The spin-off issues filed here land unlabeled, so /process-issues picks them up
and triages them. An issue that becomes ready-for-agent is implemented by
/implement-issues, whose resulting PR flows back into this pass. Three
single-purpose passes, each /loop-paced, none ever merging: triage, implement,
finalize.