Backlog to mergeable PRs in one pass: rank, approve, build, review, report.
Two phases, both automatic after the approval gate. Phase 1 opens a PR per issue. Phase 2 reviews and trims each PR. Never skip phase 2, and never fold it into phase 1: an agent told to review its own work before the PR exists stops on the review and strands uncommitted work.
-
Gather candidates.
gh issue list --state open --limit 100 --json number,title,labels,createdAt
gh pr list --state open --limit 100 --json number,title,headRefName,body
-
Filter hard before ranking. Read each candidate's full body (gh issue view <N>). Drop it if any of:
| Condition | Why |
|---|
| An open PR already targets it | Grep PR bodies for #<N>. Expect this to catch several. |
| The issue says it needs the owner's call | "needs the maintainer", "pick one", "decide", two lettered options |
| It is a pure strategy, legal, or pricing question | Not implementable, no correct answer without the user |
| It is blocked on upstream or an external event | Nothing to land today |
| It names a fleet-floor or version gate not yet met | Premature, and dangerous |
Do not drop an issue for being wrong. A wrong diagnosis is still a real bug report (see Hard rules).
-
Rank by tractability, cheapest first: blast radius (one file beats one subsystem), whether a test can prove it, whether the fix is named in the issue, whether it touches fleet state (a migration makes it harder), and whether CI can verify it (Docker or live gated is harder).
-
Show the shortlist and get approval. Present the top N (default 10, or the user's number) with a one-line "what this is" and a one-line "why it is easy" each. Use AskUserQuestion with multiSelect: true so the user picks a subset. Also list what you filtered out and why, grouped, in one or two lines per group. That list is often the most useful part: it tells the user what is actually blocked on them.
If fewer than N survive the filter, say so and stop at the real number. Never pad the list with decision-gated issues to hit N. Two real candidates beats ten with eight duds.
-
Warn once, before dispatch: ask the user not to merge these PRs until phase 2 reports. Merging mid-flight closes the branch, so a trim that lands afterwards goes nowhere and needs re-targeting as a fresh PR. Say it once, do not nag.
-
Phase 1: one subagent per approved issue. Dispatch all in a single message so they run in parallel:
Agent({ subagent_type: "general-purpose", isolation: "worktree", ... }).
Build each prompt from the "Phase 1 prompt" section below.
-
Verify each PR yourself. Do not report from the agent's own summary. For each:
gh pr view <N> --json mergeable,mergeStateStatus,statusCheckRollup
Green means merge-gate-ci concluded SUCCESS with zero FAILURE/TIMED_OUT. A finished agent that says "green" may be reporting a stale head: check the head SHA is the one it pushed.
-
Phase 2: one review subagent per PR, again in one message. Build each from the "Phase 2 prompt" section below.
-
Final report. Per PR: number, issue, net LOC delta from the trim, real bugs the review found, and CI state. Then, separately and prominently:
- Bugs the review found in the batch's own work. These matter more than the trim.
- Anything an agent refused to do, and why. A refusal is usually a finding.
- Anything an agent did NOT verify. Carry it forward verbatim; do not launder it into a claim.
- Follow-up issues opened for out-of-scope findings.
Add per-issue, from the issue body: the fleet-impact question when it touches on-disk or config state, which of several suggested options to take and why, and any constraint that must not change.