# UI Rewrite — Islands Layout 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:** Replace the current `ClaudeDo.Ui` with a high-fidelity three-island Avalonia interface per the design handoff at `docs/UI Rewrite/design_handoff_claudedo/`.

**Architecture:** Data-layer additions (`IsStarred`, `IsMyDay`, `Notes` columns; default-list seeding); a new `Design/` resource folder (`Tokens.axaml`, `IslandStyles.axaml`); embedded Inter Tight + JetBrains Mono fonts; a chromeless `MainWindow` containing a three-column `Grid` of island `Border`s — Lists / Tasks / Details — backed by new `IslandsShellViewModel`, `ListsIslandViewModel`, `TasksIslandViewModel`, `DetailsIslandViewModel`. Existing `WorkerClient`, `Repositories` and SignalR plumbing are preserved.

**Tech Stack:** .NET 8.0, Avalonia 12.0.0 (Fluent theme), CommunityToolkit.Mvvm, Entity Framework Core (SQLite), xUnit.

**Reference files:**
- `docs/UI Rewrite/design_handoff_claudedo/README.md` — full handoff
- `docs/UI Rewrite/design_handoff_claudedo/Tokens.axaml` — design tokens
- `docs/UI Rewrite/design_handoff_claudedo/IslandStyles.axaml` — control styles
- `docs/UI Rewrite/design_handoff_claudedo/styles.css` — measurement source of truth
- `docs/UI Rewrite/design_handoff_claudedo/ClaudeDo-standalone.html` — interactive reference

**Confirmed decisions (from brainstorm):**
- Full rewrite of `ClaudeDo.Ui` (Views + ViewModels). `WorkerClient`, repositories, SignalR untouched.
- Schema: add `IsStarred`, `IsMyDay`, `Notes` to `TaskEntity`. Seed `My Day`, `Important`, `Planned` Lists on install.
- `Running` and `Review` lists are **virtual filters** (status-based), not seeded list rows.
- Window: chromeless (`SystemDecorations="None"` + `ExtendClientAreaToDecorationsHint="True"`).
- Fonts: embed `Inter Tight` and `JetBrains Mono`.
- Resource folder: `src/ClaudeDo.Ui/Design/`.

---

## File Structure

**Data layer (new / modified):**
- Modify: `src/ClaudeDo.Data/Models/TaskEntity.cs` — add 3 properties
- Modify: `src/ClaudeDo.Data/Configuration/TaskEntityConfiguration.cs` — column mappings
- Create: `src/ClaudeDo.Data/Migrations/<timestamp>_AddTaskFlagsAndNotes.cs` — EF migration
- Modify: `src/ClaudeDo.Data/Migrations/ClaudeDoDbContextModelSnapshot.cs` — generated update
- Modify: `src/ClaudeDo.Installer/...` (seed call site — discover during Phase 1)

**UI design assets (new):**
- Create: `src/ClaudeDo.Ui/Design/Tokens.axaml`
- Create: `src/ClaudeDo.Ui/Design/IslandStyles.axaml`
- Create: `src/ClaudeDo.Ui/Assets/Fonts/InterTight-*.ttf` (Regular, Medium, SemiBold)
- Create: `src/ClaudeDo.Ui/Assets/Fonts/JetBrainsMono-Regular.ttf`
- Modify: `src/ClaudeDo.Ui/App.axaml` — merge Tokens + Styles
- Modify: `src/ClaudeDo.Ui/ClaudeDo.Ui.csproj` — embed font assets

**Shell + Islands (new — replaces existing Views/ViewModels):**
- Create: `src/ClaudeDo.Ui/Views/MainWindow.axaml` (replace) — chromeless, 3-column Grid
- Create: `src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs` (replace `MainWindowViewModel`)
- Create: `src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs`
- Create: `src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs`
- Create: `src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml` (UserControl)
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs`
- Create: `src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs`
- Create: `src/ClaudeDo.Ui/Views/Islands/AgentStripView.axaml`
- Create: `src/ClaudeDo.Ui/Views/Islands/SessionTerminalView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/LogLineViewModel.cs`

**Modals (new):**
- Create: `src/ClaudeDo.Ui/Views/Modals/DiffModalView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Modals/DiffModalViewModel.cs`
- Create: `src/ClaudeDo.Ui/Views/Modals/WorktreeModalView.axaml`
- Create: `src/ClaudeDo.Ui/ViewModels/Modals/WorktreeModalViewModel.cs`

**To delete (after rewrite verified working):**
- `src/ClaudeDo.Ui/Views/StatusBarView.axaml(.cs)`, `TaskListView.axaml(.cs)`, `TaskDetailView.axaml(.cs)`, `TaskEditorView.axaml(.cs)`, `ListEditorView.axaml(.cs)`
- `src/ClaudeDo.Ui/ViewModels/StatusBarViewModel.cs`, `TaskListViewModel.cs`, `TaskDetailViewModel.cs`, `TaskItemViewModel.cs`, `MainWindowViewModel.cs`, `TaskEditorViewModel.cs`, `ListEditorViewModel.cs`, `ListItemViewModel.cs`, `SubtaskItemViewModel.cs`

**Tests (new):**
- Create: `tests/ClaudeDo.Worker.Tests/UiSchema/TaskEntityFlagsTests.cs`
- Create: `tests/ClaudeDo.Worker.Tests/UiSchema/DefaultListSeedTests.cs`

---

## Phase 1 — Schema and seed

### Task 1: Add `IsStarred`, `IsMyDay`, `Notes` to `TaskEntity`

**Files:**
- Modify: `src/ClaudeDo.Data/Models/TaskEntity.cs`
- Modify: `src/ClaudeDo.Data/Configuration/TaskEntityConfiguration.cs`
- Test: `tests/ClaudeDo.Worker.Tests/UiSchema/TaskEntityFlagsTests.cs` (new)

- [ ] **Step 1: Write failing test** — `tests/ClaudeDo.Worker.Tests/UiSchema/TaskEntityFlagsTests.cs`

```csharp
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using Microsoft.EntityFrameworkCore;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.UiSchema;

public class TaskEntityFlagsTests : IDisposable
{
    private readonly string _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo-flags-{Guid.NewGuid():N}.db");

    private ClaudeDoDbContext NewContext()
    {
        var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite($"Data Source={_dbPath}")
            .Options;
        var ctx = new ClaudeDoDbContext(opts);
        ctx.Database.EnsureCreated();
        return ctx;
    }

    [Fact]
    public async Task Persists_IsStarred_IsMyDay_And_Notes()
    {
        await using var ctx = NewContext();
        var list = new ListEntity { Id = "l1", Name = "L", CreatedAt = DateTime.UtcNow };
        ctx.Lists.Add(list);
        ctx.Tasks.Add(new TaskEntity
        {
            Id = "t1", ListId = "l1", Title = "T", CreatedAt = DateTime.UtcNow,
            IsStarred = true, IsMyDay = true, Notes = "hello"
        });
        await ctx.SaveChangesAsync();

        await using var ctx2 = NewContext();
        var loaded = await ctx2.Tasks.SingleAsync();
        Assert.True(loaded.IsStarred);
        Assert.True(loaded.IsMyDay);
        Assert.Equal("hello", loaded.Notes);
    }

    public void Dispose()
    {
        if (File.Exists(_dbPath)) File.Delete(_dbPath);
    }
}
```

- [ ] **Step 2: Run test — verify fail**

Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj --filter FullyQualifiedName~TaskEntityFlagsTests`
Expected: build error — `TaskEntity` has no `IsStarred`/`IsMyDay`/`Notes`.

- [ ] **Step 3: Add properties to `TaskEntity`** — append before navigation block in `src/ClaudeDo.Data/Models/TaskEntity.cs`:

```csharp
public bool IsStarred { get; set; }
public bool IsMyDay { get; set; }
public string? Notes { get; set; }
```

- [ ] **Step 4: Update `TaskEntityConfiguration`** — add column mappings inside `Configure(EntityTypeBuilder<TaskEntity> b)`:

```csharp
b.Property(t => t.IsStarred).HasColumnName("is_starred").HasDefaultValue(false);
b.Property(t => t.IsMyDay).HasColumnName("is_my_day").HasDefaultValue(false);
b.Property(t => t.Notes).HasColumnName("notes");
```

(Match existing snake_case style — verify by reading the file first.)

- [ ] **Step 5: Run test — verify pass**

Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj --filter FullyQualifiedName~TaskEntityFlagsTests`
Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add src/ClaudeDo.Data/Models/TaskEntity.cs src/ClaudeDo.Data/Configuration/TaskEntityConfiguration.cs tests/ClaudeDo.Worker.Tests/UiSchema/TaskEntityFlagsTests.cs
git commit -m "feat(data): add IsStarred, IsMyDay, Notes to TaskEntity"
```

---

### Task 2: Generate EF Core migration for new columns

**Files:**
- Create: `src/ClaudeDo.Data/Migrations/<timestamp>_AddTaskFlagsAndNotes.cs`
- Modify: `src/ClaudeDo.Data/Migrations/ClaudeDoDbContextModelSnapshot.cs` (generated)

- [ ] **Step 1: Generate migration**

Run from repo root:
```bash
dotnet ef migrations add AddTaskFlagsAndNotes --project src/ClaudeDo.Data/ClaudeDo.Data.csproj --startup-project src/ClaudeDo.Data/ClaudeDo.Data.csproj
```

If `dotnet-ef` is missing: `dotnet tool install --global dotnet-ef --version 8.*`.

- [ ] **Step 2: Inspect the generated `Up`** — confirm three `AddColumn<>` calls for `is_starred`, `is_my_day`, `notes`. If column names mismatch, edit them by hand.

- [ ] **Step 3: Apply migration in test (already covered by `EnsureCreated` in tests)**

Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj --filter FullyQualifiedName~TaskEntityFlagsTests`
Expected: still PASS.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Data/Migrations/
git commit -m "feat(data): migration for IsStarred/IsMyDay/Notes columns"
```

---

### Task 3: Seed default Lists ("My Day", "Important", "Planned") on install

**Files:**
- Discover seed call site — search `src/ClaudeDo.Installer/` and `src/ClaudeDo.App/` for `EnsureCreated`, `Migrate`, or existing tag seeding ("agent" tag).
- Modify: the discovered seeder OR create `src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs` if no central seeder exists.
- Test: `tests/ClaudeDo.Worker.Tests/UiSchema/DefaultListSeedTests.cs` (new)

- [ ] **Step 1: Locate seed site**

Run:
```bash
grep -rn "agent.*manual\|GetOrCreateAsync\|EnsureCreated\|Migrate(" src/ClaudeDo.Installer src/ClaudeDo.App
```

If a central seeder exists, add list-seeding there. Otherwise create `DefaultListsSeeder`.

- [ ] **Step 2: Write failing test** — `tests/ClaudeDo.Worker.Tests/UiSchema/DefaultListSeedTests.cs`

```csharp
using ClaudeDo.Data;
using ClaudeDo.Data.Seeding; // adjust namespace if seeded elsewhere
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace ClaudeDo.Worker.Tests.UiSchema;

public class DefaultListSeedTests : IDisposable
{
    private readonly string _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo-seed-{Guid.NewGuid():N}.db");

    private ClaudeDoDbContext NewContext()
    {
        var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite($"Data Source={_dbPath}").Options;
        var ctx = new ClaudeDoDbContext(opts);
        ctx.Database.EnsureCreated();
        return ctx;
    }

    [Fact]
    public async Task Seeds_MyDay_Important_Planned_Lists_Idempotently()
    {
        await using (var ctx = NewContext())
        {
            await DefaultListsSeeder.SeedAsync(ctx);
            await DefaultListsSeeder.SeedAsync(ctx); // idempotent
        }

        await using var verify = NewContext();
        var names = verify.Lists.Select(l => l.Name).OrderBy(n => n).ToList();
        Assert.Equal(new[] { "Important", "My Day", "Planned" }, names);
    }

    public void Dispose() { if (File.Exists(_dbPath)) File.Delete(_dbPath); }
}
```

- [ ] **Step 3: Run test — verify fail**

Run: `dotnet test ... --filter FullyQualifiedName~DefaultListSeedTests`
Expected: build error.

- [ ] **Step 4: Implement seeder** — create `src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs`:

```csharp
using ClaudeDo.Data.Models;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Data.Seeding;

public static class DefaultListsSeeder
{
    private static readonly string[] Defaults = { "My Day", "Important", "Planned" };

    public static async Task SeedAsync(ClaudeDoDbContext ctx, CancellationToken ct = default)
    {
        var existing = await ctx.Lists.Select(l => l.Name).ToListAsync(ct);
        var now = DateTime.UtcNow;
        foreach (var name in Defaults.Where(n => !existing.Contains(n)))
        {
            ctx.Lists.Add(new ListEntity
            {
                Id = Guid.NewGuid().ToString("N"),
                Name = name,
                CreatedAt = now,
            });
        }
        await ctx.SaveChangesAsync(ct);
    }
}
```

- [ ] **Step 5: Wire seeder into install path** — call `DefaultListsSeeder.SeedAsync(ctx)` from the same code path that seeds the "agent"/"manual" tags. If none exists, call it once on app startup after `Database.Migrate()` in `ClaudeDo.App`.

- [ ] **Step 6: Run test — verify pass**

Run: `dotnet test ... --filter FullyQualifiedName~DefaultListSeedTests`
Expected: PASS.

- [ ] **Step 7: Commit**

```bash
git add src/ClaudeDo.Data/Seeding/ tests/ClaudeDo.Worker.Tests/UiSchema/DefaultListSeedTests.cs <wired-call-site>
git commit -m "feat(data): seed default Lists (My Day, Important, Planned)"
```

---

## Phase 2 — Design tokens, fonts, and shell wiring

### Task 4: Embed Inter Tight + JetBrains Mono fonts

**Files:**
- Create: `src/ClaudeDo.Ui/Assets/Fonts/InterTight-Regular.ttf`, `InterTight-Medium.ttf`, `InterTight-SemiBold.ttf`
- Create: `src/ClaudeDo.Ui/Assets/Fonts/JetBrainsMono-Regular.ttf`
- Modify: `src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`

- [ ] **Step 1: Download font files** — fetch from Google Fonts (`https://fonts.google.com/specimen/Inter+Tight`, `https://fonts.google.com/specimen/JetBrains+Mono`). Place TTFs in `src/ClaudeDo.Ui/Assets/Fonts/`. SIL OFL license — include `OFL.txt` next to each family.

- [ ] **Step 2: Mark as Avalonia resources** — in `src/ClaudeDo.Ui/ClaudeDo.Ui.csproj` add (or extend the existing `<ItemGroup>` for `AvaloniaResource`):

```xml
<ItemGroup>
  <AvaloniaResource Include="Assets/Fonts/*.ttf" />
  <AvaloniaResource Include="Assets/Fonts/OFL.txt" />
</ItemGroup>
```

- [ ] **Step 3: Build to confirm assets resolve**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`
Expected: build succeeds.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/Assets/Fonts/ src/ClaudeDo.Ui/ClaudeDo.Ui.csproj
git commit -m "feat(ui): embed Inter Tight and JetBrains Mono fonts"
```

---

### Task 5: Port `Tokens.axaml` into the Ui project

**Files:**
- Create: `src/ClaudeDo.Ui/Design/Tokens.axaml`

- [ ] **Step 1: Copy and adapt** — open `docs/UI Rewrite/design_handoff_claudedo/Tokens.axaml` and copy its `<ResourceDictionary>` content into `src/ClaudeDo.Ui/Design/Tokens.axaml`.

- [ ] **Step 2: Replace placeholder font URIs** — anywhere the file references `avares://YourApp/...`, replace with `avares://ClaudeDo.Ui/Assets/Fonts/#Inter Tight` and `avares://ClaudeDo.Ui/Assets/Fonts/#JetBrains Mono`.

- [ ] **Step 3: Verify required keys present** — open the file and confirm these brushes/values exist (per README §"Design tokens"):
  - `VoidBrush #0A0E0C`, `DeepBrush #0D1311`, `SurfaceBrush #161D1A`, `Surface2Brush #1C2422`, `Surface3Brush #222B28`, `LineBrush #2A3330`
  - `TextBrush #E4EBE4`, `TextDimBrush #9AA8A0`, `TextMuteBrush #6B7973`, `TextFaintBrush #4A5550`
  - `MossBrush #7C9166`, `SageBrush #8B9D7A`, `PeatBrush #D4A574`, `BloodBrush #C87060`
  - `IslandRadius 14`, `ModalRadius 12`, `ChipRadius 10`, `RowRadius 8`, `ButtonRadius 6`
  - `MotionFast 0:0:0.12`, `MotionBase 0:0:0.18`, `MotionSlow 0:0:0.30`
  - `IslandShadow`, `ModalShadow` BoxShadow values
  - `SansFamily`, `MonoFamily` `FontFamily` values

  If any are missing, add them inline using the values from the README §"Design tokens (reference)".

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/Design/Tokens.axaml
git commit -m "feat(ui): add design Tokens resource dictionary"
```

---

### Task 6: Port `IslandStyles.axaml` into the Ui project

**Files:**
- Create: `src/ClaudeDo.Ui/Design/IslandStyles.axaml`

- [ ] **Step 1: Copy** the full `<Styles>` content from `docs/UI Rewrite/design_handoff_claudedo/IslandStyles.axaml` into `src/ClaudeDo.Ui/Design/IslandStyles.axaml`.

- [ ] **Step 2: Confirm classed selectors present** — file must define styles for at least:
  - `Border.island`, `Border.island-header`
  - `Border.list-item`, `Border.list-item.active`
  - `TextBox.search`
  - `Border.task-row`, `Border.task-row.selected`
  - `Ellipse.task-check`, `Ellipse.task-check.done`
  - `Border.chip` and status variants `chip.running`, `chip.review`, `chip.error`, `chip.queued`, `chip.idle`
  - `Border.agent-strip` and status variants
  - `Border.terminal`, `TextBlock.log-sys`, `log-tool`, `log-claude`, `log-stdout`, `log-stderr`, `log-done`, `log-msg`
  - `Button.icon-btn`

  If any classed selector is missing, add a minimal one referencing the relevant token brush.

- [ ] **Step 3: Commit**

```bash
git add src/ClaudeDo.Ui/Design/IslandStyles.axaml
git commit -m "feat(ui): add island control styles"
```

---

### Task 7: Wire tokens and styles into `App.axaml`

**Files:**
- Modify: `src/ClaudeDo.Ui/App.axaml`

- [ ] **Step 1: Read current App.axaml** to preserve any DI/region wiring:

Run: open `src/ClaudeDo.Ui/App.axaml` and note its existing structure.

- [ ] **Step 2: Edit** — ensure the `<Application>` root contains:

```xml
<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceInclude Source="avares://ClaudeDo.Ui/Design/Tokens.axaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

<Application.Styles>
  <FluentTheme />
  <StyleInclude Source="avares://ClaudeDo.Ui/Design/IslandStyles.axaml" />
</Application.Styles>
```

(Preserve any existing `RequestedThemeVariant`, converter resources, etc. — only add the includes if not already present.)

- [ ] **Step 3: Build**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`
Expected: success.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/App.axaml
git commit -m "feat(ui): merge Tokens and IslandStyles into App"
```

---

## Phase 3 — Chromeless shell + three-island layout

### Task 8: Replace `MainWindowViewModel` with `IslandsShellViewModel`

**Files:**
- Create: `src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs`

- [ ] **Step 1: Write the VM** with three child VMs and a width-driven boolean for collapsing the Details column:

```csharp
using CommunityToolkit.Mvvm.ComponentModel;
using ClaudeDo.Ui.ViewModels.Islands;

namespace ClaudeDo.Ui.ViewModels;

public sealed partial class IslandsShellViewModel : ViewModelBase
{
    public ListsIslandViewModel Lists { get; }
    public TasksIslandViewModel Tasks { get; }
    public DetailsIslandViewModel Details { get; }

    [ObservableProperty]
    private double _windowWidth = 1280;

    public bool ShowDetails => WindowWidth >= 1100;
    public bool ShowLists => WindowWidth >= 780;

    partial void OnWindowWidthChanged(double value)
    {
        OnPropertyChanged(nameof(ShowDetails));
        OnPropertyChanged(nameof(ShowLists));
    }

    public IslandsShellViewModel(
        ListsIslandViewModel lists,
        TasksIslandViewModel tasks,
        DetailsIslandViewModel details)
    {
        Lists = lists; Tasks = tasks; Details = details;
        Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList);
        Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask);
    }
}
```

- [ ] **Step 2: Register in DI** — modify `src/ClaudeDo.App/Program.cs` (or wherever `MainWindowViewModel` is registered) to register the new VMs:

```csharp
services.AddSingleton<IslandsShellViewModel>();
services.AddSingleton<ListsIslandViewModel>();
services.AddSingleton<TasksIslandViewModel>();
services.AddSingleton<DetailsIslandViewModel>();
```

(Adjust to scoped/transient if existing patterns demand.)

- [ ] **Step 3: Build** — `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj` — fails until child VM stubs exist; that's fine, next task creates them.

- [ ] **Step 4: Defer commit** until child VMs compile.

---

### Task 9: Stub child island VMs (compile-only skeletons)

**Files (all new):**
- `src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs`

- [ ] **Step 1: Write minimal stubs** so `IslandsShellViewModel` compiles:

```csharp
// ListsIslandViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
public sealed partial class ListsIslandViewModel : ViewModelBase
{
    public event EventHandler? SelectionChanged;
    [ObservableProperty] private ListNavItemViewModel? _selectedList;
    partial void OnSelectedListChanged(ListNavItemViewModel? value) =>
        SelectionChanged?.Invoke(this, EventArgs.Empty);
}

// TasksIslandViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
public sealed partial class TasksIslandViewModel : ViewModelBase
{
    public event EventHandler? SelectionChanged;
    [ObservableProperty] private TaskRowViewModel? _selectedTask;
    public void LoadForList(ListNavItemViewModel? list) { /* Phase 5 */ }
    partial void OnSelectedTaskChanged(TaskRowViewModel? value) =>
        SelectionChanged?.Invoke(this, EventArgs.Empty);
}

// DetailsIslandViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
public sealed partial class DetailsIslandViewModel : ViewModelBase
{
    [ObservableProperty] private TaskRowViewModel? _task;
    public void Bind(TaskRowViewModel? task) => Task = task;
}
```

- [ ] **Step 2: Add minimal `ListNavItemViewModel` and `TaskRowViewModel`** so references resolve:

```csharp
// ListNavItemViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
public sealed partial class ListNavItemViewModel : ViewModelBase
{
    public required string Id { get; init; }
    public required string Name { get; init; }
    [ObservableProperty] private int _count;
    [ObservableProperty] private bool _isActive;
    public string? IconKey { get; init; }
}

// TaskRowViewModel.cs (placeholder — fleshed out in Phase 5)
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
public sealed partial class TaskRowViewModel : ViewModelBase
{
    public required string Id { get; init; }
    [ObservableProperty] private string _title = "";
    [ObservableProperty] private bool _done;
}
```

- [ ] **Step 3: Build** — `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj` should succeed.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs src/ClaudeDo.Ui/ViewModels/Islands/ src/ClaudeDo.App
git commit -m "feat(ui): scaffold islands shell and child VMs"
```

---

### Task 10: Replace `MainWindow.axaml` with chromeless three-column shell

**Files:**
- Modify: `src/ClaudeDo.Ui/Views/MainWindow.axaml`
- Modify: `src/ClaudeDo.Ui/Views/MainWindow.axaml.cs`

- [ ] **Step 1: Rewrite `MainWindow.axaml`**:

```xml
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:ClaudeDo.Ui.ViewModels"
        xmlns:islands="using:ClaudeDo.Ui.Views.Islands"
        x:Class="ClaudeDo.Ui.Views.MainWindow"
        x:DataType="vm:IslandsShellViewModel"
        Title="ClaudeDo"
        Width="1280" Height="820" MinWidth="780" MinHeight="600"
        Background="{DynamicResource VoidBrush}"
        SystemDecorations="None"
        ExtendClientAreaToDecorationsHint="True"
        ExtendClientAreaTitleBarHeightHint="-1">
  <Grid RowDefinitions="36,*">
    <!-- Custom title bar -->
    <Border Grid.Row="0" Background="{DynamicResource DeepBrush}" PointerPressed="OnTitleBarPressed">
      <Grid ColumnDefinitions="*,Auto">
        <TextBlock Grid.Column="0" Margin="14,0" VerticalAlignment="Center"
                   FontFamily="{DynamicResource SansFamily}" FontSize="12"
                   Foreground="{DynamicResource TextDimBrush}" Text="ClaudeDo"/>
        <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="0">
          <Button Classes="title-btn" Click="OnMinimize" Content="—"/>
          <Button Classes="title-btn" Click="OnToggleMax" Content="▢"/>
          <Button Classes="title-btn close" Click="OnClose" Content="✕"/>
        </StackPanel>
      </Grid>
    </Border>

    <!-- Three islands -->
    <Grid Grid.Row="1" Margin="7" ColumnDefinitions="260,*,320">
      <Border Grid.Column="0" Classes="island" Margin="7">
        <islands:ListsIslandView DataContext="{Binding Lists}"/>
      </Border>
      <Border Grid.Column="1" Classes="island" Margin="7">
        <islands:TasksIslandView DataContext="{Binding Tasks}"/>
      </Border>
      <Border Grid.Column="2" Classes="island" Margin="7"
              IsVisible="{Binding ShowDetails}">
        <islands:DetailsIslandView DataContext="{Binding Details}"/>
      </Border>
    </Grid>
  </Grid>
</Window>
```

- [ ] **Step 2: Code-behind handlers** — `MainWindow.axaml.cs`:

```csharp
using Avalonia.Controls;
using Avalonia.Input;
using ClaudeDo.Ui.ViewModels;

namespace ClaudeDo.Ui.Views;

public partial class MainWindow : Window
{
    public MainWindow() { InitializeComponent(); }

    private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
    {
        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
            BeginMoveDrag(e);
    }
    private void OnMinimize(object? s, Avalonia.Interactivity.RoutedEventArgs e) =>
        WindowState = WindowState.Minimized;
    private void OnToggleMax(object? s, Avalonia.Interactivity.RoutedEventArgs e) =>
        WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
    private void OnClose(object? s, Avalonia.Interactivity.RoutedEventArgs e) => Close();

    protected override void OnSizeChanged(SizeChangedEventArgs e)
    {
        base.OnSizeChanged(e);
        if (DataContext is IslandsShellViewModel vm) vm.WindowWidth = Bounds.Width;
    }
}
```

- [ ] **Step 3: Stub island views** so the AXAML compiles — create three minimal UserControls:

```xml
<!-- src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml -->
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.ListsIslandView"
             x:DataType="vm:ListsIslandViewModel">
  <TextBlock Margin="14" Text="Lists (placeholder)"
             Foreground="{DynamicResource TextDimBrush}"/>
</UserControl>
```

(Identical pattern for `TasksIslandView.axaml` and `DetailsIslandView.axaml`. Each needs an empty `.axaml.cs` with `InitializeComponent()`.)

- [ ] **Step 4: Run the app**

Run: `dotnet run --project src/ClaudeDo.App/ClaudeDo.App.csproj`
Expected: chromeless dark window, three islands with correct widths and 14px gaps; resize below 1100px collapses Details.

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/Views/MainWindow.axaml src/ClaudeDo.Ui/Views/MainWindow.axaml.cs src/ClaudeDo.Ui/Views/Islands/
git commit -m "feat(ui): chromeless three-island shell"
```

---

## Phase 4 — Lists Island

### Task 11: Build `ListsIslandViewModel` (real)

**Files:**
- Modify: `src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs`

The Lists island shows: a search box, then nav items in this fixed order — `My Day`, `Important`, `Planned`, `Running` (virtual filter — all tasks with status Running), `Review` (virtual: status Done with worktree state Active), then one entry per real list (excluding the three seeded "smart" lists already shown above). Counts live-update from the Tasks repository.

- [ ] **Step 1: Define `ListKind` enum and replace stub**:

```csharp
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using ClaudeDo.Data.Repositories;

namespace ClaudeDo.Ui.ViewModels.Islands;

public enum ListKind { Smart, Virtual, User }

public sealed partial class ListsIslandViewModel : ViewModelBase
{
    private readonly TaskRepository _tasks;
    private readonly ListRepository _lists;

    public event EventHandler? SelectionChanged;

    public ObservableCollection<ListNavItemViewModel> Items { get; } = new();

    [ObservableProperty] private string _searchText = "";
    [ObservableProperty] private ListNavItemViewModel? _selectedList;

    public ListsIslandViewModel(TaskRepository tasks, ListRepository lists)
    {
        _tasks = tasks; _lists = lists;
    }

    public async Task LoadAsync(CancellationToken ct = default)
    {
        Items.Clear();
        Items.Add(new ListNavItemViewModel { Id = "smart:my-day", Name = "My Day", Kind = ListKind.Smart, IconKey = "Sun" });
        Items.Add(new ListNavItemViewModel { Id = "smart:important", Name = "Important", Kind = ListKind.Smart, IconKey = "Star" });
        Items.Add(new ListNavItemViewModel { Id = "smart:planned", Name = "Planned", Kind = ListKind.Smart, IconKey = "Calendar" });
        Items.Add(new ListNavItemViewModel { Id = "virtual:running", Name = "Running", Kind = ListKind.Virtual, IconKey = "Pulse" });
        Items.Add(new ListNavItemViewModel { Id = "virtual:review", Name = "Review", Kind = ListKind.Virtual, IconKey = "Eye" });

        var seedNames = new HashSet<string>(new[] { "My Day", "Important", "Planned" });
        foreach (var l in await _lists.GetAllAsync(ct))
            if (!seedNames.Contains(l.Name))
                Items.Add(new ListNavItemViewModel { Id = $"user:{l.Id}", Name = l.Name, Kind = ListKind.User, IconKey = "Folder" });

        await RefreshCountsAsync(ct);
        SelectedList = Items.FirstOrDefault();
    }

    public async Task RefreshCountsAsync(CancellationToken ct = default)
    {
        // Implementation note: extend TaskRepository with count-by-kind helpers if missing.
        // Leave counts at 0 for now; populate as Phase 5 wires task loads.
        foreach (var i in Items) i.Count = 0;
        await Task.CompletedTask;
    }

    partial void OnSelectedListChanged(ListNavItemViewModel? value)
    {
        foreach (var i in Items) i.IsActive = ReferenceEquals(i, value);
        SelectionChanged?.Invoke(this, EventArgs.Empty);
    }
}
```

- [ ] **Step 2: Extend `ListNavItemViewModel`** — add `Kind` property:

```csharp
public required ListKind Kind { get; init; }
```

- [ ] **Step 3: Trigger `LoadAsync`** — in `IslandsShellViewModel` constructor, after wiring events:

```csharp
_ = Lists.LoadAsync();
```

- [ ] **Step 4: Build**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`
Expected: success (assuming `ListRepository.GetAllAsync` exists — if not, use the existing query method or extend the repository).

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs
git commit -m "feat(ui): ListsIslandViewModel with smart/virtual/user lists"
```

---

### Task 12: Build `ListsIslandView`

**Files:**
- Modify: `src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml`

- [ ] **Step 1: Replace placeholder** with header + search + nav `ItemsControl`:

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.ListsIslandView"
             x:DataType="vm:ListsIslandViewModel">
  <DockPanel LastChildFill="True">
    <Border DockPanel.Dock="Top" Classes="island-header">
      <StackPanel Spacing="6" Margin="14,12">
        <TextBlock Classes="eyebrow" Text="WORKSPACE"/>
        <TextBlock FontFamily="{DynamicResource SansFamily}" FontSize="18"
                   FontWeight="SemiBold" Foreground="{DynamicResource TextBrush}"
                   Text="Lists"/>
        <TextBox Classes="search" Margin="0,8,0,0" Watermark="Search…"
                 Text="{Binding SearchText, Mode=TwoWay}"/>
      </StackPanel>
    </Border>
    <ScrollViewer>
      <ItemsControl ItemsSource="{Binding Items}" Margin="6">
        <ItemsControl.ItemTemplate>
          <DataTemplate DataType="vm:ListNavItemViewModel">
            <Border Classes="list-item" Classes.active="{Binding IsActive}">
              <Grid ColumnDefinitions="20,*,Auto" Margin="10,8">
                <PathIcon Grid.Column="0" Width="14" Height="14"/>
                <TextBlock Grid.Column="1" Text="{Binding Name}"
                           VerticalAlignment="Center" Margin="8,0"
                           Foreground="{DynamicResource TextBrush}" FontSize="13"/>
                <TextBlock Grid.Column="2" Text="{Binding Count}"
                           FontFamily="{DynamicResource MonoFamily}" FontSize="10"
                           Foreground="{DynamicResource TextFaintBrush}"/>
              </Grid>
            </Border>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </ScrollViewer>
  </DockPanel>
</UserControl>
```

- [ ] **Step 2: Selection behavior** — wrap each row in a `Button` or attach a `Tapped` handler that sets `((ListsIslandViewModel)DataContext).SelectedList = item`. Quick path: replace `Border` with a `Button Classes="list-item-btn"` styled flat, `Command="{Binding $parent[ItemsControl].DataContext.SelectCommand}"` with `[RelayCommand] private void Select(ListNavItemViewModel item) => SelectedList = item;` added to the VM.

- [ ] **Step 3: Run the app** — verify list items render, search box shows, click selection toggles `active` class.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs
git commit -m "feat(ui): Lists island view with search and nav items"
```

---

## Phase 5 — Tasks Island

### Task 13: `TaskRowViewModel` — full

**Files:**
- Modify: `src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs`
- Test: `tests/ClaudeDo.Worker.Tests/UiVm/TaskRowViewModelTests.cs` (new)

- [ ] **Step 1: Write the VM** based on README §"Task model (MVVM)":

```csharp
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using ClaudeDo.Data.Models;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.ViewModels.Islands;

public sealed partial class TaskRowViewModel : ViewModelBase
{
    public required string Id { get; init; }
    [ObservableProperty] private string _title = "";
    [ObservableProperty] private string _listName = "";
    [ObservableProperty] private bool _done;
    [ObservableProperty] private bool _isStarred;
    [ObservableProperty] private bool _isMyDay;
    [ObservableProperty] private bool _isSelected;
    [ObservableProperty] private TaskStatus _status;
    [ObservableProperty] private string? _branch;
    [ObservableProperty] private string? _diffStat;
    [ObservableProperty] private string? _liveTail;

    public string StatusChipClass => Status switch
    {
        TaskStatus.Running => "running",
        TaskStatus.Failed => "error",
        TaskStatus.Done => "review",
        TaskStatus.Queued => "queued",
        _ => "idle",
    };

    public static TaskRowViewModel FromEntity(TaskEntity t) => new()
    {
        Id = t.Id, Title = t.Title, ListName = t.List?.Name ?? "",
        Done = t.Status == TaskStatus.Done,
        IsStarred = t.IsStarred, IsMyDay = t.IsMyDay,
        Status = t.Status,
        Branch = t.Worktree?.BranchName,
        DiffStat = t.Worktree?.DiffStat,
    };
}
```

- [ ] **Step 2: Write VM test** — `tests/ClaudeDo.Worker.Tests/UiVm/TaskRowViewModelTests.cs`:

```csharp
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.ViewModels.Islands;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.UiVm;

public class TaskRowViewModelTests
{
    [Theory]
    [InlineData(TaskStatus.Running, "running")]
    [InlineData(TaskStatus.Failed, "error")]
    [InlineData(TaskStatus.Done, "review")]
    [InlineData(TaskStatus.Queued, "queued")]
    [InlineData(TaskStatus.Manual, "idle")]
    public void StatusChipClass_Maps_Correctly(TaskStatus s, string expected)
    {
        var vm = new TaskRowViewModel { Id = "t" };
        vm.Status = s;
        Assert.Equal(expected, vm.StatusChipClass);
    }
}
```

- [ ] **Step 3: Run tests**

Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj --filter FullyQualifiedName~TaskRowViewModelTests`
Expected: PASS.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs tests/ClaudeDo.Worker.Tests/UiVm/
git commit -m "feat(ui): TaskRowViewModel with status chip mapping"
```

---

### Task 14: `TasksIslandViewModel` — load + add + filter

**Files:**
- Modify: `src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs`

- [ ] **Step 1: Implement** — load on list selection; add task on Enter; expose header counts:

```csharp
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.ViewModels.Islands;

public sealed partial class TasksIslandViewModel : ViewModelBase
{
    private readonly TaskRepository _tasks;
    private ListNavItemViewModel? _currentList;

    public event EventHandler? SelectionChanged;

    public ObservableCollection<TaskRowViewModel> Items { get; } = new();

    [ObservableProperty] private string _newTaskTitle = "";
    [ObservableProperty] private TaskRowViewModel? _selectedTask;
    [ObservableProperty] private string _headerTitle = "";
    [ObservableProperty] private string _headerEyebrow = "";
    [ObservableProperty] private string _subtitle = "";

    public TasksIslandViewModel(TaskRepository tasks) { _tasks = tasks; }

    public async void LoadForList(ListNavItemViewModel? list)
    {
        _currentList = list;
        Items.Clear();
        if (list is null) return;

        HeaderTitle = list.Name;
        HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd").ToUpperInvariant();

        var all = await _tasks.GetAllAsync();
        IEnumerable<TaskEntity> filtered = list.Kind switch
        {
            ListKind.Smart when list.Id == "smart:my-day" => all.Where(t => t.IsMyDay),
            ListKind.Smart when list.Id == "smart:important" => all.Where(t => t.IsStarred),
            ListKind.Smart when list.Id == "smart:planned" => all.Where(t => t.ScheduledFor != null),
            ListKind.Virtual when list.Id == "virtual:running" => all.Where(t => t.Status == TaskStatus.Running),
            ListKind.Virtual when list.Id == "virtual:review" => all.Where(t => t.Status == TaskStatus.Done && t.Worktree?.State == WorktreeState.Active),
            ListKind.User => all.Where(t => $"user:{t.ListId}" == list.Id),
            _ => Enumerable.Empty<TaskEntity>(),
        };

        foreach (var t in filtered) Items.Add(TaskRowViewModel.FromEntity(t));
        UpdateSubtitle();
    }

    private void UpdateSubtitle()
    {
        var open = Items.Count(i => !i.Done);
        var running = Items.Count(i => i.Status == TaskStatus.Running);
        var review = Items.Count(i => i.Status == TaskStatus.Done && !i.Done /* TODO refine */);
        Subtitle = $"{open} open · {running} running · {review} in review";
    }

    [RelayCommand]
    private async Task AddAsync()
    {
        if (string.IsNullOrWhiteSpace(NewTaskTitle) || _currentList?.Kind != ListKind.User) return;
        var listId = _currentList.Id["user:".Length..];
        var entity = new TaskEntity
        {
            Id = Guid.NewGuid().ToString("N"),
            ListId = listId,
            Title = NewTaskTitle.Trim(),
            CreatedAt = DateTime.UtcNow,
        };
        await _tasks.CreateAsync(entity);
        Items.Insert(0, TaskRowViewModel.FromEntity(entity));
        NewTaskTitle = "";
        UpdateSubtitle();
    }

    partial void OnSelectedTaskChanged(TaskRowViewModel? value)
    {
        foreach (var i in Items) i.IsSelected = ReferenceEquals(i, value);
        SelectionChanged?.Invoke(this, EventArgs.Empty);
    }
}
```

- [ ] **Step 2: Verify repository methods exist** — `TaskRepository.GetAllAsync` and `CreateAsync`. If names differ, adjust.

- [ ] **Step 3: Build**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`
Expected: success.

- [ ] **Step 4: Commit**

```bash
git add src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs
git commit -m "feat(ui): TasksIslandViewModel with smart/virtual/user filtering"
```

---

### Task 15: `TasksIslandView` + `TaskRowView`

**Files:**
- Modify: `src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml`
- Create: `src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml(.cs)`

- [ ] **Step 1: `TaskRowView.axaml`** — extracted UserControl per README §"Task list":

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.TaskRowView"
             x:DataType="vm:TaskRowViewModel">
  <Border Classes="task-row" Classes.selected="{Binding IsSelected}">
    <Grid ColumnDefinitions="36,*,32" Margin="10,10">
      <Button Grid.Column="0" Classes="check-btn" VerticalAlignment="Center"
              Command="{Binding $parent[ItemsControl].DataContext.ToggleDoneCommand}"
              CommandParameter="{Binding}">
        <Ellipse Width="18" Height="18" Classes="task-check"
                 Classes.done="{Binding Done}"/>
      </Button>
      <StackPanel Grid.Column="1" Spacing="6" VerticalAlignment="Center">
        <TextBlock Text="{Binding Title}" FontSize="14"
                   Foreground="{DynamicResource TextBrush}"
                   TextDecorations="{Binding Done, Converter={StaticResource StrikeIfTrue}}"/>
        <StackPanel Orientation="Horizontal" Spacing="8">
          <Border Classes="chip" Classes.running="{Binding Status, Converter={StaticResource EqStatusRunning}}">
            <TextBlock Text="{Binding Status}" FontSize="10"
                       FontFamily="{DynamicResource MonoFamily}" Margin="6,2"/>
          </Border>
          <Border Classes="chip">
            <TextBlock Text="{Binding ListName}" FontSize="10" Margin="6,2"/>
          </Border>
          <Border Classes="chip" IsVisible="{Binding Branch, Converter={StaticResource NotNullToBool}}">
            <TextBlock Text="{Binding Branch}" FontFamily="{DynamicResource MonoFamily}" FontSize="10" Margin="6,2"/>
          </Border>
          <Border Classes="chip" IsVisible="{Binding DiffStat, Converter={StaticResource NotNullToBool}}">
            <TextBlock Text="{Binding DiffStat}" FontFamily="{DynamicResource MonoFamily}" FontSize="10" Margin="6,2"/>
          </Border>
        </StackPanel>
        <TextBlock Text="{Binding LiveTail}" FontFamily="{DynamicResource MonoFamily}"
                   FontSize="11" Foreground="{DynamicResource TextMuteBrush}"
                   TextTrimming="CharacterEllipsis" MaxLines="1"
                   IsVisible="{Binding LiveTail, Converter={StaticResource NotNullToBool}}"/>
      </StackPanel>
      <Button Grid.Column="2" Classes="icon-btn" VerticalAlignment="Center"
              Command="{Binding $parent[ItemsControl].DataContext.ToggleStarCommand}"
              CommandParameter="{Binding}">
        <PathIcon Width="14" Height="14"/>
      </Button>
    </Grid>
  </Border>
</UserControl>
```

- [ ] **Step 2: Add converters** — `NotNullToBool`, `StrikeIfTrue`, `EqStatusRunning` (and one per status). Place under `src/ClaudeDo.Ui/Converters/`. Register them as resources in `App.axaml`. (Each converter is ~10 lines; copy the pattern from `StatusColorConverter.cs`.)

- [ ] **Step 3: Add `[RelayCommand]` ToggleDone / ToggleStar** to `TasksIslandViewModel`:

```csharp
[RelayCommand] private async Task ToggleDoneAsync(TaskRowViewModel row)
{
    row.Done = !row.Done;
    var entity = await _tasks.GetByIdAsync(row.Id);
    if (entity != null)
    {
        entity.Status = row.Done ? TaskStatus.Done : TaskStatus.Manual;
        await _tasks.UpdateAsync(entity);
    }
    UpdateSubtitle();
}
[RelayCommand] private async Task ToggleStarAsync(TaskRowViewModel row)
{
    row.IsStarred = !row.IsStarred;
    var entity = await _tasks.GetByIdAsync(row.Id);
    if (entity != null) { entity.IsStarred = row.IsStarred; await _tasks.UpdateAsync(entity); }
}
```

- [ ] **Step 4: `TasksIslandView.axaml`** — header, add-task box, scrollable list:

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             xmlns:islands="using:ClaudeDo.Ui.Views.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.TasksIslandView"
             x:DataType="vm:TasksIslandViewModel">
  <DockPanel LastChildFill="True">
    <Border DockPanel.Dock="Top" Classes="island-header">
      <StackPanel Margin="18,14" Spacing="6">
        <TextBlock Classes="eyebrow" Text="{Binding HeaderEyebrow}"/>
        <TextBlock FontFamily="{DynamicResource SansFamily}" FontSize="24"
                   FontWeight="SemiBold" Foreground="{DynamicResource TextBrush}"
                   Text="{Binding HeaderTitle}"/>
        <TextBlock FontFamily="{DynamicResource MonoFamily}" FontSize="11"
                   Foreground="{DynamicResource TextMuteBrush}" Text="{Binding Subtitle}"/>
      </StackPanel>
    </Border>
    <Border DockPanel.Dock="Top" Margin="18,8">
      <TextBox Watermark="Add a task…" Text="{Binding NewTaskTitle, Mode=TwoWay}">
        <TextBox.KeyBindings>
          <KeyBinding Gesture="Enter" Command="{Binding AddCommand}"/>
        </TextBox.KeyBindings>
      </TextBox>
    </Border>
    <ScrollViewer>
      <ItemsControl ItemsSource="{Binding Items}" Margin="10">
        <ItemsControl.ItemTemplate>
          <DataTemplate DataType="vm:TaskRowViewModel">
            <islands:TaskRowView/>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </ScrollViewer>
  </DockPanel>
</UserControl>
```

- [ ] **Step 5: Selection handler** — add `Tapped` on `TaskRowView` setting `((TasksIslandViewModel)DataContext.parent).SelectedTask = vm`. Or use a `[RelayCommand] private void Select(TaskRowViewModel row) => SelectedTask = row;` and bind via `InputElement.Tapped` behavior.

- [ ] **Step 6: Run app** — verify rows render with chips, add-task on Enter prepends a row, selection updates Details.

- [ ] **Step 7: Commit**

```bash
git add src/ClaudeDo.Ui/Views/Islands/ src/ClaudeDo.Ui/Converters/ src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs src/ClaudeDo.Ui/App.axaml
git commit -m "feat(ui): tasks island with rows, chips, add-task, selection"
```

---

## Phase 6 — Details Island

### Task 16: `DetailsIslandViewModel` — bind selected task + agent state

**Files:**
- Modify: `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs`
- Create: `src/ClaudeDo.Ui/ViewModels/Islands/LogLineViewModel.cs`

- [ ] **Step 1: `LogLineViewModel`**:

```csharp
namespace ClaudeDo.Ui.ViewModels.Islands;

public enum LogKind { Sys, Tool, Claude, Stdout, Stderr, Done, Msg }

public sealed class LogLineViewModel
{
    public required LogKind Kind { get; init; }
    public required string Text { get; init; }
    public string ClassName => Kind switch
    {
        LogKind.Sys => "log-sys", LogKind.Tool => "log-tool", LogKind.Claude => "log-claude",
        LogKind.Stdout => "log-stdout", LogKind.Stderr => "log-stderr",
        LogKind.Done => "log-done", LogKind.Msg => "log-msg",
    };
}
```

- [ ] **Step 2: `DetailsIslandViewModel`** — bind everything the AgentStrip + Terminal + Subtasks + Notes need:

```csharp
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Services;

namespace ClaudeDo.Ui.ViewModels.Islands;

public sealed partial class DetailsIslandViewModel : ViewModelBase
{
    private readonly TaskRepository _tasks;
    private readonly SubtaskRepository _subtasks;
    private readonly WorkerClient _worker;

    [ObservableProperty] private TaskRowViewModel? _task;
    [ObservableProperty] private string _editableTitle = "";
    [ObservableProperty] private string _notes = "";
    [ObservableProperty] private string _promptInput = "";
    [ObservableProperty] private string _agentStatusLabel = "Idle";
    [ObservableProperty] private string? _model;
    [ObservableProperty] private string? _worktreePath;
    [ObservableProperty] private string? _branchLine;
    [ObservableProperty] private int _turns;
    [ObservableProperty] private int _tokens;
    [ObservableProperty] private TimeSpan _elapsed;

    public ObservableCollection<LogLineViewModel> Log { get; } = new();
    public ObservableCollection<SubtaskRowViewModel> Subtasks { get; } = new();

    public DetailsIslandViewModel(TaskRepository tasks, SubtaskRepository subtasks, WorkerClient worker)
    {
        _tasks = tasks; _subtasks = subtasks; _worker = worker;
    }

    public async void Bind(TaskRowViewModel? row)
    {
        Task = row;
        Log.Clear(); Subtasks.Clear();
        if (row == null) return;
        var entity = await _tasks.GetByIdAsync(row.Id);
        if (entity == null) return;
        EditableTitle = entity.Title;
        Notes = entity.Notes ?? "";
        Model = entity.Model;
        WorktreePath = entity.Worktree?.Path;
        BranchLine = entity.Worktree is { } w ? $"{w.BranchName} ← main" : null;
        AgentStatusLabel = entity.Status.ToString();
        foreach (var s in await _subtasks.GetForTaskAsync(row.Id))
            Subtasks.Add(new SubtaskRowViewModel { Id = s.Id, Title = s.Title, Done = s.Completed });
    }

    [RelayCommand] private async Task SendPromptAsync()
    {
        if (string.IsNullOrWhiteSpace(PromptInput) || Task == null) return;
        Log.Add(new LogLineViewModel { Kind = LogKind.Msg, Text = $"[you] {PromptInput}" });
        await _worker.SendPromptAsync(Task.Id, PromptInput); // adjust to actual API
        PromptInput = "";
    }

    [RelayCommand] private async Task ApproveMergeAsync() { /* call worker merge */ await Task.CompletedTask; }
    [RelayCommand] private async Task StopAsync() { /* call worker stop */ await Task.CompletedTask; }
}

public sealed partial class SubtaskRowViewModel : ViewModelBase
{
    public required string Id { get; init; }
    [ObservableProperty] private string _title = "";
    [ObservableProperty] private bool _done;
}
```

- [ ] **Step 3: Wire SignalR live log** — subscribe to `WorkerClient` log events in `Bind` and append to `Log`. Use the existing event pattern; verify the API surface in `WorkerClient.cs` first.

- [ ] **Step 4: Build + commit**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj`

```bash
git add src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs src/ClaudeDo.Ui/ViewModels/Islands/LogLineViewModel.cs
git commit -m "feat(ui): DetailsIslandViewModel with agent state and log"
```

---

### Task 17: Build `DetailsIslandView` + `AgentStripView` + `SessionTerminalView`

**Files:**
- Modify: `src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml`
- Create: `src/ClaudeDo.Ui/Views/Islands/AgentStripView.axaml(.cs)`
- Create: `src/ClaudeDo.Ui/Views/Islands/SessionTerminalView.axaml(.cs)`

- [ ] **Step 1: `AgentStripView.axaml`** — three rows per README §"Agent strip":

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.AgentStripView"
             x:DataType="vm:DetailsIslandViewModel">
  <Border Classes="agent-strip">
    <StackPanel Margin="14,12" Spacing="6">
      <StackPanel Orientation="Horizontal" Spacing="10">
        <Ellipse Width="8" Height="8" Fill="{DynamicResource MossBrush}"/>
        <TextBlock Text="{Binding AgentStatusLabel}" FontSize="12"
                   Foreground="{DynamicResource TextBrush}"/>
        <TextBlock Text="{Binding Model}" FontFamily="{DynamicResource MonoFamily}"
                   FontSize="11" Foreground="{DynamicResource TextDimBrush}"/>
        <TextBlock Text="{Binding Turns, StringFormat='turns: {0}'}" FontSize="11"
                   Foreground="{DynamicResource TextMuteBrush}"/>
        <TextBlock Text="{Binding Tokens, StringFormat='tok: {0}'}" FontSize="11"
                   Foreground="{DynamicResource TextMuteBrush}"/>
      </StackPanel>
      <TextBlock Text="{Binding WorktreePath}" FontFamily="{DynamicResource MonoFamily}"
                 FontSize="11" Foreground="{DynamicResource TextDimBrush}"
                 TextTrimming="CharacterEllipsis"/>
      <TextBlock Text="{Binding BranchLine}" FontFamily="{DynamicResource MonoFamily}"
                 FontSize="11" Foreground="{DynamicResource TextDimBrush}"/>
      <StackPanel Orientation="Horizontal" Spacing="8" Margin="0,6,0,0">
        <Button Content="Open diff"/>
        <Button Content="Worktree"/>
        <Button Content="Stop" Command="{Binding StopCommand}"/>
        <Button Content="Approve &amp; merge" Command="{Binding ApproveMergeCommand}"/>
      </StackPanel>
    </StackPanel>
  </Border>
</UserControl>
```

- [ ] **Step 2: `SessionTerminalView.axaml`** — log + prompt input:

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.SessionTerminalView"
             x:DataType="vm:DetailsIslandViewModel">
  <Border Classes="terminal" Padding="10">
    <DockPanel LastChildFill="True">
      <Grid DockPanel.Dock="Bottom" ColumnDefinitions="Auto,*" Margin="0,8,0,0">
        <TextBlock Grid.Column="0" Text="[you]" FontFamily="{DynamicResource MonoFamily}"
                   FontSize="11" Foreground="{DynamicResource MossBrush}"
                   VerticalAlignment="Center" Margin="0,0,8,0"/>
        <TextBox Grid.Column="1" Text="{Binding PromptInput, Mode=TwoWay}">
          <TextBox.KeyBindings>
            <KeyBinding Gesture="Enter" Command="{Binding SendPromptCommand}"/>
          </TextBox.KeyBindings>
        </TextBox>
      </Grid>
      <ScrollViewer Name="LogScroll" VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Log}">
          <ItemsControl.ItemTemplate>
            <DataTemplate DataType="vm:LogLineViewModel">
              <TextBlock Text="{Binding Text}" Classes="{Binding ClassName}"
                         FontFamily="{DynamicResource MonoFamily}" FontSize="11"
                         TextWrapping="Wrap"/>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </ScrollViewer>
    </DockPanel>
  </Border>
</UserControl>
```

In code-behind: subscribe to `Log.CollectionChanged` and call `LogScroll.ScrollToEnd()`.

- [ ] **Step 3: `DetailsIslandView.axaml`** — assemble sections per README §"Details island":

```xml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
             xmlns:islands="using:ClaudeDo.Ui.Views.Islands"
             x:Class="ClaudeDo.Ui.Views.Islands.DetailsIslandView"
             x:DataType="vm:DetailsIslandViewModel">
  <ScrollViewer>
    <StackPanel Margin="18,14" Spacing="14">
      <TextBox Text="{Binding EditableTitle, Mode=TwoWay}" FontSize="18"
               BorderThickness="0" Background="Transparent"
               Foreground="{DynamicResource TextBrush}"/>
      <islands:AgentStripView/>
      <islands:SessionTerminalView Height="260"/>
      <ItemsControl ItemsSource="{Binding Subtasks}">
        <ItemsControl.ItemTemplate>
          <DataTemplate DataType="vm:SubtaskRowViewModel">
            <StackPanel Orientation="Horizontal" Spacing="8" Margin="0,2">
              <CheckBox IsChecked="{Binding Done, Mode=TwoWay}"/>
              <TextBlock Text="{Binding Title}" VerticalAlignment="Center"
                         Foreground="{DynamicResource TextBrush}"/>
            </StackPanel>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
      <TextBox Text="{Binding Notes, Mode=TwoWay}" AcceptsReturn="True"
               TextWrapping="Wrap" MinHeight="80" Watermark="Notes…"/>
    </StackPanel>
  </ScrollViewer>
</UserControl>
```

- [ ] **Step 4: Run + visually verify**

Run: `dotnet run --project src/ClaudeDo.App/ClaudeDo.App.csproj`
Expected: clicking a task populates Details with title, agent strip, terminal area, notes.

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml src/ClaudeDo.Ui/Views/Islands/AgentStripView.axaml src/ClaudeDo.Ui/Views/Islands/AgentStripView.axaml.cs src/ClaudeDo.Ui/Views/Islands/SessionTerminalView.axaml src/ClaudeDo.Ui/Views/Islands/SessionTerminalView.axaml.cs
git commit -m "feat(ui): details island with agent strip, terminal, subtasks, notes"
```

---

## Phase 7 — Modals

### Task 18: Diff modal

**Files:**
- Create: `src/ClaudeDo.Ui/Views/Modals/DiffModalView.axaml(.cs)`
- Create: `src/ClaudeDo.Ui/ViewModels/Modals/DiffModalViewModel.cs`

- [ ] **Step 1: ViewModel** — exposes `Files: ObservableCollection<DiffFileVm>`, `SelectedFile`, each file has `Hunks: List<DiffLineVm>` with `Kind: Add|Del|Ctx`, `OldNo`, `NewNo`, `Text`. Populate from `git diff` output (extend `GitService` with a parser if missing — initial scope: stub data).

- [ ] **Step 2: View** — borderless `Window` (`SystemDecorations="None"`, `WindowStartupLocation="CenterOwner"`, `Background="Transparent"`). Inner `Border Classes="modal"` with `Grid ColumnDefinitions="240,*"`: left `ListBox` of files (each row showing name + `+N −N` chips), right `ItemsControl` of hunk lines styled by kind (`del` red-tinted, `add` green-tinted, `ctx` neutral) — per README §"Diff modal".

- [ ] **Step 3: Wire button** — `AgentStripView` "Open diff" `Button.Click` opens the window with the current task's diff.

- [ ] **Step 4: Esc closes** — `KeyBindings` in modal: `<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>`.

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/Views/Modals/DiffModalView.axaml src/ClaudeDo.Ui/Views/Modals/DiffModalView.axaml.cs src/ClaudeDo.Ui/ViewModels/Modals/DiffModalViewModel.cs
git commit -m "feat(ui): diff modal with file sidebar and tinted hunks"
```

---

### Task 19: Worktree modal

**Files:**
- Create: `src/ClaudeDo.Ui/Views/Modals/WorktreeModalView.axaml(.cs)`
- Create: `src/ClaudeDo.Ui/ViewModels/Modals/WorktreeModalViewModel.cs`

- [ ] **Step 1: ViewModel** — `TreeNodes: ObservableCollection<WorktreeNodeVm>` (recursive, with `Name`, `Status: 'M'|'A'|null`, `Children`).

- [ ] **Step 2: View** — modal `Window` (same chrome pattern as diff modal). Body: `TreeView` bound to `TreeNodes`, each node `StackPanel Horizontal` with name + status badge (`M` peat-tinted, `A` moss-tinted).

- [ ] **Step 3: Populate** — parse `git status --porcelain` from worktree path; build tree by splitting paths on `/`.

- [ ] **Step 4: Wire button + Esc** as in Task 18.

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/Views/Modals/WorktreeModalView.axaml src/ClaudeDo.Ui/Views/Modals/WorktreeModalView.axaml.cs src/ClaudeDo.Ui/ViewModels/Modals/WorktreeModalViewModel.cs
git commit -m "feat(ui): worktree modal with tree view and M/A badges"
```

---

## Phase 8 — Animations and keyboard shortcuts

### Task 20: Animations

**Files:**
- Modify: `src/ClaudeDo.Ui/Design/IslandStyles.axaml`

- [ ] **Step 1: Task-row hover** — add `Transitions` on `Border.task-row`:

```xml
<Style Selector="Border.task-row">
  <Setter Property="Transitions">
    <Transitions>
      <BrushTransition Property="Background" Duration="0:0:0.10"/>
    </Transitions>
  </Setter>
</Style>
```

- [ ] **Step 2: Running pulse** — add to `Ellipse.status-pulse`:

```xml
<Style Selector="Ellipse.status-pulse">
  <Style.Animations>
    <Animation Duration="0:0:1.2" IterationCount="Infinite" Easing="CubicEaseInOut">
      <KeyFrame Cue="0%"><Setter Property="Opacity" Value="0.4"/></KeyFrame>
      <KeyFrame Cue="50%"><Setter Property="Opacity" Value="1.0"/></KeyFrame>
      <KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.4"/></KeyFrame>
    </Animation>
  </Style.Animations>
</Style>
```

- [ ] **Step 3: Task-row add** — animate inserted row opacity+Y. In `TaskRowView.axaml.cs`, on `AttachedToVisualTree` start a 0.3s `Animation` on opacity (0→1) and `TranslateTransform.Y` (8→0).

- [ ] **Step 4: Modal scale-in** — modal root `Border Classes="modal"` gets `RenderTransform` with `ScaleTransform`; on open animate 0.18s from `ScaleX/Y=0.98, Opacity=0` to `1.0, 1.0`.

- [ ] **Step 5: Visual verify** — run app, observe pulse, hover, modal animation.

- [ ] **Step 6: Commit**

```bash
git add src/ClaudeDo.Ui/Design/IslandStyles.axaml src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml.cs src/ClaudeDo.Ui/Views/Modals/
git commit -m "feat(ui): pulse, hover, modal, and row-add animations"
```

---

### Task 21: Keyboard shortcuts

**Files:**
- Modify: `src/ClaudeDo.Ui/Views/MainWindow.axaml(.cs)`

- [ ] **Step 1: Window-level KeyBindings**:

```xml
<Window.KeyBindings>
  <KeyBinding Gesture="OemQuestion" Command="{Binding FocusSearchCommand}"/>
  <KeyBinding Gesture="Ctrl+N" Command="{Binding FocusAddTaskCommand}"/>
  <KeyBinding Gesture="Space" Command="{Binding ToggleSelectedDoneCommand}"/>
</Window.KeyBindings>
```

(`/` is the `OemQuestion` gesture without shift on US layouts; on DE layout you may need `KeyDown` handler instead — verify on the target machine.)

- [ ] **Step 2: Implement commands on `IslandsShellViewModel`**:

```csharp
[RelayCommand] private void FocusSearch() { /* raise event consumed by ListsIslandView code-behind to call Focus() on the search box */ }
[RelayCommand] private void FocusAddTask() { /* same pattern for tasks add box */ }
[RelayCommand] private async Task ToggleSelectedDoneAsync()
{
    if (Tasks.SelectedTask is { } row)
        await Tasks.ToggleDoneCommand.ExecuteAsync(row);
}
```

- [ ] **Step 3: Esc closes modals** — already in modal `KeyBindings` from Tasks 18–19.

- [ ] **Step 4: Visual verify**

- [ ] **Step 5: Commit**

```bash
git add src/ClaudeDo.Ui/Views/MainWindow.axaml src/ClaudeDo.Ui/Views/MainWindow.axaml.cs src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs
git commit -m "feat(ui): keyboard shortcuts (/ Ctrl+N Space Esc)"
```

---

## Phase 9 — Cleanup

### Task 22: Remove obsolete views and viewmodels

**Files (delete):**
- `src/ClaudeDo.Ui/Views/StatusBarView.axaml(.cs)`
- `src/ClaudeDo.Ui/Views/TaskListView.axaml(.cs)`
- `src/ClaudeDo.Ui/Views/TaskDetailView.axaml(.cs)`
- `src/ClaudeDo.Ui/Views/TaskEditorView.axaml(.cs)`
- `src/ClaudeDo.Ui/Views/ListEditorView.axaml(.cs)`
- `src/ClaudeDo.Ui/ViewModels/StatusBarViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/TaskListViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/TaskDetailViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/TaskEditorViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/ListEditorViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/TaskItemViewModel.cs` (only if unused)
- `src/ClaudeDo.Ui/ViewModels/ListItemViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/SubtaskItemViewModel.cs`
- `src/ClaudeDo.Ui/ViewModels/MainWindowViewModel.cs`

- [ ] **Step 1: Verify nothing references them**

Run: `grep -rn "MainWindowViewModel\|TaskListViewModel\|TaskDetailViewModel\|StatusBarViewModel\|ListEditorViewModel\|TaskEditorViewModel\|TaskItemViewModel\|ListItemViewModel\|SubtaskItemViewModel" src/`

Expected: no hits outside the files being deleted (and DI registration sites you've already migrated in Task 8).

- [ ] **Step 2: Delete** with `git rm` on every listed file.

- [ ] **Step 3: Build + run smoke test**

Run: `dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj && dotnet run --project src/ClaudeDo.App/ClaudeDo.App.csproj`
Expected: build succeeds, app launches with new shell, all interactions still work.

- [ ] **Step 4: Commit**

```bash
git commit -m "chore(ui): remove obsolete pre-rewrite views and viewmodels"
```

---

## Acceptance — final pass

Once Task 22 is committed, walk the README's Acceptance checklist (lines 236–249) interactively:

- [ ] Three-island layout, correct spacing, collapse <1100px
- [ ] Lists sidebar with icons, counts, search, active state
- [ ] Task rows with checkbox, title, meta chips, star
- [ ] Selection updates Details
- [ ] Agent strip shows status, model, turns, tokens, elapsed, worktree, branch
- [ ] Session terminal renders all log kinds with distinct colors, auto-scrolls, accepts prompt input
- [ ] Diff modal with file sidebar and tinted lines
- [ ] Worktree modal with M/A badges
- [ ] Status chip tints match
- [ ] Fonts: Inter Tight + JetBrains Mono applied
- [ ] Motion: row add/toggle, pulse, modal open, hover transitions
- [ ] Keyboard shortcuts wired

If any item is missing or visually off, file a follow-up task — do not silently skip.
