Use when merging parallel worktrees back together after parallel implementation
Merge parallel worktrees into unified branch after parallel implementation.
MUST:
Skipping steps = lost features. Rushing = broken integrations. Undocumented decisions = confusion.
When dispatching a conflict resolution subagent:
resolving-merge-conflicts via the Skill toolIf you catch yourself resolving a conflict without having loaded the skill: STOP. Dispatch a subagent that loads it.
| Input | Required | Description |
|---|---|---|
base_branch |
Yes | Branch all worktrees branched from |
worktrees |
Yes | List: worktree paths, purposes, dependencies |
interface_contracts |
Yes | Path to implementation plan defining contracts |
test_command |
No | Defaults to project standard |
| Output | Type | Description |
|---|---|---|
unified_branch |
Git branch | All worktree changes merged |
merge_log |
Inline | Decision trail for each conflict |
verification_report |
Inline | Test results and contract status |
If NO to any: STOP and address before proceeding.
Build dependency graph:
| Round | Criteria | Example |
|---|---|---|
| 1 | No dependencies (foundations) | setup-worktree |
| 2 | Depends only on Round 1 | api-worktree, ui-worktree |
| N | Depends only on prior rounds | integration-worktree |
Create merge plan:
## Merge Order
### Round 1 (no dependencies)
- [ ] setup-worktree -> base-branch
### Round 2 (depends on Round 1)
- [ ] api-worktree -> base-branch (parallel)
- [ ] ui-worktree -> base-branch (parallel)
### Round 3 (depends on Round 2)
- [ ] integration-worktree -> base-branch
Dispatch: /merge-worktree-execute
Dispatch: /merge-worktree-resolve
Dispatch: /merge-worktree-verify
| Pattern | Scenario | Resolution |
|---|---|---|
| Same Interface | Both implemented a shared interface method | Check contract for expected behavior. Choose contract-compliant version. If both match, synthesize best parts. If neither matches, fix to match. |
| Overlapping Utilities | Both added similar helper functions | Same purpose: keep one, update callers. Different purposes: rename to clarify, keep both. |
| Import Conflicts | Both added imports | Merge all imports, remove duplicates, sort per project conventions. |
| Test Conflicts | Both added tests | Keep ALL tests from both. Ensure no duplicate test names. Verify no conflicting shared fixtures. |
| Error | Response |
|---|---|
| Uncommitted changes in worktree | Do NOT stash by reflex. Follow the procedure below. |
| Tests fail after merge | STOP. Do NOT proceed to next round. Invoke systematic-debugging. Fix. Retest. Only continue when passing. |
| Interface contract violation | CRITICAL: "Contract violation detected. Contract: [spec]. Expected: [X]. Actual: [Y]. Location: [file:line]. MUST fix before merge proceeds." |
Show what is actually at risk before offering any option:
git -C [worktree-path] status --porcelain -uall
git -C [worktree-path] diff # Unstaged
git -C [worktree-path] diff --staged # Staged
To inspect committed content for comparison, read it; do not mutate the tree:
git show HEAD:<path>
Then ask via AskUserQuestion, in this order — least destructive first:
Worktree [path] has uncommitted changes. / Effect of each option below / Recoverable: varies by option, stated per optionCommit them — nothing is discarded; message [suggested]. Recoverable.Abort for manual handling — nothing is touched. Recoverable.Set aside specific files — name the EXACT paths; never ., never a bare
directory. Scope is limited to the paths you name.Stash the whole tree — LAST RESORT. git stash is TREE-WIDE: it captures
every uncommitted change in the checkout, not only the files being merged. In
a checkout shared with another agent it will take work the operator did not
author and cannot see. Recovery is git stash list then git stash pop — but
a pop CONFLICT can leave the tree in a partial state, half-applied and with
the stash entry still present.If the operator chooses option 3 or 4, VERIFY afterwards — a stash round-trip damages staged, modified, and untracked files alike, and it fails silently:
files=$(git status --porcelain -uall) || { echo "sweep failed: git status errored"; exit 1; }
printf '%s\n' "$files" | grep -v '^D \|^.D' | sed 's/^...//;s/.* -> //' |
while IFS= read -r f; do
case "$f" in *__init__.py|*.gitkeep|*/py.typed) continue;; esac
[ -f "$f" ] && [ ! -s "$f" ] && echo "TRUNCATED: $f"
done
echo "sweep complete"
Any name it prints that you did not deliberately empty is a truncation. Output of
only sweep complete is clean. git status is checked explicitly: if it errors,
the sweep prints sweep failed and exits 1 rather than silently reporting an
empty result as clean.
If merge goes wrong after commit:
Prefer the non-destructive reset. It rewinds the commit and KEEPS the working tree:
# Identify pre-merge commit
git log --oneline -5
# Reset to before merge (preserve working tree)
git reset --soft HEAD~1
# Re-attempt with lessons learned
Before proposing it, enumerate exactly what it destroys:
git status --porcelain -uall
Then ask via AskUserQuestion, never as prose:
Running: git reset --hard [pre-merge-commit-sha] / Effect: discards the uncommitted changes listed above and every commit after [pre-merge-commit-sha] / Recoverable: commits via reflog; uncommitted changes NOT recoverableRun it (uncommitted work destroyed) and Cancel (keep the tree, fix
forward).Run it only after an explicit confirmation. Never run it and describe the impact afterwards.