| name | ad-merge |
| description | Evaluate and merge a GitHub pull request. Four phases — preflight (`gh` auth + PR resolution), evaluate (CI / fresh-context review / linked task / unresolved comments / mergeability), decision (CI green = hard gate; others = warnings yielding to user), merge via `gh pr merge` with auto-detected mode (squash / rebase / merge) and `--delete-branch`. Helper posture — surfaces warnings, does not block on the senior engineer's judgment. Triggers on "merge this PR", "evaluate the PR", "is it mergeable", "gh pr merge", "/ad-merge". |
| summary | Evaluate and merge a GitHub pull request. Four phases — preflight, evaluate (CI / fresh-context review / linked task / unresolved comments / mergeability), decision (CI green = hard gate; others = warnings), merge with auto-detected mode + `--delete-branch`. |
| allowed-tools | Read, Bash, Grep |
/ad-merge
Implements ADR-0025. Evaluates a PR's mergeability and performs the merge via gh pr merge. CI green is the only hard gate; everything else surfaces as a warning the senior engineer decides on.
Step 0 — Confirm regime
Run when:
- A PR is open against the repo and the user wants to land it.
- The user asks "merge this PR", "evaluate the PR", "is it mergeable".
Route elsewhere when:
- No PR exists yet →
/ad-pr first.
- Commits are not on the branch yet →
/ad-commit first.
Release-only mode. /ad-release first invokes /ad-merge --release --preflight before opening its release PR. This preflight requires a repository that allows merge commits; reject a repository that permits only squash or rebase, because its tagged release commit would not remain an ancestor of the base branch. After the PR exists, /ad-release invokes /ad-merge --release <PR>; that mode forces --merge and never offers a merge-mode choice.
Phase 1 — Preflight
Resolve the target before probing: if the user passed a PR number / URL, preserve it; otherwise omit the optional argument. Then run the deterministic preflight from the consumer repository root:
node .claude/skills/ad-merge/scripts/gh-preflight.mjs merge [number-or-url]
If this skill was loaded from another base directory, substitute that base. Execute it; do not re-derive its probes in prose. Its JSON reports github (command, installed, authenticated), git (branch, upstream, aheadOfUpstream), baseBranch, pullRequest, pullRequestState (present / absent / unavailable), targetRepository, checks, mergeMethods, and structured errors. It performs read-only gh / git probes and never switches GitHub accounts. Before execution, consult the repository's binding docs: if they name an approved executable frontend wrapper, run AGENTIC_GH=<wrapper> node .claude/skills/ad-merge/scripts/gh-preflight.mjs merge [number-or-url]; otherwise run the command above. Never use gh auth switch.
Let <github-command> be the returned github.command. Use exactly <github-command> for every later GitHub read or write in this workflow; never substitute bare gh, which could select a different account than the preflight.
If github.installed is false, distinguish the failure before offering recovery: when github.command is not gh, surface: The configured wrapper <github-command> is unavailable. Restore the approved executable wrapper, then rerun this skill. Do not fall back to a different frontend. Otherwise surface the GitHub CLI install hint. Stop. If github.authenticated is false, surface: Run <github-command> auth login, then rerun this skill. Stop afterward (same soft-fail rule as ad-pr). Surface every errors entry; a failed probe is not a passing fact. If pullRequestState is unavailable, report that the PR probe failed and stop. Only when it is absent may you say: "No PR found for branch <branch>. Open one with /ad-pr first."
Phase 2 — Evaluate
The preflight already returned the deterministic checks, pullRequest, and targetRepository fields. Use those values rather than running gh pr checks / gh pr view again. Derive <base-owner>/<base-repo> from targetRepository, never from the consumer checkout. Run the remaining comments probe and report it alongside them:
<github-command> api repos/<base-owner>/<base-repo>/pulls/<num>/comments
Findings format (pass / warn / fail):
CI status: <pass | pending | fail> (gh pr checks)
Fresh-context review: <pass | warn — none found> (.agentic/reviews/ or gh pr reviews)
Linked task / ADR: <pass | warn — none> (commit messages + PR body scan)
Unresolved comments: <pass | warn — N unresolved>
Mergeability: <pass | dirty | blocked | behind> (gh pr view mergeStateStatus)
Fresh-context review check — scan for either:
- A file under
.agentic/reviews/* whose name references the PR's commit range or number.
- An entry in preflight
pullRequest.reviews with state: APPROVED from a reviewer (or state: COMMENTED with content).
Linked task / ADR — scan the PR body for task-NNNN, ADR-NNNN, spec-NNNN, #<issue>, Closes, Fixes. Scan local <base>..HEAD commit bodies only when targetRepository is the consumer repository and the local branch is pullRequest.headRefName; otherwise say that local history is not evidence for this PR.
Unresolved comments — count entries from gh api that lack a resolved flag or carry an in-progress thread state.
Phase 3 — Decision
Apply the bar:
- CI failing → Hard stop. Refuse to merge until CI is green, unless the user explicitly overrides ("merge anyway"). On override, log a loud warning that this is a deliberate CI-failing merge and the responsibility is the user's. Per ADR-0025 §3, even the hard gate yields to explicit user authorization, but the override is surfaced visibly.
- CI pending → Ask the user: wait for CI, or proceed anyway? Default = wait.
- CI green + warnings (no fresh-context review / no linked task / unresolved comments) → Surface each warning, ask the user to confirm the merge, proceed on confirm.
- All green → Proceed.
State the decision back to the user before Phase 4 so they can interject.
Phase 4 — Merge
Use mergeMethods from the preflight to detect the repo's allowed merge modes. Re-run the read-only preflight if the report is stale.
When invoked with --release --preflight, stop after this check: require mergeCommitAllowed: true and report whether the release PR may be opened. When invoked with --release <PR>, repeat the check immediately before merging. If merge commits are no longer allowed, stop; never substitute squash or rebase.
Decision tree:
- Release-only mode → use
<github-command> pr merge <num> --merge --delete-branch. Do not ask for a mode, and reject a request for --squash or --rebase.
- Exactly one mode allowed → use it.
- Multiple modes allowed → ask the user: "Repo allows squash / rebase / merge-commit. Pick one." Wait for their choice.
- None allowed (rare) → surface the policy error and stop.
Run the merge:
<github-command> pr merge <pullRequest.url> --squash
<github-command> pr merge <pullRequest.url> --squash --delete-branch
--delete-branch by default for feature branches (feat/*, fix/*, chore/*, docs/*, refactor/*). Skip --delete-branch if the source branch is a long-lived integration branch (e.g., cli, develop, release/*).
Capture and report the merge commit URL.
Output contract
The output is a merged PR. The skill returns:
- The merge commit URL.
- A one-line summary of what was merged (
<count> commits merged into <base> via <mode>).
- The list of warnings the user proceeded past (if any), for the audit trail.
Next
- After merge: pull the latest base locally (
git checkout <base> && git pull).
- If the merge surfaced a recurring drift (no fresh-context review on multiple PRs, no linked task on several merges):
/ad-drift to scan for systemic gaps, or update WORKFLOW.md §10 / §11 expectations.
- If the merge closed a task: confirm the task file's
Status: is done and the Notes log captures the merge commit URL.
- If the merge shipped a binding decision worth recording:
/ad-adr (three-criteria rule — hard to reverse, surprising without context, real trade-off).