433 lines
21 KiB
Markdown
433 lines
21 KiB
Markdown
# Git Merge/Review — Shared Foundation + Layer A Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** Build the shared worker conflict contract (so parallel Layer B/C sessions branch from frozen interfaces) and rework the Git tab into a single Approve+merge cockpit.
|
||
|
||
**Architecture:** Phase 0 adds the conflict-resolution contract to `IWorkerClient`/`WorkerClient` (real `_hub.InvokeAsync` bodies — the worker hub methods are implemented later by Layer C; calls simply fail at runtime until then) plus client-side DTOs and test-fake updates, then commits + pushes so B and C branch from it. Phase A reworks `WorkConsole.axaml`'s Git tab and routes single-task merge/approve conflicts into a `RequestConflictResolution` seam (wired to Layer C's resolver by the integrator at merge time).
|
||
|
||
**Tech Stack:** .NET 8, Avalonia 12 (Fluent), CommunityToolkit.Mvvm, SignalR, xUnit. Build individual csproj with `-c Release` (`.slnx` needs .NET 9; a running Worker locks `Debug`).
|
||
|
||
**Reference spec:** `docs/superpowers/specs/2026-06-05-git-merge-review-rework-design.md`
|
||
|
||
**Note on the canonical diff renderer:** the unified diff model/control already exists — `DiffFileViewModel`/`DiffLineViewModel`/`UnifiedDiffParser` (in `src/ClaudeDo.Ui/ViewModels/Modals/`) rendered by `DiffLinesView` (`src/ClaudeDo.Ui/Views/Controls/DiffLinesView.axaml`). `DiffModalView` and `PlanningDiffView` already use it. So "consolidate diff renderers" for this scope is just verifying that (Task A.3); migrating `WorktreeModalView`'s bespoke diff onto `DiffLinesView` is Layer B's job.
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
**Phase 0 (foundation — pushed before B/C branch):**
|
||
- Modify `src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs` — 5 new method signatures.
|
||
- Modify `src/ClaudeDo.Ui/Services/WorkerClient.cs` — 5 `InvokeAsync` bodies + 3 new DTO records.
|
||
- Modify `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs` — 5 new `virtual` no-op methods.
|
||
- Modify `tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs` — 5 new methods on `FakeWorkerClient`.
|
||
|
||
**Phase A (Layer A — this session, after foundation commit):**
|
||
- Modify `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` — `RequestConflictResolution` seam; route Approve/Merge conflicts into it.
|
||
- Modify `src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml` — fuse REVIEW + MERGE sections into one cockpit block.
|
||
- Test: `tests/ClaudeDo.Ui.Tests/ViewModels/DetailsIslandPlanningTests.cs` (or a sibling test file in the same folder).
|
||
|
||
---
|
||
|
||
## Phase 0 — Shared Foundation
|
||
|
||
### Task 0.1: Add the conflict contract (interface + client + DTOs)
|
||
|
||
**Files:**
|
||
- Modify: `src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs`
|
||
- Modify: `src/ClaudeDo.Ui/Services/WorkerClient.cs`
|
||
|
||
- [ ] **Step 1: Add the 5 method signatures to `IWorkerClient`**
|
||
|
||
In `src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs`, after the existing
|
||
`Task CancelReviewAsync(string taskId);` line (line 45), add:
|
||
|
||
```csharp
|
||
// ── Conflict resolution (worker hub side implemented by Layer C) ──
|
||
Task<MergeResultDto> StartConflictMergeAsync(string taskId, string targetBranch);
|
||
Task<MergeConflictsDto> GetMergeConflictsAsync(string taskId);
|
||
Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent);
|
||
Task<MergeResultDto> ContinueMergeAsync(string taskId);
|
||
Task AbortMergeAsync(string taskId);
|
||
```
|
||
|
||
- [ ] **Step 2: Add the 3 DTO records to `WorkerClient.cs`**
|
||
|
||
In `src/ClaudeDo.Ui/Services/WorkerClient.cs`, immediately after line 534
|
||
(`public record MergeTargetsDto(...)`), add:
|
||
|
||
```csharp
|
||
public record MergeConflictsDto(string TaskId, IReadOnlyList<ConflictFileDto> Files);
|
||
public record ConflictFileDto(string Path, IReadOnlyList<ConflictHunkDto> Hunks);
|
||
public record ConflictHunkDto(string Ours, string Theirs, string? Base);
|
||
```
|
||
|
||
- [ ] **Step 3: Add the 5 client method bodies to `WorkerClient.cs`**
|
||
|
||
In `src/ClaudeDo.Ui/Services/WorkerClient.cs`, right after the `MergeTaskAsync`
|
||
method (ends at line 270), add:
|
||
|
||
```csharp
|
||
public Task<MergeResultDto> StartConflictMergeAsync(string taskId, string targetBranch)
|
||
=> _hub.InvokeAsync<MergeResultDto>("StartConflictMerge", taskId, targetBranch);
|
||
|
||
public Task<MergeConflictsDto> GetMergeConflictsAsync(string taskId)
|
||
=> _hub.InvokeAsync<MergeConflictsDto>("GetMergeConflicts", taskId);
|
||
|
||
public Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent)
|
||
=> _hub.InvokeAsync("WriteConflictResolution", taskId, path, resolvedContent);
|
||
|
||
public Task<MergeResultDto> ContinueMergeAsync(string taskId)
|
||
=> _hub.InvokeAsync<MergeResultDto>("ContinueMerge", taskId);
|
||
|
||
public Task AbortMergeAsync(string taskId)
|
||
=> _hub.InvokeAsync("AbortMerge", taskId);
|
||
```
|
||
|
||
- [ ] **Step 4: Build the UI project**
|
||
|
||
Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj -c Release`
|
||
Expected: build FAILS — the two test projects won't compile yet, but the UI project
|
||
itself should succeed. If the UI project reports "does not implement interface member"
|
||
it means a body is missing; fix before continuing. (Test projects are fixed in 0.2.)
|
||
|
||
### Task 0.2: Update the hand-rolled test fakes
|
||
|
||
**Files:**
|
||
- Modify: `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`
|
||
- Modify: `tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs`
|
||
|
||
- [ ] **Step 1: Add 5 virtual no-ops to `StubWorkerClient`**
|
||
|
||
In `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`, after the `MergeTaskAsync` override
|
||
(line 57), add:
|
||
|
||
```csharp
|
||
public virtual Task<MergeResultDto> StartConflictMergeAsync(string taskId, string targetBranch) => Task.FromResult(new MergeResultDto("conflict", System.Array.Empty<string>(), null));
|
||
public virtual Task<MergeConflictsDto> GetMergeConflictsAsync(string taskId) => Task.FromResult(new MergeConflictsDto(taskId, System.Array.Empty<ConflictFileDto>()));
|
||
public virtual Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent) => Task.CompletedTask;
|
||
public virtual Task<MergeResultDto> ContinueMergeAsync(string taskId) => Task.FromResult(new MergeResultDto("merged", System.Array.Empty<string>(), null));
|
||
public virtual Task AbortMergeAsync(string taskId) => Task.CompletedTask;
|
||
```
|
||
|
||
- [ ] **Step 2: Add 5 methods to `FakeWorkerClient`**
|
||
|
||
In `tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs`, after the
|
||
`MergeTaskAsync` method (line 47), add:
|
||
|
||
```csharp
|
||
public Task<MergeResultDto> StartConflictMergeAsync(string taskId, string targetBranch) => Task.FromResult(new MergeResultDto("conflict", System.Array.Empty<string>(), null));
|
||
public Task<MergeConflictsDto> GetMergeConflictsAsync(string taskId) => Task.FromResult(new MergeConflictsDto(taskId, System.Array.Empty<ConflictFileDto>()));
|
||
public Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent) => Task.CompletedTask;
|
||
public Task<MergeResultDto> ContinueMergeAsync(string taskId) => Task.FromResult(new MergeResultDto("merged", System.Array.Empty<string>(), null));
|
||
public Task AbortMergeAsync(string taskId) => Task.CompletedTask;
|
||
```
|
||
|
||
- [ ] **Step 3: Build both test projects**
|
||
|
||
Run: `dotnet build tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release && dotnet build tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release`
|
||
Expected: both BUILD succeed.
|
||
|
||
- [ ] **Step 4: Run the UI test suite to confirm green baseline**
|
||
|
||
Run: `dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release`
|
||
Expected: PASS (no behavior changed yet).
|
||
|
||
### Task 0.3: Commit and push the foundation
|
||
|
||
- [ ] **Step 1: Commit**
|
||
|
||
```bash
|
||
git add src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs src/ClaudeDo.Ui/Services/WorkerClient.cs tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs
|
||
git commit -m "feat(ui): add conflict-resolution worker contract (foundation for merge rework)"
|
||
```
|
||
|
||
- [ ] **Step 2: Push so Layer B/C can branch from this commit**
|
||
|
||
Run: `git push`
|
||
Expected: pushed to `main`. (First push to git.kuns.dev may fail auth — retry once.)
|
||
**This commit is the branch point for the Layer B and Layer C kickoff prompts.**
|
||
|
||
---
|
||
|
||
## Phase A — Layer A Review/Merge Cockpit
|
||
|
||
### Task A.1: Conflict-resolution seam + route Approve/Merge conflicts into it (TDD)
|
||
|
||
**Files:**
|
||
- Modify: `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs`
|
||
- Test: `tests/ClaudeDo.Ui.Tests/ViewModels/DetailsIslandConflictSeamTests.cs` (new)
|
||
|
||
- [ ] **Step 1: Write the failing test**
|
||
|
||
Create `tests/ClaudeDo.Ui.Tests/ViewModels/DetailsIslandConflictSeamTests.cs`. Mirror
|
||
the VM-construction harness used in
|
||
`tests/ClaudeDo.Ui.Tests/ViewModels/DetailsIslandPlanningTests.cs` (same folder) —
|
||
construct `DetailsIslandViewModel` exactly as that file does, including its
|
||
`StubWorkerClient` subclass pattern. The test:
|
||
|
||
```csharp
|
||
[Fact]
|
||
public async Task ApproveReview_OnConflict_InvokesConflictResolutionSeam()
|
||
{
|
||
string? resolvedTaskId = null;
|
||
string? resolvedTarget = null;
|
||
|
||
// Construct the VM as in DetailsIslandPlanningTests, with a worker stub whose
|
||
// ApproveReviewAsync returns a conflict result:
|
||
// public override Task<MergeResultDto?> ApproveReviewAsync(string id, string target)
|
||
// => Task.FromResult<MergeResultDto?>(new MergeResultDto("conflict", new[]{"a.cs"}, null));
|
||
var vm = CreateVm(/* worker stub above */);
|
||
vm.RequestConflictResolution = (taskId, target) =>
|
||
{
|
||
resolvedTaskId = taskId; resolvedTarget = target;
|
||
return System.Threading.Tasks.Task.CompletedTask;
|
||
};
|
||
// assign a task in WaitingForReview + a SelectedMergeTarget = "main" via the same
|
||
// helpers DetailsIslandPlanningTests uses.
|
||
|
||
await vm.ApproveReviewCommand.ExecuteAsync(null);
|
||
|
||
Assert.Equal(/* the seeded task id */, resolvedTaskId);
|
||
Assert.Equal("main", resolvedTarget);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Run the test to verify it fails**
|
||
|
||
Run: `dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release --filter ApproveReview_OnConflict_InvokesConflictResolutionSeam`
|
||
Expected: FAIL — `RequestConflictResolution` property does not exist (compile error).
|
||
|
||
- [ ] **Step 3: Add the seam property**
|
||
|
||
In `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs`, beside the other
|
||
view-wired delegates (`ShowDiffModal`, `ShowMergeModal` around line 387–390), add:
|
||
|
||
```csharp
|
||
// Invoked when a single-task merge/approve hits a conflict. Wired by the
|
||
// integrator to Layer C's conflict resolver. Args: (taskId, targetBranch).
|
||
public Func<string, string, System.Threading.Tasks.Task>? RequestConflictResolution { get; set; }
|
||
```
|
||
|
||
- [ ] **Step 4: Route the Approve conflict branch into the seam**
|
||
|
||
In `ApproveReviewAsync` (around line 1453), replace the conflict branch body so it
|
||
prefers the seam, falling back to the current preview-text behavior:
|
||
|
||
```csharp
|
||
var result = await _worker.ApproveReviewAsync(Task.Id, SelectedMergeTarget ?? "");
|
||
if (result?.Status == "conflict")
|
||
{
|
||
if (RequestConflictResolution is not null)
|
||
{
|
||
await RequestConflictResolution(Task.Id, SelectedMergeTarget ?? "");
|
||
}
|
||
else
|
||
{
|
||
var (text, _, _) = MergePreviewPresenter.Describe(
|
||
new MergePreviewDto("conflict", result.ConflictFiles, 0));
|
||
MergePreviewText = text; MergeIsClean = false; MergeIsConflict = true;
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 5: Route the manual Merge conflict branch into the seam**
|
||
|
||
In `MergeAsync` (around line 1170), apply the same pattern to its conflict branch:
|
||
|
||
```csharp
|
||
var result = await _worker.MergeTaskAsync(Task.Id, SelectedMergeTarget ?? "", false, "Merge task");
|
||
if (result.Status == "conflict")
|
||
{
|
||
if (RequestConflictResolution is not null)
|
||
{
|
||
await RequestConflictResolution(Task.Id, SelectedMergeTarget ?? "");
|
||
}
|
||
else
|
||
{
|
||
var (text, _, _) = MergePreviewPresenter.Describe(
|
||
new MergePreviewDto("conflict", result.ConflictFiles, 0));
|
||
MergePreviewText = text; MergeIsClean = false; MergeIsConflict = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
await RefreshMergePreviewAsync();
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 6: Run the test to verify it passes**
|
||
|
||
Run: `dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release --filter ApproveReview_OnConflict_InvokesConflictResolutionSeam`
|
||
Expected: PASS.
|
||
|
||
- [ ] **Step 7: Run the full UI suite (no regressions)**
|
||
|
||
Run: `dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release`
|
||
Expected: PASS.
|
||
|
||
- [ ] **Step 8: Commit**
|
||
|
||
```bash
|
||
git add src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs tests/ClaudeDo.Ui.Tests/ViewModels/DetailsIslandConflictSeamTests.cs
|
||
git commit -m "feat(ui): route single-task merge conflicts into a resolution seam"
|
||
```
|
||
|
||
### Task A.2: Fuse the Git tab into one Approve+merge cockpit
|
||
|
||
**Files:**
|
||
- Modify: `src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml`
|
||
|
||
- [ ] **Step 1: Replace the two Git-tab sections with one cockpit block**
|
||
|
||
In `src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml`, replace the entire Git
|
||
`ScrollViewer` body (lines 255–313 — the `<!-- Git: ... -->` block containing the
|
||
separate `REVIEW` `StackPanel` and the `MERGE & WORKTREE` `StackPanel`) with a single
|
||
cockpit where Approve sits with the merge target/preview/actions. Keep the existing
|
||
control class names (`section-label`, `field-label`, `btn`, `btn accent`, `meta`) and
|
||
the existing bindings (`SelectedMergeTarget`, `MergeTargetBranches`, `MergePreviewText`,
|
||
`MergeIsClean`, `MergeIsConflict`, `ShowMergePreviewMuted`, `OpenDiffCommand`,
|
||
`ApproveReviewCommand`, `MergeCommand`, `ShowSingleMerge`, `OpenWorktreeCommand`,
|
||
`ReviewCombinedDiffCommand`, `MergeAllCommand`, `CanMergeAll`, `MergeAllDisabledReason`,
|
||
`MergeAllError`):
|
||
|
||
```xml
|
||
<!-- Git: one Approve + merge cockpit -->
|
||
<ScrollViewer IsVisible="{Binding IsGitTab}" Padding="14,10">
|
||
<StackPanel Spacing="12" IsVisible="{Binding ShowMergeSection}">
|
||
<TextBlock Classes="section-label" Text="MERGE" />
|
||
|
||
<StackPanel Spacing="4">
|
||
<TextBlock Classes="field-label" Text="Target branch" />
|
||
<ComboBox ItemsSource="{Binding MergeTargetBranches}"
|
||
SelectedItem="{Binding SelectedMergeTarget, Mode=TwoWay}"
|
||
HorizontalAlignment="Stretch" />
|
||
</StackPanel>
|
||
|
||
<StackPanel Spacing="0">
|
||
<TextBlock Classes="meta" Text="{Binding MergePreviewText}" TextWrapping="Wrap"
|
||
Foreground="{DynamicResource MossBrush}"
|
||
IsVisible="{Binding MergeIsClean}" />
|
||
<TextBlock Classes="meta" Text="{Binding MergePreviewText}" TextWrapping="Wrap"
|
||
Foreground="{DynamicResource BloodBrush}"
|
||
IsVisible="{Binding MergeIsConflict}" />
|
||
<TextBlock Classes="meta" Text="{Binding MergePreviewText}" TextWrapping="Wrap"
|
||
Foreground="{DynamicResource TextMuteBrush}"
|
||
IsVisible="{Binding ShowMergePreviewMuted}" />
|
||
</StackPanel>
|
||
|
||
<!-- Primary action: Approve flows straight into the merge.
|
||
Approve is the review-gated path; the plain Merge button covers
|
||
already-reviewed / kept worktrees. -->
|
||
<WrapPanel Orientation="Horizontal">
|
||
<Button Classes="btn accent" Content="Approve & Merge" Margin="0,0,8,8"
|
||
Command="{Binding ApproveReviewCommand}"
|
||
IsVisible="{Binding IsWaitingForReview}" />
|
||
<Button Classes="btn accent" Content="Merge" Margin="0,0,8,8"
|
||
Command="{Binding MergeCommand}"
|
||
IsVisible="{Binding ShowSingleMerge}" />
|
||
<Button Classes="btn" Content="Open Diff" Margin="0,0,8,8"
|
||
Command="{Binding OpenDiffCommand}" />
|
||
<Button Classes="btn" Margin="0,0,8,8"
|
||
Command="{Binding OpenWorktreeCommand}">
|
||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||
<TextBlock Text="Worktree" />
|
||
<PathIcon Data="{StaticResource Icon.ArrowOut}" Width="11" Height="11" />
|
||
</StackPanel>
|
||
</Button>
|
||
<Button Classes="btn" Content="Review Combined Diff" Margin="0,0,8,8"
|
||
Command="{Binding ReviewCombinedDiffCommand}" />
|
||
<Button Classes="btn accent" Content="Merge All Subtasks" Margin="0,0,0,8"
|
||
Command="{Binding MergeAllCommand}"
|
||
IsEnabled="{Binding CanMergeAll}"
|
||
ToolTip.Tip="{Binding MergeAllDisabledReason}" />
|
||
</WrapPanel>
|
||
|
||
<TextBlock Text="{Binding MergeAllError}"
|
||
Foreground="{DynamicResource BloodBrush}"
|
||
TextWrapping="Wrap"
|
||
IsVisible="{Binding MergeAllError,
|
||
Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
```
|
||
|
||
Note: the cockpit now shows whenever `ShowMergeSection` is true. `ShowMergeSection`
|
||
(DetailsIslandViewModel line 161) must be true while `IsWaitingForReview` so the
|
||
Approve button appears. Check its expression in Step 2.
|
||
|
||
- [ ] **Step 2: Verify `ShowMergeSection` covers the review state**
|
||
|
||
Read `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` line 161. If
|
||
`ShowMergeSection` is false while `IsWaitingForReview` (e.g. it requires a non-review
|
||
state), widen it to also be true when `IsWaitingForReview && WorktreePath != null`, and
|
||
ensure `OnPropertyChanged(nameof(ShowMergeSection))` already fires on the relevant state
|
||
transitions (it is notified via `NotifySessionSections`). Make the minimal change needed
|
||
so the Approve button is visible in review state. If it already covers review, change
|
||
nothing.
|
||
|
||
- [ ] **Step 3: Build the app project**
|
||
|
||
Run: `dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release`
|
||
Expected: BUILD succeeds (pulls in Ui + Data).
|
||
|
||
- [ ] **Step 4: Visual verification (manual — flag for the user)**
|
||
|
||
This is an AXAML layout change with no automated coverage. Launch the app, open a task
|
||
in `WaitingForReview`, open the Git tab, and confirm: the single MERGE block shows the
|
||
target combo, the colored preview line, an "Approve & Merge" button (review state), and
|
||
the diff/worktree/combined/merge-all actions. **Explicitly tell the user this needs a
|
||
visual pass — do not claim it works without running it.**
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs
|
||
git commit -m "feat(ui): fuse git tab into one approve+merge cockpit"
|
||
```
|
||
|
||
### Task A.3: Verify diff-renderer consolidation
|
||
|
||
**Files:** none modified (verification only).
|
||
|
||
- [ ] **Step 1: Confirm DiffModal + Planning already use the canonical renderer**
|
||
|
||
Run: `rg -l "DiffLinesView" src/ClaudeDo.Ui/Views`
|
||
Expected: matches in `Modals/DiffModalView.axaml` and `Planning/PlanningDiffView.axaml`.
|
||
If `PlanningDiffView.axaml` does NOT use `DiffLinesView`, change its diff `ItemsControl`
|
||
to a `<controls:DiffLinesView Lines="{Binding SelectedFile.Lines}" />` (matching
|
||
`DiffModalView.axaml`'s usage) and rebuild the App project. If both already use it, this
|
||
task is a no-op — record that and move on. (`WorktreeModalView`'s bespoke diff is
|
||
intentionally left for Layer B.)
|
||
|
||
---
|
||
|
||
## Self-Review
|
||
|
||
- **Spec coverage:** Foundation contract (spec §"Frozen worker conflict contract") →
|
||
Task 0.1. Test fakes (spec parallel-boundaries row) → Task 0.2. Branch point (spec
|
||
§"built & pushed this session") → Task 0.3. Layer A cockpit + Approve/merge flow
|
||
together (spec §"Layer A") → Task A.2. Single-task approve-on-conflict opens resolver
|
||
via seam (spec §"Layer A" + §"integration seams") → Task A.1. Diff consolidation
|
||
(spec §"One diff model") → Task A.3. Output-footer feedback unchanged → not touched
|
||
(correct). No spec requirement left unmapped for this session's scope.
|
||
- **Placeholder scan:** none — every code step has concrete code; the only "mirror the
|
||
existing harness" reference (Task A.1 Step 1) points at a real file with a working
|
||
pattern, not a TODO.
|
||
- **Type consistency:** `MergeConflictsDto`/`ConflictFileDto`/`ConflictHunkDto` and the
|
||
5 method names match between `IWorkerClient` (0.1 Step 1), `WorkerClient` (0.1 Steps
|
||
2–3), and both fakes (0.2). The seam `RequestConflictResolution` is
|
||
`Func<string,string,Task>?` everywhere (A.1 Steps 1, 3–5). DTO field names match the
|
||
spec.
|
||
|
||
---
|
||
|
||
## Integration notes (for the integrator merging A + B + C)
|
||
|
||
- Wire `DetailsIslandViewModel.RequestConflictResolution` and Layer B's equivalent
|
||
callback to Layer C's `ConflictResolverViewModel` factory + `ShowConflictResolver`
|
||
dialog delegate.
|
||
- Layer C implements the worker hub methods `StartConflictMerge`, `GetMergeConflicts`,
|
||
`WriteConflictResolution`, `ContinueMerge`, `AbortMerge`; the client side from Task
|
||
0.1 already calls them by name.
|