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>
This commit is contained in:
29
docs/open.md
29
docs/open.md
@@ -65,9 +65,9 @@ Bedingt durch Slice "Planning B/C". Ablauf identisch zur alten open.md:
|
||||
8. Delete-Versuch auf Parent mit Children → freundlicher Fehlerdialog, kein Delete.
|
||||
|
||||
**Bekannte Follow-ups (non-blocking):**
|
||||
- ⬜ `Border.badge.planned` (blau) ist in `IslandStyles.axaml` definiert, wird aber nie angewendet — `TaskRowView` behält die `planning`-Klasse für `Active` UND `Finalized`, daher amber statt blau bei finalisiert. Entweder Class-Swap auf `planned` bei `Finalized`, oder die unused Style+Brush entfernen.
|
||||
- ⬜ Tote `Instance`-Statics auf `BoolToItalicConverter` und `BoolToDraftOpacityConverter` — `App.axaml` registriert via Resource-Dictionary, die statischen Members können weg.
|
||||
- ⬜ `Ui.Tests` IWorkerClient-Fakes (`DetailsIslandPlanningTests`, `PlanningDiffViewModelTests`, `ConflictResolutionViewModelTests`) fehlen `OpenInteractiveTerminalAsync` und `QueuePlanningSubtasksAsync` — Constructor-Drift, Fakes auf gemeinsame abstrakte Basis rebasen.
|
||||
- ✅ `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
|
||||
|
||||
@@ -129,11 +129,11 @@ Voraussetzung: funktionierendes Gitea-Release unter `git.kuns.dev/releases/Claud
|
||||
### 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` ⬜
|
||||
Siehe §1.1 — `planning`-Klasse bleibt amber, blauer `planned`-Style nicht angewendet.
|
||||
### 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` — siehe §1.1.
|
||||
### 2.9 (NEU) Tote Converter-Statics entfernen ✅
|
||||
`BoolToItalicConverter.Instance`, `BoolToDraftOpacityConverter.Instance` entfernt.
|
||||
|
||||
---
|
||||
|
||||
@@ -161,8 +161,11 @@ Siehe §1.1 — `planning`-Klasse bleibt amber, blauer `planned`-Style nicht ang
|
||||
|
||||
## 4. Service-Deployment
|
||||
|
||||
### 4.1 Windows-Service-Hosting ✅
|
||||
- `Program.cs` ruft `builder.Host.UseWindowsService(o => o.ServiceName = "ClaudeDoWorker")`.
|
||||
### 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.
|
||||
@@ -220,13 +223,13 @@ Siehe §1.1 — `planning`-Klasse bleibt amber, blauer `planned`-Style nicht ang
|
||||
|
||||
| Stelle | Issue | Status |
|
||||
|---|---|---|
|
||||
| `WorkerHub.GetActive` returnt `IReadOnlyList<object>` mit anonymen Typen | Sollte expliziter `ActiveTaskDto` sein, den Worker UND UI teilen | ⬜ |
|
||||
| `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 | ⬜ |
|
||||
| Tote Converter-Instances (`BoolToItalicConverter.Instance`, `BoolToDraftOpacityConverter.Instance`) | Per Resource-Dictionary registriert, Statics ungenutzt | ⬜ |
|
||||
| 1 unausgeführter `// TODO` in `DetailsIslandViewModel` (`SendPromptAsync` ohne Hub-Methode) | Entweder Hub-Methode bauen oder TODO entfernen | ⬜ |
|
||||
| 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) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user