From bcc9682a017ac585ddea10a39cd22826d9044cfa Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 11 Jun 2026 19:07:43 -0400 Subject: [PATCH] docs(roadmap): promote Phases 13 (real lint gate) + 14 (desktop e2e) into v1.1; fold backlog 999.5 self-service onboarding into admin-credential phase --- .planning/ROADMAP.md | 79 +++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 1bf7b27..2c23d04 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -3,7 +3,7 @@ ## Milestones - ✅ **v1.0 MVP** — Phases 1–6 (shipped 2026-06-10) — see [`milestones/v1.0-ROADMAP.md`](milestones/v1.0-ROADMAP.md) -- 🚧 **v1.1 Operability & Polish** — Phases 7–12 (planning) — mobile test harness, Gitea CI (runs the harness), faster write-back, in-app admin, per-event reminders, guided setup +- 🚧 **v1.1 Operability & Polish** — Phases 7–14 (planning) — mobile test harness, Gitea CI (runs the harness), faster write-back, in-app admin, per-event reminders, guided setup, real lint gate, desktop e2e ## Phases @@ -21,7 +21,7 @@ Full phase detail archived in [`milestones/v1.0-ROADMAP.md`](milestones/v1.0-ROA -### 🚧 v1.1 Operability & Polish (Phases 7–12) +### 🚧 v1.1 Operability & Polish (Phases 7–14) Make FamilySync configurable, administrable, and maintainable for real multi-member use — without hand-editing env files or the database. The new critical path runs **mobile test harness → Gitea CI** (CI consumes the harness specs for UI regression), and the **admin role → reminders / setup wizard** chain (a single `/api/admin` + `/api/setup` route surface carrying the v1.1 DB migration). Faster write-back is a fully independent track. @@ -31,6 +31,8 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem - [ ] **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 - [ ] **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 - [ ] **Phase 12: Initial Setup Wizard** - First-run validated bootstrap of env/VAPID/DB/OIDC + first app password, reusing the admin route surface +- [ ] **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 +- [ ] **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 ## Phase Details @@ -155,6 +157,8 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem - **App password never logged/echoed** (Pitfall 7): custom zod-validator `hook` returns a generic 400 (no Zod `received`/`value` field); no `console.log` of request bodies in `routes/admin*`. - Hard constraints: Drizzle **generate+migrate, never push** (false destructive diff on populated MariaDB); reuse `broker/crypto.ts` `encryptPassword` (no changes to crypto); `/api/admin/credentials` and `/api/admin/calendars/:id/shared` are the single shared surface — do NOT duplicate them into `/api/setup/*` in Phase 12. +**Folded-in scope** (from backlog 999.5, self-service member onboarding): the credential surface this phase builds is the same one a member needs on first login. Expose a `needsProviderSetup` signal (member has no `member_credentials` row) and let a member enter/validate (CalDAV PROPFIND) + encrypt their **own** Fastmail app password — the self-service counterpart of the admin-managed flow, sharing the validation/encryption/initial-sync path. Non-technical-friendly instructions (link to Fastmail's app-password page, required Calendars/CalDAV scope) are the hard UX constraint. Member-scoped: a member can only set their own credential; never log/echo the password. + **Plans**: TBD **UI hint**: yes @@ -206,6 +210,46 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem **Plans**: TBD **UI hint**: yes +### Phase 13: Real Lint Gate (ESLint) + +**Goal**: The CI lint gate actually fails on lint violations. A real ESLint flat config (`eslint.config.js`, `typescript-eslint`; React + react-hooks plugins for `apps/pwa`) plus a package-level `lint` script in `apps/api` and `apps/pwa` makes the existing root `pnpm -r --if-present lint` run a real linter, replacing the hollow no-op gate that exits 0 because no linter exists. +**Mode:** standard +**Depends on**: Phase 8 (the CI `fast-checks` job already runs `pnpm lint`; this fills the slot Phase 8 shipped wired to auto-activate once a package `lint` script lands). Independent of all other phases. +**Requirements**: TBD (promoted from backlog 999.16) +**Success Criteria** (what must be TRUE): + + 1. `pnpm lint` runs ESLint across both `apps/api` and `apps/pwa` and exits non-zero on an introduced violation (verified by a deliberate test violation), where today it exits 0 with no linter present. + 2. The CI `fast-checks` lint step blocks a PR to main on lint violations — the gate can now fail. + 3. The first real run's existing violations are resolved (fix / warn / disable decided per rule) so the baseline gate ends green. + +**Pitfalls this phase owns**: + + - Pick a baseline ruleset (recommended vs strict-type-checked) deliberately — strict surfaces a large upfront cleanup; decide blocking vs advisory before flipping the gate to blocking. + - `typecheck`/tsc already gates type errors; ESLint should not duplicate type-checking rules unnecessarily. + +**Plans**: TBD +**UI hint**: no + +### Phase 14: Desktop E2E Coverage + +**Goal**: The Phase 8 regression gate exercises the desktop layout and flows, not just mobile. A `desktop` Playwright project (`devices['Desktop Chrome']`, no touch, wide viewport) is added to `apps/pwa/playwright.config.ts`, and the existing mobile-authored specs are reviewed/adjusted (or appropriately skipped) so `pnpm test:e2e` passes on a no-touch desktop viewport as well as the `iphone`/`pixel` profiles. +**Mode:** standard +**Depends on**: Phase 7 (the harness it extends) and Phase 8 (CI runs `pnpm test:e2e` and picks up the new project automatically — no CI plumbing change needed beyond any desktop-profile runtime/wait). Independent of Phases 9–13. +**Requirements**: TBD (promoted from backlog 999.15) +**Success Criteria** (what must be TRUE): + + 1. A `desktop` project exists in `playwright.config.ts` (Desktop Chrome, wide viewport, no `hasTouch`). + 2. The existing e2e specs pass (or are explicitly, justifiably skipped) on the desktop profile — touch-gesture / mobile-drawer / mobile-only-layout assumptions are handled. + 3. `pnpm test:e2e` in CI runs and gates on both mobile and desktop profiles (blocking-vs-advisory for desktop decided when planned). + +**Pitfalls this phase owns**: + + - The real work is the spec-compat pass, not CI plumbing — Phase 8 reused the Phase 7 harness unchanged, so the config addition is small but specs authored for touch/mobile need per-spec review. + - Desktop WebKit is optional — the Apple member is already covered on mobile Safari via `iphone`; Desktop Chrome is likely sufficient for a shared/wall browser. + +**Plans**: TBD +**UI hint**: no + ## Progress | Phase | Milestone | Plans Complete | Status | Completed | @@ -222,6 +266,8 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem | 10. Admin Role & Settings | v1.1 | 0/? | Not started | - | | 11. Per-Event Reminders | v1.1 | 0/? | Not started | - | | 12. Initial Setup Wizard | v1.1 | 0/? | Not started | - | +| 13. Real Lint Gate (ESLint) | v1.1 | 0/? | Not started | - | +| 14. Desktop E2E Coverage | v1.1 | 0/? | Not started | - | ## Backlog @@ -255,31 +301,6 @@ Plans: - [ ] TBD (promote with /gsd-review-backlog when ready) -### Phase 999.5: First-login provider setup — prompt + instructions to add a Fastmail app password (BACKLOG) - -**Goal:** [Captured for future planning] On a member's first login there is no onboarding to connect their own calendar provider. Today the broker uses a single seeded Fastmail app password (the operator's), so a second member (e.g. the wife) who logs in sees only what that token reaches — she has no way to attach her **own** Fastmail personal calendar (the D-09 per-member app-password model). Add a first-login flow that detects a member has no `member_credentials` row and prompts them to create + paste a Fastmail app password, with clear step-by-step instructions (where to generate it in Fastmail settings, required scope: Calendars/CalDAV, that one app password covers all of that account's calendars). Store it encrypted (APP_PASSWORD_ENCRYPTION_KEY, existing crypto path), then trigger an initial sync so their personal calendar lane populates. - -**Context** (surfaced 2026-06-07, Gate 2 live testing): the wife logged in on her iPhone and added the PWA to her Home Screen, but there is no provider-setup step — so her personal calendar can't be connected. This is the onboarding half of the "each member's personal calendar" v1 requirement. - -**Scope to decide when promoted:** - -- Detect "no credential yet" state server-side (`GET /api/me` exposes a `needsProviderSetup` flag, or a dedicated endpoint) and gate a setup screen in the PWA. -- App-password entry UI + validation (test the credential with a CalDAV PROPFIND before saving), encrypted storage, and triggering the first sync. -- Non-technical-friendly instructions (the hard UX constraint) — ideally with a direct link to Fastmail's app-password page and a screenshot/walkthrough. -- Decide the model: does every member attach their own personal calendar, or do some members only see the shared family calendar? (Open question from D-16.) -- Security: never log/echo the app password; member-scoped; T-03-19 style scoping. - -**Severity:** high for true multi-member use — without it the second member has no personal calendar. Tags: phase-03, onboarding, auth, caldav, per-member-credential, D-09. - -> **Note:** v1.1 covers the *admin-managed* counterpart (ADMIN-01, Phase 10) — an admin can set any member's app password. *Self-service* member onboarding (member adds their own) stays deferred here. - -**Requirements:** TBD -**Plans:** 0 plans - -Plans: - -- [ ] TBD (promote with /gsd-review-backlog when ready) - ### Phase 999.10: Admin Settings / Administration section — manage app passwords + designate the shared calendar via UI (BACKLOG) **Goal:** [Captured for future planning] Add an in-app **Settings/Administration** section, gated to an administrator role, for configuration that today requires manual backend/DB steps: @@ -404,6 +425,8 @@ Plans: **Context:** Deferred from Phase 8 (Gitea CI) planning, 2026-06-11 — user wants both mobile and desktop validated, but desktop needs a config addition + spec review that is out of Phase 8's CI-plumbing scope. Tags: testing, playwright, e2e, desktop, harness, ci. +> **Promoted into v1.1 Phase 14 (Desktop E2E Coverage) — 2026-06-11.** Backlog entry retained for history. + **Requirements:** TBD **Plans:** 0 plans @@ -426,6 +449,8 @@ Plans: **Context:** Raised during Phase 8 execution, 2026-06-11 — user noted the `--if-present` lint step "didn't fix the linter, just made it so it didn't have to exist to proceed" and wants a lint gate that actually fails. Tags: ci, lint, eslint, typescript-eslint, quality, gitea. +> **Promoted into v1.1 Phase 13 (Real Lint Gate / ESLint) — 2026-06-11.** Backlog entry retained for history. + **Requirements:** TBD **Plans:** 0 plans