# Online Inbox — implementation plan Date: 2026-06-10 Spec: `docs/superpowers/specs/2026-06-10-online-inbox-design.md` Contract: `docs/online-inbox-api-contract.md` TDD, one commit per task, Conventional Commits. Build with `-c Release` per CLAUDE.md. ## Phase 1 — Worker sync engine (buildable now, no Zitadel package needed) ### Task 1 — Config - Add `OnlineInboxConfig` + nested `ZitadelClientConfig` records. - Add `online_inbox` (`OnlineInbox`) property to `WorkerConfig`; default `enabled=false`. - `Load` leaves it untouched when absent (defaults = disabled). - Test: missing section → disabled defaults; populated section round-trips. ### Task 2 — DTOs + Idle-backlog helper - `Online/Dtos.cs`: `RemoteList(Id, Name)`, `RemoteTask(Id, ListId, Title, Description, CreatedAt)`, `MirrorTask(Id, ListId, Title, Description)`. - `Online/OnlineBacklog.cs`: `static Task> CurrentAsync(TaskRepository/ctx)` + the filter predicate (Idle, no parent, PlanningPhase None, BlockedBy null). - Test the filter against real SQLite seeded with mixed tasks. ### Task 3 — Auth abstraction + token store - `Online/Interfaces/IOnlineAuthProvider.cs`. - `Online/OnlineTokenStore.cs`: DPAPI CurrentUser persistence at `~/.todo-app/online-inbox.token`; `Save(refreshToken)`, `Read()`, `Clear()`. (Windows-only encryption; thin + guarded.) - A trivial `StaticTokenAuthProvider` (returns a configured token or null) for tests + as the temporary default until Zitadel is wired. - Test: token store round-trip (Windows); static provider returns/omits token. ### Task 4 — API client - `Online/IOnlineInboxApi.cs` + `Online/OnlineInboxApiClient.cs` (typed `HttpClient`). - Attaches `Authorization: Bearer` from `IOnlineAuthProvider`; refuses non-HTTPS non-loopback base URLs; throws a typed `OnlineInboxException` on non-2xx. - Test with a stubbed `HttpMessageHandler`: each method hits the right path/verb/body; 401 surfaces; bearer attached. ### Task 5 — Sync service - `Online/OnlineSyncService.cs` (`BackgroundService`) implementing the §5 reconcile loop. - DI: register only when `enabled`; resolve repos per-cycle via a scope. - Per-cycle try/catch + structured logging; skip when no token; unknown-list skip. - Test against a **fake `IOnlineInboxApi`** + real SQLite: pull→import→flag creates local Idle tasks; mirror payload == Idle backlog; lists pushed; unknown list skipped & not flagged; disabled/no-token = no api calls. ### Task 6 — Wire-up + docs - Register the stack in `Program.cs` behind the enabled flag. - Update `src/ClaudeDo.Worker/CLAUDE.md` (new `Online/` area) and `src/ClaudeDo.Worker/Config` notes. Add `online_inbox` to the config section. ## Phase 2 — UI + real auth (AFTER the VPS reports client config) ### Task 7 — Hub + config plumbing - Hub: `GetOnlineInboxConfig` / `SetOnlineInboxConfig` / `SetOnlineInboxAuth(refreshToken)` / `ClearOnlineInboxAuth`. Update `IWorkerClient` + `WorkerClient` + test fakes (both test projects — see the IWorkerClient-fakes memory). ### Task 8 — Settings UI - "Online Inbox" section in `SettingsModalViewModel`: enable toggle, base URL, Sign in/out, status. Localized keys in en.json + de.json (parity). - Visual verification = manual (flag it). ### Task 9 — ZitadelAuthProvider - Add the Zitadel package reference; implement `ZitadelAuthProvider` (refresh-token → access token, cached to expiry) using the reported authority/client-id/flow. - Swap it in for `StaticTokenAuthProvider` in DI when enabled. - Manual smoke against the live VPS API (tracked, not an automated test). ## Notes - No real network / no real Zitadel / no real Claude in any automated test. - Stage files by explicit path in subagents; sonnet model; build+test+commit by the orchestrator.