Durch die GUID namen kann ein User die Worktrees keinem Task zuordnen, und in Rider Müllen mir die Worktrees die BRanches voll. Also sollten wir einfach den Task titel oder Nummer stattdessen nehmen
ClaudeDo-Task: 63f08d944d3341c081267158ef4bbb3e
Lists default to the Text icon instead of the folder icon, rows align
across depths with a guide line per open folder, folder names are
semibold, folder counts only show while collapsed and zero counts hide.
Drop the unused deferredUntil parameter the branch added to HandoffRequested: the worker still sends three args, so the four-arg client handler would never bind. The reset time already arrives via HandlerHandoffDeferred.
Message-template tokens must be snake_case (repo rule) -- fixes 4 PascalCase
tokens introduced in 57dd6766. TryInvokeAsync's swallowing catch also no
longer logs a Warning for OperationCanceledException, which is routine
(e.g. worker shutdown/reconnect), not a failure worth a trace.
ClaudeDo-Task: d2ae3831-733f-4d40-93d7-70c93da11590
Folders can now contain other folders, to arbitrary depth, via a
self-referencing parent_folder_id (ON DELETE SET NULL, same pattern as
lists.folder_id). Nesting/un-nesting is drag-and-drop only: dropping a
folder onto another folder's header middle moves it in, the outer
quarters or any top-level/in-folder drop insert it beside a target.
Dropping a folder into itself or one of its own subfolders is silently
ignored. Deleting a folder releases its lists and subfolders back to
the top level, unchanged from the existing list behavior.
ListFolderNavItemViewModel.Lists became Children (object, mixing lists
and subfolders, mirroring TopLevel) so the existing implicit, type-matched
DataTemplates render nested folders recursively without new XAML, and each
nesting level gets its indent for free from the folder template's own
margin. Folders/RootLists/ContainerOf/PersistOrderAsync/RecomputeFolderCounts
now walk the tree instead of assuming one level.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
## Problem
Beim DeleteTask-Bug (Fix 352aaa52) gab es null Spuren: Der UI-Prozess (ClaudeDo.App) hat gar kein File-Logging (weder Serilog noch `Microsoft.Extensions.Logging` sind in App/Ui referenziert), und im Worker loggt SignalR fehlgeschlagene Hub-Invocations nur auf Debug (`Microsoft.AspNetCore` steht in `src/ClaudeDo.Worker/Program.cs` Z. 60 auf Warning). Die Ursache ließ sich nur finden, ind
ClaudeDo-Task: d2ae3831-733f-4d40-93d7-70c93da11590
Der CancellationToken-Parameter auf der nicht-streamenden Hub-Methode liess SignalR jede Invocation vor dem Methodenrumpf abbrechen. Die Tests riefen den Hub direkt auf und sahen das nicht; neuer Reflection-Guard verbietet CancellationToken auf allen nicht-streamenden Hub-Methoden.
Neue Tabelle mission_control_sessions (Migration AddMissionControlSessions), befuellt vom
InteractiveLaunchSpecService bei Task-, Ad-hoc- und Handoff-Kacheln. Flyout im Mission-Control-Header
listet die letzten 14 Tage / 50 Sessions, Handoffs eingerueckt unter ihrer Ursprungs-Session, mit
Fortsetzen (--resume im gespeicherten cwd) und Brief anzeigen.
## Ziel
Jede Mission-Control-Session (Ad-hoc UND Task-Session) soll ihre Arbeit an eine frische Session
uebergeben koennen: "gib das ab an eine neue Session". Die alte Kachel wird geschlossen, an
derselben Position oeffnet eine frische Session im selben cwd, die mit einem Brief startet, den
die abgebende Session selbst geschrieben hat.
Motivation: eine lange Session mit vollem/verrottetem Kontex
ClaudeDo-Task: 8b846deb-ad10-4243-9272-9aea2a01ec93
Ein Handoff schloss die alte Pane und haengte die neue hinten an -- die Kachel
sprang ans Ende und das ganze Grid ordnete sich neu. Die neue Pane wird jetzt am
Index der alten eingesetzt.
Dazu bekommt jede Kachel eine aus IdentityKey (Default: TaskId) abgeleitete Farbe:
3px-Streifen links am Tile, Punkt im Focus-Mode-Tab. Kein State, keine Persistenz --
derselbe Task faerbt nach App-Neustart gleich, und ein Handoff erbt die Farbe, weil
er dieselbe TaskId behaelt. Nur kuehle Hues, damit nichts als Status gelesen wird.
MergeTargetsDto/MergeTargets bekommen TargetDirty (via HasChangesAsync,
includeUntracked: false). Das Merge-Modal zeigt Warnung + Stash-Retry
sofort in InitializeAsync statt erst nach dem dirty_target-Fehlschlag;
der REVIEW-Block in der Detail-Pane zeigt denselben Hinweis neben
Approve. Der dirty_target-Rueckgabepfad bleibt als Absicherung bestehen.
Behebt Befund F6 aus docs/test-session-2026-09-16.md.
F4: DetailsIslandView/TasksIslandView hatten je eine view-lokale Confirm/Error-Dialog-Kopie,
teils mit hartkodiertem "Delete"-Knopf fuer jede Aktion (inkl. der Stash+Merge-Bestaetigung,
die nichts loescht). Beide Kopien entfernt; DetailsIslandViewModel/TasksIslandViewModel routen
Confirm/Error jetzt durch den geteilten Dialogs-Seam (IDialogService, propagiert vom Shell wie
schon bei Lists/Tasks) statt ueber eine vom Code-behind gesetzte Func-Property.
IDialogService.ConfirmAsync bekommt einen optionalen confirmLabel-Parameter (Default: lokalisiertes
"Confirm"); nur die echten Delete-Aktionen (Task loeschen, abgeschlossene Tasks leeren) uebergeben
das lokalisierte "Delete"-Label.
F5: "Cancel"/"Confirm"/"Delete"/"OK" und die Titel "Confirm"/"Error" durch Loc.T mit neuen Keys
unter "dialog.*" ersetzt (en.json + de.json). Mission Controls eigene Dialog-Kopie bleibt bestehen
(eigenes Fenster, WindowDialogService ist an den Haupt-Owner gebunden) — nur ihre Labels wurden
lokalisiert.
Out of scope (siehe Task-Beschreibung): die Nachrichtentexte der Dialoge bleiben unlokalisiert.
Overlap detection was file-name-only: 9/15 tasks touching en.json/de.json in
run #316 all got flagged even though only one pair actually collided on a
key, and a removed method with no textual file conflict (#297 vs #286) went
unflagged entirely.
- removedSymbols per task entry: a line-pattern scan of `-` diff lines for
C# method/property/field/type/event declarations (skips .axaml/.json).
- symbolWarnings at the set level: cross-references those names against
every other task's `+` lines, catching same-symbol collisions across
files that overlaps/subsets (both purely file-based) can't see.
- overlaps narrows to key-path level for .json files (base commit vs. task
branch head, both sides parsed and flattened) so two tasks adding
different locale keys to the same file no longer collide; unparsable
JSON on either side falls back to the old file-level check.
- GitService.ShowFileAsync: `git show ref:path`, used to read a task
branch's json content at its base/head commit.
Out of scope per the task: no Roslyn/parser, no AXAML analysis, and the
merge-helper prompt in PromptFiles.cs doesn't mention the new fields yet.
Build: dotnet build src/ClaudeDo.App (pulls in Ui+Data) + src/ClaudeDo.Worker, both clean.
Tests: dotnet test tests/ClaudeDo.Worker.Tests --filter "FullyQualifiedName~MergeMcpTools|FullyQualifiedName~ExternalMcpToolSchema" -- 51 passed, 0 failed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Befund F7 aus der Sichtpruefung 2026-09-16. TaskNumber war bereits im
WorktreeMaintenanceService-Join und in WorktreeOverviewRow vorhanden, fehlte
aber im Wire-DTO und in der UI-Kette; jetzt bis zur Row-/Conflict-Anzeige
durchgereicht (#<number> dimmed vor dem Titel, ausgeblendet bei 0). DIFF-Spalte
bekommt CharacterEllipsis-Trimming + Tooltip statt hartem Cut, Spalte von 80
auf 110px verbreitert (Header und Row synchron).
Ctrl+V mit einem Bild in der Zwischenablage schreibt es als temporaeres
PNG unter ~/.claudeDo/tmp/ und fuegt den Pfad in den Composer ein, genau
wie ein Datei-Drop -- Claude liest die Datei selbst. Text-Paste laeuft
unveraendert ueber den bestehenden bracketed-paste-Pfad.
Avalonia 12.0.4 liest Clipboard-Bitmaps bereits selbst (IClipboard.
TryGetDataAsync + AsyncDataTransferExtensions.TryGetBitmapAsync), kein
Win32-P/Invoke noetig. Alte Tmp-Dateien (>24h) werden beim App-Start
aufgeraeumt (ClaudeDo.App/Program.cs), keine eigene Aufraeum-Infrastruktur.
VerticalScrollSync mirrort nur noch Y, X blieb pro Pane unabhaengig. Beide
Panes nutzen dieselbe Schrift/Breite, also X 1:1 mitspiegeln statt wie bei
Y ueber TargetOffset umzurechnen. Die Echo-Abbruchbedingung prueft jetzt
beide Achsen (vorher nur Y), sonst wuerde der neue X-Schreibvorgang seine
eigene Echo-Bedingung nie erfuellen und endlos weiterlaufen.
Klasse in ScrollSync umbenannt (DiffTextView + ConflictResolverView
mitgezogen), da sie nicht mehr nur vertikal synchronisiert.
Kein Headless-Test hinzugefuegt: DiffDispatcherGuardTests.cs dokumentiert
bereits, dass ein bootstrapptes Avalonia Application/Dispatcher in
ClaudeDo.Ui.Tests nach drei eskalierenden Versuchen als Sackgasse verifiziert
wurde (Dispatcher-Thread-Affinitaet kollidiert mit xUnits Worker-Pool ohne
Thread-Garantie; Avalonia.Headless.XUnit kollidiert mit xunit 2.9.3). Als
Finding avaloniaedit-ui-test-dead-end festgehalten. Sichtpruefung bleibt
offen (Side-by-side + Merge-Editor mit ueberbreiten Zeilen).
ToggleFolderAsync war ein async [RelayCommand]; AsyncRelayCommand sperrt
sich waehrend des DB-Writes selbst, schnelle Klicks gingen verloren
(Befund F1, docs/test-session-2026-09-16.md).
Variante A: Toggle ist jetzt synchron (reiner UI-State), Persistierung
fire-and-forget mit einem CancellationTokenSource pro Ordner, damit ein
zweiter Klick vor dem ersten Schreibvorgang diesen abbricht - die DB
landet immer beim zuletzt sichtbaren Zustand, nie bei einem veralteten.
Gleiches Muster wie SaveTitleAsync/SaveDescriptionAsync in
DetailsIslandViewModel.
Gegenprueft: RemoveFromFolderAsync/CreateListAsync haben denselben
async-RelayCommand-Schnitt, sind aber Einzelklick-Aktionen (Kontextmenue/
Button), kein Rapid-Click-Ziel wie der Ordner-Toggle - bewusst nicht
umgebaut. Die Modal-oeffnenden Commands sind unkritisch, ein Modal
blockiert ohnehin.
preview_merge baute bisher immer, sobald die Liste ein Verify-Kommando
konfiguriert hatte -- anders als das Geschwister-Tool preview_merge_set,
dessen runVerify-Parameter per Default false ist. Ein einzelnes preview_merge
im Merge-Helper-Recheck stiess dadurch einen ungewollten Scratch-Build an.
runVerify (Default false) jetzt symmetrisch bei preview_merge ergaenzt,
Description-Text und die Merge-Helper-Prompt-Doku angepasst.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Der Queue-Picker gatete eine task_dependencies-Kante auf status='done'.
review_task(skipVerify) laesst einen erfolgreich gemergten Task aber
bewusst auf WaitingForReview stehen, bis verify_merges einmal zentral
laeuft -- damit blockierte jeder Nachfolger ueber den gesamten Batch plus
Full-Suite-Verify hinweg, bei freien Slots.
Eine Kante gilt jetzt als erfuellt, wenn der Vorgaenger 'done' ist ODER
sein Merge gelandet ist (worktrees.state = 'merged'). TaskBlocking
(get_task/list_tasks/wait_for_task_change) und der UI-Wartehinweis folgen
derselben Regel, damit "blocked" nicht meldet, was der Picker claimt.
Abgeleitet: revert_merge/abort_merge setzen den Worktree auf Active und
blockieren wieder; reject_rerun/reject_park haben nie gemergt und bleiben
unerfuellt; ein Task ohne Worktree wird nur ueber 'done' erfuellt; ein
fehlgeschlagener verify_merges gibt Nachfolger frei, weil der Code so
oder so auf main liegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured 2026-09-16 against the live endpoint: a clean burst allows exactly 5
requests before a 429, with Retry-After: 300 (a flat 5-req/5-min quota, no
ratelimit-* headers on either the 200s or the 429s). Lowered
UsagePollIntervalActiveSeconds/UsagePollIntervalIdleSeconds default from
300/900 to 120 each (clamp floor 90, up from 60) — leaves ~2.5 requests of
headroom per window for manual refreshes and any other consumer of the same
OAuth token, unlike 60s which consumes the whole quota by itself. The quota
is a flat per-account limit, not tied to ClaudeDo's own running/idle state,
so both keys share the same default now (kept separate for config
compatibility).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RestartWorkerService verliess sich auf WorkerLocator.Find(), das bei einem
Debug-Build ohne install.json in der Verzeichniskette auf die Registry
zurueckfaellt und den Pfad der *installierten* Worker.exe liefert. Der
passt nicht zum laufenden Debug-Prozess, also ueberspringt
KillMatchingWorkerProcesses ihn stillschweigend (Pfadvergleich) - der neu
gestartete Worker startet vom falschen Pfad und stirbt sofort am bereits
belegten Port, waehrend der echte Worker unberuehrt weiterlaeuft. Der
Neustart nimmt jetzt den Pfad des tatsaechlich laufenden
ClaudeDo.Worker-Prozesses als Quelle, mit dem Locator nur noch als
Fallback, wenn keiner laeuft.
ShutdownWorkerAndCloseApp only closed MainWindow via CloseWindow, relying
on ShutdownMode.OnMainWindowClose to bring the process down. That mode
backs off if any window cancels its own Closing event, and
MissionControlWindow always does (it hides instead of closing) - so the
process never actually exited while Mission Control, Cheat Sheets, or the
diff viewer were still open. Call IClassicDesktopStyleApplicationLifetime
.Shutdown() directly, which force-closes every window regardless of
per-window cancellation.