Commit Graph
1724 Commits
Author SHA1 Message Date
mika kuns d6b22ea528 feat(usage): pin the TokenTracker CLI invocations 2026-08-24 13:36:59 +02:00
mika kuns 580d4e0717 feat(usage): hold the last good TokenTracker export fail-open 2026-08-24 13:36:59 +02:00
mika kuns 2fca0cc4d3 feat(usage): aggregate TokenTracker sessions by model and per task 2026-08-24 13:35:36 +02:00
mika kuns 106f9a374a feat(usage): parse the TokenTracker sessions export defensively 2026-08-24 13:34:01 +02:00
mika kuns 16952b6432 feat(usage): add TokenTracker export models and test fixture 2026-08-24 13:32:46 +02:00
mika kuns 1cb574bd96 feat(usage): reproduce TokenTracker session_hash for task attribution 2026-08-24 13:31:29 +02:00
mika kuns 0e74650d70 docs(usage): spec for TokenTracker as the token-analytics backend 2026-08-24 10:29:56 +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 67083e1a4a refactor(claude-do): merge Dedupe: FormatRemaining aus UsagePill und UsageMonitorModal
ClaudeDo-Task: 94ea71ad-22a1-4612-8743-425d5fb43d75
2026-08-24 10:02:22 +02:00
Mika Kuns 9c59c77a28 refactor(claude-do): merge Dedupe: ShellOpen-Helper für "Pfad im Explorer öffnen" (5 Co
ClaudeDo-Task: 4084c060-3cf0-4474-930a-ea79695b9ba6
2026-08-24 09:54:37 +02:00
Mika Kuns b2a1b79029 refactor(ui): dedupe path-open helper across five view models
ListsIslandViewModel.OpenInExplorer, MergeSectionViewModel.OpenWorktree,
WorktreesOverviewModalViewModel.OpenInExplorer, AboutModalViewModel.OpenPath and
TasksIslandViewModel.OpenTaskWorktree each reimplemented "open this path in the
shell" with their own existence check, launch mechanism and error handling.
Replace all five with the new ShellOpen.Path helper and route failures through
the existing ErrorReported -> footer-strip convention instead of bare catch {}.
2026-08-24 09:38:41 +02:00
Mika Kuns d309302043 refactor(claude-do): merge Dedupe: "Liste hat ein verlinktes Repo?" — ein Prädikat stat
ClaudeDo-Task: 594ee78e-7112-427a-9cde-76f6610e7fd8
2026-08-24 09:38:36 +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 e164f1e33f refactor(ui): dedupe FormatRemaining between UsagePill and UsageMonitorModal
Both viewmodels carried byte-identical copies of the reset-countdown
formatter. Moved it to UsageTimeFormat.FormatRemaining and pointed
both call sites at it; locale keys and output unchanged.
2026-08-24 09:27:15 +02:00
Mika Kuns 79438c00d2 feat(Data): Improve App Prompts 2026-08-24 09:25:43 +02:00
mika kuns 29171b104b fix(ui): failureReason "usage_limit" nachziehen + Worktree-Gate prüft die Platte
Zwei liegengebliebene Consumer aus den letzten beiden Commits:

- TaskRunner klassifiziert seit 07dd7570 "usage_limit", aber weder
  TaskRowViewModel.FailureReasonLabel noch vm.failureReason (de/en) noch
  die get_task-Tool-Beschreibung kannten den Wert — die UI zeigte
  "Grund unbekannt", das MCP-Doc listete weiterhin max_turns|timeout|error.
- TaskRowViewModel.CanOpenWorktree prüfte nur auf einen nicht-leeren
  String. Die Zeile behält den Path eines gemergten/verworfenen Worktrees,
  also war der Menüpunkt aktiv und Process.Start warf in den Footer.
  Jetzt zusätzlich Directory.Exists — dieselbe Prüfung, die
  WorktreesOverviewModalViewModel und MergeSectionViewModel schon machen.
2026-08-24 09:05:31 +02:00
mika kuns 3dfae30fff fix(worker): Usage-Limit-Auto-Continue verbrennt nicht mehr alle Kandidaten außer dem ersten
OverrideSlotService hält genau einen Slot, und ein Usage-Limit stoppt
naturgemäß alle laufenden Tasks gleichzeitig — "mehr Kandidaten als
Kapazität" ist also der Normalfall. Der Coordinator stempelte
UsageLimitAutoContinuedAt aber VOR dem ContinueTask (der Marker ist die
Dedupe-Guard), sodass Kandidat 2..n den Marker bekamen, deren
ContinueTask mit "override slot busy" in den catch flog und sie danach
dauerhaft aus GetUsageLimitAutoContinueCandidatesAsync ausgeschlossen
waren — ein Continue, der nie lief.

- Vorab-Check auf CurrentSlot: bei belegtem Slot bricht der Tick ab,
  statt die restlichen Kandidaten zu verbrennen.
- Neuer TaskRepository.ReleaseUsageLimitAutoContinueClaimAsync gibt den
  Claim zurück, wenn ContinueTask wirft; danach return, der 30s-Backstop
  holt den Rest im nächsten Tick.
- Regressionstest mit zwei Kandidaten, deterministisch über eine
  TaskCompletionSource im FakeClaudeProcess (StartInSlot setzt _slot
  synchron unter dem Lock, bevor die Arbeit startet).
2026-08-24 09:05:20 +02:00
mika kuns 55bfb765e6 feat(claude-do): merge feat(worker): Toggle "Continue on session limit reset" — lim
ClaudeDo-Task: 6a8a7151695d4b93bd6d13e66d9307c5
2026-08-21 19:16:20 +02:00
mika kuns 07dd75700d feat(worker): Toggle "Continue on session limit reset" für Usage-Limit-Abbrüche
Klassifiziert einen echten Usage-Limit-Abbruch als eigene FailureReason
"usage_limit" (TaskRunner.ClassifyFailureReason: nur bei terminal_reason
"api_error" plus einem Limit-Muster im gerenderten Fehlertext, nicht an
Status==Failed allein). Neuer Toggle AutoContinueOnUsageLimit (app_settings,
Default aus) unter Settings → General → "Usage limit stop":

- UsageLimitAutoContinueCoordinator feuert pro Task genau einmal ContinueTask
  über OverrideSlotService, sobald das 5h-Fenster (UsageState.Snapshot.FiveHour
  .ResetsAt) tatsächlich zurückgesetzt ist; ein persistenter Marker
  (TaskEntity.UsageLimitAutoContinuedAt) verhindert einen zweiten Anlauf bei
  einem erneuten Limit-Treffer.
- QueueService schedult zusätzlich einen exakten Wake-Timer auf den
  Reset-Zeitpunkt, statt nur auf den 30s-Backstop zu warten.
- Fail-open durchgängig: kein Snapshot/keine Reset-Zeit → kein Timer, kein
  Continue, kein Throw. Toggle aus ändert das heutige Verhalten nicht.

Migration AddUsageLimitAutoContinue fügt beide Spalten hinzu; die von
`dotnet ef migrations add` mitgescaffoldete leere UpdateData auf app_settings
(columns/values: []) erzeugte ungültiges SQL ("near WHERE") und wurde entfernt
— TaskNumberMigrationTests deckte das über den vollen Migrate()-Pfad auf.
2026-08-21 18:43:02 +02:00
mika kuns 290dd1b614 feat(claude-do): merge feat(ui): Hauptfenster-Close mit offenen Mission-Control-Ses
ClaudeDo-Task: a963f289b38449a3a8d0c52fc9bfd9e7
2026-08-21 18:20:44 +02:00
mika kuns 0892883a82 feat(ui): confirm main-window close when Mission Control sessions are open
Closing the main window used to silently kill every open Mission Control
ConPTY session (App's ShutdownMode.OnMainWindowClose tears the process down
without warning). Intercept Window.Closing, and when at least one pane is
open, ask via the existing ConfirmAsync pattern before proceeding; cancelling
leaves the window and sessions alive, confirming force-closes as before.
2026-08-21 18:14:48 +02:00
mika kuns 89d30d1463 feat(claude-do): merge feat(ui): "Worktree öffnen" im Kontextmenü der Task-Zeile
ClaudeDo-Task: 9be17e29e0b54f42ba9385b0cebe89d5
2026-08-21 18:14:45 +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 b598ec2fac fix(claude-do): merge fix(ui): Usage-Pill bleibt nach App-Start leer — Initial-Loa
ClaudeDo-Task: cdb71ff5aefd4ae9819352a845e0be89
2026-08-21 18:03:03 +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 ac9b0dab29 feat(ui): "Worktree öffnen" im Kontextmenü der Task-Zeile
Group 1 of the task-row context menu gets a fourth gated entry, right
after "Open interactive session": it opens the task's worktree folder
via Process.Start/UseShellExecute, mirroring ListsIslandViewModel's
OpenInExplorer. Missing worktree greys the item out with a tooltip
reason instead of hiding it, keeping Group 1's fixed length. Failures
report through TasksIslandViewModel.ErrorReported (footer strip), not
a silent catch.
2026-08-21 17:58:31 +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 93be76a144 feat(claude-do): merge feat(mcp): update_task kann isManual setzen + List-Handler s
ClaudeDo-Task: 0af82db9dd3e43eb9071f98f807ef452
2026-08-21 16:56:48 +02:00
mika kuns ea2271cc3e feat(mcp): update_task kann isManual setzen + List-Handler stellt seinen Task auf Review
Teil A: update_task bekommt einen optionalen isManual-Parameter (null =
unveraendert); TaskDto/TaskRefDto spiegeln IsManual zurueck.

Teil B: neuer submit_task_for_review MCP-Tool (LifecycleMcpTools) laesst
einen Handler-Task selbst auf WaitingForReview gehen -- fuer einen
worktree-losen Task wird HandlerHeadCommit vom aktuellen HEAD gestempelt,
sonst werden offene Aenderungen committet. Die Submit-Logik ist aus
WorkerHub.SubmitTaskForReview in InteractiveReviewSubmissionService
extrahiert; der Hub ruft sie jetzt nur noch auf. Der Merge-Prompt-Default
weist die Endzweige (merge ohne Rerun, merge_final) an, den eigenen
Handler-Task nach der Summary einzureichen.
2026-08-21 16:51:18 +02:00
mika kuns 36720d33ae fix(claude-do): merge fix(worker): Interaktive Session auf einem Nicht-Repo-Workin
ClaudeDo-Task: 15e126d564874973be2a5bbe7d796646
2026-08-21 16:51:15 +02:00
mika kuns 302d657644 feat(claude-do): merge feat(ui): Usage-Monitor zeigt hinter der Restzeit die Reset-
ClaudeDo-Task: 533f0a42c08e4044a8eaa9235f960dbe
2026-08-21 16:45:30 +02:00
mika kuns ff37a97ade fix(claude-do): merge fix(ui): Description-Editbox nutzt die volle Höhe der Descri
ClaudeDo-Task: 2e98a45690e842b7a498034dc9483a15
2026-08-21 16:39:47 +02:00
mika kuns 0d1dd169ac fix(claude-do): fix(worker): Interaktive Session auf einem Nicht-Repo-Workin
Eine interaktive ("Quick") Session soll auf Pfaden, die kein Git-Repo sind, keinen Worktree erzwingen, sondern direkt im ausgewählten Ordner starten. Der User hat entschieden: BEIDE Einstiegspunkte prüfen — den task-gebundenen Pfad fixen, den Ad-hoc-Pfad verifizieren.

## Pfad 1 (der eigentliche Fix) — task-gebundene Session

- Einstieg: Kontextmenü der Task-Zeile "Open interactive session" — src/

ClaudeDo-Task: 15e126d564874973be2a5bbe7d796646
2026-08-21 16:36:11 +02:00
mika kuns a6d165a177 fix(ui): Description-Editbox nutzt volle Kartenhoehe statt 320px-Deckel
Die Edit-TextBox war per festem MaxHeight auf 320px begrenzt, auch wenn die
Description-Card per Splitter groesser gezogen wurde. Die Card begrenzt die
Hoehe bereits selbst (2/3-Cap in UpdateRowLimits), daher stretcht die TextBox
jetzt ohne eigenen Deckel bis zur verfuegbaren Card-Hoehe.
2026-08-21 16:28:35 +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 8dce2d0898 chore(claude-do): merge feat(ui): Erststart-Banner und Textkette Repo→Task→Queue→Rev
ClaudeDo-Task: 60ad0392-ef5e-4204-9445-415788ccd45b
2026-08-21 15:54:21 +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 c47cdc1c7c chore(claude-do): merge feat(ui): Settings-Modal auf Sidebar-Kategorien umbauen (UX-
ClaudeDo-Task: 314cf7a5-ed81-4b55-895c-abc30301d02e
2026-08-21 15:08:32 +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 a80a31eeac chore(claude-do): merge feat(ui): Task-Zeile entdichten und Kontextmenü gruppieren (
ClaudeDo-Task: a43e22e7-ba96-4ee0-8aa6-482f7c567db6
2026-08-21 14:39:10 +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 c3ef460a40 chore(claude-do): merge [C5] Periodische Dienste: nur Aktivität und Fehler melden —
ClaudeDo-Task: a18f685b-ecbd-40b6-ae0b-83ada726eae6
2026-08-21 13:56:54 +02:00
mika kuns c143eb2481 chore(claude-do): merge [C4] Rebase-after-Merge sichtbar machen + WorktreeMaintenanc
ClaudeDo-Task: 0a64e32d-6308-4f2d-a69e-fb8b3cabd01a
2026-08-21 13:51:01 +02:00
mika kuns ab449d51eb chore(claude-do): merge [C3] Worktree-Anlage beim Task-Start an der Task-Zeile sicht
ClaudeDo-Task: 68254f28-13f4-40e8-94c0-e15d2fe58b37
2026-08-21 13:45:24 +02:00
mika kuns beca1d8c99 chore(claude-do): merge [C2] Startup-Recovery über den OperationProgress-Kanal sicht
ClaudeDo-Task: c264783d-2952-4e8f-9ef6-4dd0d493d8a7
2026-08-21 13:39:42 +02:00
mika kuns 6ec81f653b feat(worker): surface startup recovery via OperationProgress channel
The six Lifecycle/*Recovery hosted services now broadcast one
OperationProgress("startup-recovery", <phase>, current, total) message each
after they finish, instead of leaving the UI on a bare "connecting" text
during worker startup. IslandsShellViewModel subscribes and swaps in
"Recovering... (i/n)" (existing ops.worker.startupRecovery key, no locale
changes) while Worker.IsReconnecting is true, and clears it once actually
connected so a later transient reconnect doesn't replay stale text.

OperationProgress broadcasts to Clients.All with no replay-on-connect, so a
UI that hasn't finished its SignalR handshake yet can miss some or all of
these messages and simply keep showing "connecting" as before -- accepted
rather than adding a cached-state + reconnect-replay path (mirroring
RefreshExternalMergeConflictsAsync) for what is a fast, best-effort,
local-only startup sweep with no UI-visible failure mode beyond that.
2026-08-21 13:36:25 +02:00
mika kuns eba0d842b5 feat(worker): report worktree-creation phase on OperationProgress
Fills the silent gap between Queued and the first agent output: WorktreeManager
now broadcasts a "creating_worktree" phase (before the initial git worktree add
and again for the self-heal retry section) through the existing OperationProgress
channel, and the task row shows it via the pre-existing but unused
ops.worker.creatingWorktree locale key until the next entity refresh clears it.

Confirmed the 2026-08-07 triage finding still holds: TaskRunner already
broadcasts WorktreeUpdated right after WorktreeManager.CreateAsync
(TaskRunner.cs:342-346, :501) -- no second broadcast added there.
2026-08-21 13:35:26 +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 2986896223 docs(worker): C5 periodische Dienste abgesagt — kein Mehrwert über bestehende Broadcasts
Usage/Prime/OnlineSync melden Aktivität schon (UsageUpdated, Prime*-Events, TaskUpdated),
Fehler aller vier Dienste laufen bereits über BroadcastLogSink in den WorkerLog-Footer,
und QueueServices einzige bedeutsamen Zustandswechsel (Gate/Throttle) sind schon
change-gated broadcastet. Ein zusätzlicher OperationProgress-Kanal wäre Duplikat mit
Tick-Strom-Risiko am Backstop-Timer statt Mehrwert.
2026-08-21 13:14:14 +02:00