# Gh Batch Runner

> Run several planned GitHub Issues as one unit: collect them under an Epic Issue with sub-issues, implement each on a shared epic/ branch in dependency order, verify the branch as a whole, and open a single Pull Request into the integration branch. Use when the user wants several Issues fixed, verified and released together. Triggers include requests such as Issue1,2,3をまとめて対応して / まとめて修正してリリースしたい / この3件を一緒に出して, 'batch these issues', 'do issues 1 2 3 together', 'ship these issues as one release'. Prerequisite: every member Issue needs an agreed plan comment from gh-issue-planner. For a single Issue, or for Issues that ship separately, use gh-issue-resolver instead.

- Skill: `northraystudio/gh-batch-runner` (Agent Skill)
- Install (CLI): `npx skillmds@latest add northraystudio/gh-batch-runner`
- Raw SKILL.md: https://api.skillmd.com/api/skills/northraystudio/gh-batch-runner/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: northraystudio (https://skillmd.com/u/northraystudio)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/northraystudio/gh-batch-runner

---


# 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 by `gh-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:

```bash
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:

```bash
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:

```bash
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

```bash
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:

1. Full test suite on the epic branch.
2. Re-run every diagnosis whose triggers fire for the *combined* diff
   (`gh-issue-resolver` 8.1 table, applied to `git diff <integration-branch>...HEAD`).
3. 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-issues` after the user approves; never fixed in this PR
4. 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

```bash
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-planner` for it.
- A member's verification does not converge in 3 iterations → return to
  `gh-issue-planner` for 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

