oh-merge
Holistically review pending GitHub issue PRs from oh-task, then squash-merge them as a cohesive batch.
Invocation
/oh-merge
Flow
Load project background from AGENTS.md, relevant .oh/ artifacts, and RNA MCP context when available.
Find all PRs with oh-merge label:
gh pr list --label oh-merge --json number,title,headRefName,baseRefName,additions,deletions
Only PRs with the oh-merge label are eligible for merge.
IMPORTANT: PRs with merge conflicts ARE eligible. The skill rebases and resolves conflicts in step 6. Do NOT skip PRs because of conflict status - that's exactly what this skill handles.
Build dependency graph and identify stacks:
Create adjacency list: baseRefName → [PRs targeting it]
Find root PRs: those where baseRefName = main (or master)
Identify stacks: chains where child PRs target parent PR branches
Detect cycles: If a branch eventually targets itself, report error and skip
Example graph:
main ← PR #42 (issue/123) ← PR #43 (issue/456)
main ← PR #44 (issue/789) [separate stack]
Select stack to process:
- If multiple independent stacks exist, pick first by lowest root PR number (FIFO by age)
- If multiple PRs target the same base branch, order by PR number (lowest first)
- Report other stacks as "queued for next run"
- Process only one stack per invocation to keep merges atomic
- Orphaned children: If a child PR targets a branch that doesn't exist (parent merged externally), update its base to main and treat as a root
Batch review - evaluate all PRs in the selected stack together:
- Collect combined diff of all PRs against main
- Run the repo-local
/review skill on the combined changes
- Evaluate:
- Do changes conflict logically? (same code modified differently)
- Is there duplicate work? (two PRs solving same problem)
- Do changes compose well? (feature A + feature B = coherent whole)
- Any cross-cutting concerns? (shared dependencies, API changes)
- If concerns found:
- Report issues
- Ask human whether to proceed or address first
This batch review supplements, but never replaces, each PR's project-specific /ship gate.
Merge stack in dependency order (root first, then children):
For each PR in the stack, starting from the root:
- Rewrite the branch onto its final target first:
- Fetch, check out, and rebase onto the current target branch.
- Resolve conflicts, run repo-local
/review, and force-push with lease.
- For a child whose parent has merged, retarget it to
main, rebase, and force-push before any validation.
- Any later rewrite or retarget invalidates all prior ship, independent final-diff approval, and CI evidence.
a. Verify the full RNA ship gate completed on the final pushed commit:
- The PR is ready for review, not draft.
- Required
/ship step comments are present, including RNA-grounded review,
independent review, regression oracle, merit assessment, manual verification,
delivery verification, and final comment sweep.
- The final acceptance-criteria gate passed.
- A fresh, separate repo-local
/review sub-agent posted explicit APPROVE for the exact final commit.
- CodeRabbit is optional supplemental feedback: never trigger or wait for it, but all actionable comments already present are addressed.
If any evidence is missing, stop and run
/ship <pr-number>; do not merge directly.
b. Verify CI is passing on that same final commit:
gh pr checks <pr-number> --fail-on-error
If CI is failing, stop and report error.
c. Squash merge:
gh pr merge <pr-number> --squash
d. For child PRs in the stack (after parent merged):
Update base branch to main:
gh pr edit <child-pr-number> --base main
Rebase child onto main:
gh pr checkout <child-pr-number>
git rebase origin/main
git push --force-with-lease
The rewrite invalidates prior validation. Return to step 0 and rerun the full ship gate, fresh independent final-diff review, and CI before merging.
e. On merge failure - If any PR in the stack fails to merge:
- Stop processing the stack
- Report which PRs were merged successfully and which failed
- Signal error: already-merged PRs stay merged, failed PR remains open
- Next oh-merge run will see the failed PR as a new root (its parent is now in main)
Note: GitHub automatically closes linked issues when the PR merges through
Closes #N in the PR body.
Stacked PRs
When PRs target other PR branches (not main), oh-merge detects the stack and processes it:
Detection:
gh pr list --label oh-merge --json number,title,headRefName,baseRefName
- PRs with
baseRefName = main are roots
- PRs with
baseRefName = issue/<number> are children targeting that parent
- Ignore merge conflict status - we handle conflicts during rebase
Graph building:
adjacency[baseRefName] = [list of PRs targeting it]
Example:
adjacency["main"] = [PR #42, PR #44]
adjacency["issue/123"] = [PR #43]
Stack 1: main ← #42 (issue/123) ← #43 (issue/456)
Stack 2: main ← #44 (issue/789)
Merge sequence (for Stack 1):
- Merge #42 to main
- Update #43:
gh pr edit 43 --base main
- Rebase #43 onto main:
git rebase origin/main && git push --force-with-lease
- Merge #43 to main
After processing:
Before: PR #43 → issue/123 → main
PR #42 → main
PR #44 → main (separate stack)
After: PR #42 merged to main (issue #123 auto-closed)
PR #43 rebased onto main, merged to main (issue #456 auto-closed)
PR #44 remains for next oh-merge run
Batch Review Criteria
The holistic review checks what individual PR reviews can't:
- Logical conflicts: PR A assumes X, PR B assumes not-X
- Duplication: Both PRs add similar functionality
- Integration issues: Combined changes break something neither breaks alone
- Ordering dependencies: PR B depends on PR A being merged first (auto-detected for stacked PRs)
- Scope creep: Batch as a whole does more than originally intended
Prerequisites
- PRs must have the
oh-merge label (human approval gate)
- Each PR must have completed the repository-specific
/ship pipeline
- PRs must be ready for review, not draft
- Every PR must have an explicit
APPROVE from a fresh, separate repo-local /review sub-agent on the exact final commit
- Any later diff change invalidates that approval and requires a new fresh reviewer
- CodeRabbit is optional supplemental feedback: never trigger or wait for it, but address actionable comments already present
- PRs must have CI passing
- PRs should have "Closes #N" in body for auto-close (created by oh-task)
- Batch review must pass (or human override)
Note: The oh-merge label must be created in the repo. This is opt-in per repo.
Exit Conditions
- Success: Selected stack fully merged
- Partial success: Some PRs in stack merged, then failure
- Needs attention: Batch review raised concerns - waiting for human decision
- Error: Unrecoverable failure (code conflicts, CI failing, cycle detected)
- No work: No PRs with
oh-merge label found
Completion Signaling (MANDATORY)
CRITICAL: You MUST signal completion when done. Call the signal_completion tool as your FINAL action.
Signal based on outcome:
| Outcome |
Call |
| Stack merged |
signal_completion(status: "success", message: "Merged N PRs") |
| No PRs to merge |
signal_completion(status: "success", message: "No PRs with oh-merge label") |
| Partial success |
signal_completion(status: "error", error: "Merged N PRs, failed on PR #X: <reason>") |
| Unrecoverable failure |
signal_completion(status: "error", error: "<reason>") |
If you do not signal, the orchestrator will not know you are done and the session becomes orphaned.
Fallback: If the signal_completion tool is not available, output your completion status as your final message in the format: COMPLETION: status=<status> message=<message> or COMPLETION: status=<status> error=<reason>.
Example
Basic (no stacks)
$ /oh-merge
Finding PRs with oh-merge label...
Found 2 PRs:
PR #42 "Fix validation bug" (issue/123) → main (CI ✓)
PR #44 "Refactor validator" (issue/789) → main (CI ✓)
Building dependency graph...
Stack 1: main ← #42
Stack 2: main ← #44
2 independent stacks, processing Stack 1
Running batch review on Stack 1 (1 PR)...
Batch review complete: ✓ No issues
Processing PR #42 (Fix validation bug)...
Verifying CI... ✓
Rebasing onto main... clean
Squash merging... ✓
Issue #123 will auto-close on merge
Merge complete.
Merged: 1 PR (#42)
Remaining: 1 PR (#44 - queued for next run)
signal_completion(status: "success", message: "Merged 1 PR (#42)")
Done.
Stacked PRs
$ /oh-merge
Finding PRs with oh-merge label...
Found 3 PRs:
PR #42 "Fix validation bug" (issue/123) → main (CI ✓)
PR #43 "Add edge case tests" (issue/456) → issue/123 (CI ✓)
PR #44 "Refactor validator" (issue/789) → main (CI ✓)
Building dependency graph...
Stack 1: main ← #42 (issue/123) ← #43 (issue/456)
Stack 2: main ← #44
2 stacks found, processing Stack 1 (2 PRs)
Running batch review on Stack 1...
Collecting diffs: +547 -103 across 8 files
Batch review complete: ✓ No issues
Processing stack root: PR #42 (Fix validation bug)...
Verifying CI... ✓
Rebasing onto main... clean
Squash merging... ✓
Issue #123 will auto-close on merge
Processing stack child: PR #43 (Add edge case tests)...
Verifying CI... ✓
Updating base branch to main... done
Rebasing onto main... clean
Squash merging... ✓
Issue #456 will auto-close on merge
Stack merged.
Merged: 2 PRs (#42, #43)
Remaining: 1 PR (#44 - queued for next run)
signal_completion(status: "success", message: "Merged 2 PRs (#42, #43)")
Done.
1---2name: oh-merge3description: Review and merge open PRs for GitHub issues as a cohesive batch4---56# oh-merge78Holistically review pending GitHub issue PRs from `oh-task`, then squash-merge them as a cohesive batch.910## Invocation1112`/oh-merge`1314## Flow15161. Load project background from `AGENTS.md`, relevant `.oh/` artifacts, and RNA MCP context when available.17182. Find all PRs with `oh-merge` label:1920 ```bash21 gh pr list --label oh-merge --json number,title,headRefName,baseRefName,additions,deletions22 ```2324 **Only PRs with the `oh-merge` label are eligible for merge.**2526 **IMPORTANT:** PRs with merge conflicts ARE eligible. The skill rebases and resolves conflicts in step 6. Do NOT skip PRs because of conflict status - that's exactly what this skill handles.27283. **Build dependency graph and identify stacks:**29 - Create adjacency list: `baseRefName → [PRs targeting it]`30 - Find root PRs: those where `baseRefName = main` (or master)31 - Identify stacks: chains where child PRs target parent PR branches32 - **Detect cycles**: If a branch eventually targets itself, report error and skip33 - Example graph:3435 ```36 main ← PR #42 (issue/123) ← PR #43 (issue/456)37 main ← PR #44 (issue/789) [separate stack]38 ```39404. **Select stack to process:**41 - If multiple independent stacks exist, pick first by lowest root PR number (FIFO by age)42 - If multiple PRs target the same base branch, order by PR number (lowest first)43 - Report other stacks as "queued for next run"44 - Process only one stack per invocation to keep merges atomic45 - **Orphaned children**: If a child PR targets a branch that doesn't exist (parent merged externally), update its base to main and treat as a root46475. **Batch review** - evaluate all PRs in the selected stack together:48 - Collect combined diff of all PRs against main49 - Run the repo-local `/review` skill on the combined changes50 - Evaluate:51 - Do changes conflict logically? (same code modified differently)52 - Is there duplicate work? (two PRs solving same problem)53 - Do changes compose well? (feature A + feature B = coherent whole)54 - Any cross-cutting concerns? (shared dependencies, API changes)55 - If concerns found:56 - Report issues57 - Ask human whether to proceed or address first5859 This batch review supplements, but never replaces, each PR's project-specific `/ship` gate.60616. **Merge stack in dependency order** (root first, then children):6263 For each PR in the stack, starting from the root:6465 1. **Rewrite the branch onto its final target first:**66 - Fetch, check out, and rebase onto the current target branch.67 - Resolve conflicts, run repo-local `/review`, and force-push with lease.68 - For a child whose parent has merged, retarget it to `main`, rebase, and force-push before any validation.69 - Any later rewrite or retarget invalidates all prior ship, independent final-diff approval, and CI evidence.7071 a. **Verify the full RNA ship gate completed on the final pushed commit:**72 - The PR is ready for review, not draft.73 - Required `/ship` step comments are present, including RNA-grounded review,74 independent review, regression oracle, merit assessment, manual verification,75 delivery verification, and final comment sweep.76 - The final acceptance-criteria gate passed.77 - A fresh, separate repo-local `/review` sub-agent posted explicit `APPROVE` for the exact final commit.78 - CodeRabbit is optional supplemental feedback: never trigger or wait for it, but all actionable comments already present are addressed.79 If any evidence is missing, stop and run `/ship <pr-number>`; do not merge directly.8081 b. **Verify CI is passing on that same final commit:**8283 ```bash84 gh pr checks <pr-number> --fail-on-error85 ```8687 If CI is failing, stop and report error.8889 c. Squash merge:9091 ```bash92 gh pr merge <pr-number> --squash93 ```9495 d. **For child PRs in the stack** (after parent merged):96 - Update base branch to main:9798 ```bash99 gh pr edit <child-pr-number> --base main100 ```101102 - Rebase child onto main:103104 ```bash105 gh pr checkout <child-pr-number>106 git rebase origin/main107 git push --force-with-lease108 ```109110 - The rewrite invalidates prior validation. Return to step 0 and rerun the full ship gate, fresh independent final-diff review, and CI before merging.111112 e. **On merge failure** - If any PR in the stack fails to merge:113 - Stop processing the stack114 - Report which PRs were merged successfully and which failed115 - Signal error: already-merged PRs stay merged, failed PR remains open116 - Next oh-merge run will see the failed PR as a new root (its parent is now in main)117118 **Note:** GitHub automatically closes linked issues when the PR merges through119 `Closes #N` in the PR body.120121## Stacked PRs122123When PRs target other PR branches (not main), oh-merge detects the stack and processes it:124125**Detection:**126127```bash128gh pr list --label oh-merge --json number,title,headRefName,baseRefName129```130131- PRs with `baseRefName = main` are roots132- PRs with `baseRefName = issue/<number>` are children targeting that parent133- Ignore merge conflict status - we handle conflicts during rebase134135**Graph building:**136137```text138adjacency[baseRefName] = [list of PRs targeting it]139140Example:141 adjacency["main"] = [PR #42, PR #44]142 adjacency["issue/123"] = [PR #43]143144Stack 1: main ← #42 (issue/123) ← #43 (issue/456)145Stack 2: main ← #44 (issue/789)146```147148**Merge sequence** (for Stack 1):1491501. Merge #42 to main1512. Update #43: `gh pr edit 43 --base main`1523. Rebase #43 onto main: `git rebase origin/main && git push --force-with-lease`1534. Merge #43 to main154155**After processing:**156157```text158Before: PR #43 → issue/123 → main159 PR #42 → main160 PR #44 → main (separate stack)161162After: PR #42 merged to main (issue #123 auto-closed)163 PR #43 rebased onto main, merged to main (issue #456 auto-closed)164 PR #44 remains for next oh-merge run165```166167## Batch Review Criteria168169The holistic review checks what individual PR reviews can't:170171- **Logical conflicts**: PR A assumes X, PR B assumes not-X172- **Duplication**: Both PRs add similar functionality173- **Integration issues**: Combined changes break something neither breaks alone174- **Ordering dependencies**: PR B depends on PR A being merged first (auto-detected for stacked PRs)175- **Scope creep**: Batch as a whole does more than originally intended176177## Prerequisites178179- PRs must have the `oh-merge` label (human approval gate)180- Each PR must have completed the repository-specific `/ship` pipeline181- PRs must be ready for review, not draft182- Every PR must have an explicit `APPROVE` from a fresh, separate repo-local `/review` sub-agent on the exact final commit183- Any later diff change invalidates that approval and requires a new fresh reviewer184- CodeRabbit is optional supplemental feedback: never trigger or wait for it, but address actionable comments already present185- PRs must have CI passing186- PRs should have "Closes #N" in body for auto-close (created by oh-task)187- Batch review must pass (or human override)188189**Note:** The `oh-merge` label must be created in the repo. This is opt-in per repo.190191## Exit Conditions192193- **Success**: Selected stack fully merged194- **Partial success**: Some PRs in stack merged, then failure195- **Needs attention**: Batch review raised concerns - waiting for human decision196- **Error**: Unrecoverable failure (code conflicts, CI failing, cycle detected)197- **No work**: No PRs with `oh-merge` label found198199## Completion Signaling (MANDATORY)200201**CRITICAL: You MUST signal completion when done.** Call the `signal_completion` tool as your FINAL action.202203**Signal based on outcome:**204205| Outcome | Call |206| --------- | ------ |207| Stack merged | `signal_completion(status: "success", message: "Merged N PRs")` |208| No PRs to merge | `signal_completion(status: "success", message: "No PRs with oh-merge label")` |209| Partial success | `signal_completion(status: "error", error: "Merged N PRs, failed on PR #X: <reason>")` |210| Unrecoverable failure | `signal_completion(status: "error", error: "<reason>")` |211212**If you do not signal, the orchestrator will not know you are done and the session becomes orphaned.**213214**Fallback:** If the `signal_completion` tool is not available, output your completion status as your final message in the format: `COMPLETION: status=<status> message=<message>` or `COMPLETION: status=<status> error=<reason>`.215216## Example217218### Basic (no stacks)219220```text221$ /oh-merge222223Finding PRs with oh-merge label...224Found 2 PRs:225 PR #42 "Fix validation bug" (issue/123) → main (CI ✓)226 PR #44 "Refactor validator" (issue/789) → main (CI ✓)227228Building dependency graph...229 Stack 1: main ← #42230 Stack 2: main ← #44231 2 independent stacks, processing Stack 1232233Running batch review on Stack 1 (1 PR)...234Batch review complete: ✓ No issues235236Processing PR #42 (Fix validation bug)...237 Verifying CI... ✓238 Rebasing onto main... clean239 Squash merging... ✓240 Issue #123 will auto-close on merge241242Merge complete.243 Merged: 1 PR (#42)244 Remaining: 1 PR (#44 - queued for next run)245246signal_completion(status: "success", message: "Merged 1 PR (#42)")247Done.248```249250### Stacked PRs251252```text253$ /oh-merge254255Finding PRs with oh-merge label...256Found 3 PRs:257 PR #42 "Fix validation bug" (issue/123) → main (CI ✓)258 PR #43 "Add edge case tests" (issue/456) → issue/123 (CI ✓)259 PR #44 "Refactor validator" (issue/789) → main (CI ✓)260261Building dependency graph...262 Stack 1: main ← #42 (issue/123) ← #43 (issue/456)263 Stack 2: main ← #44264 2 stacks found, processing Stack 1 (2 PRs)265266Running batch review on Stack 1...267Collecting diffs: +547 -103 across 8 files268Batch review complete: ✓ No issues269270Processing stack root: PR #42 (Fix validation bug)...271 Verifying CI... ✓272 Rebasing onto main... clean273 Squash merging... ✓274 Issue #123 will auto-close on merge275276Processing stack child: PR #43 (Add edge case tests)...277 Verifying CI... ✓278 Updating base branch to main... done279 Rebasing onto main... clean280 Squash merging... ✓281 Issue #456 will auto-close on merge282283Stack merged.284 Merged: 2 PRs (#42, #43)285 Remaining: 1 PR (#44 - queued for next run)286287signal_completion(status: "success", message: "Merged 2 PRs (#42, #43)")288Done.289```