Post

validate-merge-prs

validate-merge-prs

Orchestrates the full PR-merge lifecycle for all open PRs in the current repo. Discovers PRs, builds a dependency graph, validates each in parallel via review-cycle, computes a safe merge order, and presents a merge plan for approval before executing.

Workflow

digraph validate_merge {
    rankdir=TB;
    discover [label="Discover open PRs\n(gh pr list)" shape=box];
    graph [label="Build dependency graph\n(branches + files + metadata)" shape=box];
    validate [label="Parallel validation\n(sub-agent per PR\nrunning review-cycle)" shape=box];
    classify [label="Classify: ready vs blocked" shape=diamond];
    order [label="Compute merge order\n(topo sort + overlap tie-break)" shape=box];
    report [label="Present report\n+ merge plan" shape=box];
    approve [label="User approves?" shape=diamond];
    merge [label="Sequential merge\n(rebase between steps)" shape=box];
    done [label="Done — report results" shape=doublecircle];
    blocked [label="Report blockers only" shape=doublecircle];

    discover -> graph;
    graph -> validate;
    validate -> classify;
    classify -> order [label="some ready"];
    classify -> blocked [label="all blocked"];
    order -> report;
    report -> approve;
    approve -> merge [label="yes"];
    approve -> done [label="no / adjust"];
    merge -> done;
}

Phase 1: Discovery & Dependency Analysis

1. Discover Open PRs

gh pr list --author @me --state open --json number,title,headRefName,baseRefName,url,reviewDecision,statusCheckRollup,mergeable,body,isDraft,createdAt

If zero PRs are found, report “No open PRs found” and stop.

Print a summary table of discovered PRs before continuing:

| # | Title | Branch | Base | CI | Reviews | Draft |

2. Build Dependency Graph

Construct a directed “must merge before” graph from three signal sources:

Branch topology (hard dependency): If PR-A’s headRefName equals PR-B’s baseRefName, A must merge before B.

PR metadata (hard dependency): Scan each PR’s body and title for patterns: depends on #N, after #N, blocks #N, requires #N. These create directed edges.

File overlap (soft signal): For each PR pair, compare changed files:

gh pr diff <number> --name-only

Compute pairwise file intersections. Overlapping files don’t create hard dependencies — they inform merge ordering as a tie-breaker.

3. Cycle Detection

Check the dependency graph for cycles. If found:

  • Flag all PRs in the cycle as errored
  • Exclude them from the merge plan
  • Report the cycle: “Dependency cycle detected: #A → #B → #C → #A”

Phase 2: Parallel Validation

Dispatch one sub-agent per PR to validate it. All PRs validate concurrently — validation happens on each PR’s own branch and doesn’t require merge order.

4. Dispatch Validation Agents

For each PR, spawn a sub-agent (via Agent tool) with this prompt template:

You are validating PR #<number> (<title>) on branch <headRefName>.

1. Check out the branch: git checkout <headRefName>
2. Run /review-cycle <number>
3. After review-cycle completes, check current status:
   - gh pr view <number> --json statusCheckRollup,reviewDecision,mergeable
4. Report back with:
   - PASS or FAIL
   - What review-cycle fixed (if anything)
   - What remains broken (if anything)
   - Current CI status (passing/failing/pending)
   - Current review status (approved/changes_requested/review_required/none)
   - Mergeable state (mergeable/conflicting/unknown)

Parallelism: Launch all validation agents simultaneously using multiple Agent tool calls in a single message. Use run_in_background: true for each agent.

Wait for all agents to complete before proceeding to Phase 3.

5. Classify Results

After all validation agents report back, classify each PR:

Ready to merge:

  • CI passing (all required status checks green)
  • At least one approving review (or reviews not required per repo settings)
  • No merge conflicts with base branch
  • review-cycle completed without unresolved issues

Blocked (with reason):

  • CI failing after review-cycle exhausted its iterations → "CI failing: <failure summary>"
  • Missing required review approvals → "Missing required review approval"
  • Merge conflicts → "Merge conflict with base branch"
  • Draft PR → "PR is in draft status"
  • Depends on a blocked PR → "Blocked by #<number> which is also blocked"

Phase 3: Merge Plan & Report

6. Compute Merge Order

For all “ready” PRs, compute the merge order:

  1. Topological sort on the dependency graph — hard dependencies determine base ordering
  2. File overlap tie-break — among PRs at the same topological level, merge those that share files with later PRs first (so later PRs can rebase cleanly)
  3. Final tie-break — oldest PR first (by createdAt)

7. Detect Merge Method

Auto-detect the repo’s merge convention:

git log --merges --oneline -20 <default-branch>
  • If most recent merges show “Squash” pattern → use --squash
  • If most recent merges show standard merge commits → use --merge
  • If no merge commits found (linear history) → use --rebase
  • If unable to determine → ask the user

8. Present Report — HARD GATE

Present the full report in this format:

## PR Queue Status

### Ready to Merge (in order)
1. #<number> - <title> (base: <baseRefName>) — CI: <status>, Reviews: <count> approved
   ↳ depends on #<dep>, will rebase after merge  [only if applicable]

### Blocked (needs attention)
- #<number> - <title> — <block reason>

### Merge Order Rationale
- #X before #Y: <reason>

### Merge Method
Detected: <squash|merge|rebase> (based on repo history)

ASK THE USER: “This is the proposed merge plan. Approve to proceed, or tell me what to adjust.”

DO NOT merge anything until the user explicitly approves. This gate is non-negotiable.

Phase 4: Merge Execution

Triggered only after the user approves the merge plan.

9. Sequential Merge

For each PR in the computed merge order:

a. Pre-merge re-check:

gh pr view <number> --json statusCheckRollup,reviewDecision,mergeable

Verify CI is still passing, reviews are still approved, and PR is still mergeable. If any check fails, skip this PR and report why — do not merge stale PRs.

b. Merge:

gh pr merge <number> --<method> --delete-branch

Where <method> is the detected merge method from step 7.

c. Rebase downstream PRs:

If any queued PR had its baseRefName pointing at the just-merged branch:

gh pr edit <downstream-number> --base <default-branch>

The downstream PR’s CI will re-run automatically after the base change.

d. Continue to the next PR in the merge order.

10. Failure Handling

If a merge fails:

  • Stop the affected dependency chain — do not merge any PR that depends on the failed one
  • Continue with independent PRs — PRs with no dependency on the failed one can still merge
  • After all possible merges are attempted, present a final status report

11. Final Report

After all merges complete (or fail), present:

## Merge Results

### Successfully Merged
- #<number> - <title> ✓

### Failed
- #<number> - <title> — <failure reason>

### Skipped (dependency on failed PR)
- #<number> - <title> — blocked by #<failed-number>

### Still Blocked (from Phase 2)
- #<number> - <title> — <original block reason>

Key Design Decisions

Decision Choice Rationale
Single invocation One skill does discovery through merge User wants full automation, not multi-step manual process
Parallel validation Sub-agent per PR via Agent tool PRs validate independently on their own branches
Topo sort + overlap Dependency-aware merge ordering Handles stacked branches and minimizes rebase conflicts
Merge method auto-detect Inspect recent git history Consistent with existing project conventions
Batch blocker reporting One report at the end User wants a coherent picture, not interruptions
Approval gate before merge Present plan, wait for explicit approval Autonomous validation but human-approved merging

Safety Rails

  • Never force-push. If a rebase is needed, use gh pr edit --base to retarget the PR. Let CI re-run naturally.
  • Re-check before every merge. CI and review status can change between validation and merge. Always verify immediately before merging.
  • Stop the chain on failure. If PR #42 fails to merge, do not merge #45 that depends on it. Independent PRs can still proceed.
  • Skip draft PRs. Flag them as blocked — they are not ready for merge.
  • Delete branches after merge. Use --delete-branch to clean up merged branches consistently.
  • Detect cycles. If the dependency graph has a cycle, exclude those PRs and report the cycle rather than entering an infinite loop.

Common Mistakes

Mistake Fix
Merging without user approval The hard gate at step 8 is non-negotiable — always wait for explicit approval
Merging in wrong order Always follow the computed topological sort — never skip ahead
Force-pushing during rebase Use gh pr edit --base to retarget, never force-push
Ignoring draft PRs Draft PRs must be flagged as blocked, not validated and merged
Trusting stale CI results Always re-check statusCheckRollup and reviewDecision immediately before merging
Continuing a dependency chain after failure If a parent PR fails, all downstream PRs in that chain must be skipped
Merging PRs that are in a cycle Exclude cyclic PRs and report the cycle to the user
This post is licensed under CC BY 4.0 by the author.

Trending Tags