# Ops Merge

> Autonomous salvage + PR merge pipeline. FIRST scans every repo in every org for orphan worktrees, feature branches without PRs, uncommitted/staged/stashed work, and unpushed commits — dispatches subagents to finish/PR all loose local work. THEN scans all open PRs, dispatches fixers for CI/conflicts/reviews, and merges. Use --main to also sync dev↔main branches. Use --no-salvage to skip Phase 0 (P…

- **Type:** Skill
- **Install:** `agentstack add skill-lifecycle-innovations-limited-claude-ops-ops-merge`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Lifecycle-Innovations-Limited](https://agentstack.voostack.com/s/lifecycle-innovations-limited)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Lifecycle-Innovations-Limited](https://github.com/Lifecycle-Innovations-Limited)
- **Source:** https://github.com/Lifecycle-Innovations-Limited/claude-ops/tree/main/claude-ops/skills/ops-merge
- **Website:** https://github.com/Lifecycle-Innovations-Limited/claude-ops/wiki

## Install

```sh
agentstack add skill-lifecycle-innovations-limited-claude-ops-ops-merge
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

## Runtime Context

Before executing, load:

1. **Preferences**: `cat ${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json` — read `owner`, `timezone`, project registry
2. **Daemon health**: `cat ${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json` — if `action_needed` set, surface to user
3. **Secrets**: GitHub token: env `$GITHUB_TOKEN` → Doppler MCP (`mcp__doppler__*`) → `doppler secrets get GITHUB_TOKEN --plain` → password manager

# OPS ► MERGE

## CLI/API Reference

### gh CLI (GitHub)

| Command                                                                                                                   | Usage                | Output                         |
| ------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------------------------ |
| `gh pr list --repo  --json number,title,state,headRefName,statusCheckRollup,reviewDecision,mergeable,isDraft` | List PRs with status | JSON array                     |
| `gh pr view  --repo  --json title,body,state,mergeable,reviews`                                                  | PR details           | JSON                           |
| `gh pr checks  --repo `                                                                                          | CI check status      | Check list                     |
| `gh pr merge  --repo  --squash --admin`                                                                          | Squash merge PR      | Merge result                   |
| `gh pr create --repo  --title "" --body "" --base dev`                                                        | Create PR            | PR URL                         |
| `gh run list --repo  --limit 5 --json conclusion,name,headBranch`                                                   | CI runs              | JSON array                     |
| `gh run view  --repo  --log-failed`                                                                             | Failed CI logs       | Log output                     |
| `gh run watch  --repo `                                                                                     | Stream CI run        | Live output (use with Monitor) |
| `gh api repos//pulls//comments --jq '.[].body'`                                                                  | PR review comments   | Comment text                   |

---

## Agent Teams support

If `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` is set, use **Agent Teams** for fixer agents (Phase 3). This enables:

- Steering fixers mid-flight if priorities change (e.g., a critical PR should be merged first)
- Fixers can report blockers and you can redirect them without waiting for completion
- Shared context: if fixer-A discovers a breaking change that affects fixer-B's PR, you can notify B

**Team setup** (only when flag is enabled, Phase 3):

```
TeamCreate("merge-fixers")
Agent(team_name="merge-fixers", name="fixer-[repo]", ...)
```

Use `SendMessage(to="fixer-my-api", content="PR #2958 was just merged — rebase your branch")` to coordinate.

If the flag is NOT set, fall back to standard parallel subagents with `isolation: "worktree"`.

## Pre-gathered PR data

```!
${CLAUDE_PLUGIN_ROOT}/bin/ops-merge-scan 2>/dev/null || echo '{"prs":[],"error":"merge-scan failed"}'
```

## Pre-gathered salvage data (orphan worktrees, branches without PRs, uncommitted/unpushed work)

```!
${CLAUDE_PLUGIN_ROOT}/bin/ops-merge-salvage-scan 2>/dev/null || echo '{"repos":[],"error":"salvage-scan failed"}'
```

## Your task

You are the **merge orchestrator**. Your job is to get every open PR across the owner's repos merged — fixing whatever blocks them first.

### Parse arguments

From `$ARGUMENTS`:

- `--main` → after all PRs merge to dev, also sync dev↔main for repos that have both branches
- `--repo ` → scope to one repo only (e.g., `--repo your-org/my-api`)
- `--dry-run` → report what would happen, don't dispatch agents or merge anything
- `--force` → skip the confirmation prompt before merging
- `--no-salvage` → skip Phase 0 (Salvage). Behaves like the legacy PR-only pipeline.
- `--salvage-only` → run Phase 0 only and stop. Useful for "find and finish all loose local work" without touching the existing PR queue yet.
- (empty) → process all repos: salvage local work first, then merge PRs to dev only

### Phase 0 — Salvage scan (run BEFORE the PR queue)

**Goal:** every repo in every org gets a clean slate before the PR merge pipeline runs. Find and finish every piece of local work that isn't already on `dev`/`main` and isn't already in an open PR — orphan worktrees, feature branches without PRs, uncommitted/staged/stashed changes, and unpushed commits.

**Skip this phase only if `--no-salvage` is set.**

Parse the JSON returned by `ops-merge-salvage-scan`. For each repo with `has_salvage: true`, classify each finding into one of:

| Finding                                                            | Classification          | Action                                                                                             |
| ------------------------------------------------------------------ | ----------------------- | -------------------------------------------------------------------------------------------------- |
| Worktree with `dirty_files > 0` or `unpushed_commits > 0`          | `worktree-incomplete`   | Dispatch salvager: finish work in the worktree, commit, push, open PR if missing                   |
| Worktree on a branch with `has_open_pr: false`                     | `worktree-orphan-pr`    | Dispatch salvager: confirm work is complete (lint/type/test gate), push if needed, open PR         |
| Local branch with `integrated: false` and `has_open_pr: false`     | `branch-no-pr`          | Dispatch salvager: review state, push if unpushed, open PR targeting integration branch            |
| Local branch with `integrated: true` and `has_open_pr: false`      | `branch-already-merged` | Surface to user → `[Delete local branch]` / `[Keep]` (NEVER auto-delete — Safety Rails)            |
| Main checkout: `dirty_files > 0` or `staged_files > 0` or stash >0 | `checkout-dirty`        | Surface to user → `[Stash & continue]` / `[Open salvage worktree]` / `[Skip]`. Never auto-discard. |
| Main checkout: `unpushed_commits > 0` on a non-integration branch  | `checkout-unpushed`     | Dispatch salvager: push the branch, open PR if missing                                             |

**Print the salvage queue:**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► MERGE — Phase 0: Salvage Queue
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

| Repo | Location | Branch | State | Classification | Action |
|------|----------|--------|-------|----------------|--------|
| my-api | .worktrees/feat-x | feat/x | 3 dirty, 2 unpushed | worktree-incomplete | salvage |
| my-app | (main checkout) | feat/y | 5 unpushed, no PR | checkout-unpushed | salvage |
| mise | refs/heads/old-experiment | old-experiment | integrated, no PR | branch-already-merged | confirm delete |
| ... | ... | ... | ... | ... | ... |

Salvageable: N  |  Needs user input: N  |  Clean: M
──────────────────────────────────────────────────────
```

If `--dry-run`, print the queue and stop here (skip Phase 1+).
If `--salvage-only`, run the salvage dispatch loop below, then stop (skip Phase 1+).

**Confirmation gate:** unless `--force`, use `AskUserQuestion` (max 4 options — Plugin Rule 1) to confirm before dispatching salvagers:

```
N pieces of loose local work found across M repos.

  [Salvage all N — dispatch agents]  [Let me pick which ones]  [Skip Phase 0 — go to PR queue]  [Abort]
```

**Dispatch salvager subagents** (max 5 concurrent, one repo per agent — never share the main checkout per CLAUDE.md worktree isolation rule). Use `subagent_type: "general-purpose"` for now (or a future `salvage-fixer` agent if one exists). Each salvager gets this brief:

```
Task: Finish and PR loose local work in 
Repo path: 
Findings:
  - :  — 
  - ...

Worktree isolation: if the work lives in an existing .worktrees/* dir, work IN that directory.
If the work lives only on a local branch (no worktree), create one:
  git -C  worktree add .worktrees/salvage- 

For each finding:
  1. cd into the worktree.
  2. Inspect state: `git status`, `git log ..HEAD --oneline`, `git diff --stat`.
  3. Read recent commit messages + any TODOs/HEREs in the diff. Decide whether the work is:
       (a) complete and just needs commit/push/PR — proceed
       (b) incomplete but obvious next step — finish it
       (c) ambiguous or risky → ABORT this finding and return it for human review.
  4. If finishing work: make the smallest correct commit. Quality gate locally
     (per-repo: type-check + lint + relevant tests).
  5. Commit with a clear message. NEVER use --no-verify unless a hook is genuinely
     broken and unrelated to your change.
  6. Push: `git push -u origin ` (or `--force-with-lease` if branch already remote).
  7. Open PR (only if has_open_pr=false in the brief):
       gh pr create --repo  --base  --head  \
         --title "" \
         --body "Salvaged by /ops:merge Phase 0. "
  8. Return structured JSON:
       {
         "repo": "...",
         "branch": "...",
         "status": "pr_opened" | "pushed_only" | "aborted_for_review" | "failed",
         "pr_number": ,
         "pr_url": "",
         "end_sha": "",
         "notes": "..."
       }

DO NOT call `gh pr merge` — newly opened PRs flow through Phase 1+ like any other PR.
DO NOT delete branches, worktrees, or stashes. Salvage = finish + PR, never destroy.
DO NOT touch files outside the assigned worktree.
```

**After all salvagers return:** re-run `ops-merge-scan` so the freshly opened PRs join the Phase 1 queue. Surface any `aborted_for_review` findings to the user with a brief explanation and `[Open in editor]` / `[Skip]` options.

**Branches classified `branch-already-merged` and `checkout-dirty`** are surfaced one-by-one to the user via `AskUserQuestion` (never auto-handled — see Safety Rails).

### Phase 1 — Classify the PR queue

Parse the pre-gathered JSON. For each PR, it's already classified as one of:

| Classification          | Meaning                                                           | Action                                      |
| ----------------------- | ----------------------------------------------------------------- | ------------------------------------------- |
| `ready`                 | CI green, approved, no conflicts                                  | Merge immediately                           |
| `needs-rebase`          | `mergeable: CONFLICTING`                                          | Dispatch fixer: rebase on base branch       |
| `needs-ci-fix`          | CI failures in `statusCheckRollup`                                | Dispatch fixer: investigate logs, fix, push |
| `needs-review-response` | `reviewDecision: CHANGES_REQUESTED`                               | Dispatch fixer: resolve comments            |
| `blocked`               | `mergeStateStatus: BLOCKED` (branch protection, required reviews) | Note why, skip                              |
| `draft`                 | `isDraft: true`                                                   | Skip — not ready for merge                  |

Print the queue:

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► MERGE — PR Queue
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

| Repo | PR | Title | Status | Action |
|------|----|-------|--------|--------|
| my-api | #2958 | fix(migration) | ready | merge |
| my-app | #4456 | feat(apple-04) | needs-ci-fix | dispatch fixer |
| ... | ... | ... | ... | ... |

Ready: N  |  Fix needed: N  |  Blocked: N  |  Draft: N
──────────────────────────────────────────────────────
```

If `--dry-run`, stop here. Print the queue and exit.

### Phase 2 — Confirm and merge ready PRs

Unless `--force` was passed, use `AskUserQuestion` to confirm before merging:

```
Ready to merge N PRs:
  [repo]#[number] — [title] → [base]
  [repo]#[number] — [title] → [base]

  [Merge all N now]  [Let me pick which ones]  [Dry run — don't merge]
```

If user picks "Let me pick", show each PR with `[Merge]` / `[Skip]` options via `AskUserQuestion`.

For each confirmed PR:

1. Verify CI is still green: `gh pr checks  --repo `
2. If green: `gh pr merge  --repo  --squash --admin`
3. Report: `✓ Merged # to `

### Phase 3 — Dispatch fixers for PRs that need work

**HARD RULE: Fixers do NOT merge. The orchestrator merges in Phase 5 after independent verification.**

Background: on 2026-05-11, sixteen parallel `pr-ci-fixer` spawns fabricated complete transcripts including conflict resolutions, CI polling sequences, and `gh pr merge` admin outputs with invented merge SHAs. Zero merges actually executed. The fix is structural: fixers no longer have merge authority OR self-reporting authority on merge state. They push, the orchestrator verifies the push and merges.

For PRs classified as `needs-rebase`, `needs-ci-fix`, or `needs-review-response`:

**Dispatch subagents** (max 5 concurrent, one repo per agent, subagent_type: `pr-ci-fixer`).

Each fixer agent gets this brief:

```
Task: Fix PR # in  ()
Repo path: 
Branch: 
Base: 

Pre-work: capture START_SHA = current `git ls-remote origin `.

For needs-rebase:
  1. Worktree: `git worktree add .worktrees/fix- ` inside .
  2. `git fetch origin && git rebase origin/`.
  3. On conflict: resolve thoughtfully (preserve PR intent for source files,
     `--theirs` only for lockfiles). If unresolvable, ABORT and return structured failure.
  4. Quality gate locally (per repo): type-check + lint + relevant tests.
  5. `git push --force-with-lease origin `.

For needs-ci-fix:
  1. Worktree as above.
  2. Pull failed-check logs, diagnose, apply surgical fix.
  3. Quality gate locally.
  4. Commit + `git push --force-with-lease origin ` (no `--no-verify`
     unless a hook is genuinely broken and unrelated to your change).

After push (every classification):
  5. Capture END_SHA = `git rev-parse HEAD`.
  6. Confirm remote: `git ls-remote origin ` MUST return END_SHA.
     If mismatch, retry once; if still mismatched, return failure.
  7. Verify CI: poll `gh pr view  --repo  --json statusCheckRollup`
     until all required checks are non-pending. Capture the literal JSON output.
  8. Clean up worktree: `git worktree remove .worktrees/fix- --force`.
  9. Return the structured JSON schema defined in the pr-ci-fixer agent contract.

DO NOT call `gh pr merge` under any circumstances. Your job ends at "CI is green
on the pushed SHA." The orchestrator will independently verify and merge.

DO NOT file a tracking issue for a CI failure you could not fix — in ANY repo.
No `gh issue create`, no `github.rest.issues.create`, no cross-repo issue. If you
cannot get CI green, return the structured failure in your JSON result and STOP;
the orchestrator decides what happens next. (Rationale: emergent issue-filing on
unfixable CI produced 17+ duplicate `[TEAM] CI failure on PR #N` issues — one per
failing check, zero dedup, cross-posted into the wrong repo. A fixer's only valid
outputs are a pushed green SHA or a structured failure report — never an issue.)
If a tracking issue is ever genuinely wanted, that is the orchestrator's call and
it MUST first `gh issue list --search "PR # in:title" --state open` to dedup.
```

Use `model: "haiku"` for fixer agents (matches agent definition default).

### Phase 4 — Resolve surfaced conflicts

For each PR returned with `status: "conflict"` from a fixer agent:

1. Display the conflict summary:

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► MERGE — Conflict in #
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 Branch:  → 
 Conflicting files:
   - 
   - 

──────────────────────────────────────────────────────
```

2. Use AskUserQuestion (max 4 options — CLAUDE.md Rule 1):

```
[A

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Lifecycle-Innovations-Limited](https://github.com/Lifecycle-Innovations-Limited)
- **Source:** [Lifecycle-Innovations-Limited/claude-ops](https://github.com/Lifecycle-Innovations-Limited/claude-ops)
- **License:** MIT
- **Homepage:** https://github.com/Lifecycle-Innovations-Limited/claude-ops/wiki

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-lifecycle-innovations-limited-claude-ops-ops-merge
- Seller: https://agentstack.voostack.com/s/lifecycle-innovations-limited
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
