# 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.
