Files
ClaudeDo/docs/open.md
mika kuns 26c4e5771b feat(worker): run worker as per-user logon task instead of Windows service
A LocalSystem Windows service can't see the logged-in user's Claude CLI
authentication, so the worker now runs as the current user via a hidden
per-user logon Scheduled Task with restart-on-failure.

- Worker is WinExe (no console window) with a Serilog rolling file sink and
  a single-instance mutex so the logon task, app ensure-running, and Restart
  button can't fight over the SignalR port.
- Installer replaces the service steps (register/start/stop) with autostart
  task steps, migrates the legacy ClaudeDoWorker service away on update, and
  removes the task on uninstall. ServicePage drops the service-account UI.
- UI gains a WorkerLocator; the app ensures the worker is running at startup
  and the Restart button kills+relaunches this install's worker process.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 09:39:41 +02:00

20 KiB
Raw Blame History

ClaudeDo — Offene Punkte

Stand: 2026-04-30. Neu erstellt nach Code-Audit gegen plan.md, improvement-plan.md und mailbox-proposal.md.

Die alte Version dieses Dokuments war auf 2026-04-13 ("nach Slice F") datiert und ignorierte die seither gelandeten Slices (Planning Sessions, Prime Claude, Self-Update, Externe MCP-Tools, editierbare Status/Tags, BlockedBy-Chains). Diese Version trennt sauber zwischen erledigt, teilweise, offen — und listet das, was inzwischen gebaut wurde, explizit als „shipped" auf, damit es nicht verloren geht.

Legende: DONE — 🟡 PARTIAL — OPEN — DROPPED


0. Was seit dem 2026-04-13 dazugekommen ist

Diese Slices gab es im alten Dokument noch nicht (oder nur als Platzhalter). Sie sind fertig im Code, brauchen aber jeweils noch ein paar Polish-Punkte (siehe Sektion 2/3).

Slice Worker-Anker UI-Anker Status
Planning Sessions (Plan B+C) Planning/PlanningSessionManager, PlanningChainCoordinator, PlanningMcpService Views/Planning/PlanningDiffView, ConflictResolutionView, UnfinishedPlanningModalView Code, manuelle Verifikation siehe §1.1
Prime Claude (geplante Recurrence) Prime/PrimeScheduler, NextDueCalculator, PrimeRunner ViewModels/Modals/PrimeClaudeTabViewModel, Views/Controls/ThemedDatePicker Code, manuelle Verifikation siehe §1.2
Self-Update System (Gitea Releases) ClaudeDo.Releases (ReleaseClient, SelfUpdater, ChecksumVerifier, VersionComparer), ClaudeDo.Installer (Pages/Steps/Core) Code, manuelle Verifikation siehe §1.3
Externes MCP-Endpoint (11 Tools für Drittsessions) External/ExternalMcpService (ListTaskLists, ListTasks, GetTask, AddTask, UpdateTask, UpdateTaskStatus, SetTaskTags, ListTags, DeleteTask, RunTaskNow, CancelTask), ExternalMcpAuthMiddleware (X-ClaudeDo-Key) Code, ohne Tests am Endpoint selbst
Editierbare Status & Tags (entkoppelt vom agent-Tag) WorkerHub.SetTaskStatus, SetTaskTags, UpdateTaskAgentSettings; Queue-Picker filtert nicht mehr nach agent-Tag DetailsIslandViewModel, Status-/Tag-Kontextmenü in TasksIslandView Code
BlockedBy-Chains (sequenzielle Subtask-Ausführung) TaskStateService.BlockOn/UnblockAsync, QueuePicker filter BlockedByTaskId IS NULL, PlanningChainCoordinator.OnChildFinishedAsync Drittes Feld neben Status und PlanningPhase Code, Migration 20260423154708_AddPlanningSupport
Worker-State-Konsolidierung TaskStateService ist alleiniger Owner von Status/PlanningPhase/BlockedByTaskId-Writes; OverrideSlotService ausgelagert; QueueWaker + QueuePicker getrennt Code
MarkdownView / Tabbed Settings / About-Modal / Prime-Status-Footer / Doppelklick-Edit Views/MarkdownView, SettingsModalView als TabControl, AboutModalView, transient Prime-Status in Footer, DoubleTapped an List/Task-Rows Code

1. Verification (vor allem anderen)

Der Großteil der Verification-Steps aus plan.md ist im Code abgedeckt — was fehlt ist die manuelle Bestätigung mit explizit notiertem Pass-Kriterium. Ohne falsifizierbare Observable produziert ein Manual-Run nur "sah ok aus".

1.0 Plan-Verification 113

# Item Status Pass-Kriterium (was muss konkret zu sehen sein)
1 Schema-Init ~/.todo-app/todo.db + *-wal + *-shm existieren; EF-Migrationsverlauf in __EFMigrationsHistory enthält alle 8 Migrationen; Worker-Log: „listening on …"
1a SignalR-Endpoint curl http://127.0.0.1:47821/hub → HTTP 400 (kein Handshake)
1b Hub-Roundtrip Ping 🟡 UI-Statusbar zeigt „Connected"; WorkerClient.PingAsync() liefert "pong" (UI-Test fehlt)
2 claude --version Preflight Worker/Lifecycle/ClaudeCliPreflight.cs + Wiring in Program.cs. Kaputter claude_binLogCritical(...) + Environment.Exit(1). Skip via CLAUDEDO_SKIP_CLI_PREFLIGHT=1. Tests: tests/.../Lifecycle/ClaudeCliPreflightTests.cs
3 Smoke-Spawn (claude -p Prompt „ping") task_runs-Row mit session_id NOT NULL, result non-empty, output_tokens > 0
4 E2E Happy Path (Non-Worktree) Liste „Test" anlegen → Task „Schreibe ein Haiku über Intralogistik" → tasks.status='Done', tasks.result IS NOT NULL, Logfile unter ~/.todo-app/logs/<taskId>.ndjson, UI-Row mit Done-Badge
5 Worktree Happy Path Liste mit working_dir auf temp-Repo, Task mit Codeänderung → worktrees.state='active', head_commit IS NOT NULL, diff_stat non-empty, Branch claudedo/<id> auf Disk
6 No-Changes-Run Prompt der nichts ändert → tasks.status='Done' aber worktrees.head_commit IS NULL, diff_stat IS NULL
7 Kein Git-Repo working_dir=C:\Temp (kein Repo) → tasks.status='Failed', keine worktrees-Row, Worker-Log enthält Git-Fehler
8 Merge-UI 🟡 MergeTask-Hub-Methode + MergeModalView vorhanden, manueller Run nicht durchgespielt → worktrees.state='merged', im Ziel-Repo git log zeigt Commit, git worktree list ohne Branch
9 Override-Parallelität 🟡 OverrideSlotService-Tests grün; UI-E2E nicht durchgespielt → WorkerHub.GetActive ≥ 2 Einträge bei Run+RunNow
10 Schedule 🟡 QueuePicker-Tests grün; UI-E2E nicht → scheduled_for=now+2min bleibt Queued, dann automatisch Running, started_at >= scheduled_for
11 Worker-Offline-Erkennung 🟡 WorkerClient.OnServerConnectionClosed + Auto-Reconnect implementiert (WithAutomaticReconnect); visuell prüfen: nach taskkill der Worker-Exe wechselt Statusbar in ≤ 5s auf „Offline", RunNow-Buttons disabled
12 Live-Stream 🟡 ClaudeProcess streamt NDJSON via TaskMessage-Event, UI hat LiveTail; visuell prüfen: während Run laufen ndjson-Zeilen ein
13 Wake-up (WakeQueue nach Anlage) 🟡 QueueWaker.Wake() wird bei Enqueue aufgerufen; visuell prüfen: Task wechselt in ≤ 1s auf Running (statt nach queue_backstop_interval_ms=30s)

Empfohlener Sprint: Steps 37 in einem Rutsch durchspielen (alles non-UI), parallel daneben 813 visuell beim normalen App-Lauf abhaken.

1.1 Planning Sessions — Manual Verification (unverändert relevant)

Bedingt durch Slice "Planning B/C". Ablauf identisch zur alten open.md:

  1. Manual-Task mit Title + TODO-Description anlegen.
  2. Rechtsklick → Open planning Session → Windows Terminal mit Claude CLI öffnet.
  3. In CLI: zwei Children via mcp__claudedo__create_child_task anlegen.
  4. UI: Drafts erscheinen eingerückt, italic, mit DRAFT-Badge; Parent zeigt PLANNING-Badge.
  5. Chevron klappt ein/aus.
  6. CLI finalize → Children werden Queued (erste) bzw. Queued+BlockedBy (Rest); Parent flippt von Active auf Finalized (PLANNED-Badge); erste Child startet automatisch.
  7. Neuer Planning-Task, Terminal ohne Finalize schließen → Rechtsklick öffnet Resume/Finalize-now/Discard-Modal.
  8. Delete-Versuch auf Parent mit Children → freundlicher Fehlerdialog, kein Delete.

Bekannte Follow-ups (non-blocking):

  • Border.badge.planned (blau) wird jetzt bei Finalized angewendet — TaskRowView nutzt Classes.planning/Classes.planned gebunden an IsPlanActive/IsPlanFinalized; der Child-„PLANNED"-Badge nutzt direkt planned.
  • Tote Instance-Statics auf BoolToItalicConverter und BoolToDraftOpacityConverter entfernt (Registrierung läuft über das Resource-Dictionary in App.axaml).
  • Ui.Tests IWorkerClient-Fakes auf gemeinsame Basis StubWorkerClient rebased — kein Constructor-Drift mehr; die drei Fakes überschreiben nur ihre relevanten Member.

1.2 Prime Claude — Manual Verification

Slice "Prime" (Recurrence-Scheduler).

  1. Settings → Prime-Claude-Tab → Schedule mit at: 09:00, every: workday, task_template: "Daily Standup" anlegen.
  2. Test mit verschobenem IPrimeClock (oder Schedule mit at: now+1min) → bei Trigger erscheint Toast/Footer-Notification „Prime fired", neuer Task entsteht in der Ziel-Liste.
  3. Worker-Restart innerhalb des Schedule-Fensters → Catch-up läuft genau einmal (kein Doppelfeuer).
  4. Schedule editieren → next_due_at wird neu berechnet; UI-Anzeige aktualisiert.
  5. Schedule löschen → keine weiteren Trigger, keine ghost-Tasks.

1.3 Self-Update — Manual Verification (aus alter open.md, weiterhin gültig)

Voraussetzung: funktionierendes Gitea-Release unter git.kuns.dev/releases/ClaudeDo mit drei Assets — ClaudeDo-<version>-win-x64.zip, ClaudeDo.Installer-<version>.exe, checksums.txt.

  1. Baseline-Version (z.B. 0.2.x) normal installieren.
  2. Neues Release v0.3.0 mit frischem Installer + App-Zip + Checksums veröffentlichen.
  3. App starten → Banner erscheint: Update available: v0.2.x → v0.3.0.
  4. Update now klicken → App schließt, Installer öffnet im Update-Mode, läuft, restartet Worker.
  5. App neu starten → Banner weg; Help → Check for updates zeigt kurz „You're up to date (v0.3.0)".
  6. v0.2.x-Installer manuell starten → bietet Self-Update auf v0.3.0 an. Update → laufende Exe wird ersetzt, Wizard öffnet auf neuer Version.
  7. Schritt 6 mit Continue anyway → Wizard öffnet ohne Self-Update.
  8. Schritt 6 mit Cancel → Installer beendet ohne Aktion.
  9. Network-Kill in App und Installer beim Start → silent fallback (kein Error, kein Banner).

2. UI-Polish

2.1 Folder-Picker für Working Directory

  • Datei: Views/ListSettingsModalView.axaml + zugehöriges VM
  • Aktuell: plain TextBox — Pfad muss getippt werden.
  • Soll: Button „…" daneben → öffnet IStorageProvider.OpenFolderPickerAsync, schreibt Pfad ins Feld.
  • Aufwand: klein.

2.2 Delete-Confirmation

  • Aktuell: Listen/Tasks-Delete läuft direkt ohne Rückfrage. Datenverlust-Risiko.
  • Soll: generischer ConfirmDialog (1× bauen, mehrfach nutzen), Mini-Dialog „Wirklich löschen?".
  • Aufwand: klein.

2.3 Markdown-Rendering Result + Description

  • Views/MarkdownView.axaml + Detail-Pane verwenden Markdown.Avalonia.

2.4 Live-Log Auto-Scroll

  • Datei: Views/DetailsIslandView.axaml(.cs) (Live-Tail-Section)
  • Aktuell: ndjson-Zeilen werden angehängt, Scrollposition bleibt stehen.
  • Soll: Sticky-Bottom-Pattern — bei jeder neuen Zeile ScrollToEnd(), solange User nicht manuell hochgescrollt hat. Attached-Behavior reicht.

2.5 Diff-Viewer 🟡

  • DiffModalView.axaml + PlanningDiffView existieren; integriert für Planning-Merges.
  • Offen: Task-Level-Diff (Worktree vs. main) noch nicht im Modal-Flow geprüft. Verwenden statt Process.Start("cmd /k git diff …").

2.6 Status-Bar Active-Tasks Live-Update

  • Datei: ViewModels/StatusBarViewModel
  • Risiko: RunNowCommand.NotifyCanExecuteChanged triggert nicht pro Item bei Connection-Change.
  • Soll: WeakReferenceMessenger-Connection-Change-Message; alle TaskRowViewModel lauschen.
  • Aufwand: klein, muss sauber gemacht werden.

2.7 Settings-Dialog

  • SettingsModalView als TabControl, Tabs: General, Prime Claude, etc. Persistiert in ~/.todo-app/ui.config.json und worker.config.json.

2.8 (NEU) Planning-Phase Badge-Farbe für Finalized

Finalized zeigt jetzt den blauen planned-Badge (Class-Binding in TaskRowView).

2.9 (NEU) Tote Converter-Statics entfernen

BoolToItalicConverter.Instance, BoolToDraftOpacityConverter.Instance entfernt.


3. Worker-Robustheit

3.1 CLI-Preflight beim Worker-Start

  • src/ClaudeDo.Worker/Lifecycle/ClaudeCliPreflight.cs + Wiring in Program.cs. Tests: tests/.../Lifecycle/ClaudeCliPreflightTests.cs. Skippable via CLAUDEDO_SKIP_CLI_PREFLIGHT=1.

3.2 Worktree-Cleanup beim Anlege-Failed

  • Datei: src/ClaudeDo.Worker/Runner/WorktreeManager.cs
  • Soll: try/finally — bei Fehler zwischen git worktree add und DB-Insert git worktree remove --force als Best-Effort-Cleanup.
  • Aufwand: klein.

3.3 Logging über file-Sink

  • ILogger ist überall verdrahtet, aber kein File-Sink konfiguriert.
  • Soll: Serilog oder Karambolage.Extensions.Logging.File — für Service-Modus zwingend, console-only ist im SCM-Fenster verloren.
  • Aufwand: klein.

3.4 Tag-Negation / Exclusion

  • Tags sind weiterhin rein additiv (list_tags task_tags). Nach Slice „Editierbare Tags" weniger dringend, aber nicht gelöst.
  • Soll: entweder neue Tabelle task_tag_exclusions oder Prefix !tag im task_tags-Eintrag.
  • Aufwand: mittel — Schema + Repo + Tests + UI.

4. Service-Deployment

4.1 Worker-Autostart als Per-User-Task (ersetzt Windows-Service)

  • Der Worker läuft nicht mehr als Windows-Service (LocalSystem konnte die Claude-CLI-Auth des Users nicht sehen). Stattdessen: per-user Logon-Scheduled-Task „ClaudeDoWorker" (schtasks /Create /XML), läuft als angemeldeter User, versteckt, mit Restart-on-Failure.
  • Worker ist WinExe (kein Konsolenfenster) + Serilog-File-Sink (~/.todo-app/logs/worker-*.log) + Single-Instance-Mutex.
  • Installer migriert beim Update den alten Service automatisch weg (sc stop/delete) und registriert die Task; Uninstall entfernt Task + Worker-Prozess. App startet/neustartet den Worker als Prozess und sorgt beim Start dafür, dass er läuft.
  • Implementiert 2026-05-29, getestet (Build + Unit-Tests grün), manuelle E2E-Verifikation am Gerät ausstehend (Update von 1.0.2-alpha → Task, Logoff/Logon-Autostart, Uninstall).

4.2 Pfad-Auflösung absolut

  • WorkerConfig.Load expandiert ~/%USERPROFILE% für alle Pfad-Felder.

4.3 Install-Skripte / Doku

  • Datei (neu): docs/install-service.md ODER scripts/install-service.cmd
  • Inhalt: dotnet publish + sc.exe create + sc.exe failure + Hinweis auf obj= (User-Account) wegen Claude-CLI-Session.
  • Aufwand: klein.

4.4 Installer-Projekt

  • ClaudeDo.Installer (WPF) + ClaudeDo.Releases mit Pages/Steps/Core/Theme — Self-Update funktioniert (siehe §1.3).

5. Tests / CI

5.1 CI-Pipeline (Gitea Actions)

  • Datei (neu): .gitea/workflows/ci.yml
  • Inhalt: dotnet restoredotnet build (csproj-weise wegen .slnx-Bug auf .NET 8) → dotnet test. Auf Push + PR.
  • Achtung: Pipeline darf NICHT die .slnx als Build-Target nehmen — explizite csproj-Liste in einem checked-in Build-Skript.
  • Aufwand: klein.

5.2 SignalR-Hub-Tests

  • tests/ClaudeDo.Worker.Tests/Hub/PlanningHubTests.cs, AgentSettingsHubTests.cs testen Hub-Methoden via Fakes (kein realer SignalR-Roundtrip, aber alle Code-Pfade abgedeckt).
  • Optional verbleibt: echter Roundtrip-Test mit WebApplicationFactory<Program> + HubConnectionBuilder für End-to-End-Validierung der SignalR-Pipeline. Niedriger Mehrwert solange Fakes alle Methoden treffen.

5.3 Smoke-Test gegen echten claude

  • Datei (neu): tests/ClaudeDo.Worker.Tests/Runner/ClaudeProcessSmokeTest.cs
  • Soll: Real-CLI-Test mit [Fact(Skip="...")] ausgegraut, nur lokal aktiviert wenn CLAUDE_AUTHENTICATED=1 Env-Var gesetzt ist.
  • Aufwand: klein.

5.4 (NEU) ExternalMcpService-Tests

  • External/ExternalMcpService hat 11 Tools, aber nur partielle Coverage in tests/.../External/ExternalMcpServiceTests.cs. Für jedes Tool mindestens einen Happy-Path + einen Error-Pfad ergänzen.

6. Dokumentation

6.1 README.md

  • Komplett fehlt. Mind. 1× kurz: was ist es, wie starten (Worker + UI), wo Config, wie Self-Update.

6.2 docs/architecture.md 🟡

  • In plan.md enthalten — entweder konsolidieren oder explizit ausgliedern. CLAUDE.md-Dateien pro Projekt sind aktuell de-facto-Architecture-Doc.

6.3 ADRs

  • Vorschläge: „SignalR vs. SQLite-Polling für IPC", „Worktree pro Task", „TaskStateService als alleiniger State-Owner", „BlockedByTaskId statt Status='Waiting'", „External MCP als zweite WebApplication".
  • Aufwand: klein, hilfreich für später.

6.4 (NEU) Mailbox-Proposal

  • docs/mailbox-proposal.md ist als Vorschlag vorhanden, nicht implementiert. Entscheidung: bauen, droppen oder parken? Wenn droppen → Datei entfernen, sonst klare Roadmap.

7. Bekannte Code-Schulden / Smells

Stelle Issue Status
WorkerHub.GetActive returnt IReadOnlyList<object> mit anonymen Typen Sollte expliziter ActiveTaskDto sein, den Worker UND UI teilen (gibt bereits IReadOnlyList<ActiveTaskDto> zurück)
TaskRunner führt eine if (list.WorkingDir != null)-Verzweigung mitten in der Methode Strategy-Pattern wenn die Methode wächst, aktuell noch klein genug
App.Services als public static ServiceProvider Service-Locator-Antipattern, toleriert weil nur in App.OnFrameworkInitializationCompleted
Embedded schema.sql ohne Versionierung Durch EF-Core-Migrationen ersetzt
CRLF-Warnings beim Commit .gitattributes mit * text=auto eol=lf (oder explizit pro Sprache) wäre sauberer (.gitattributes angelegt)
Tote Converter-Instances (BoolToItalicConverter.Instance, BoolToDraftOpacityConverter.Instance) Per Resource-Dictionary registriert, Statics ungenutzt (entfernt)
1 unausgeführter // TODO in DetailsIslandViewModel (SendPromptAsync ohne Hub-Methode) Entweder Hub-Methode bauen oder TODO entfernen (im Main-Code nicht mehr vorhanden)

8. Improvement Plan (improvement-plan.md, Stand 2026-04-13)

ID Item Status Bemerkung
IP-1 UI ↔ Worker Auto-Reconnect WorkerClient mit WithAutomaticReconnect() + Reconnect-Handler
IP-2 Listen-Modus „Notes" (non-autonomous) Nach Slice „editierbare Status/Tags" weniger dringend (man kann jetzt einen Task ohne agent-Tag idle lassen), aber lists.kind als sauberer Mode-Switch fehlt.
IP-3 Doppelklick öffnet Edit-Dialog DoubleTapped-Handler in ListsIslandView, TasksIslandView
IP-4 Tag Multi-Select Control Tags sind via Picker im Detail-Pane editierbar, aber kein dediziertes Multi-Select-Control mit Auto-Vervollständigung in Editor-Dialogen.
IP-5 Rechtsklick-Kontextmenü Listen + Tasks haben Context-Menüs (Edit, Delete, Run Now, Show Diff, Merge, Cancel, Status, Tags)
IP-6 Schema-Migration-Mechanismus EF-Core-Migrations + __EFMigrationsHistory
IP-7 Status-Bar Reconnect-States connected/connecting/reconnecting/offline farbcodiert
IP-8 Tag-Repository GetAllKnownTagsAsync TagRepository.GetAllAsync + WorkerClient.GetAllTagsAsync

9. Empfohlene Reihenfolge für die nächsten Sessions

Block 1 — Verification durchspielen (kein neuer Code, nur Beweis):

  1. §1.0 Steps 37 manuell (Smoke + E2E + Worktree + No-Changes + Kein-Repo) — ist die Pipeline wirklich lebendig?
  2. §1.1 Planning-Walkthrough — nach den uncommitted Coordinator-Änderungen einmal durchspielen.
  3. §1.2 Prime-Walkthrough — Schedule-Trigger einmal beobachten.

Block 2 — Niedrig hängende UI-Polish (eine Session): 4. §2.1 Folder-Picker 5. §2.2 Delete-Confirmation 6. §2.4 Live-Log Auto-Scroll 7. §2.6 Status-Bar Live-Update 8. §2.8 Planning-Badge-Farbe + §2.9 tote Converter weg

Block 3 — Robustheit & Service-Deployment: 9. §3.2 Worktree-Cleanup 10. §3.3 File-Sink-Logging 11. §4.3 Install-Skripte/Doku

Block 4 — Sicherheitsnetz: 12. §5.1 Gitea-Actions CI-Pipeline (csproj-weise) 13. §5.3 Smoke-Test gegen echten claude 14. §5.4 ExternalMcpService-Tests vervollständigen

Block 5 — Dokumentation & Aufräumen: 15. §6.1 README 16. §6.3 ADRs (mind. die fünf wichtigsten) 17. §6.4 Mailbox-Proposal: bauen/droppen entscheiden 18. §7 Smells: ActiveTaskDto, .gitattributes, TODO-Comment

Block 6 — Optional / wenn Bedarf konkret wird: 19. §3.4 Tag-Negation 20. §IP-2 Notes-Modus 21. §IP-4 Tag Multi-Select Control