docs(open): mark the usage-monitor pass done and drop the light-theme checks

The app is dark-only (App.axaml pins RequestedThemeVariant="Dark", Tokens.axaml
has no light variant), so every "Dark/Light" line was unverifiable by
construction. Adds the roadblock-reply session-id finding from the same pass.
This commit is contained in:
mika kuns
2026-08-06 10:02:14 +02:00
parent b6ecbb13f5
commit 730ecb1abc
+22 -15
View File
@@ -11,6 +11,7 @@ Checkliste lebt in `docs/verification-handoff.md`.
- **Settings-Modal: Leeren eines NumericUpDown wirft `InvalidCastException`** (gefunden bei der Sichtprüfung 2026-08-06). `NumericUpDown.Value` ist `decimal?`; sobald das Textfeld leer ist (der normale Weg, einen Wert zu ändern: alten Wert löschen, neuen tippen) schreibt die TwoWay-Bindung `null` in eine nicht-nullbare Ziel-Property. Betrifft **acht** Felder: `ModelPresetRowViewModel.MaxTurns` (`decimal`), `General.MaxTurnsCeiling`, `General.MaxParallelExecutions`, `General.UsageGateFiveHourPct`, `General.UsageGateSevenDayPct`, `Worktrees.WorktreeAutoCleanupDays`, `Prime.DailyPrepMaxTasks`, `OnlineInbox.PollIntervalSeconds` (alle `int`). Nur `AgentConfigEditorViewModel.MaxTurns` ist `decimal?` und deshalb heil. Fix-Optionen: (a) die Properties nullable machen (wie im Agent-Editor) und an den Verwendungsstellen coercen, ODER (b) ein kleiner `IValueConverter`, der beim ConvertBack `null` auf `BindingOperations.DoNothing` abbildet — eine Klasse plus acht Binding-Änderungen, keine DTO-/Validate-Umbauten.
- **Roadblock-Reply (und Continue) bleiben tot, wenn der Task während des Laufs offen war** (Sichtprüfung 2026-08-06). `LatestRunSessionId` wird ausschließlich im Auswahl-Ladepfad gesetzt (`DetailsIslandViewModel.cs:619`). Hat man den Task schon offen, während er läuft und in den Roadblock läuft, war die Session-Id beim Auswählen noch `null`, und kein Live-Refresh holt sie nach: `RefreshBoundTaskAsync` (804) aktualisiert nur die Task-Zeile, `RefreshWorktreeAsync` (821) nur Worktree/Diff, der `TaskFinished`-Handler (364-369) ruft nur diese beiden. Ergebnis: Antwortfeld deaktiviert samt „keine Session"-Hinweis, obwohl es eine Session gibt — nach erneutem Öffnen des Tasks funktioniert es. Derselbe Gate hängt an `CanContinue` (1053), der Continue-Button ist also gleichzeitig tot. Fix: `LatestRunSessionId` beim Run-Ende mitziehen (`TaskRunRepository.GetLatestByTaskIdAsync` im `TaskFinished`-Pfad).
- **Verify-Gate wird für worktree-lose Tasks komplett übersprungen** (Sichtprüfung 2026-08-06). `TaskMergeService.ApproveAndMergeAsync` (Zeile 537-543): hat ein Task keinen aktiven `WorktreeEntity`, geht der Pfad direkt auf `_state.ApproveReviewAsync``Done` und meldet `merged` — der Gate läuft nie, das `verifyCommand` aus `LoadMergeContextAsync` wird auf Zeile 532 sogar explizit verworfen (`var (task, list, wt, _)`). Betroffen sind genau die **List-Handler-Tasks** (per Design worktree-los, arbeiten via `HandlerBaseCommit`/`HandlerHeadCommit` direkt in `list.WorkingDir`) und Sandbox-Runs — also ausgerechnet der Lauf, der am meisten auf einmal nach main bringt. Reproduziert: bei gesetztem `VerifyCommand = cmd /c exit 1` hat „Approve & Merge" den Handler-Task kommentarlos auf `Done` gesetzt. Fix: den Gate auch in diesem Zweig laufen lassen (Arbeitsverzeichnis ist `list.WorkingDir`), bevor `ApproveReviewAsync` gerufen wird.
- **Verify-Gate ist nur an einem von drei Merge-Einstiegspunkten verdrahtet** (Sichtprüfung 2026-08-06, reproduziert mit `VerifyCommand = cmd /c exit 1`). Der Gate greift funktional korrekt (Merge landet, Task bleibt aus `Done`), aber nur `DetailsIslandViewModel.ApproveReviewAsync` (Zeile 1125) kennt `verify_failed` und zeigt `result.ErrorMessage`. `MergeModalViewModel` (Zeile 113-115) fällt in den `default`-Zweig und zeigt wörtlich **„Unknown status: verify_failed"** — der Output-Tail des Verify-Kommandos wird verworfen. `WorktreesOverviewModalViewModel` (Zeile 405) mappt ihn auf generisches `BatchMergeOutcome.Failed`, der Grund geht ebenfalls verloren. Fix: beide Stellen um einen `verify_failed`-Zweig ergänzen, der `result.ErrorMessage` durchreicht (Text existiert schon als `vm.detailsIsland.verifyFailed`).
- **List-Handler-Handoff lässt eine leere Geister-Pane zurück** (Sichtprüfung 2026-08-06). Nach `handoff_list_handler` öffnet `MissionControlViewModel.OpenMergeHelperHandoffConPtySessionAsync` (Zeile 255) bewusst eine **zweite** Pane für dieselbe Task-Id und lässt die erste stehen — laut Kommentar (Zeile 250-254), damit Mika die letzte Nachricht der alten Session noch lesen kann. In der Praxis ist die alte Pane nach dem Prozessende aber **komplett leer**, es gibt also nichts mehr zu lesen; es bleibt nur ein toter Kachel-Platzhalter, den man von Hand schließen muss. Fix-Optionen: (a) die alte Pane beim Handoff automatisch schließen, ODER (b) ihren letzten Output erhalten und sie sichtbar als „beendet" markieren — die aktuelle Zwischenlösung erfüllt ihren Zweck nicht.
@@ -67,15 +68,15 @@ verifiziert und deshalb hier entfernt. Es bleiben die Entscheidungen, die daran
nicht `Idle` — das ist korrekt so (`HasInteractiveSession` überschreibt den Status-Chip, solange
die ConPTY-Session läuft). Die drei dabei gefundenen Punkte stehen unter „Bugs" bzw.
„Feature-Wünsche".
- **Roadblock reply field (2026-08-05)** — build + unit tests all green, but **not visually
verified**: on a `Done` (or `WaitingForReview`/`Failed`/`Cancelled`) task with a reported
roadblock, the ROADBLOCK card shows a reply textbox + Send button under the roadblock text.
Check layout/spacing against the AskUser question card it's modeled on, Enter-to-send, the
disabled state + hint text when there's no session ID to resume, and that an override-slot-busy
error shows up in the footer log strip (not a modal) with the typed text still in the field.
Approve should go straight to `Done` with no merge attempt. Design choice: the review range
lives as two new nullable columns directly on `TaskEntity` (not a phantom `WorktreeEntity` row),
specifically so `list_worktrees`/the Worktrees overview never see it.
- **Roadblock reply field (2026-08-05)** — **Kernfunktion am 2026-08-06 verifiziert**: auf einem
Task mit gemeldetem Roadblock erscheint das Antwortfeld unter dem Roadblock-Text, die Antwort
setzt die Session fort, und der Task landet danach sauber auf `WaitingForReview`. Dabei ist der
Bug oben aufgefallen (Feld deaktiviert, solange der Task seit vor dem Lauf offen ist). Layout der
Roadblock-Karte ist in Ordnung. **Noch offen:** Enter-zum-Senden, und dass ein „Override-Slot
besetzt"-Fehler im Footer-Strip landet (nicht als Modal) mit dem getippten Text weiterhin im
Feld. Design-Entscheidung dazu: der Review-Bereich lebt als zwei nullable Spalten
direkt auf `TaskEntity` (kein Phantom-`WorktreeEntity`), damit `list_worktrees`/die
Worktrees-Übersicht ihn nie sehen.
- **Post-merge verify gate (2026-08-05)** — build + unit tests all green (incl. real-process
`VerifyCommandRunner` exit-code/output/timeout tests and `TaskMergeService` success/failure/
@@ -107,10 +108,16 @@ Planning-Worktree ablegt (`PlanningTranscriptLocator`), und persistiert sie. Uni
## Offene Verifikation (2026-08-05, Usage Monitor)
- **Visueller Pass Usage-Pill** (Footer **und** Mission-Control-Header): Text/Tooltip,
Dot-Zustände normal/warn/stale/blocked, Dark/Light.
- **Visueller Pass Usage-Monitor-Modal**: Gauges (dynamisch aus `limits[]`), Modell-/Task-
Tabellen, Info-Bänder (stale/gate-blocked), 7d/30d-Presets + Custom-Range, Dark/Light.
> **Kein Light-Theme.** `App.axaml:6` setzt `RequestedThemeVariant="Dark"` fest, es gibt keinen
> Umschalter und `Tokens.axaml` kennt keine Light-Variante. „Dark/Light"-Checks sind deshalb
> überall aus dieser Datei entfernt — sie waren nie erfüllbar.
- **Visueller Pass Usage-Pill** (Footer **und** Mission-Control-Header) — **am 2026-08-06
verifiziert**: Text/Tooltip lesbar, Dot-Zustände plausibel, Pill in Footer und
Mission-Control-Header identisch.
- **Visueller Pass Usage-Monitor-Modal** — **am 2026-08-06 verifiziert**: Gauges (dynamisch aus
`limits[]`), Info-Bänder (der Throttle-Hinweis „Queue throttled: 1/3 slots (7d)" stand real an),
7d/30d-Presets + Custom-Range. Die Analyse-Tabellen sind ausbaufähig → siehe „UX / Nits".
- **E2E Gate**: das Gate greift real, sobald ein Bucket (`five_hour`/`seven_day`) die
konfigurierte Schwelle reißt — Queue-Nachschub pausiert, laufende Runs/`RunNow`/ConPTY/
Planning/Prime bleiben unberührt — und die Queue nimmt nach dem Reset selbstständig wieder
@@ -126,8 +133,8 @@ ist, sonst 15 Min.), 429-Backoff mit `Retry-After`, und ein „Jetzt aktualisier
Usage-Monitor-Modal (`RefreshUsage``UsageMonitorService.RefreshNowAsync`, 10s-Cooldown).
Unit-Tests grün, **offen**:
- **Visueller Pass Refresh-Button** im Modal (Button + Spinner + Hinweiszeile, Dark/Light,
en/de) — Teil des oben schon offenen Modal-Passes.
- **Visueller Pass Refresh-Button** im Modal **am 2026-08-06 verifiziert** (Button, Hinweiszeile
„Polled every 5 min while a task runs, otherwise every 15 min.").
- **E2E:** über ≥20 Min. mit und ohne laufenden Task beobachten, dass keine 429s mehr im
Worker-Log auftauchen und die Pill trotzdem aktuell bleibt.
- **Beachten:** die Pill wird jetzt erst nach 3× 15 Min. als `stale` markiert — ein echter