docs(online-inbox): API contract, desktop design spec, and implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
docs/superpowers/plans/2026-06-10-online-inbox.md
Normal file
72
docs/superpowers/plans/2026-06-10-online-inbox.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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<List<MirrorTask>> 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.
|
||||
Reference in New Issue
Block a user