189 Commits
Author SHA1 Message Date
mika kuns bd5aa265d1 feat(mission-control): Handler-Run per Knopf abbrechen
Ein "Abandon run"-Knopf an Handler-Panes fragt HandlerRoundState.Get(handlerTaskId)
nach den noch offenen (Queued/Running) Tasks des Laufs, bestaetigt mit Anzahl,
cancelt jeden ueber den bestehenden CancelTaskAsync-Pfad und schliesst danach die
Session -- ein Schritt statt Tile schliessen + Queue separat abbrechen. Ohne
offene Tasks (oder ohne Round-Eintrag) schliesst die Aktion direkt, ohne Dialog.
Nur an den beiden Handler-Panes sichtbar (IsListHandler), normale Task-/Ad-hoc-
Panes bleiben unveraendert.
2026-09-02 22:39:12 +02:00
mika kuns b298594a2c chore(claude-do): merge fix(ui): Listen-Settings-Modal verschluckt Ladefehler nach d
ClaudeDo-Task: 9256fcfe-6cad-4350-a979-0c49cd5b9799
2026-09-02 22:38:49 +02:00
mika kuns df3e7ba0c7 fix(ui): Dequeue-X auf blockierten Queue-Zeilen anzeigen und Kette konsistent halten
CanRemoveFromQueue schloss IsWaiting (queued+blocked) faelschlich aus, sodass ein
Kettenglied in der Mitte nicht entfernbar war. Zusaetzlich unblockt
TaskStateService.DequeueToIdleAsync jetzt den Nachfolger eines dequeueten
Kettenglieds, sonst bliebe er dauerhaft auf einen nicht mehr laufenden
Vorgaenger blockiert.
2026-09-02 22:34:50 +02:00
mika kuns a1b20d9f27 fix(ui): report and gate ListSettingsModal load failures instead of swallowing them
BeginLoad's fire-and-forget LoadAsync now catches instead of leaving an
unobserved task exception, reports it via the new ErrorReported event
(wired into FlashFooterError like the other modal VMs), and keeps Save
locked so the failure can't clobber list_config with blank fields.
LoadTicketProjectsAsync's empty catch now reports too; behavior for the
dropdown itself is unchanged.
2026-09-02 22:33:48 +02:00
mika kuns f24654dfb8 perf(ui): show list settings modal before its seven-round-trip load finishes
OpenListSettingsAsync/CreateListAsync used to await LoadAsync (name/agent
config/list_config x2/ticket settings/ticket projects, all serial) before
showing the window -- an unreachable ticket system alone could add a 10s
HTTP timeout to every open. LoadAsync now runs via BeginLoad (fire-and-forget,
mirrors UsageMonitorModalViewModel), the modal shows immediately and fields
fill in behind it.

GetListConfigAsync no longer runs twice per open -- AgentConfigEditorViewModel
exposes the row it already fetched (LastLoadedListConfig) instead of the
caller re-fetching it. Ticket-project loading is split into its own
fire-and-forget step so a slow/unreachable ticket system can't block the
rest of the fields, and its tri-state Save logic now also treats "never
finished loading" like "not available" -- leaving the stored
TicketProjectId link untouched instead of overwriting it with 0. Save/Delete
are gated (CanExecute) on the core load being done, so a save can't land
before the fields it writes are populated.

Not measured with OperationTiming against a live worker in this session (no
running app instance available) -- verified instead via 4 new unit tests
that assert GetListConfigAsync fires exactly once and that a hung/failing
ticket-projects call neither blocks LoadAsync's other fields nor corrupts
the saved TicketProjectId. Before/after OperationTiming numbers and a visual
pass are still open follow-ups.
2026-09-02 22:18:54 +02:00
mika kuns fe8186514f feat(worker): Handler-Session vor dem Usage-Limit warnen und Handoff bis zum Reset zurueckhalten
Eine PowerUse-Handler-Session soll am 5h-Limit nicht mehr mitten im Merge
sterben, sondern vorher selbst sauber uebergeben:

- HandlerUsageGuard (neu) warnt die laufende Session genau einmal pro Runde,
  sobald FiveHour.Utilization den Schwellwert PowerUseHandoffPct erreicht.
  Die Warnung wird per HandlerUsageWarning-Broadcast an Mission Control
  geschickt und dort in das Pane der Handler-TaskId getippt.
- handoff_list_handler broadcastet den Handoff dann nicht sofort, sondern parkt
  ihn bis ResetsAt + 60 s; das Ergebnis traegt deferredUntil. Ohne ResetsAt
  wird wie bisher sofort gesendet.
- Der geparkte Handoff liegt im HandlerRoundState (handler-rounds.json) und
  wird vom Guard-Tick ausgeloest, also auch nach einem Worker-Neustart wieder
  eingeplant bzw. sofort gesendet, wenn er ueberfaellig ist.
- Review-Befund aus #272: HandlerRestartCoordinator wartet nicht mehr selbst
  bis ResetsAt, sondern parkt den Neustart im selben Slot. AutoRestartUsed wird
  erst gesetzt, wenn der Broadcast wirklich rausging - ein Worker-Absturz
  waehrend der Wartezeit laesst die Runde nicht mehr dauerhaft tot zurueck.
  Zurueckgehaltener Handoff und Auto-Neustart teilen sich einen Slot, also
  oeffnet nie beides dieselbe Runde. UsageLimitProofPct entfaellt zugunsten von
  PowerUseHandoffPct.
- PtyTerminalSession.SendLineAsync schreibt in die laufende Session. Gegen eine
  echte claude-Session verifiziert: Text und Enter muessen zwei getrennte
  Writes sein, die Enter-Bytes kommen von Terminal.GenerateKeyInput.
2026-09-02 21:35:29 +02:00
mika kuns 016b9203f8 feat(worker): Handler-Session nach Usage-Limit-Reset automatisch neu starten
Eine List-Handler-ConPTY-Session, die mitten in einer Phase stirbt, war bisher
ein stilles totes Tile: InteractiveTerminalViewModel.OnSessionProcessExited
setzte nur bei einem Tod innerhalb des 5s-Grace-Fensters ein sichtbares Banner.

Neu:
- HandlerRoundState (JSON unter ~/.claudeDo/handler-rounds.json, keine
  EF-Migration): Phase + Task-Set der laufenden Runde, geschrieben von
  BuildForMergeHelperAsync ("triage") und BuildForMergeHelperHandoffAsync.
  Ohne das existieren beide nur im transienten HandoffRequested-Broadcast.
- HandlerRestartCoordinator: entscheidet den Neustart, fail-closed. Beleg fuer
  "war das Usage-Limit" ist FiveHour.Utilization >= 95 zum Todeszeitpunkt --
  eine ConPTY-Session hat kein stdout, TaskRunner.IsUsageLimitMessage hat hier
  nichts zu lesen. Nur bei PowerUse (beide Keys), max ein Neustart pro Runde,
  Warten bis FiveHour.ResetsAt, dann HandoffRequested erneut broadcasten.
  Verbraucht kein Handoff-Budget (HandoffRoundTracker.Register wird nicht
  angefasst). WorkerLog-Zeilen beim Tod und beim Neustart.
- Ui meldet den Exit-Code (nur dort bekannt) via
  WorkerHub.ReportInteractiveSessionExited, ohne selbst zu filtern.

Sibling zu UsageLimitAutoContinueCoordinator, kein Umbau davon: der sieht nur
headless Runs.
2026-09-02 19:17:51 +02:00
Mika Kuns c25732cff8 feat(claude-do): depends_on als Menge statt Einzelfeld
Ein Task kann jetzt auf beliebig viele Vorgaenger warten. Die Einzelspalte
tasks.depends_on_task_id wird durch die Beziehungstabelle task_dependencies
ersetzt (Composite-PK, beide FKs ON DELETE CASCADE -- das reproduziert das
alte ON DELETE SET NULL: geloeschter Vorgaenger = Kante weg = Task wieder
pickbar). BlockedByTaskId bleibt unberuehrt.

- Migration AddTaskDependencies kopiert jede bestehende depends_on_task_id
  verlustfrei in die Tabelle, bevor die Spalte fallengelassen wird
- QueuePicker gatet per doppelt negiertem NOT EXISTS: kein Task mit 0 Kanten
  wird blockiert, alle Kanten muessen done sein, eine Kante auf einen
  verschwundenen Task blockiert weiter
- SetDependsOnAsync ersetzt die ganze Menge; Zykluspruefung ist jetzt eine
  BFS ueber den Graphen, faengt also auch A->B->C->A
- blockedReason nennt alle offenen Vorgaenger (External/TaskBlocking, von
  ExternalMcpService und TaskWaitMcpTools gemeinsam genutzt)
- add_task/update_task nehmen dependsOnTaskIds; die alte Skalarform bleibt
  als Ein-Element-Menge inkl. ""-Clear-Sentinel gueltig
- UI: Fan-in-Zeile bleibt flach und listet alle Vorgaenger im After-Label,
  die lineare Kette rendert unveraendert
2026-09-02 15:08:05 +02:00
Mika Kuns dc1e67d07b fix(tests): stop Ui.Tests order-dependent flakiness on shared Loc.Current
Loc.Current (src/ClaudeDo.Ui/Localization/Loc.cs) is a process-wide mutable
static; which localizer is active depended on xUnit's parallel test
scheduling. Ui.Tests: disable collection parallelization (cheap, pure VM
suite) and install the real localizer in a module initializer so no test
can ever observe the uninitialized key-echo default. Worker.Tests: install
the same module initializer but leave parallelization on — disabling it
measured a 27% wall-clock regression on its real-SQLite/git suite, and the
initializer alone already removes the race. AgentConfigEditorViewModelTests
now installs its own localizer instead of relying on ambient state.
2026-09-02 12:55:51 +02:00
mika kuns 56bc1651a8 fix(ui): don't leave an orphaned list behind on cancel
"New list" pre-creates the DB row before opening List Settings.
Cancelling or closing that modal left an untitled empty list behind;
CreateListAsync now rolls the row back unless the modal reports Saved.

Also moves the new-list/import-repo buttons up next to the "My Lists"
label instead of a row below the list, and puts Notes above Important
in the smart-list nav order.
2026-09-02 10:00:43 +02:00
CubeGameLPandClaude Opus 5 0f04b796f4 feat(notes): notes get their own list, a day log and an Enter chain
The editor was reachable from one button that only appeared in My Day,
which is how it got forgotten. It is now a permanent sidebar entry next
to My Day and Planned, and it fills the tasks island rather than the
details pane, because a note list is content.

Inside, the date picker is gone: today sits at the top with the capture
box, older days follow as dated groups. Enter in a note saves it and
opens the next one, Shift+Enter breaks the line so notes can be several,
Backspace on an empty note deletes it and steps back up. Deleting also
has a visible hover button instead of only "clear the text and hope".
Agent-written notes carry a marker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:19:37 +02:00
mika kuns 22e70ffcb4 feat(ui): Mission Control als Session-Hub
- "+" öffnet ein Menü mit allen Listen (working dir) statt direkt den
  Ordner-Dialog; "Ordner wählen…" bleibt als Fallback erste Zeile, damit
  das Menü auch bei fehlgeschlagener Listen-Abfrage nutzbar bleibt
- Rail-Rows: Kontextmenü "Sitzung öffnen" (gesperrt für Running — der
  Worktree gehört dort dem autonomen Lauf)
- Aktivitäts-Strip statt reiner Queue: WaitingForReview/Failed/Roadblock
  kommen dazu, Attention zuerst, unterschieden nur über Farbe (Aktionen
  bleiben in der Task-Detailansicht)
2026-08-28 14:17:38 +02:00
mika kuns 741c7ac58d feat(ui): Review-Merge-Button — Claude nimmt Waiting-for-Review pro Liste ab
Neuer Button in der virtuellen Review-Liste: öffnet das Merge-Helper-Auswahlmodal
im Review-Modus (alle WaitingForReview-Tasks repo-verknüpfter Listen, gruppiert
unter Listen-Headern, alle vorgetickt) und startet pro gewählter Liste eine
ConPTY-Handler-Session direkt in der Merge-Phase — via bestehendem Handoff-Spec
(nextPhase "merge"), kein Triage/Wait davor. Handler-Host-Task wie beim vollen
"Let Claude handle it"-Lauf (HandlerBaseCommit, IsManual).
2026-08-27 08:44:33 +02:00
Mika Kuns c1ab042d0a fix(ui): block delete on interactive-session tasks, clear ghost selection
Delete (row context menu + detail header) now gates on HasInteractiveSession
the same way SendToQueue already does, so an open ConPTY session's claude
process can't be orphaned by deleting its task out from under it.

Row removal (in-UI delete and the delta-refresh path for externally deleted
tasks) now clears SelectedTask via SelectFrom(null, "row-removed") when the
removed row was selected, so the detail pane no longer keeps showing a
deleted task with live buttons.
2026-08-26 15:46:40 +02:00
Mika Kuns f70aec495b fix(ui): store ScheduledFor as real UTC instead of mislabeled local time
The DB's global UtcConverter only tags DateTimes as Utc on read
(SpecifyKind), it never converts on write. SetScheduledForAsync persisted
the ThemedDatePicker's Local/Unspecified wall-clock value verbatim, so
QueuePicker's comparison against DateTime.UtcNow fired scheduled tasks late
by the local UTC offset (e.g. 2h in CEST). Convert to UTC at the write
boundary, and ToLocalTime() at the read/compare sites (overdue checks in
TaskRowViewModel/TasksIslandViewModel, the date-picker's edit seed) so
existing scheduled/overdue display doesn't shift.

Existing DB rows hold local wall-clock values mistagged as Utc; no
migration added (few rows, one-time 2h-class shift accepted per the
originating audit finding).
2026-08-26 15:46:10 +02:00
mika kuns 8930b564e4 refactor(ipc): declare the SignalR wire DTOs once in ClaudeDo.Data
WorkerHub and IWorkerClient each carried their own copy of every record and
had already drifted — MergePreviewDto lost its verify fields on the client
side, two records disagreed on their name. They now live in Data/Wire.cs and
reach both sides via a <Using> item, so a hub signature change is a compile
error instead of a silently dropped JSON field.
2026-08-26 13:55:40 +02:00
mika kuns 76d836a278 test: dedupe the nested test doubles into one shared file
TestDbFactory was redeclared as a private nested class in 40 test files,
NullServiceProvider and StubNotesApi in 14 each, DefaultStub in 5 — 73
declarations, all semantically identical (StubNotesApi differed only in
formatting and type qualification). They now live in TestDoubles.cs next to the
existing StubWorkerClient, which was already the shared-double pattern in this
project.
2026-08-26 10:11:58 +02:00
mika kuns 57be4961a8 feat(prime): add the action kind to the settings viewmodels 2026-08-25 09:01:33 +02:00
Mika Kuns 664816881a refactor(claude-do): merge TaskRowViewModel: CanX und DisabledReason aus einer Gate-Met
ClaudeDo-Task: 6c4b27aa-1b5e-4d73-98f5-dcafa3935247
2026-08-24 10:08:55 +02:00
Mika Kuns baba921696 refactor(ui): derive TaskRowViewModel CanX/DisabledReason pairs from one gate method
SendToQueue, Cancel, Refine, Planning and OpenWorktree each had a CanX property
and a hand-written negation of it for the DisabledReason tooltip, kept in sync
only by a comment's promise. Replace each pair with a private gate method
returning (Can, Reason) so there is one source of truth per condition, and add
a state-matrix test pinning down the CanX == (Reason == null) invariant that
the old comment only asserted.
2026-08-24 09:36:00 +02:00
Mika Kuns 9578c95074 refactor(ui): unify list-linked-repo predicate into RepoLinkage
ListsIslandViewModel and SettingsModalViewModel each re-implemented "does
this list have a linked WorkingDir" with different whitespace handling.
RepoLinkage.IsLinked/IsLinkedInDb is now the single definition; both
callers derive from it, closing the whitespace-only WorkingDir gap where
the Settings modal disagreed with the ListsIsland banner.
2026-08-24 09:28:20 +02:00
mika kuns eb2160dfc2 fix(claude-do): merge fix(worker): List-Handler-Session kennt ihre eigene Handler-
ClaudeDo-Task: 997d0ffa-48ae-42d7-bea0-3aed4cf81768
2026-08-21 18:09:02 +02:00
mika kuns 9c4a63d2c5 fix(worker): thread handler task id into list-handler kickoff prompts
Neither the initial nor the handoff kickoff ever told a list-handler
session its own handler task id, so handoff_list_handler(taskId, ...)
was unrenderable -- the handoff chain broke exactly where it was needed
(#200/#201 on 2026-08-21).

Add {handlerTaskId} to both MergeHelperInitialDefault and
MergeHelperHandoffDefault, thread a handlerTaskId parameter through
BuildForMergeHelperAsync (interface, WorkerHub.GetMergeHelperLaunchSpec,
IWorkerClient/WorkerClient, and the MissionControlViewModel call site,
which already had the id from CreateMergeHelperTaskAsync but never
passed it on), and render it in BuildForMergeHelperHandoffAsync from the
taskId parameter it already receives.

RenderTemplate leaves unknown/missing tokens untouched, so a
user-edited override without the new token still renders fine -- no
forced migration for override users.
2026-08-21 18:00:16 +02:00
mika kuns 7f94bf4aed fix(ui): UsagePillViewModel loads snapshot on connection restore
The ctor-time LoadAsync races WorkerClient.StartAsync, so the SignalR
call fails silently and the pill only fills on the next worker poll
(up to 900s). Subscribe to ConnectionRestoredEvent, which also fires
on the first successful connect, to retry the cached snapshot.
2026-08-21 17:47:49 +02:00
mika kuns 8f983851b4 feat(ui): Usage-Monitor zeigt Reset-Uhrzeit hinter der Restzeit 2026-08-21 16:22:26 +02:00
mika kuns 111dd0fb0d feat(ui): Erststart-Banner und Textkette Repo→Task→Queue→Review (UX-Struktur C)
HasNoLinkedRepo on ListsIslandViewModel drives a dismiss-less banner under the
Lists-Island header when no User list has a linked repo; recomputed on load
and after list CRUD / list-settings save. Sharpens the #192 empty-state texts
into one readable chain (banner → no-repo list → repo-linked list) and adds a
line to the WorkConsole review gate spelling out that Approve also merges the
whole unit.
2026-08-21 15:25:04 +02:00
mika kuns 56c3719e98 feat(ui): Settings-Modal auf Sidebar-Kategorien umbauen
TabControl bleibt Content-Host, TabStrip wird retempliert (nur
PART_SelectedContentHost) und durch eine zweigruppige Sidebar
(BASIS/ERWEITERT) ersetzt. General wird in Allgemein/Ausfuehrung/Berichte
gesplittet, Session Skills (Checkbox-Liste aus General + Skills-Tab)
zu einer Liste zusammengelegt, und ein Repo-Hinweisstreifen erscheint
auf Worktrees/Prime Claude/Session Skills/Berichte solange keine Liste
ein WorkingDir hat. Fensterbreite 580 -> 700.
2026-08-21 15:04:15 +02:00
mika kuns 51dc7bd61b feat(ui): Task-Zeile entdichten und Kontextmenü gruppieren (UX-Struktur A)
Gruppe 1 (Send to queue, Remove from queue, Cancel execution, Open quick
session, Refine task) ist jetzt fix sichtbar und gated per IsEnabled + Grund
statt komplett auszublenden. Planning/Schedule wandern in Untermenüs (Mark as
existierte bereits); der Planning-Kopf zeigt einen Grund, wenn er sonst leer
wäre. Refine-Button, ungesetzter Star und Dequeue-X erscheinen nur bei
Hover-oder-Selektion (TaskRowViewModel.ShowRowActions); ein gesetzter Star und
der Refining-Spinner bleiben immer sichtbar. Der Branch-Chip wandert aus der
Zeile in eine Meta-Zeile in TaskHeaderBar.

A3 headless verifiziert: Avalonia 12 zeigt ToolTips auf IsEnabled=false
Controls nur mit ToolTip.ShowOnDisabled="True" (Default ist false) — dieses
Attached Property existiert bereits und wird für die neuen Disabled-Reason-
Tooltips genutzt.
2026-08-21 14:34:14 +02:00
mika kuns 42de97e16a feat(worker): surface rebase and worktree-maintenance progress on OperationProgress
RebaseOthersAfterMergeAsync now broadcasts a "rebasing" phase with i/n over the
WaitingForReview branches it checks, so the merge/continue_merge callers stop
showing the stalled "Merging…" phase while the best-effort rebase loop runs
(rebase still runs before the verify gate; a failed rebase still leaves the
merge itself successful). WorktreeMaintenanceService gained an optional
HubBroadcaster to report the same i/n shape per worktree during
cleanup/reset, with no new UI surface (deliberately out of scope). Both
review-action viewmodels now also listen on OperationProgressEvent (which
carries the total that the elapsed-seconds-only MergeProgressEvent drops) to
render "Rebasing other worktrees… (i/n)".
2026-08-21 13:33:02 +02:00
mika kuns 7f337b36c7 fix(worker): route done-toggle and dequeue through guarded TaskStateService transitions
The task-list done toggle (both islands) and RemoveFromQueue wrote TaskEntity.Status
directly via EF, bypassing TaskStateService: no TaskUpdated broadcast, no guard against
a concurrent picker claim (lost update), and no status-based filter. Added guarded
MarkDoneAsync/UnmarkDoneAsync/DequeueToIdleAsync transitions plus matching hub methods
(SetTaskDone/UnsetTaskDone/DequeueTask) and IWorkerClient wrappers; the three UI call
sites now route through the hub with optimistic-then-revert row updates and
ErrorReported on failure. RemoveFromQueueAsync dequeues each queued child individually
through the same guarded path instead of cascading via a raw EF update.

Also closes two hub guard gaps: UpdateListConfig's delete branch now preserves a list's
SerializeOnFileOverlap flag instead of dropping it, and SubmitTaskForReview's Idle/Failed
status gate now runs before either mutation branch so a Done/Cancelled task can't get
committed or stamped and then rejected.
2026-08-21 11:47:24 +02:00
mika kuns 52efe3ec48 fix(ui): Reactivität — Description-Save-Race, UsageMonitor-Leak, Listen-Live-Refresh
Drei unabhängige Reactivity-Bugs aus dem Polish-Audit 2026-08-20:

1. Description-Autosave überschrieb den falschen Task, weil SaveDescriptionAsync
   Task.Id/EditableDescription erst nach dem 400ms-Debounce las statt Row+Wert an
   der Aufrufstelle zu capturen (wie SaveTitleAsync es schon tat). Bind() cancelt
   jetzt zusätzlich einen laufenden Title-/Description-Save der vorherigen Row.
2. UsageMonitorModalViewModel abonnierte UsageUpdatedEvent erst nach dem
   Erst-Load-Await — schloss man das Modal währenddessen, lief das Unsubscribe in
   Close() ins Leere und die VM hing für immer am WorkerClient. Ein _isClosed-Flag
   wird jetzt nach dem Await geprüft, bevor abonniert wird.
3. Per MCP erstellte Listen blieben unsichtbar: RefreshRowAsync hatte keinen
   Add-Zweig für unbekannte Ids und verglich zudem die falsche Id-Form (der
   Worker broadcastet die rohe DB-Id, nie die "user:"-prefixte Row-Id). Ein
   Reconnect lud zudem nur Counts statt der vollen Listen neu.
2026-08-21 11:41:58 +02:00
mika kuns b997af4423 fix(ui): notify IsTasksEmptyRepoHintVisible on every list switch
IsTasksEmptyRepoHintVisible reads _currentList?.Kind directly, but Kind
isn't itself observed — the NotifyPropertyChangedFor chain only fires
when IsLetClaudeVisible's value actually changes. Switching from a
Smart list to an empty User list without a WorkingDir changes Kind
while IsLetClaudeVisible (and Has*) stay false in both, so the hint
never notified and stayed stale.
2026-08-21 10:34:46 +02:00
mika kuns 9159f1b55c feat(ui): Empty States für Detail-Pane und Task-Liste (UX-Audit #4)
Zentrierter, gedimmter Platzhalter (Mission-Control-Muster) im
Detail-Pane bei fehlender Selektion (nicht in Notes-/Prep-Modus) und
in der Task-Liste bei 0 sichtbaren Tasks; User-Listen ohne
WorkingDir bekommen zusätzlich den Repo-Verknüpfungs-Hinweis.
2026-08-21 10:27:02 +02:00
mika kuns 2935eef75a fix(ui): Aktions- und Bestätigungs-Konsistenz (UX-Audit #3)
Sechs Konsistenz-Fixes: Delete-Task ins Zeilen-Kontextmenü (routet über den
Worker wie DetailsIslandViewModel.DeleteTaskAsync, damit ein gelöschtes Child
den WaitingForChildren-Parent korrekt weiterschaltet); EnqueueCommand als
"Send to queue"-Button im Detail-Pane verdrahtet; Settings-Eintrag im
Listen-Kontextmenü ergänzt; Worktree-Discard und Reset-All laufen jetzt über
das jeweils vorhandene Confirm-Hook (ConfirmAction / neuer ConfirmAsync-Hook
auf WorktreesSettingsTabViewModel) statt ohne Rückfrage bzw. über ein
Inline-Reveal-Banner; killSessionTip/closeSession und die deutschen
Queue-Strings im usageMonitor vereinheitlicht.

Kein zweiter Dialog-Mechanismus eingeführt — überall die vorhandenen Hooks
wiederverwendet.
2026-08-21 09:51:51 +02:00
mika kuns dbb73bae38 fix(ui): surface precondition hints for silently-disabled review buttons (UX-Audit #2)
Cancel now shows an amber hint + tooltip while a merge drain is in
progress (ShowMergeDrainHint mirrors IsMergeDraining). Continue,
Reset & Retry, Open Diff, Worktree, and Review Combined Diff gain
ToolTip.Tip + ToolTip.ShowOnDisabled explaining why they're locked.
No CanExecute or behavior changed, only added communication.
2026-08-21 09:31:56 +02:00
mika kuns 99bb5be5da fix(ui): silent no-ops raise ErrorReported instead of swallowing failures (UX-Audit #1)
Stop/Enqueue/Dequeue/Reset&Retry (DetailsIslandViewModel), status/cancel/reject
commands (TasksIslandViewModel), Mission Control's drag-enqueue and queue
refresh, and "Open findings folder" (ListsIslandViewModel) used to catch {}
or silently return on a blocked precondition. They now report through the
existing ErrorReported -> FlashFooterError path, with new en/de locale keys
and Ui.Tests covering each converted command.
2026-08-21 09:13:00 +02:00
Mika Kuns c795827162 test(ui): [B3] guard against a synchronous diff parse blocking the dispatcher
Adds DiffDispatcherGuardTests, the one blockade-regression test in the
operation-feedback effort. Uses a queueing SynchronizationContext (Option B —
this project has no bootstrapped Avalonia dispatcher anywhere) to prove
DiffViewerViewModel's UnifiedDiffParser.Parse offload ([B1]) truly defers past
the calling thread rather than racing on timing. [B2]'s DiffAlignment.Build
offload in DiffTextView is deliberately not covered — three escalating
attempts to construct that control safely in-process each broke under the
full test suite (dispatcher thread-affinity crashes, then process-wide
Application state polluting an unrelated test), and Avalonia.Headless.XUnit
collides with this project's xUnit v2 stack. Verified the test fails against
the pre-[B1] code and passes with it; full suite green aside from a
pre-existing, unrelated flaky test.
2026-08-12 14:55:37 +02:00
Mika Kuns f6994f32d5 fix(ui): show ParseOp indicator on the planning-mode diff parse too
ParseOp only covered LoadFilesAsync; OnDisplayedDiffChanged's parse (driven
by ToggleCombinedAsync and OnSelectedSubtaskChanged) had no visible
feedback even though the offload was already correct. Both call sites now
go through one ParseOffUiThreadAsync helper (Begin on the UI thread before
Task.Run, End via `using` regardless of a stale generation), so the
already-visible toolbar indicator covers the planning path without
duplicating the begin/offload/end ceremony.
2026-08-12 13:41:05 +02:00
Mika Kuns 9fc8ed391e fix(ui): offload UnifiedDiffParser.Parse off the UI thread in DiffViewerViewModel
Both call sites (LoadFilesAsync, OnDisplayedDiffChanged) ran the parser
synchronously on the dispatcher, freezing the window on a large diff. The
Files-mode parse+tree-build now runs via Task.Run behind a ParseOp
OperationStatus wired to the toolbar; the planning-mode parse is
fire-and-forget with a generation counter so a fast second DisplayedDiff
change can't write stale PlanningFiles.
2026-08-12 13:19:20 +02:00
Mika Kuns 69b07fff26 chore(claude-do): merge [A4] WeeklyReport, DailyPrep und Planning-Aktionen auf Opera
ClaudeDo-Task: 7c6f2f66-8cd7-4abb-8151-aed42ae739b5
2026-08-12 10:10:57 +02:00
Mika Kuns ec4cbcbaf9 chore(claude-do): [A3] Settings + Update-Check: OperationStatus für Skill-Inst
Vorgaben: `docs/superpowers/plans/2026-08-11-operation-feedback.md`, Gruppe A, Entwurf A3. P0-1 ist gemergt: `src/ClaudeDo.Ui/Services/OperationStatus.cs` + `src/ClaudeDo.Ui/Views/Controls/OperationIndicator.axaml`.

SCOPE-ÄNDERUNG (Nutzer, 2026-08-12): OnlineInbox Sign-In/Sign-Out ist AUS DEM SCOPE GENOMMEN — vorerst unwichtig. `ViewModels/Modals/Settings/OnlineInboxSettingsViewModel.cs` und die

ClaudeDo-Task: 2356806f-f16e-4652-b615-4562e95c3a65
2026-08-12 09:55:04 +02:00
Mika Kuns 5fca459579 chore(claude-do): merge [A2] WorktreesOverview + Reset-All: getrennte OperationStatu
ClaudeDo-Task: cec07e2c-2814-4a43-898c-a6be07a13f01
2026-08-12 09:54:58 +02:00
Mika Kuns bea85a9ee5 feat(ui): route detail-pane review actions through OperationStatus
Approve, Submit, Reject, and Park each get their own OperationStatus so the
button locks and shows an OperationIndicator for the duration of the call;
Approve additionally scopes a MergeProgressEvent subscription to its own
task id to sharpen the label from "merging" to "verifying" mid-flight.
MergeSectionViewModel's preview refresh gets the same treatment for display
only, since it has no command button to gate.
2026-08-12 09:42:15 +02:00
Mika Kuns bbd6151ba6 feat(claude-do): [A4] WeeklyReport, DailyPrep und Planning-Aktionen auf OperationStatus umstellen
WeeklyReportModalViewModel.Generate, PrepPanelViewModel.PlanDayAsync und die beiden
Planning-Aktionen (QueuePlanningSubtasksAsync, FinalizePlanningSessionAsync) laufen jetzt
durch je eine OperationStatus + OperationIndicator statt handgebauter Spinner. Die beiden
Planning-Aktionen teilen sich eine Instanz, angezeigt im Tasks-Island-Header, weil sie aus
einem sofort schliessenden Kontextmenue ausgeloest werden und es keine dauerhafte
Pro-Zeilen-Flaeche gibt, an die ein Indikator gehaengt werden koennte.
2026-08-12 09:39:42 +02:00
Mika Kuns a0d5db0db0 feat(ui): separate OperationStatus per worktree action (refresh/cleanup/reset/force-remove/batch-merge)
Replaces the shared IsBusy/IsMerging flags in WorktreesOverviewModalViewModel and the
reset flow in WorktreesSettingsTabViewModel with dedicated OperationStatus instances
shown via OperationIndicator, so a running Refresh no longer blocks the Cleanup
indicator. ForceRemove gains a CanExecute guard against re-entrancy while it's running,
and the reconcile tick's busy guard now checks all four action statuses instead of the
old IsBusy||IsMerging pair.
2026-08-12 09:39:10 +02:00
mika kuns cad0582b37 fix(merge): conventional merge-commit default and live verify progress
The merge commit message was hand-rolled per caller ("Merge task: <title>",
"Merge <branch>", "Merge subtask") and ignored the task's commit type. Every
caller now passes a blank message and TaskMergeService fills in
CommitMessageBuilder.BuildMerge -> {commitType}(list-slug): merge <title> plus the
ClaudeDo-Task trailer; the merge modal prefills it from GetMergeTargets.

A merge whose list has a verify command holds the MergeTask call for minutes (5m46s
on this repo), during which the modal only disabled its button - no spinner, no
message, so a landed merge looked like a dead app. TaskMergeService now broadcasts
MergeProgress(taskId, phase, elapsedSeconds) for the merging and verifying phases
(re-reported every 30s) plus a WorkerLog line when verify starts; the modal shows a
spinner and the localized phase.
2026-08-11 19:14:36 +02:00
mika kuns fc9df7f9ac Merge task: UI polish: ListBox Padding scroll clip, 4s log rebuild, ClassifyItems side effects 2026-08-11 18:34:36 +02:00
mika kuns e55c84cda4 fix(ui): stop ClearCompletedAsync from mutating chain-display fields
ClassifyItems() layers ApplyChainGrouping on top of partitioning, which
writes ShowAsChainMember/ChainStep/ChainAfterLabel on every row.
ClearCompletedAsync only needed the completed-section count/rows, so it
got that mutation as an unwanted side effect of counting.

Split the pure overdue/open/completed partition into PartitionItems();
ClassifyItems() now composes it with the chain-grouping mutation for
Regroup's actual render path. ClearCompletedAsync uses PartitionItems
directly. Regroup's behavior is unchanged.
2026-08-11 18:32:59 +02:00
mika kuns d327a1007e fix(ui): reconcile the log visualizer tick instead of clearing Rows
The 4s reconcile tick called Apply(), which did Rows.Clear() + refill on
every tick -- resetting the user's scroll position and selection while
they're mid-read, exactly what the overlay exists to avoid.

Reconcile granularly instead: find the overlap between the previous and
latest snapshot (the ring buffer is append-mostly, evicting from the
front as entries age out of the 30-min window), trim rows for evicted
entries off the tail, and insert new entries at the head. A tick with no
change now leaves Rows untouched. The WarnErrorOnly toggle still goes
through the full Apply() rebuild, unchanged.
2026-08-11 18:32:39 +02:00
mika kuns e343baf21a fix(ui): close the outgoing ConPTY pane on merge-helper handoff
Each handoff tile is a live claude process with the full mcp__claudedo__* surface; leaving the
outgoing phase's pane open leaked one process per phase. Close it via CloseConPtySession before
opening the next phase's tile, restoring the one-pane-per-TaskId invariant so
OpenConPtySessionAsync's dedupe and OnPaneSubmitForReview can go back to FirstOrDefault.
2026-08-11 16:55:25 +02:00