Maps duplication into three buckets (parallel impls, entry-point sprawl, dead/leftover) and defines six phased unification slices: groundwork, DialogService, MergeCoordinator, WorktreeActions, AgentConfigeditor, unified DiffViewer.
7.1 KiB
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-<slice>.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+ theMergeConflicts/ConflictFileContentrecords it returns (src/ClaudeDo.Worker/Lifecycle/TaskMergeService.cs:250) if unused elsewhere. - Remove
WorkerHub.GetMergeConflicts(src/ClaudeDo.Worker/Hub/WorkerHub.cs:378) +MergeConflictsDto/ConflictFileDto/ConflictHunkDtoif unused. - Remove
WorkerClient's"GetMergeConflicts"invoke (src/ClaudeDo.Ui/Services/WorkerClient.cs:276) + theIWorkerClientmember + every fake override (tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs,TasksIslandViewModelPlanningTests.cs, others — grepGetMergeConflicts). - Delete
TaskMergeServiceTests.cs:672GetConflictsAsync_AfterConflictMerge_ReturnsOursAndTheirs. - Verify with grep first:
GetConflictsAsyncandGetMergeConflictshave no callers outside this chain + tests. - Acceptance: Worker + Ui build; Worker.Tests + Ui.Tests green;
GetMergeConflictDocumentspath untouched.
0b. Single task-creation path (C2).
- Identify the path MCP
ExternalMcpService.AddTaskuses; expose a thin creation method (repository or a smallTaskCreationService) that applies the same defaults (ListId, SortOrder, CreatedAt). - Re-point
TasksIslandViewModel.AddAsyncat it instead ofdb.Tasks.Adddirect 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 beforegit 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 inMainWindow.axaml.cs+IslandsShellViewModel.cs:59-71. - Inject it into
ListsIslandViewModel,TasksIslandViewModel,IslandsShellViewModel. Collapse the three List-Settings doors (Lists context menu, Tasks header, shell bridgeIslandsShellViewModel.cs:190-194) to onedialogs.OpenListSettings(row)call; same for Repo Import (2→1) and Worktrees Overview (2→1, keep thelistId?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 ofIslandsShellViewModel.RequestConflictResolutionAsync(:49) plus the "open MergeModal → on conflict open resolver" flow currently split acrossMergeModalViewModel:108andDiffModalViewModel:103. - Remove the
Func<string,string,Task>? RequestConflictResolutionfromWorktreesOverviewModalViewModel:83,DiffModalViewModel:75,MergeModalViewModel:33,MergeSectionViewModel:51, and theDetailsIslandViewModel:347delegate; 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 overIWorkerClient(uses the Phase-2 coordinator for Merge, the Phase-5 viewer for Diff — until then, current calls). WorktreesOverviewModalViewModelrows compose one each;MergeSectionViewModelhosts 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)overInheritanceResolverexposing Model/SystemPrompt/AgentPath/MaxTurns + reset commands +InheritedBadgestate; persists via the scope's hub method (UpdateListConfig/UpdateTaskAgentSettings/ app settings). - Embed in
SettingsModalViewModel,ListSettingsModalViewModel, and the DetailsAgentSettingsSectionViewModelhost; 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
DiffViewerViewModelwithDiffSourceenum/abstraction (DirtyWorktree | BranchVsBase | CommitRange | PlanningAggregate | IntegrationBranch) and an optional file-tree pane (portWorktreeModal's tree + Avalonia-12 selection workaround); reuseUnifiedDiffParser+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.