GitHub Batch Runner
Overview
Implement several Issues as one release: one integration branch, one verification of
the whole, one Pull Request. Per-Issue implementation follows the gh-issue-resolver
contract unchanged — this skill owns only what is batch-level: membership, ordering,
whole-branch verification, and the single PR.
Use this skill only when the answer to "do these ship together?" is yes. Independent
Issues use gh-issue-resolver once per Issue; a dependency chain that ships
separately uses the stacked flow (also gh-issue-resolver, with the dependency's
branch as base).
Prerequisites
- Every member Issue has an agreed plan comment (
<!-- gh-issue-planner:agreed-plan -->) posted bygh-issue-planner. If any Issue lacks one, stop and run the planner for that Issue first — planning several Issues in one pass is supported there. - The repository's integration branch is known (CLAUDE.md
## Flow; falls back to the default branch).
Workflow
Step 1: Collect the batch under an Epic Issue
The batch's membership is an Epic Issue holding the members as sub-issues — not a branch name, not a comment convention. Create the Epic if the user did not name one:
gh issue create --title "Epic: <what ships together>" --label epic --body "$(cat <<'EOF'
## まとめて対応する Issue
<one line per member Issue>
## 統合先
<integration branch>
---
<!-- gh-batch-runner:batch-plan -->
*Generated by `gh-batch-runner`. Membership is the sub-issue list; edit that, not this body.*
EOF
)"
Then attach each member. Reading takes the Issue number; writing takes the REST
integer id, which is not the same thing as the I_kwDO… node id that
gh issue view reports under id — the endpoint rejects that with HTTP 422. Use -F
rather than -f so the value stays an integer instead of becoming a string:
sub_id=$(gh api repos/{owner}/{repo}/issues/<member-number> --jq .id)
gh api -X POST repos/{owner}/{repo}/issues/<epic-number>/sub_issues -F sub_issue_id="$sub_id"
Step 2: Re-read dependencies and order the work
Read the current state every run — dependencies are recorded on Issues at any time, including after the batch started:
gh api repos/{owner}/{repo}/issues/<epic-number>/sub_issues --jq '.[].number'
gh api repos/{owner}/{repo}/issues/<member>/dependencies/blocked_by --jq '.[].number'
Order the members so every blocker lands before what it blocks. If the dependencies form a cycle, stop and report it — do not pick an order arbitrarily.
Step 3: Create the epic branch
git branch epic/<epic-number>-<slug> <integration-branch>
git worktree add ../epic-<epic-number>-<slug> epic/<epic-number>-<slug>
The epic branch is a staging area, not a protected branch. Branch protection stays on the integration branch, and that is where the gate is.
Step 4: Implement each Issue on the epic branch
For each member Issue, in the Step 2 order, follow gh-issue-resolver Steps 3–4 with:
<base-ref>= the epic branch- no child PR — the change lands as one commit per Issue:
git commit -m "<type>(#<member>): <short description>" - the Issue's own agreed plan as the scope wall, exactly as in the individual flow
- per-Issue verification as usual, with the epic branch as the baseline
A member whose verification cannot converge stops the batch at that member (Step 7) — later members are not started on top of an unverified commit.
Step 5: Verify the epic branch as a whole
Per-Issue verification does not cover what the combination broke. Re-run the full verification once more against the integration branch as baseline:
- Full test suite on the epic branch.
- Re-run every diagnosis whose triggers fire for the combined diff
(
gh-issue-resolver8.1 table, applied togit diff <integration-branch>...HEAD). - Classify findings against the integration branch:
- regression of the batch — passes on the integration branch, fails on the epic branch → fix it here, under the usual limits (≤3 iterations, inside the union of the members' agreed 影響範囲)
- pre-existing — already fails on the integration branch → hand to
report-to-issuesafter the user approves; never fixed in this PR
- A regression that cannot be attributed to a single member is still the batch's: fix it in the member whose 影響範囲 covers it, or stop (Step 7) if none does.
Step 6: Open one PR into the integration branch
gh pr create --base <integration-branch> --head epic/<epic-number>-<slug> \
--title "epic(#<epic-number>): <what ships together>" --body "$(cat <<'EOF'
## Summary
<what ships together and why it ships as one>
Closes #<epic-number>
Closes #<member-1>
Closes #<member-2>
## Issues in this batch
- #<member-1> — <one line>
- #<member-2> — <one line>
## Verification
- Per Issue: <PASS/FAIL summary>
- Epic branch vs <integration-branch>: <full test suite + diagnoses re-run>
- Batch regressions fixed: <ids, or none>
EOF
)"
This PR is the only place CI and review run for the batch. Do not weaken it to compensate for the epic branch being unprotected.
Step 7: Stop conditions
Stop, leave the branch and the Epic in place, and report — never merge past a failure:
- A member has no agreed plan → run
gh-issue-plannerfor it. - A member's verification does not converge in 3 iterations → return to
gh-issue-plannerfor that member (the rest of the batch stays unstarted). - A fix needs to leave the union of the members' 影響範囲 → return to the planner.
- A dependency discovered mid-run changes the order or the flow → record it on the Issue
first (
dependencies/blocked_by), then either continue with the new order or stop and say why. - The dependency graph has a cycle → report the cycle.
Step 8: Report completion
✅ Batch #<epic-number> implemented and verified.
Members: #12 #15 #18 | Epic branch: epic/<n>-<slug> | PR: <url>
Tests: pass | Batch regressions fixed: 1 | Pre-existing handed off: 2
Key Principles
- Membership lives in sub-issues, not in branch names or comment text; re-read it, and the dependencies, at the start of every run
- No child PRs. One commit per Issue, one PR per batch — the integration PR is the gate
- Verify twice: per Issue against the epic branch, then the epic branch against the integration branch. Neither replaces the other
- Batch regressions are measured against the integration branch. What already fails
there is pre-existing and goes to
report-to-issues— never into this PR - The per-Issue limits are unchanged: regressions only, ≤3 iterations, inside the agreed 影響範囲. This skill adds a batch level, it does not relax the Issue level
- Never relax tests, types, or thresholds to make the batch green