# Feature unification — phased plan Date: 2026-06-19 Design: `docs/superpowers/specs/2026-06-19-feature-unification-design.md` Six slices, sequenced cheapest/lowest-risk first. Each ends green (`dotnet build -c Release` + the touched test project) and is independently committable. Phases 0–1 are detailed here; 2–5 are scoped, and each gets its own `docs/superpowers/plans/2026-06-19-unify-.md` when picked up (per the 2026-06-05 layer-A/B/C convention). Build per-csproj (`-c Release`) — `.slnx` needs .NET 9 and a running Worker locks `Debug`. --- ## Phase 0 — Groundwork (Bucket C). No UX change. **0a. Delete the dead hunks conflict API (C1).** - Remove `TaskMergeService.GetConflictsAsync` + the `MergeConflicts`/`ConflictFileContent` records it returns (`src/ClaudeDo.Worker/Lifecycle/TaskMergeService.cs:250`) if unused elsewhere. - Remove `WorkerHub.GetMergeConflicts` (`src/ClaudeDo.Worker/Hub/WorkerHub.cs:378`) + `MergeConflictsDto`/`ConflictFileDto`/`ConflictHunkDto` if unused. - Remove `WorkerClient`'s `"GetMergeConflicts"` invoke (`src/ClaudeDo.Ui/Services/WorkerClient.cs:276`) + the `IWorkerClient` member + every fake override (`tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`, `TasksIslandViewModelPlanningTests.cs`, others — grep `GetMergeConflicts`). - Delete `TaskMergeServiceTests.cs:672` `GetConflictsAsync_AfterConflictMerge_ReturnsOursAndTheirs`. - Verify with grep first: `GetConflictsAsync` and `GetMergeConflicts` have **no** callers outside this chain + tests. - Acceptance: Worker + Ui build; Worker.Tests + Ui.Tests green; `GetMergeConflictDocuments` path untouched. **0b. Single task-creation path (C2).** - Identify the path MCP `ExternalMcpService.AddTask` uses; expose a thin creation method (repository or a small `TaskCreationService`) that applies the same defaults (ListId, SortOrder, CreatedAt). - Re-point `TasksIslandViewModel.AddAsync` at it instead of `db.Tasks.Add` direct EF. - Acceptance: quick-add still works; one creation path; Ui.Tests + Worker.Tests green. **0c. Prune stale worktrees (C3).** - `git worktree list`; remove the orphaned `.claude/worktrees/*` entries (confirm each is unwanted with Mika before `git worktree remove`). - Acceptance: only intended worktrees remain; no tracked files change. > C4 (naming alignment) intentionally NOT in this phase — see design. --- ## Phase 1 — DialogService (B3–B5). Low–medium. **Goal:** one `IDialogService` replaces the scattered `Show*` Func seams and the duplicate open-commands. - New `IDialogService` (Ui/Services) with typed methods: `OpenListSettings(ListNavItemViewModel)`, `OpenRepoImport()`, `OpenWorktreesOverview(string? listId)`, `OpenWeeklyReport()`, `OpenAbout()`, `OpenWorkerConnectionHelp()`. Implementation owns the factories + `ModalShell`/TCS wiring currently in `MainWindow.axaml.cs` + `IslandsShellViewModel.cs:59-71`. - Inject it into `ListsIslandViewModel`, `TasksIslandViewModel`, `IslandsShellViewModel`. Collapse the three List-Settings doors (Lists context menu, Tasks header, shell bridge `IslandsShellViewModel.cs:190-194`) to one `dialogs.OpenListSettings(row)` call; same for Repo Import (2→1) and Worktrees Overview (2→1, keep the `listId?` param for global-vs-per-list). - Keep `ModalShell`/TCS dialog pattern; this only centralizes *opening*. - Update fakes/ctors per the IWorkerClient-fakes hazard (ctor changes ripple to Ui.Tests). - Acceptance: every dialog opens via one method; no duplicate open-commands; Ui.Tests green; visual gap flagged (open each dialog from each former door). --- ## Phase 2 — MergeCoordinator (B1). Medium. **Goal:** delete the five `RequestConflictResolution` seams; one coordinator. - New `IMergeCoordinator` (Ui) `MergeAsync(taskId, targetBranch)` = the body of `IslandsShellViewModel.RequestConflictResolutionAsync` (`:49`) plus the "open MergeModal → on conflict open resolver" flow currently split across `MergeModalViewModel:108` and `DiffModalViewModel:103`. - Remove the `Func? RequestConflictResolution` from `WorktreesOverviewModalViewModel:83`, `DiffModalViewModel:75`, `MergeModalViewModel:33`, `MergeSectionViewModel:51`, and the `DetailsIslandViewModel:347` delegate; inject the coordinator instead. - Re-point doors: review Approve, Diff Merge button, WorktreesOverview single + batch (`:331`), Details merge section. - Update seam tests (`WorktreesOverviewBatchMergeTests.cs:145`, `DetailsIslandConflictSeamTests.cs:84`) to assert via the coordinator. - Acceptance: one merge entry API; resolver still opens for single-task AND planning conflict; Ui.Tests green; visual gap flagged (force a conflict from Approve and from the Diff Merge button). --- ## Phase 3 — WorktreeActions (A3). Medium. **Goal:** one per-task worktree-actions VM reused by overview rows + Details. - New `WorktreeActionsViewModel(taskId)` with Merge/Diff/Discard/Keep/ForceRemove over `IWorkerClient` (uses the Phase-2 coordinator for Merge, the Phase-5 viewer for Diff — until then, current calls). - `WorktreesOverviewModalViewModel` rows compose one each; `MergeSectionViewModel` hosts one for the active task. Remove the duplicated commands. - Acceptance: both surfaces drive the same VM; Ui.Tests green; visual gap flagged. --- ## Phase 4 — AgentConfigEditor (A2). Medium. **Goal:** one config editor for Global | List | Task scope. - New `AgentConfigEditorViewModel(scope)` over `InheritanceResolver` exposing Model/SystemPrompt/AgentPath/MaxTurns + reset commands + `InheritedBadge` state; persists via the scope's hub method (`UpdateListConfig` / `UpdateTaskAgentSettings` / app settings). - Embed in `SettingsModalViewModel`, `ListSettingsModalViewModel`, and the Details `AgentSettingsSectionViewModel` host; delete the duplicated field/reset logic. - Acceptance: identical editor in all three scopes; Localization parity; Ui.Tests green; visual gap flagged. --- ## Phase 5 — DiffViewer (A1 + B2). High; last. **Goal:** one diff component replaces DiffModal + WorktreeModal + PlanningDiff. - New `DiffViewerViewModel` with `DiffSource` enum/abstraction (`DirtyWorktree | BranchVsBase | CommitRange | PlanningAggregate | IntegrationBranch`) and an optional file-tree pane (port `WorktreeModal`'s tree + Avalonia-12 selection workaround); reuse `UnifiedDiffParser` + `DiffLinesView`; keep PlanningDiff's combined-mode toggle as a source switch. - Re-point all B2 doors to open it with the right source. Remove the three old VMs/views. - Update `DiffModalViewModelTests`, `PlanningDiffViewModelTests`. - Acceptance: every diff door opens the one viewer; whole-unified AND file-tree layouts work; Ui.Tests green; visual gap flagged (worktree-dirty, post-merge commit-range, planning per-subtask + integration). --- ## Sequencing rationale 0 (delete/no-UX) → 1 (isolated, unblocks nothing but cheap) → 2 (coordinator; 3 & 5 lean on it for Merge/Diff) → 3 → 4 (independent) → 5 (biggest, most UX-sensitive, benefits from 2's coordinator). Stop after any phase and the app is shippable. ## Per-phase commits Conventional Commits, one per phase (or per sub-step in Phase 0): e.g. `refactor(merge): single MergeCoordinator replaces 5 conflict seams`. Stage by path (never `git add -A` — concurrent sessions). Commit the spec + this plan first.