5.7 KiB
5.7 KiB
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/AbortMergeAsyncsignatures and how in-progress-merge state is located (repo + target branch from task/list, not shared hub state); - how the childless approve path (
ApproveAndMergeAsync) threadsleaveConflictsInTree. 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 staysWaitingForReview,repoPathreturned. - Add optional param
leaveConflictsInTree = falsetoMergeTaskandReviewTask(approve branch). When true, call theleaveConflictsInTree:trueengine 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;TaskUpdatedfired. - Add
[McpServerTool] continue_merge(taskId)→ContinueMergeAsync;abort_merge(taskId)→AbortMergeAsync. Locate the merge from the task's repo/target. EmitTaskUpdated. - 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.MergeHelperInitialtoClaudeDo.Data/PromptFiles.cs(file namesmerge-helper-system.md/merge-helper-initial.md, built-inDefaultFor,Rendertokens for the initial brief). - System prompt encodes §7 behaviour (per-status algorithm, ask-on-uncertainty, MCP-only merge state, summary format).
- Initial brief renders a task table
{id,title,status,list,repo}+ scope label. - TDD:
PromptFilestests — kinds resolve, defaults non-empty,Rendersubstitutes brief tokens. - Commit:
feat(data): add merge-helper prompt templates
B2 — BuildForMergeHelper launch spec
- TDD (
Worker.Tests): distinct-repo--add-dirset 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_TIMEOUTenv 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(...)+WorkerClientimpl.- Update hand-rolled
IWorkerClientfakes 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 toLetClaudeHandleCommandonListsIslandViewModel(param =ListNavItemViewModelor a global sentinel). - VM raises
LetClaudeHandleRequested(MergeHelperScope);IslandsShellViewModelforwards 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 inTerminalLaunchDescriptor→ newConPtyPaneViewModel(never deduped) → add toConPtySessions/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.jsonparity 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).