# 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 -- `) 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//brief.md`; allowed-tools + `--permission-mode default` + `MCP_TOOL_TIMEOUT` env correct; single-line kickoff points at the brief. - Implement `InteractiveLaunchSpecService.BuildForMergeHelper(IReadOnlyList 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` 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).