# 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 -- ` — 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: C2–C5. **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.** C2–C5 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:"`). - **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 D1–D3 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).