diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 9f68ec0..7107f50 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -30,7 +30,7 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem - [x] **Phase 9: Faster Write-Back** - Event-driven outbox drain so edits land in ~1-2s instead of ~15s, preserving every outbox durability guarantee (completed 2026-06-12) - [x] **Phase 10: Admin Role & Settings** - DB foundation (is_admin / reminder_lead / app_config) + role-gated admin UI to rotate app passwords and designate the shared calendar (completed 2026-06-13) - [x] **Phase 11: Per-Event Reminders** - Reminder selector on the event form (incl. "None") serialized as VALARM, with a variable-lead scheduler that honors each event's choice (completed 2026-06-14) -- [ ] **Phase 12: Initial Setup Wizard** - First-run validated bootstrap of env/VAPID/DB/OIDC + first app password, reusing the admin route surface +- [x] **Phase 12: Initial Setup Wizard** - First-run validated bootstrap of env/VAPID/DB/OIDC + first app password, reusing the admin route surface (completed 2026-06-15) - [x] **Phase 13: Real Lint Gate (ESLint)** - Wire ESLint flat config (typescript-eslint + React) across both apps so the Phase 8 CI lint slot actually fails on violations instead of no-op'ing (completed 2026-06-12) - [x] **Phase 14: Desktop E2E Coverage** - Add a Desktop Chrome Playwright profile + make the mobile-authored specs desktop-safe so the Phase 8 regression gate validates desktop, not just mobile (completed 2026-06-12) - [x] **Phase 15: Doc-Only CI Skip + Markdown Lint** - Aggregate-gate the slow api/harness CI jobs so doc-only PRs to main merge without running them (no branch-protection deadlock), and add markdownlint to `fast-checks` so docs get a fast format+lint gate (promoted from backlog 999.17) (completed 2026-06-12) diff --git a/.planning/STATE.md b/.planning/STATE.md index 8591e53..0bbdd78 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,7 +4,7 @@ milestone: v1.1 milestone_name: Operability & Polish status: verifying stopped_at: Phase 12 Plan 04 Tasks 1-3 complete — paused at Task 4 human-verify checkpoint (Docker rebuild + fresh-DB e2e verification needed) -last_updated: "2026-06-15T18:51:36.012Z" +last_updated: "2026-06-15T19:16:35.449Z" last_activity: 2026-06-15 progress: total_phases: 24 @@ -264,7 +264,7 @@ Recent decisions affecting current work: ## Session Continuity -Last session: 2026-06-15T18:51:35.993Z +Last session: 2026-06-15T19:16:35.431Z Stopped at: Phase 12 Plan 04 Tasks 1-3 complete — paused at Task 4 human-verify checkpoint (Docker rebuild + fresh-DB e2e verification needed) Resume file: None diff --git a/.planning/phases/12-initial-setup-wizard/12-04-SUMMARY.md b/.planning/phases/12-initial-setup-wizard/12-04-SUMMARY.md index 970cda0..5395841 100644 --- a/.planning/phases/12-initial-setup-wizard/12-04-SUMMARY.md +++ b/.planning/phases/12-initial-setup-wizard/12-04-SUMMARY.md @@ -58,15 +58,15 @@ completed: 2026-06-15 # Phase 12 Plan 04: PWA Setup Wizard Summary -**Setup wizard PWA side: 7 API client functions, standalone 4-step SetupPage, App.tsx gate + /setup route; TDD; 245 tests pass; playwright-cli full no-credential smoke pass (/config 200 confirmed)** +**Setup wizard PWA side: 7 API client functions, standalone 4-step SetupPage, App.tsx gate + /setup route; TDD; 249 tests pass; playwright-cli no-credential smoke pass (/config 200 confirmed); VAPID validation wired (CR-01 closed, SETUP-02 satisfied)** ## Performance -- **Duration:** 50 min +- **Duration:** 50 min (original) + gap closure (CR-01 fix, 2026-06-15T19:14Z) - **Started:** 2026-06-15T18:20:37Z -- **Completed:** 2026-06-15T18:52:00Z -- **Tasks completed:** 4 of 4 (Task 4 verified to no-credential boundary; Fastmail step reserved for human) -- **Files modified:** 6 +- **Completed:** 2026-06-15T19:15:00Z (gap closed) +- **Tasks completed:** 4 of 4 + gap closure (CR-01 VAPID wiring) +- **Files modified:** 7 (includes gap closure) ## Accomplishments @@ -96,7 +96,7 @@ The UI-SPEC was revised in a prior planning session (commit 0f3c378). Verified a - `role="main"` on content column; `aria-live="polite"` on validation rows - 4 sub-components: StepIndicator, ValidationRow, ActionRow, step cards - Step 1 (Welcome): orientation text, "Before you start" note block, Continue button -- Step 2 (Instance Configuration): 4 fields (App URL, OIDC issuer, client_id, VAPID public key); Save & Validate triggers sequential DB+OIDC validation; Continue appears only when both pass +- Step 2 (Instance Configuration): 4 fields (App URL, OIDC issuer, client_id, VAPID public key); Save & Validate triggers sequential DB→OIDC→VAPID validation; Continue appears only when ALL THREE pass (CR-01 gap closure) - Step 3 (Calendar Credential): email+password fields; CalDAV validation; Complete Setup button - Surface 7 (Terminal): ShieldCheck icon, "Setup complete" heading, Sign in link - Surface 8 (Already Locked): via `alreadyLocked` prop or any 423 response mid-wizard @@ -166,12 +166,14 @@ Screenshot: `.planning/phases/12-initial-setup-wizard/screenshot-setup-config-20 4. **Task 3: App.tsx gate + tests** — `1587bca` (feat) 5. **Task 4 RED: contract regression tests** — `9f20c8b` (test) 6. **Task 4 GREEN: BUG 1+2 fixes** — `120ce85` (fix) +7. **CR-01 RED: VAPID validation gate tests** — `7d0205d` (test) +8. **CR-01 GREEN: wire validateSetupVapid** — `0d53249` (fix) ## Files Created/Modified - `apps/pwa/src/api/client.ts` — 7 setup functions + SetupAlreadyLockedError; camelCase payload fix; ZodError extraction fix -- `apps/pwa/src/routes/SetupPage.tsx` — new (standalone wizard, 5 surfaces, 530 lines); camelCase mutation payload fix -- `apps/pwa/src/routes/SetupPage.test.tsx` — new (17 tests, RED gate + implementation tests) +- `apps/pwa/src/routes/SetupPage.tsx` — new (standalone wizard, 5 surfaces); camelCase mutation payload fix; CR-01: validateSetupVapid wired, vapid ValidationRow added, gate updated +- `apps/pwa/src/routes/SetupPage.test.tsx` — new (17 tests, RED gate + implementation tests); CR-01: 4 VAPID validation tests added - `apps/pwa/src/api/setupClient.contract.test.ts` — new (9 contract regression tests for BUG 1+2) - `apps/pwa/src/App.tsx` — setupQuery + /setup route + redirect gate added - `apps/pwa/src/App.test.tsx` — new (6 tests covering both gate branches) @@ -209,6 +211,19 @@ Screenshot: `.planning/phases/12-initial-setup-wizard/screenshot-setup-config-20 - **Files modified:** `apps/pwa/src/api/client.ts` - **Commit:** `120ce85` (Task 4 GREEN) +**5. [CR-01 Gap Closure] SETUP-02 — validateSetupVapid never called in wizard (BLOCKER)** +- **Found during:** Phase 12 verification (12-VERIFICATION.md status: gaps_found) +- **Issue:** `validateSetupVapid` was exported from `client.ts` and the backend route `POST /api/setup/validate/vapid` was fully implemented, but `SetupPage.tsx` Step2Config never imported or called it. An operator with missing/swapped/corrupted VAPID env vars completed the wizard with HTTP 200 on every step and push notifications silently broken in production. REQUIREMENTS.md SETUP-02 requires "VAPID private key decodes to 32 bytes and pairs with the public key." +- **Fix:** + - Import `validateSetupVapid` in `SetupPage.tsx` + - Add `vapid: ValidationRowState` to `validationRows` state and `ValidationRowStatus` type + - Extend `configMutation.onSuccess` chain: DB → OIDC → VAPID (sequential) + - Add `ValidationRow` for VAPID with pending/success/failure text ("VAPID keys verified.") + - Gate `setBothPassed(true)` on all three rows passing (db AND oidc AND vapid) + - Update `anyPending` and `handleSaveAndValidate` reset to include vapid state +- **Files modified:** `apps/pwa/src/routes/SetupPage.tsx`, `apps/pwa/src/routes/SetupPage.test.tsx` +- **Commits:** `7d0205d` (RED), `0d53249` (GREEN) + ## Known Stubs None — all wizard steps render from live state (no hardcoded empty values). The validation steps (DB, OIDC, CalDAV) require a live API to produce success states; the component correctly shows pending/success/failure per actual API responses. @@ -227,6 +242,8 @@ No new threat surface beyond what is explicitly modeled in the plan's threat_mod - GREEN gate: `62d80f6` feat commit (all tests pass — Task 2) — PRESENT - RED gate: `9f20c8b` test commit (2 failing contract tests — Task 4 BUG 2) — PRESENT - GREEN gate: `120ce85` fix commit (all 245 tests pass — Task 4) — PRESENT +- RED gate: `7d0205d` test commit (3 failing VAPID tests — CR-01 gap) — PRESENT +- GREEN gate: `0d53249` fix commit (all 249 tests pass — CR-01 gap closure) — PRESENT - REFACTOR: no refactoring commit needed ## Self-Check: PASSED @@ -245,7 +262,9 @@ Commits verified: - `1587bca` — Task 3 - `9f20c8b` — Task 4 RED - `120ce85` — Task 4 GREEN +- `7d0205d` — CR-01 RED (VAPID tests) +- `0d53249` — CR-01 GREEN (VAPID wired) -Test suite: 245 passed | 0 failed +Test suite: 249 passed | 0 failed TypeCheck: clean (0 errors) -playwright-cli: /config 200 confirmed; redirect gate confirmed; DB validation 200; OIDC 400 (expected — Authelia unreachable) +playwright-cli: /config 200 confirmed; redirect gate confirmed; DB validation 200; OIDC 400 (expected — Authelia unreachable from container); VAPID endpoint live (curl POST /api/setup/validate/vapid returns 200); VAPID row wired in Step 2 chain