# Merge Helper — Implementation Plan

Spec: `docs/superpowers/specs/2026-07-24-merge-helper-design.md`
Approach: subagent-driven (one subagent per task, `sonnet`, TDD, stage files by path — never `git add -A`). Build with `-c Release` per-csproj (a running Worker locks `Debug`). Commit per task, Conventional Commits.

---

## Phase A — Worker MCP conflict tools

Independently useful; merges first. All in `src/ClaudeDo.Worker/External/ExternalMcpService.cs` + tests in `tests/ClaudeDo.Worker.Tests/`.

### A1 — Verify engine surface (spike, no commit)
Read `TaskMergeService.MergeAsync` / `ContinueMergeAsync` / `AbortMergeAsync` and the hub conflict flow (`WorkerHub.StartConflictMerge`/`ContinueConflictMerge`/`AbortConflictMerge`). Pin down:
- exact `ContinueMergeAsync` / `AbortMergeAsync` signatures and how in-progress-merge state is located (repo + target branch from task/list, not shared hub state);
- how the childless approve path (`ApproveAndMergeAsync`) threads `leaveConflictsInTree`.
Record findings in the task notes; feeds A2/A3.

### A2 — `leaveConflictsInTree` on review_task / merge_task
- TDD: tests in `Worker.Tests` (real git) — clean merge → Done; conflict + flag → `conflict_in_tree`, markers present, task stays `WaitingForReview`, `repoPath` returned.
- Add optional param `leaveConflictsInTree = false` to `MergeTask` and `ReviewTask` (approve branch). When true, call the `leaveConflictsInTree:true` engine path and map the conflict result to `{ mergeStatus/merged, conflicts, repoPath }`.
- Keep default behaviour (abort-on-conflict) byte-identical when the flag is absent/false.
- Commit: `feat(worker): let review_task/merge_task leave conflicts in tree via MCP`

### A3 — `continue_merge` + `abort_merge` MCP tools
- TDD: continue after on-disk resolution → committed, task Done, worktree merged; continue with markers remaining → returns conflicts; abort → markers gone, task `WaitingForReview`; both on no-active-merge → clean MCP error; `TaskUpdated` fired.
- Add `[McpServerTool] continue_merge(taskId)` → `ContinueMergeAsync`; `abort_merge(taskId)` → `AbortMergeAsync`. Locate the merge from the task's repo/target. Emit `TaskUpdated`.
- **Route both single-task and orchestrated (parent/children) in-progress merges** where locatable from the task (per A1 findings): detect the kind and call the matching engine continue/abort (`TaskMergeService` vs `PlanningMergeOrchestrator.Continue/Abort`). If the orchestrated path can't be located without hub UI state, leave it to the manual fallback (documented in the B1 prompt) and note the gap in `docs/open.md`.
- Commit: `feat(worker): add continue_merge and abort_merge MCP tools`

---

## Phase B — Worker launch for the merge-helper session

### B1 — Prompt templates
- Add `PromptKind.MergeHelper` + `PromptKind.MergeHelperInitial` to `ClaudeDo.Data/PromptFiles.cs` (file names `merge-helper-system.md` / `merge-helper-initial.md`, built-in `DefaultFor`, `Render` tokens for the initial brief).
- System prompt encodes §7 behaviour (per-status algorithm, ask-on-uncertainty, summary format). Merge-state rule: **prefer MCP tools whenever they apply**; hand-merge (Edit + `git commit -- <paths>`) is an accepted fallback only for merges the MCP tools can't reach (§5.3), never a shortcut around them.
- Initial brief renders a task table `{id,title,status,list,repo}` + scope label.
- TDD: `PromptFiles` tests — kinds resolve, defaults non-empty, `Render` substitutes brief tokens.
- Commit: `feat(data): add merge-helper prompt templates`

### B2 — `BuildForMergeHelper` launch spec
- TDD (`Worker.Tests`): distinct-repo `--add-dir` set computed from selected tasks; correct cwd per scope (per-list repo vs first repo global); brief file written to `~/.todo-app/merge-helper-sessions/<guid>/brief.md`; allowed-tools + `--permission-mode default` + `MCP_TOOL_TIMEOUT` env correct; single-line kickoff points at the brief.
- Implement `InteractiveLaunchSpecService.BuildForMergeHelper(IReadOnlyList<string> taskIds, MergeHelperScope scope, ct)`. Reuse the planning brief-file/kickoff pattern.
- Commit: `feat(worker): build merge-helper interactive launch spec`

### B3 — Hub endpoint + client method
- `WorkerHub.GetMergeHelperLaunchSpec(string[] taskIds, string? listId)`; `IWorkerClient.GetMergeHelperLaunchSpecAsync(...)` + `WorkerClient` impl.
- Update hand-rolled `IWorkerClient` fakes in **both** test projects (see gotcha memory).
- Commit: `feat(worker): expose merge-helper launch spec over the hub`

---

## Phase C — UI

### C1 — Selection dialog (View + VM)
- New `MergeHelperSelectionViewModel` + `MergeHelperSelectionDialog.axaml` (compiled bindings, `TaskCompletionSource<T>` pattern). Checkbox rows (title, status badge, list/repo), grouping in global mode, default ticks per §4, select-all/none, confirm disabled when empty.
- Candidates via existing `list_tasks`/worker client; filter client-side.
- TDD (`Ui.Tests`): default-tick logic, empty→confirm-disabled, returns ordered selected IDs + list mapping.
- Commit: `feat(ui): add merge-helper task selection dialog`

### C2 — Entry points + event plumbing
- Per-list context-menu item **"Let Claude handle it"** in `ListsIslandView.axaml` (user-list rows) + one global entry in the footer. Bind to `LetClaudeHandleCommand` on `ListsIslandViewModel` (param = `ListNavItemViewModel` or a global sentinel).
- VM raises `LetClaudeHandleRequested(MergeHelperScope)`; `IslandsShellViewModel` forwards to Mission Control.
- Commit: `feat(ui): add "Let Claude handle it" entry points`

### C3 — Mission Control wiring
- `MissionControlViewModel.OpenMergeHelperConPtySessionAsync(scope)`: open selection dialog → on confirm, `GetMergeHelperLaunchSpecAsync` → wrap in `TerminalLaunchDescriptor` → new `ConPtyPaneViewModel` (never deduped) → add to `ConPtySessions`/`Panes`.
- Commit: `feat(ui): open merge-helper ConPTY tile from selection`

---

## Verify (per task + at the end)
- Read each subagent diff; build the touched csproj `-c Release`; run the relevant test project.
- `locales/en.json` + `de.json` parity for any new UI strings (Localization.Tests enforces it).
- Flag visual-verification gaps (dialog layout, tile) for the user — never claim UI works without a run.
- End-to-end ConPTY smoke (real Claude) is a manual item in `docs/open.md`.

## Commit docs first
`docs(merge-helper): spec + implementation plan` (this file + the spec).
