Files
ClaudeDo/docs/superpowers/plans/2026-08-11-operation-feedback.md
T
mika kuns f9a8ed761a
Changelog / changelog (push) Successful in 2s
Release / release (push) Successful in 43s
docs: record the operation-feedback state before the v2.10.0 cut
Plan checkboxes stood at zero although P0, A, B, C1 and D are all merged --
anyone reading it would have assumed nothing was built. Ticks the merged
packages, records A5's cancellation and the visual pass, and names the two
leftovers (the three unused ops.* keys from A3/A4, C2-C5 as the only open
group). Also corrects the batch_* tool count from eight to seven and puts the
OperationTiming kill-switch note in the Ui CLAUDE.md back into English.
2026-08-17 11:45:03 +02:00

516 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Feedback für langlaufende Operationen — Vorgaben für die Umsetzung
> **Dieses Dokument ist absichtlich kein Task-Skript.** Es enthält die Vorgaben pro Gruppe.
> Die konkreten Tasks schreibt der **Merge-Helper je Gruppe** beim Ausführen — er kennt dann
> den tatsächlichen Codestand. Was hier steht, ist bindend; was hier nicht steht, entscheidet
> er. Checkboxen stehen auf Paketebene, damit der Fortschritt sichtbar bleibt.
**Design:** `docs/superpowers/specs/2026-08-11-operation-feedback-design.md` — dort stehen die
Belege (Datei:Zeile) und die Begründungen. Dieses Dokument wiederholt sie nicht.
**Ziel:** Jede Operation, die länger als ~300 ms dauern kann, zeigt an, dass sie läuft, was sie
tut und wie lange sie schon läuft — über **einen** Mechanismus statt pro Fall neu gebaut.
---
## Ausführungsmodell
```
P0 (Fundament) ── muss ALLEIN und ZUERST landen
├── Gruppe A UI-Stille Merge-Helper 1
├── Gruppe B UI-Freeze Merge-Helper 2
└── Gruppe C Worker-Stille Merge-Helper 3
Gruppe D MCP-Stille Merge-Helper 4 ── unabhängig von P0, kann sofort starten
```
**Ein Merge-Helper pro Gruppe.** Nach P0 laufen A, B, C und D parallel. Jede Gruppe hält sich
strikt an ihr Datei-Eigentum — das ist die einzige Absicherung gegen gegenseitiges
Überschreiben, weil alle im gemeinsamen `main`-Checkout arbeiten.
### Datei-Eigentum (bindend)
| Gruppe | Besitzt exklusiv | Darf **nicht** anfassen |
|---|---|---|
| **P0** | `Ui/Services/OperationStatus.cs` (neu), `Ui/Views/Controls/OperationIndicator.axaml(.cs)` (neu), **beide `locales/*.json`**, `Ui/Services/WorkerClient.cs` (nur Timing-Hook) | alles andere |
| **A** | Island-VMs, Modal-VMs, deren AXAML, `Ui/Services/UpdateCheckService.cs` | `locales/*`, `WorkerClient`, `IWorkerClient`, Worker-Projekt |
| **B** | `Ui/ViewModels/Modals/DiffViewerViewModel.cs`, `Ui/Views/Controls/DiffTextView.axaml.cs` | `locales/*`, Island-VMs, Worker-Projekt |
| **C** | `Worker/Hub/HubBroadcaster.cs`, `Worker/Hub/WorkerHub.cs`, `Ui/Services/WorkerClient.cs`, `IWorkerClient.cs`, `StubWorkerClient.cs`, `Worker/Lifecycle/*Recovery.cs`, `Worker/Runner/WorktreeManager.cs`, `Worker/Worktrees/WorktreeMaintenanceService.cs` | `locales/*`, Island-VMs, `Worker/External/*` |
| **D** | `Worker/External/*McpTools.cs`, `Worker/External/ExternalMcpService.cs`, `Worker/Lifecycle/TaskMergeService.cs` (nur die Progress-Schleife) | `locales/*`, alles im Ui-Projekt |
**Locale-Regel:** Nur P0 schreibt in `en.json`/`de.json`. Braucht eine Gruppe doch einen Key,
den P0 nicht vorgesehen hat: als **letzte** Änderung des Pakets anhängen und im Merge-Helper
als bekannte Konfliktstelle behandeln. Nie mitten in der Gruppe.
---
## Vorgaben für den Merge-Helper selbst
Gilt für alle vier Gruppen:
- **Agent-Modell:** `sonnet` für Implementierer und Reviewer. Nie haiku, nie opus, nie Fable.
- **`maxTurns` explizit auf 200 setzen.** `model_presets` ist NULL, sonst bekommt ein
sonnet-Task 30 Turns und stirbt mit „exited with code 1 and no result".
- **`serializeOnFileOverlap` auf der Gruppenliste einschalten.** Innerhalb einer Gruppe
fassen mehrere Pakete dieselben Dateien an.
- **Bauen:** `dotnet build ClaudeDo.slnx` schlägt auf .NET 8 fehl. Einzelprojekte mit
`-c Release` bauen (ein laufender Worker sperrt `Debug`):
```
dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release
dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release
```
- **Testen — pro Gruppe die relevanten Projekte:**
```
A, B dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release
dotnet test tests/ClaudeDo.Localization.Tests/... -c Release (nur wenn Keys berührt)
C Ui.Tests + ClaudeDo.Worker.Tests (Worker.Tests enthält auch UiVm-Tests)
D dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release
P0 Ui.Tests + Localization.Tests
```
Fällt ein Test innerhalb der Verify-Kette, erst die Suite **allein** laufen lassen — die
`Ui.Tests` haben eine bekannte reihenfolgen-abhängige Flakiness, die nichts mit dem Merge zu
tun hat.
- **Nie `git add -A`.** Immer explizit nach Pfad stagen und `git commit -- <pfade>` — der
`main`-Checkout ist von parallelen Sessions geteilt, ein blankes Commit fegt fremde WIP mit.
- **Nach dem Batch-Merge `main` selbst prüfen.** Approve baut und testet nicht. Neun grüne
Branches haben `main` schon zweimal zerlegt, davon einmal durch eine Test-Kollision, die ein
reiner src-Build nicht sieht. Also: Build **und** die betroffenen Testprojekte.
- **Vor Approve prüfen, ob der Branch überhaupt etwas enthält** (`changedFileCount`). Ein
blockierter Task wird sonst `Done` mit leerem Branch.
- **Keine EF-Migration** in diesem Vorhaben. Parallele Migrationen aus Geschwister-Branches
löschen sich beim SQLite-Table-Rebuild gegenseitig die Spalten, und die Tests bemerken es
nicht (`EnsureCreated`).
- **Keine Tests, die die echte `claude`-CLI starten.**
- **Visuelle Prüfung ist nicht durch Tests ersetzbar.** Jedes Paket in A und B endet mit einer
offenen visuellen Prüfung für den Nutzer. Nie behaupten, die UI funktioniere.
---
## P0 — Fundament
- [x] **P0-1 — `OperationStatus` + `OperationIndicator` + Locale-Keys**
- [x] **P0-2 — Timing-Hook für die Messung**
- [x] **P0-3 — Regel in den CLAUDE.md-Dateien**
> **Stand 2026-08-17.** P0, A (außer A5), B, C1 und D sind gemergt; der visuelle Pass über A und B
> ist durch, ohne Fund. Nachtrag zu P0-2: der Timing-Sink liegt seit dem Release-Vorlauf hinter
> `CLAUDEDO_OP_TIMING=1` und ist **standardmäßig aus** — die 63 `InvokeTimedAsync`-Call-Sites
> bleiben bewusst stehen. Offen: C2C5.
**Vorbedingung:** keine. Muss allein landen, bevor A/B/C starten.
### Der Vertrag (bindend — 19 Pakete hängen daran)
`src/ClaudeDo.Ui/Services/OperationStatus.cs`, eine `ObservableObject`-Klasse:
| Member | Verhalten |
|---|---|
| `bool IsRunning` | **sofort** `true` bei `Begin`. Treibt `CanExecute`, verhindert Doppelklick |
| `bool ShowIndicator` | erst nach **300 ms** `true`. Kein Flackern bei schnellen Calls |
| `string? Label` | lokalisierter Text |
| `string Elapsed` | `mm:ss`, **lokal getickt** |
| `bool IsStalled` | `true` nach **60 s ohne `Report`** |
| `IDisposable Begin(string label)` | startet; `Dispose` beendet **auch im Exception-Fall** |
| `void Report(string label)` | überschreibt `Label` mid-flight, setzt die Stall-Uhr zurück |
**Nachbesserung 1 (bindend):** `IsStalled` bemisst sich an der Zeit **seit dem letzten
`Report`**, nicht an der Gesamtdauer. Ein regulär mehrminütiges Verify-Gate darf sich nicht
selbst als hängend melden. Eine Operation ohne jeden `Report` gilt nach 60 s als stalled — das
ist genau der Fall, der heute wie ein Absturz aussieht.
Benutzung im ViewModel:
```csharp
using var op = Approve.Begin(Loc.T("ops.merge.merging"));
var result = await _worker.ApproveReviewAsync(...);
```
### Weitere Vorgaben
- **Zeitquelle injizierbar** über `TimeProvider` (in .NET 8 vorhanden). **Kein statischer
`DispatcherTimer`** — geteilter statischer State ist die Ursache der reihenfolgen-abhängigen
Flakiness in den `Ui.Tests`. Timer-Callbacks kommen vom Threadpool und müssen auf den
Dispatcher gepostet werden.
- **Mehrere `OperationStatus` pro VM sind erwünscht.** `WorktreesOverviewModalViewModel`
braucht getrennte für Refresh, Cleanup und Merge — sonst blockiert ein laufender Refresh die
Cleanup-Anzeige.
- **`OperationStatus` transportiert keine Fehler.** Fehlerbehandlung bleibt unverändert über
`ShowErrorAsync` / `ErrorReported` / `FlashFooterError`.
- **`OperationIndicator`** nutzt `Ellipse.spinner` aus `Design/IslandStyles.axaml` (14×14,
Accent, 0.9 s Rotation) plus Label und Elapsed. Ohne dieses Control wird die
Spinner-StackPanel aus `MergeModalView.axaml` acht Mal von Hand nachgebaut. Werte aus
`Tokens.axaml` verwenden, keine Inline-Zahlen.
- **Locale-Keys:** neuer Top-Level-Namespace `ops` in `en.json` und `de.json`, Parität wird von
`Localization.Tests` erzwungen. P0 legt die Keys für **A und C** vorab an — abgeleitet aus den
Paketlisten unten.
**Nachbesserung 3 (bindend):** **Gruppe D braucht keine Locale-Keys.**
MCP-Progress-Meldungen gehen an Agenten, nicht an den Nutzer, und bleiben englische
Klartext-Strings.
### P0-2: Messung an genau einer Stelle
Zwei Hooks, nicht zwanzig:
1. In `WorkerClient` jeden Hub-Invoke mit Dauer loggen.
2. Im DB-Pfad der Islands dasselbe.
Ein Tag Nutzung liefert eine sortierte Liste echter Ausreißer. **A5 und C werten sie aus**,
statt zu raten. Bild 2 und 4 brauchen keine Messung.
Faustregel für den Zweifelsfall: instrumentiert wird, was git aufruft, Netz nutzt, einen
Prozess startet, oder O(n) über unbegrenzt viele DB-Zeilen läuft. Einzelzeilen-Reads nicht.
### Definition of Done für P0
`OperationStatus` hat Unit-Tests mit einem Fake-`TimeProvider` für: 300-ms-Grace, 60-s-Stall
**ab letztem Report**, Elapsed-Formatierung, `Dispose` nach Exception, `Report` überschreibt
Label und setzt die Stall-Uhr zurück. `Localization.Tests` grün. Kein VM ist umgestellt — P0
liefert nur das Werkzeug.
### Task-Entwürfe (grob)
**P0-1 · `OperationStatus` + `OperationIndicator` + `ops`-Locale-Keys**
Baue das Primitive nach dem Vertrag oben, dazu das Anzeige-Control und den neuen
Locale-Namespace `ops` in beiden Sprachdateien. Keys für Gruppe A und C vorab anlegen, für D
keine. Zeitquelle über `TimeProvider`, kein statischer Timer.
*Dateien:* `Ui/Services/OperationStatus.cs` (neu), `Ui/Views/Controls/OperationIndicator.axaml(.cs)` (neu), `Localization/locales/{en,de}.json`, `tests/ClaudeDo.Ui.Tests/`
*Fertig wenn:* Unit-Tests für Grace, Stall-ab-Report, Elapsed, Dispose-nach-Exception, Report-Reset. Localization.Tests grün. Kein ViewModel angefasst.
**P0-2 · Timing-Hook für die Messung**
Zwei Hooks: Dauer jedes Hub-Invokes in `WorkerClient`, Dauer des DB-Pfads der Islands. Ziel
sind auswertbare Zahlen, keine Anzeige. Ausgabe so, dass sie nach einem Tag Nutzung sortierbar
ist.
*Dateien:* `Ui/Services/WorkerClient.cs`, der gemeinsame DB-Zugriffspfad der Island-VMs
*Fertig wenn:* Beide Hooks aktiv, Overhead vernachlässigbar, das Format ist dokumentiert.
**P0-3 · Feedback-Regel in den CLAUDE.md-Dateien**
`src/ClaudeDo.Ui/CLAUDE.md`: jeder `IWorkerClient`-Call in einem `[RelayCommand]` läuft durch
eine `OperationStatus`, Anzeige über `OperationIndicator`. `src/ClaudeDo.Worker/CLAUDE.md`: ein
MCP-Tool, das über ~5 s laufen kann, reportet Progress.
*Fertig wenn:* Beide Regeln stehen, jeweils mit einem Satz Begründung.
---
## Gruppe A — Bild 1: UI-Stille
- [x] **A1 — Detail-Pane** ⚠️ *der gemeldete Fall*
- [x] **A2 — WorktreesOverview**
- [x] **A3 — Settings-Tabs** — ohne OnlineInbox-SignIn, siehe Nachzügler unten
- [x] **A4 — Reports und Planning-UI** — ohne die beiden Planning-Calls im `DiffViewerViewModel`
- [x] ~~**A5 — Island-DB-Pfade nach Messdaten**~~ — **abgesagt** (2026-08-13): kein DB-Pfad über
500 ms (höchster p95 372 ms), die Ausreißer saßen alle auf der Hub-Seite. Wie vorgesehen
geschlossen, ohne Code.
**Nachzügler (kein Task):** drei Locale-Keys aus P0 sind ungenutzt geblieben —
`ops.onlineInbox.signingIn` (A3), `ops.planning.buildingIntegrationBranch` und
`ops.planning.loadingAggregate` (A4). Die beiden Planning-Calls liegen in
`DiffViewerViewModel.cs`, einer Datei aus Gruppe B, die A4 nicht anfassen durfte.
**Vorbedingung:** P0 gemergt.
### Vorgaben
- **A1 zuerst.** `DetailsIslandViewModel.ApproveReviewAsync` ist der gemeldete Schmerz. Dazu im
selben Paket: `SubmitForReviewAsync`, `RejectReviewAsync`, `ParkReviewAsync` und
`MergeSectionViewModel.PreviewMergeAsync`.
- **A1 abonniert `IWorkerClient.MergeProgressEvent`**, um das Label mid-flight von „merging" auf
„verifying" zu schärfen. Das Event existiert seit `cad0582`.
**Nachbesserung 2 (bindend):** A verlässt sich darauf, dass dieses Event **bestehen bleibt**.
C1 baut den generischen Kanal darunter, muss `MergeProgressEvent` aber als dünnen Forwarder
erhalten. Andernfalls müsste C `DetailsIslandViewModel` ändern — eine Datei aus Gruppe A —
und die Parallelität wäre zerstört. **A darf keinen anderen Kanal verwenden.**
- **Abo nur für die Dauer des Calls.** Der Worker broadcastet an alle Clients; ein dauerhaft
registriertes VM bekommt Events für fremde Tasks. `MergeModalViewModel` macht es richtig
vor: `+=` im `try`, `-=` im `finally`, plus `if (taskId != TaskId) return;`.
- **A2:** `IsBusy` existiert dort schon, schaltet aber nur `IsEnabled`. Die Anzeige fehlt
komplett. Betrifft Refresh, Cleanup, Reset, ForceRemove und Batch-Merge — Batch-Merge
zusätzlich mit Zeilen-Status, weil dort N Tasks sequenziell durchlaufen.
- **A3:** SessionSkill-Install ist ein `git clone` — der offensichtlichste Kandidat. Dazu
Update/Restore-Defaults, OnlineInbox-SignIn, RepoImport-Scan, Update-Check.
- **A4:** GenerateWeekReport, RunDailyPrepNow, BuildPlanningIntegrationBranch,
GetPlanningAggregate, Finalize/QueuePlanningSubtasks. `DiffViewerViewModel.IsLoadingCombined`
existiert bereits und bleibt — nicht doppeln.
- **A5 erst nach Auswertung von P0-2.** Nur Stellen umstellen, die die Messung als Ausreißer
zeigt. Kein Umstellen auf Verdacht.
- **`DetailsIslandViewModel` ist groß.** Nicht umstrukturieren, nur die Commands anfassen.
- **Bereits saubere Flächen nicht anfassen:** `MergeModalViewModel`,
`ConflictResolverViewModel`, `DiffViewerViewModel.IsLoadingCombined`,
`UsageMonitorModalViewModel`.
### Definition of Done pro Paket
Jeder umgestellte Command: `IsRunning` während des Laufs gesetzt, `CanExecute` gesperrt,
Zustand nach einer Exception zurückgesetzt — als Test in `ClaudeDo.Ui.Tests`. Kein
handgebauter Spinner, immer `OperationIndicator`. **Visuelle Prüfung offen und explizit
benannt** (Grace-Periode und Layout kann kein Test bestätigen).
### Task-Entwürfe (grob)
**A1 · Detail-Pane: Approve, Submit, Reject, Park, Preview** ⚠️ *der gemeldete Fall*
Fünf Commands im Detail-Pane bekommen je eine `OperationStatus` und einen `OperationIndicator`
neben dem auslösenden Button. Approve abonniert zusätzlich `MergeProgressEvent` **nur für die
Dauer des Calls** und filtert auf die eigene TaskId, um das Label von „merging" auf „verifying"
zu schärfen. `DetailsIslandViewModel` nicht umstrukturieren.
*Dateien:* `Ui/ViewModels/Islands/DetailsIslandViewModel.cs`, `MergeSectionViewModel.cs`, `Ui/Views/Islands/Detail/*`
*Fertig wenn:* Approve zeigt während des Verify-Gates Phase und Elapsed, Button ist gesperrt, Zustand nach Fehler zurückgesetzt. Visuelle Prüfung offen.
**A2 · WorktreesOverview: Anzeige für alle fünf Aktionen**
`IsBusy` existiert, schaltet aber nur `IsEnabled`. Getrennte `OperationStatus` für Refresh,
Cleanup, Reset, ForceRemove und Batch-Merge — der Batch-Merge zusätzlich mit Zeilen-Status, weil
er N Tasks sequenziell durchläuft.
*Dateien:* `Ui/ViewModels/Modals/WorktreesOverviewModalViewModel.cs`, `Ui/Views/Modals/WorktreesOverviewModalView.axaml`
*Fertig wenn:* Jede der fünf Aktionen zeigt sichtbar, dass sie läuft; ein laufender Refresh blockiert die Cleanup-Anzeige nicht. Visuelle Prüfung offen.
**A3 · Settings-Tabs: Skill-Install, Restore-Defaults, OnlineInbox, RepoImport, Update-Check**
Fünf Flächen, bei denen `IsBusy` heute nur den Button sperrt. Der Skill-Install ist ein
`git clone` und der offensichtlichste Kandidat.
*Dateien:* `Ui/ViewModels/Modals/Settings/*`, `Ui/ViewModels/Modals/RepoImportModalViewModel.cs`, `Ui/Services/UpdateCheckService.cs`, `Ui/Views/Modals/SettingsModalView.axaml`
*Fertig wenn:* Alle fünf zeigen Aktivität; der Skill-Install zeigt zusätzlich, dass geklont wird. Visuelle Prüfung offen.
**A4 · Reports und Planning-UI**
GenerateWeekReport, RunDailyPrepNow, BuildPlanningIntegrationBranch, GetPlanningAggregate,
Finalize/QueuePlanningSubtasks. `DiffViewerViewModel.IsLoadingCombined` existiert bereits und
bleibt unangetastet.
*Dateien:* `Ui/ViewModels/Modals/WeeklyReportModalViewModel.cs`, Planning-Pfade in `DetailsIslandViewModel`/`TasksIslandViewModel`, zugehörige AXAML
*Fertig wenn:* Report-Generierung und Planning-Integration zeigen Aktivität; kein Doppel-Indikator im Combined-Diff. Visuelle Prüfung offen.
**A5 · Island-DB-Pfade nach Messdaten**
**Erst die Zahlen aus P0-2 auswerten**, dann nur die Ausreißer umstellen. Kandidaten:
`ClearCompleted`, Drag-Reorder über viele Zeilen, Laden großer Listen. Nichts auf Verdacht.
*Dateien:* nach Messergebnis
*Fertig wenn:* Die Auswertung ist im Task dokumentiert, und jede umgestellte Stelle ist durch eine Messung begründet. Wenn nichts über der Schwelle liegt: Task mit Begründung schließen, nichts bauen.
---
## Gruppe B — Bild 2: UI-Freeze
- [x] **B1 — `UnifiedDiffParser.Parse` auslagern**
- [x] **B2 — `DiffAlignment.Build` Grenze**
- [x] **B3 — Guard-Test gegen Dispatcher-Blockade**
**Vorbedingung:** P0 gemergt (für den Indikator in B1).
### Vorgaben
- **Reihenfolge ist zwingend: erst auslagern, dann anzeigen.** Ein Spinner auf einem
blockierten UI-Thread wird nicht gezeichnet. B1 verschiebt `UnifiedDiffParser.Parse` nach
`Task.Run` und setzt danach den Indikator.
- **`UnifiedDiffParser` ist statisch und rein** — thread-safe, `Task.Run` ist unkritisch. Das
Zurückschreiben der Ergebnisse in Observable-Collections muss auf dem UI-Thread passieren.
- **B2 braucht eine Entscheidung, die der Merge-Helper trifft:** `DiffAlignment.Build` läuft in
`DiffTextView.axaml.cs` — in einem Control, nicht in einem VM. Entweder Grenze („ab N Zeilen
auslagern") oder inkrementeller Aufbau. Die Wahl gehört ins Paket, nicht hierher; die
Vorgabe ist nur: **eine sichtbare Grenze definieren, keine unbegrenzte Synchron-Arbeit.**
- **AvaloniaEdit-Fallen** (belegt, nicht neu ausprobieren): `this.TryGetResource` in einem
Control findet Brushes aus `Tokens.axaml` **nie** und scheitert lautlos; Brushes und Typeface
nicht im Konstruktor auflösen. `ScrollToVerticalOffset` ist in v12 ein No-op — schreiben über
`ScrollViewer.Offset`, lesen über `TextView.ScrollOffsetChanged`.
- Gemeinsames Editor-Boilerplate gehört in `Views/Controls/DiffEditorSetup.cs`, nicht ein
drittes Mal kopiert.
### Definition of Done
B3 ist der einzige Test im Vorhaben, der Blockade prüft: großes Diff-Fixture, Nachweis, dass
der Dispatcher weiter Nachrichten verarbeitet. Visuelle Prüfung offen (Verhalten bei großem
Diff, Indikator während des Parsens).
### Task-Entwürfe (grob)
**B1 · `UnifiedDiffParser.Parse` vom UI-Thread nehmen**
Beide Aufrufstellen im `DiffViewerViewModel` nach `Task.Run` verschieben, danach den
`OperationIndicator` setzen. Reihenfolge ist zwingend: erst auslagern, dann anzeigen — ein
Spinner auf einem blockierten Dispatcher wird nicht gezeichnet. Das Zurückschreiben in
Observable-Collections muss auf dem UI-Thread passieren.
*Dateien:* `Ui/ViewModels/Modals/DiffViewerViewModel.cs`
*Fertig wenn:* Kein synchroner Parse mehr im VM, Indikator während des Parsens sichtbar. Visuelle Prüfung mit einem großen Diff offen.
**B2 · `DiffAlignment.Build` — Grenze gegen unbegrenzte Synchron-Arbeit**
`Build` läuft in einem Control, nicht in einem VM. Der ausführende Agent entscheidet zwischen
Auslagern ab einer Zeilenzahl und inkrementellem Aufbau — die Vorgabe ist nur: **eine
sichtbare, begründete Grenze**. AvaloniaEdit-Fallen aus dem Vorgaben-Abschnitt beachten
(`TryGetResource` scheitert lautlos, `ScrollToVerticalOffset` ist ein No-op).
*Dateien:* `Ui/Views/Controls/DiffTextView.axaml.cs`, gemeinsames Boilerplate nach `DiffEditorSetup.cs`
*Fertig wenn:* Die gewählte Grenze ist im Code kommentiert und begründet; große Diffs blockieren nicht mehr unbegrenzt. Visuelle Prüfung offen.
**B3 · Guard-Test: großer Diff blockiert den Dispatcher nicht**
Headless-Test mit großem Diff-Fixture, der nachweist, dass der Dispatcher während Parse und
Build weiter Nachrichten verarbeitet. Der einzige Blockade-Test im Vorhaben — soll auch
zukünftige Regressionen fangen.
*Dateien:* `tests/ClaudeDo.Ui.Tests/`, Fixture im Testprojekt
*Fertig wenn:* Der Test fällt gegen den Stand **vor** B1/B2 und ist danach grün.
---
## Gruppe C — Bild 3: Worker-Stille
- [x] **C1 — generischer `OperationProgress`-Kanal**
- [ ] **C2 — Startup-Recovery sichtbar**
- [ ] **C3 — Worktree-Anlage sichtbar**
- [ ] **C4 — Rebase-after-Merge und WorktreeMaintenance**
- [ ] **C5 — periodische Dienste**
> **Einzige offene Gruppe.** C2C5 sind unblockiert (C1 ist gemergt). Woran man den Reststand
> erkennt: die vier Keys `ops.worker.startupRecovery`, `.creatingWorktree`, `.rebasingAfterMerge`
> und `.maintainingWorktrees` liegen ungenutzt in beiden Sprachdateien.
**Vorbedingung:** P0 gemergt. Die Abhängigkeit zur Parallelsession ist mit `cad0582`
aufgelöst.
### Vorgaben
- **Der Kanal:**
```
OperationProgress(string opKey, string phase, int current, int total)
```
`opKey` = TaskId bei task-gebundenen Operationen, sonst ein stabiler String
(`"worktree-cleanup"`, `"startup-recovery"`, `"planning-integration:<taskId>"`).
- **Kein Elapsed auf der Leitung.** Das UI tickt lokal. Die heutige Implementierung lässt den
Worker alle 30 s ticken — damit zeigt sie die ersten 30 Sekunden nur „merging". C1 stellt das
um.
- **`MergeProgressEvent` bleibt als Forwarder** (siehe Nachbesserung 2 unter Gruppe A). Vier
Zeilen. Entfernen erst, wenn A und C beide gemergt sind — nicht in dieser Gruppe.
- **`current`/`total` wird als Text gezeigt, nie als Prozentbalken.** Die meisten Operationen
haben kein sinnvolles Total.
- **C3 zuerst prüfen, nicht bauen:**
`docs/superpowers/specs/2026-08-07-ui-reaktivitaet-und-listen-performance-design.md` führt
`WorktreeManager.cs:103` als DB-Write-ohne-Broadcast. Ob das inzwischen geschlossen ist,
gehört geprüft, bevor es doppelt behoben wird.
- **C5 ist der wackeligste Punkt im Design.** Periodische Dienste (Usage, OnlineSync, Prime,
Queue) dürfen **nur Aktivität und Fehler** melden, keinen Tick-Strom. Wenn der Merge-Helper
beim Umsetzen zum Schluss kommt, dass C5 nur Rauschen erzeugt: **weglassen und begründen**,
statt es durchzuziehen.
- **Test-Fakes wachsen mit.** Ein neues Event auf `IWorkerClient`/`WorkerHub` bricht
handgeschriebene Fakes in **beiden** Testprojekten — `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`
und die UiVm-Tests unter `tests/ClaudeDo.Worker.Tests/`.
- **Broadcast-Sparsamkeit:** vor einem „fehlenden Broadcast" immer den Aufrufer prüfen. Im
08-07-Spec war ein gemeldetes Loch in Wahrheit schon vom Aufrufer abgedeckt, und der Fix
wäre ein Duplikat gewesen.
### Definition of Done
Progress-Callbacks in `ClaudeDo.Worker.Tests` mit einem Fake gezählt — keine echten Timeouts,
keine echte CLI. UI-Seite: die Anzeige beim Worker-Start ersetzt „reconnecting" durch die
laufende Recovery-Phase. Visuelle Prüfung offen.
### Task-Entwürfe (grob)
**C1 · Generischer `OperationProgress`-Kanal, `MergeProgress` darauf umstellen**
Neues Hub-Event `OperationProgress(opKey, phase, current, total)`, ohne Elapsed auf der Leitung.
Der Merge-Pfad sendet darüber. **`MergeProgressEvent` bleibt als vierzeiliger Forwarder** —
Gruppe A hängt daran und darf nicht angefasst werden. Fakes in beiden Testprojekten mitziehen.
*Dateien:* `Worker/Hub/HubBroadcaster.cs`, `Worker/Hub/WorkerHub.cs`, `Ui/Services/WorkerClient.cs`, `IWorkerClient.cs`, `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`, UiVm-Fakes in `Worker.Tests`
*Fertig wenn:* Merge-Phasen laufen über den neuen Kanal, das UI tickt Elapsed lokal, `MergeProgressEvent` funktioniert unverändert weiter, beide Testprojekte grün.
**C2 · Worker-Startup-Recovery sichtbar machen**
Die sechs Recovery-Services beim Worker-Start melden Phase und `i/n` über den Kanal
(`opKey = "startup-recovery"`). Das UI ersetzt „reconnecting" durch die laufende Phase.
*Dateien:* `Worker/Lifecycle/*Recovery.cs`, Anzeige im `IslandsShellViewModel`-Umfeld
*Fertig wenn:* Beim Worker-Start ist sichtbar, welche Recovery läuft. Visuelle Prüfung offen.
**C3 · Worktree-Anlage beim Task-Start sichtbar machen**
**Zuerst prüfen**, ob der im 08-07-Spec gemeldete Broadcast-Gap in `WorktreeManager` inzwischen
geschlossen ist — nicht doppelt beheben. Danach die stille Lücke zwischen `Queued` und erster
Ausgabe an der Task-Zeile sichtbar machen.
*Dateien:* `Worker/Runner/WorktreeManager.cs`, Task-Row-Anzeige
*Fertig wenn:* Das Prüfergebnis steht im Task; die Anlage-Phase ist an der Zeile sichtbar. Visuelle Prüfung offen.
**C4 · Rebase-after-Merge und WorktreeMaintenance in den Kanal**
`RebaseOthersAfterMergeAsync` läuft heute innerhalb des Merge-Calls **nach** dem
Phasen-Broadcast und ist damit vollständig unsichtbar. Dazu der Hintergrunddienst über alle
Worktrees, mit `i/n`.
*Dateien:* `Worker/Lifecycle/TaskMergeService.cs` (Rebase-Abschnitt), `Worker/Worktrees/WorktreeMaintenanceService.cs`
*Fertig wenn:* Beide melden Fortschritt; ein Merge zeigt nach dem Verify-Gate die Rebase-Phase statt Stille.
**C5 · Periodische Dienste — optional, Abbruch erlaubt**
Usage, OnlineSync, Prime, Queue sollen **nur Aktivität und Fehler** melden, keinen Tick-Strom.
Der wackeligste Punkt im Design: kommt der ausführende Agent zum Schluss, dass das nur
Anzeige-Rauschen erzeugt, **weglassen und begründen** statt durchziehen.
*Dateien:* `Worker/Usage/`, `Worker/Online/`, `Worker/Prime/`, `Worker/Queue/`
*Fertig wenn:* Entweder sparsame Meldungen ohne Rauschen — oder eine begründete Absage im Task.
---
## Gruppe D — Bild 4: MCP-Stille
- [x] **D1 — `ProgressReporter` extrahieren**
- [x] **D2 — die `batch_*`-Tools** — es sind **sieben**, nicht acht (die Achterzahl unten war ein
Zählfehler beim Schreiben des Plans)
- [x] **D3 — Worktree- und Diff-Tools** — drei davon; `batch_cleanup_task_worktrees` lief über D2
- [x] **D4 — Rest und Doku-Regel** — fand zwei echte Lücken, die D1D3 offen gelassen hatten:
`continue_merge` reichte sein Progress-Token nicht ins Post-Merge-Verify-Gate, und der
Unit-Merge-Drain im `PlanningMergeOrchestrator` verlor es nach dem ersten Tick. Dazu
`list_worktrees`.
**Vorbedingung:** keine. Kann sofort starten, parallel zu P0.
### Vorgaben
- **Warum das zählt:** ohne Progress bricht der MCP-Client bei Stille nach 300 s ab, während
der Worker weiterarbeitet. Das ist die dokumentierte Ursache dafür, dass ein Merge zwar
durchläuft, den Task aber nie auf `Done` bringt und Abhängige dauerhaft blockiert.
- **D1:** `TaskMergeService.RunReportingProgressAsync` ist die einzige existierende
Progress-Schleife. Als eigenständige Klasse extrahieren, plus ein Overload für
Element-Fortschritt (`i/n`). Nicht kopieren — extrahieren, damit es eine Implementierung
bleibt.
- **`TaskMergeService` ist die einzige Datei, die D mit C teilt.** D fasst dort ausschließlich
die Progress-Schleife an. Wenn beide Gruppen gleichzeitig laufen, ist das die Stelle, die der
Merge-Helper im Auge behalten muss.
- **Keine Locale-Keys** (Nachbesserung 3). Englische Klartext-Strings.
- **Zielumfang:** die 7 `batch_*`-Tools mit `i/n`, dann `cleanup_task_worktree`,
`batch_cleanup_task_worktrees`, `get_task_diff`, `preview_merge_set`. Bereits versorgt und
nicht anzufassen: die 5 Tools in `ExternalMcpService` mit `IProgress`, `TaskWaitMcpTools`,
`merge_task`/`review_task`.
- **D4 schreibt die Regel ins Worker-`CLAUDE.md`:** ein MCP-Tool, das über ~5 s laufen kann,
reportet Progress. Ohne die Regel wiederholt sich das Muster beim nächsten Tool.
### Definition of Done
Für jedes umgestellte Tool ein Test in `ClaudeDo.Worker.Tests`, der mit einem Fake-`IProgress`
zählt, dass Meldungen kommen — bei `batch_*` mindestens eine pro Element. Keine visuelle
Prüfung nötig, diese Gruppe hat keine UI.
### Task-Entwürfe (grob)
**D1 · `ProgressReporter` extrahieren**
`TaskMergeService.RunReportingProgressAsync` als eigenständige Klasse herauslösen, plus einen
Overload für Element-Fortschritt (`i/n`). **Extrahieren, nicht kopieren** — es soll eine
Implementierung bleiben. Dies ist die einzige Datei, die D mit C teilt; nur die Progress-Schleife
anfassen.
*Dateien:* `Worker/Lifecycle/TaskMergeService.cs`, neue Klasse im Worker-Projekt
*Fertig wenn:* Der Merge-Pfad nutzt die extrahierte Klasse, Verhalten unverändert, bestehende Merge-Tests grün.
**D2 · Die sieben `batch_*`-Tools mit Element-Fortschritt**
Jedes `batch_*`-Tool reportet pro verarbeitetem Element. Grund: ohne Meldung bricht der
MCP-Client nach 300 s Stille ab, während der Worker weiterläuft — genau der Mechanismus, der
einen Merge durchlaufen lässt, den Task aber nie auf `Done` bringt.
*Dateien:* `Worker/External/BatchMcpTools.cs`
*Fertig wenn:* Pro Tool ein Test mit Fake-`IProgress`, der mindestens eine Meldung je Element zählt. Englische Klartext-Strings, keine Locale-Keys.
**D3 · Worktree- und Diff-Tools mit Progress**
`cleanup_task_worktree`, `batch_cleanup_task_worktrees`, `get_task_diff`, `preview_merge_set`.
Bereits versorgt und **nicht** anzufassen: die fünf Tools mit `IProgress` in
`ExternalMcpService`, `TaskWaitMcpTools`, `merge_task`, `review_task`.
*Dateien:* die betroffenen `Worker/External/*McpTools.cs`, `ExternalMcpService.cs`
*Fertig wenn:* Jedes der vier Tools meldet Fortschritt, mit Test.
**D4 · Restliche Long-Runner und die Regel im Worker-`CLAUDE.md`**
Verbleibende Tools durchgehen, die über ~5 s laufen können, und die Regel festschreiben: ein
MCP-Tool über ~5 s reportet Progress. Ohne die Regel wiederholt sich das Muster beim nächsten
Tool.
*Dateien:* restliche `Worker/External/*McpTools.cs`, `src/ClaudeDo.Worker/CLAUDE.md`
*Fertig wenn:* Die Regel steht mit Begründung; die durchgegangenen Tools sind im Task aufgelistet — auch die, die bewusst nichts bekommen.
---
## Was nach allen vier Gruppen offen bleibt
- ~~**Visuelle Prüfung** für alle Pakete in A und B~~ — **am 2026-08-17 durch**, ohne Fund
(Worktrees-Übersicht, Settings/Skill-Install, WeeklyReport, großer Diff, Detail-Pane-Approve).
- ~~**Auswertung von P0-2**~~ — am 2026-08-13 ausgewertet, siehe A5.
- **`MergeProgressEvent`-Forwarder entfernen**, sobald A und C beide gemergt sind. Ein
Aufräum-Task, kein Paket.
- **Bewusst nicht gebaut:** Cancel, Footer-Anzeige für laufende Operationen, Prozentbalken,
Änderungen am Installer (der hat bereits eine vollständige Progress-Pipeline).