Skip to content
Back to skills

Merge Expert

ASecurity

Methodology for PR-aware delivery — probe branch protection, open/gate PRs, land via GitHub when required, or run the direct multi-worktree merge campaign.

  • 19 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 28, 2026
ai-agentsgobashgit

Works with

  • mcp

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add GobbyAI/gobby --skill merge-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Merge Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Merge Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gobbyai-merge-expert/badge)](https://www.skillsdirectory.com/skills/gobbyai-merge-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: merge-expert
description: Methodology for PR-aware delivery — probe branch protection, open/gate PRs, land via GitHub when required, or run the direct multi-worktree merge campaign.
version: "1.0.0"
category: core
internal: true
triggers: merge campaign, multi-worktree merge, merge orchestrator, surgical merge
metadata:
  gobby:
    audience: all
    depth: 0
---

# merge-expert — Gobby PR/Merge Delivery Methodology

> Internal methodology skill; loaded with `get_skill(name="merge-expert")` by
> the `merge-orchestrator` agent before it handles either the `pr` or `merge`
> stage.

Use this skill to drive bounded delivery. In the `pr` stage, decide whether
the target branch requires a GitHub PR, open or update the PR, gate on CI and
external review, and record the canonical PR verdict. In the `merge` stage,
land approved PRs via GitHub or run the existing direct merge campaign for
unprotected branches.

## Inputs

A campaign starts with a single coordinating task. Read its description,
labels, and `task_artifacts` first. Useful inputs:

- A list of worktree IDs (or "all unmerged in this project") from the
  campaign task description.
- The target branch (default `main` unless the task overrides it).
- A verification command per worktree, if the task specifies one. Otherwise
  default to whatever the project conventionally runs (`uv run pytest`, `npm
  test`, etc.); if you are unsure, skip the gate and note that in the report.
- Existing delivery state from `gobby-tasks-ops:get_delivery_state`.
- `yolo` flag from task state. Under yolo, skip PR only when the branch probe
  says no protection exists; never bypass protected-branch review gates with
  bot self-approval.

## PR Stage

For each delivery unit:

1. Call `gobby-merge:probe_branch_protection` for the target branch and
   persist the result with `gobby-tasks-ops:record_pr_state`.
2. If `requires_pr=false`, record `pr_required=false`, `pr_state=direct_merge`,
   submit the `pr` stage for review, then call
   `record_pr_verdict(verdict="approve", ...)`. The merge stage will run the
   direct merge-worker path.
3. If `requires_pr=true`, push the source branch with
   `gobby-worktrees:push_branch`, create or reuse a GitHub PR, and call
   `record_pr_opened`. Post holistic QA notes to the PR with
   `github:create_pull_request_review(event="COMMENT")`; never use APPROVE.
4. Poll `github:get_pull_request_status`, `github:get_pull_request`, and
   `github:get_pull_request_reviews`. Persist each gate snapshot with
   `record_pr_state`. If waiting on CI or humans, end the agent run; the
   dispatcher heartbeat will resume later.
5. If the PR becomes conflicting, first try
   `github:update_pull_request_branch`. If that cannot update the branch,
   perform one local AI-assisted update using `gobby-merge:merge_start`,
   `merge_resolve`, and `merge_apply`, then `gobby-worktrees:push_branch` with
   `force_with_lease=true`. Record `local_update_attempts=1`. A second conflict
   event escalates.
6. When CI, review, and mergeability are ready, call
   `record_pr_verdict(verdict="approve", findings=...)`. Use
   `request_changes` for concrete blockers and `needs_discussion` only when a
   human decision is required.

## Methodology

Run the direct merge campaign in five phases. Do not skip phases; do not
collapse them into a single LLM step.

### 1. Survey

Build a complete picture before deciding anything. Call:

- `gobby-merge:analyze_merge_landscape` — list every unmerged worktree with
  its branch, base, divergence count, files touched, last commit time, and
  originating task ref.
- `gobby-merge:predict_conflicts` — pairwise + per-target `git merge-tree`
  simulation. Lists which worktrees will conflict with each other and which
  will conflict with the target branch.
- `gobby-merge:inspect_merge_state` for any worktree whose `merge_state` is
  non-null in the landscape — there may be orphaned `MERGE_HEAD` /
  `CHERRY_PICK_HEAD` / rebase state to recover before scheduling new work.

Refuse to plan if the landscape is empty (`worktrees == []`). Close the
campaign task with `reason=already_implemented` instead.

### 2. Plan

Produce an ordered merge plan. The ordering rubric, in priority order:

1. **Recover orphans first.** Any worktree returned by `inspect_merge_state`
   with `state != "clean"` and `can_resume=true` must be recovered before
   scheduling fresh merges in the same worktree. If it exposes an
   `active_resolution_id` with pending conflicts, continue that active
   resolution with `merge_status` plus a `merge-worker`; do not abort solely
   because a previous campaign recorded no progress.
2. **Toposort by task dependencies.** If the campaign worktrees correspond to
   tasks with `blocked_by` relations, predecessors merge before dependents.
3. **Fewest predicted conflicts against the target.** Worktrees with `clean:
   true` from `predict_conflicts.target_predictions` go first — they merge
   without resolution and reduce the conflict surface for the rest.
4. **Smallest diff next.** Among the still-conflicting worktrees, prefer the
   smaller `divergence_commits` × `len(files_touched)` because a smaller
   symmetric change is easier for the AI resolver and easier to verify.
5. **Freshness as a tiebreaker.** Newer `last_commit_at` ahead of older when
   everything else is equal — newer code reflects current intent better.

For each step, decide an action:

- `merge` — full `merge-worker` flow (default). Use whenever the worktree's
  branch should land in the target wholesale.
- `cherry-pick` — when the worktree contains commits that belong on a
  different base than its current branch (rare, but happens when a worktree
  was forked from the wrong base). Drives `cherry_pick_into_worktree`.
- `merge_subset` — when only specific paths from the worktree should land
  (the rest is exploratory or stale). Drives the `merge_subset` tool.
- `abort` — when `inspect_merge_state` shows orphaned state that cannot be
  resumed cleanly, has no active resolution to continue, or when the worktree
  is purely abandoned work. Drives `merge_abort` then `delete_worktree`.

Emit the plan as a structured list into a session variable
`merge_plan` so subsequent phases can iterate it:

```yaml
- step_no: 1
  worktree_id: wt-abc
  action: merge
  expected_conflicts: []
  verify_command: "uv run pytest tests/storage/"
- step_no: 2
  worktree_id: wt-def
  action: cherry-pick
  commits: ["a1b2c3d"]
  verify_command: "uv run pytest tests/clones/"
```

If the plan is empty after filtering aborts, close the campaign task with
`reason=already_implemented`.

### 3. Execute

Iterate the plan in order. For each step:

1. **Pre-flight.** Re-run `inspect_merge_state` on the target worktree. If
   it is dirty unexpectedly, abort the step and replan.
2. **Dispatch.** Call `gobby-agents:spawn_agent` (or `dispatch_batch` for a
   group of independent `clean: true` steps) with `agent=merge-worker` and
   pass the `worktree_id`, `target_branch`, and source-branch info as
   spawn-time variables. `source_branch` is always the worktree branch; never
   use the target branch as source to "pull latest target" into the worktree.
   If the task is a TDD red-phase test-writing task whose entry criteria,
   review notes, or approval notes say the new tests are expected to fail before
   implementation, do not use that expected-failing pytest command as a green
   `verify_command`. The red pytest output is validation evidence already
   captured by QA. For these tasks, omit `verify_command`, merge cleanly, and
   cite the QA red evidence plus clean merge state in the report.
   For a clean direct merge, instruct the worker to call
   `gobby-worktrees:merge_worktree`; if the plan has a `verify_command`, include
   it in the worker prompt and require `gobby-merge:verify_in_worktree` before
   the worker records success. The worker records the `merge_sha` returned by
   `merge_worktree` as the final target branch SHA only after verification
   passes, or immediately when no verification command was supplied. Do not ask
   a clean worker to use `merge_start`/`merge_apply` as the landing path;
   `merge_apply` resolves the source/worktree branch and is not the final target
   merge SHA. The worker handles the actual merge + AI resolution. When dispatching a
   worker to continue an active resolution, keep the prompt inside the
   merge-worker MCP surface: tell it to call `merge_status`, then
   resolve exactly one pending conflict_id at a time with
   `merge_resolve(conflict_id=..., use_ai=true)`, call `merge_status` again,
   and only then continue to the next pending conflict. Once `merge_apply`
   completes, tell it to call `merge_worktree` exactly once and record
   `merge_worktree`'s `merge_sha`; never record the `merge_apply` SHA as the
   final delivery SHA. Tell it not to issue multiple `merge_resolve` calls in
   the same assistant turn or parallel tool batch. If retries/timeouts are
   exhausted, tell it to call
   `gobby-tasks-ops:record_merge_result` with `failure_reason` including the
   unresolved ids/files, then terminate through its normal `end_agent_run`
   step. Never ask the worker to use Read/Bash or synthesize manual `resolved_content`
   from file inspection.
3. **Wait for the worker.** Workers terminate themselves via `end_agent_run`;
   call `gobby-agents:wait_for_agent(run_id=...)` to wait for completion, then
   call `gobby-agents:get_agent_result` only if you need to re-read the final
   report. Do not use Bash sleep loops, tmux polling loops, or provider Monitor
   for worker waits.
4. **Verify.** Prefer worker-side verification: pass the step's
   `verify_command` to the merge-worker and require it to run
   `gobby-merge:verify_in_worktree` before `record_merge_result`. Treat exit
   code 0 as the gate. If a legacy worker did not run verification, the
   orchestrator must run the same command with `verify_in_worktree` before
   reporting success. Capture stdout/stderr in the campaign report on failure.
   Preserve required environment guards in verify commands, including
   `GOBBY_TEST_PROTECT=1` prefixes and `env GOBBY_TEST_PROTECT=1` wrappers,
   when dispatching workers or rerunning verification.
5. **Decide retry vs. escalate.**
   - Worker reports success + verify passes → mark step complete, move on.
   - Worker reports success + verify fails → re-dispatch the worker with the
     verify failure context once. If it still fails, escalate the step.
   - Worker reports failure with `needs_human_review=true` → escalate the
     step (or under `yolo`, mark the step as `force-advanced` and continue).
   - Worker reports failure with conflicts the AI couldn't resolve →
     `needs_human:merge_resolution_failed:<file_list>`.

Append a `task_lifecycle_event` per step (`merge_step_started`,
`merge_step_completed`, `merge_step_failed`) so the audit trail is complete.

### 4. Recover

Whenever `inspect_merge_state` shows orphaned state during pre-flight, treat
it as a recovery action before any new dispatch:

- `state == "merging"` with conflicts and an `active_resolution_id` → call
  `merge_status`, then dispatch a `merge-worker` to continue the pending
  conflicts in that active resolution. The no-progress redispatch cap applies
  only after a worker from the current orchestrator run completes and the next
  `merge_status` is unchanged.
- `state == "merging"` with conflicts but no resumable active resolution →
  call `merge_abort`, then re-survey before scheduling a fresh `merge` step.
- `state == "cherry-picking"` with conflicts → call
  `gobby-merge:merge_resolve` on each conflict, then **dispatch a
  `merge-worker` to run `git cherry-pick --continue`** in the worktree (no
  MCP wrapper today; worker delegation is required because the orchestrator
  is read+dispatch only).
- `state == "rebasing"` → escalate. The orchestrator does not unwind in-
  progress rebases; that is a human decision.

Recovery actions land in the campaign report under `recoveries:`.

### 5. Verify

Per-step verification is in §3. After all steps complete, run a campaign-
level sanity check:

- `gobby-merge:analyze_merge_landscape` again. Every worktree the plan was
  meant to merge should now be `status=merged` (or absent from the list).
- `gobby-worktrees:list_worktrees` filtered to `status=active` should show
  only worktrees the campaign explicitly skipped.
- For each merged worktree that the campaign successfully landed, the target
  branch's `git log` should include the merge commit (`gobby-merge` writes
  the merge commit; verify via `verify_in_worktree` running `git log
  --grep`).

If any of these post-conditions fail, do not approve the campaign — report
what is missing.

## Output

Write a campaign report and pass its reference to
`gobby-tasks-ops:record_merge_result`. Required JSON shape:

```json
{
  "campaign_id": "<task ref>",
  "target_branch": "main",
  "plan": [ /* the merge_plan from phase 2 */ ],
  "executed_steps": [
    {
      "step_no": 1,
      "worktree_id": "wt-abc",
      "action": "merge",
      "outcome": "merged",
      "verify_exit_code": 0,
      "merge_commit": "<sha>",
      "duration_seconds": 47
    }
  ],
  "recoveries": [],
  "failures": [],
  "unresolved_worktrees": [],
  "summary": "<one-paragraph human summary>"
}
```

Then transition the campaign task:

- All steps merged + verified -> `gobby-tasks-ops:record_merge_result` with
  `merge_sha` and `report_ref`.
- Any step ended in `needs_human:` -> `escalate_task` with the failure list
  in the reason.
- Unresolved merge failures -> `gobby-tasks-ops:record_merge_result` with
  `failure_reason` and `report_ref`.

## Boundaries

- Do **not** call `merge_resolve` directly. That is `merge-worker`'s job;
  the orchestrator only routes except for the single allowed local PR update
  fallback after `github:update_pull_request_branch` fails.
- Do **not** spawn agents deeper than 5 levels — the `child_session_manager`
  enforces this, but plan around it: workers cannot spawn workers.
- Do **not** `close_task` on the campaign task. Use
  `gobby-tasks-ops:record_merge_result` or `escalate_task`.
- Do **not** edit code. The orchestrator is read + dispatch only.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.