# Plan: Rider-style 3-pane merge editor Spec: `docs/superpowers/specs/2026-06-19-rider-merge-editor-design.md` TDD, one focused commit per task (Conventional Commits, `feat(merge): …`). Build with `-c Release` per project (a running Worker locks `Debug`). Run `ClaudeDo.Ui.Tests` (and `Localization.Tests` for Task 6). No real `claude` CLI in tests. Stage ONLY the files each task touches, by explicit path (parallel sessions leave WIP). Backend + seam stay unchanged. Implementer/reviewer subagents use **sonnet**. ## Task 1 — VM: active-file model + 3-pane reconstruction + readout `ConflictResolverViewModel` / `ConflictModels.cs`, additive (seam untouched). - Add `ActiveFile` (`MergeFile?`), `SelectFileCommand(MergeFile)`, default to first file after load. Keep `Files`, `Current`/`CurrentIndex`/`Next`/`Previous` (focused conflict for the header arrows), `CanContinue`, binary guard, planning routing — all unchanged. - Add computed, per `ActiveFile`: - `ActiveOursText` = concat(stable.Text | conflict.Ours) - `ActiveTheirsText` = concat(stable.Text | conflict.Theirs) - `ActiveResultText` = concat(stable.Text | conflict.Resolution ?? conflict.Ours) - `ActiveConflicts` = ordered descriptors (block + segment index) for the view. - `PositionText` → `"{conflicts} conflicts · {resolved} resolved"` for the active file; keep `CanContinue` = every file resolved AND no binary. - Switching files raises a change event the view listens to (reuse/extend `CurrentChanged` → e.g. `ActiveFileChanged`). - Tests (Ui.Tests): reconstruction text for ours/theirs/result (result seeds unresolved with Ours); resolving a block updates `ActiveResultText` + readout; switching files preserves each block's `Resolution`; `CanContinue` blocks until all files resolved; binary file still blocks. Keep all existing tests green. ## Task 2 — View: 3-pane AXAML shell + document assembly + synced scroll `Views/Conflicts/ConflictResolverView.axaml(.cs)`. Visual — verified by running. - Replace AXAML: ModalShell host kept; header row (◀/▶ focus arrows bound to Previous/Next, file switcher `ItemsControl`/`ComboBox` over `Files` bound to `SelectFileCommand`, right-aligned `PositionText`); `Grid ColumnDefinitions="*,*,*"` of three bordered panes with headers **Ours · current (merge target)** / **Result** / **Theirs · incoming (task)** (drop Base); footer Continue (`IsEnabled=CanContinue`) / Abort; binary banner (kept); `Escape`→Abort (kept). - Code-behind: build three `TextDocument`s from `ActiveFile` segments, recording each conflict's start line + line count per document; install TextMate per pane by file extension; rebuild on `ActiveFileChanged`; Ours/Theirs `IsReadOnly=true`. - Proportional synced vertical scroll across the three panes (re-entrancy guard). - Push Result edits back to the active block `Resolution` (refined in Task 4). ## Task 3 — Result pane: read-only stable, editable conflicts `ConflictResolverView.axaml.cs` + a small `IReadOnlySectionProvider` helper. - Track each conflict's result span in a `TextSegmentCollection<…>` over the Result document (anchors auto-adjust on edit). - `IReadOnlySectionProvider`: `CanInsert` only strictly inside a conflict span; `GetDeletableSegments` intersects with conflict spans only. Stable text becomes immutable; conflict regions stay editable. - Editing inside a conflict span writes the span text back to the block `Resolution` and flips it resolved (updates readout + `CanContinue`). ## Task 4 — Color blocks (IBackgroundRenderer) + accept overlay `ConflictResolverView.axaml.cs` + renderer/overlay helpers. - `IBackgroundRenderer` per pane: unresolved conflict = red (Blood tint), resolved = green/muted, Ours side = Moss tint, Theirs side = Accent tint — driven by recorded spans + block `IsResolved`. - Between-pane overlay Canvas (Ours|Result and Result|Theirs): `›` accept-ours / `‹` accept-theirs + `✕` dismiss per conflict, positioned at the block's `TextView` visual top, recomputed on scroll/resize. Click → `block.AcceptOurs/AcceptTheirs` and replace the tracked Result span; resolved blocks recolor. ## Task 5 — Polish: readout, focus arrows scroll-to-conflict, resolved styling - ◀/▶ arrows move `Current` and scroll all three panes to that conflict. - `M conflicts · K resolved` live readout; Continue tooltip/hint when blocked. - Resolved conflict recolors and drops its accept overlay; unresolved stays red. (Fold into Task 4 if small.) ## Task 6 — Localization + tokens - Add `conflictResolver.*` keys (pane headers, readout, accept tooltips, hints) to `locales/en.json` AND `locales/de.json` (keep key parity). - Add Tokens.axaml color tokens only if a needed conflict/resolved shade is missing. - Run Localization.Tests (parity) + a quick scan for hard-coded strings in the view. ## Task 7 — Verify - Build `ClaudeDo.App` + `ClaudeDo.Ui` `-c Release`; run `Ui.Tests` + `Localization.Tests`. - Update `src/ClaudeDo.Ui/CLAUDE.md` (Planning/Conflicts paragraph → new 3-pane editor). - **Visual verification gap (flag to Mika):** run the app, trigger a real conflict (single-task approve + planning unit-merge) and confirm panes/colors/accept/scroll/ gating/binary render correctly — cannot be asserted in tests.