Compare commits
10
Commits
v1.1
...
cf9e345b87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf9e345b87 | ||
|
|
23dc3cdd3f | ||
|
|
700294a643 | ||
|
|
67430bb670 | ||
|
|
4e0a5e38e8 | ||
|
|
6e1c9ca924 | ||
|
|
303484d0ab | ||
|
|
6ade8d59bf | ||
|
|
c7955a46b9 | ||
|
|
a2890d1542 |
+18
-3
@@ -16,9 +16,24 @@ v1.1 turned the v1.0 MVP into a configurable, administrable, maintainable app: g
|
|||||||
|
|
||||||
Deferred to backlog: self-service provider onboarding (999.5), provider abstraction (999.1), dark mode / theming (999.20), and a broader modern-styling refresh (999.21 — future milestone).
|
Deferred to backlog: self-service provider onboarding (999.5), provider abstraction (999.1), dark mode / theming (999.20), and a broader modern-styling refresh (999.21 — future milestone).
|
||||||
|
|
||||||
## Next Milestone
|
## Current Milestone: v1.2 Multi-Provider, Theming & Zero-Setup
|
||||||
|
|
||||||
Not yet defined. Start with `/gsd-new-milestone` (questioning → research → requirements → roadmap). Candidate seeds in the backlog: dark mode / theming (999.20), modern visual refresh (999.21), self-service onboarding (999.5), provider abstraction (999.1), dev-user full-app exercise without a real calendar (999.19), and acting on the CI dependency report (999.18).
|
**Goal:** Open FamilySync beyond Fastmail — add Google Calendar as a first-class provider behind a provider-abstraction layer with self-service per-provider onboarding — plus dark mode, multiple reminders per event, and a database that bootstraps itself with no manual setup.
|
||||||
|
|
||||||
|
**Target features:**
|
||||||
|
|
||||||
|
- **Provider abstraction** — refactor Fastmail behind a provider interface so additional calendar providers plug in cleanly (999.1).
|
||||||
|
- **Gmail / Google Calendar, full parity** — read + write-back + reminders via the Google Calendar API + OAuth2 (not CalDAV); the second provider behind the abstraction.
|
||||||
|
- **Self-service onboarding, per-provider native flow** (999.5, pivoted to generic provider) — Fastmail = app-password entry, Gmail = "Connect Google" OAuth button; low-friction for the non-technical Apple member.
|
||||||
|
- **Multiple reminders per event** — multiple VALARMs per event (deferred to v1.2 from v1.1).
|
||||||
|
- **PWA dark mode / theming** (999.20) — light/dark/system switch consuming the Phase 17 semantic-token groundwork.
|
||||||
|
- **Zero-manual-setup DB** — auto-create/migrate tables on boot if missing (bring-your-own MariaDB: creds + DB name → working schema), then the existing setup wizard handles admin + config.
|
||||||
|
- **Dev-user full-app exercise** (999.19) — exercise the whole app without a real calendar connected.
|
||||||
|
- **Act on CI dependency report** (999.18) — apply the outdated/vulnerable dependency updates CI surfaces.
|
||||||
|
|
||||||
|
**Explicitly out of this milestone:** modern visual refresh (999.21 stays in backlog — current UX is satisfactory). v1.3 is reserved for tablet/touchscreen UX.
|
||||||
|
|
||||||
|
Phase numbering continues from v1.1 (last phase 20) → v1.2 starts at Phase 21.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -127,4 +142,4 @@ This document evolves at phase transitions and milestone boundaries.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_Last updated: 2026-06-18 after v1.1 milestone — Operability & Polish shipped (Phases 7–20, 57 plans): guided setup, in-app admin + member editor, per-event reminders, faster write-back, local-auth mode, auto timezone, and full Gitea CI/CD. Next milestone undefined — start with `/gsd-new-milestone`._
|
_Last updated: 2026-06-19 — v1.2 milestone started (Multi-Provider, Theming & Zero-Setup): provider abstraction + Gmail full parity, self-service per-provider onboarding, multiple reminders per event, PWA dark mode, zero-manual-setup DB bootstrap, dev-user full-app exercise, and CI dependency updates. Phases continue from 21._
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Requirements: FamilySync — v1.2 "Multi-Provider, Theming & Zero-Setup"
|
||||||
|
|
||||||
|
**Defined:** 2026-06-19
|
||||||
|
**Milestone:** v1.2 (continues from v1.1 "Operability & Polish", shipped 2026-06-18)
|
||||||
|
**Core Value:** The household can see and co-edit one color-coded family calendar (shared + each member's personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store. v1.2 **opens the app beyond Fastmail** (Google Calendar as a first-class provider behind a provider abstraction, with self-service per-provider onboarding), adds **dark mode** and **multiple reminders per event**, and makes the database **bootstrap itself with no manual setup**.
|
||||||
|
|
||||||
|
REQ-IDs continue prior numbering (CAL ≤15, SETUP ≤04, TEST ≤02 already used). New categories: **PROV** (providers & onboarding), **THEME** (PWA theming), **DEP** (dependency maintenance).
|
||||||
|
|
||||||
|
## v1.2 Requirements
|
||||||
|
|
||||||
|
Each requirement maps to exactly one roadmap phase (see Traceability).
|
||||||
|
|
||||||
|
### Providers — abstraction & self-service onboarding
|
||||||
|
|
||||||
|
- [ ] **PROV-01**: All calendar read, write-back, and reminder operations flow through a single `CalendarProvider` abstraction; the existing Fastmail/CalDAV broker is refactored behind it as one implementation with **no behavior regression** to the working Fastmail path (outbox durability, RRULE/VALARM preservation, optimistic-202, per-uid exactly-once all preserved).
|
||||||
|
- [ ] **PROV-02**: A member can connect a **Google account via a "Connect Google" OAuth flow** (self-service, no admin involvement); the OAuth refresh token is stored encrypted (AES-256-GCM, same path as Fastmail app passwords) and `member_credentials` supports one credential **per provider per user** (`UNIQUE(user_id, provider_type)`).
|
||||||
|
- [ ] **PROV-03**: A member can self-service connect or re-enter **their own** provider credential using each provider's native mechanism — Fastmail = app-password entry, Google = OAuth consent — without an admin rotating it for them (generalizes the v1.1 admin-managed ADMIN-01 path).
|
||||||
|
- [ ] **PROV-04**: When a provider connection expires or is revoked, the user sees a **clear, in-calendar banner** (not hidden in settings) and can **reconnect in one tap** by re-running the provider's auth flow, without removing and re-adding the account.
|
||||||
|
|
||||||
|
### Calendar — Google parity & multiple reminders
|
||||||
|
|
||||||
|
- [ ] **CAL-16**: Events from a connected **Google calendar appear in the unified color-coded calendar** (read sync), with recurring events expanded correctly (Google recurrence/cancelled-instance model reconciled to the app's RRULE/EXDATE model) and all-day events not shifted.
|
||||||
|
- [ ] **CAL-17**: A user can **create / edit / delete events on a connected Google calendar** from the app, written back to Google — full parity with the Fastmail write path, including recurring-event create and whole-series delete.
|
||||||
|
- [ ] **CAL-18**: A user can set **multiple reminders on a single event** (up to 5), each firing independently; the set serializes to the connected provider's native model (multiple VALARMs for Fastmail / multiple `overrides` for Google), **preserves reminders authored in another client** on edit, and the scheduler fires each lead exactly once (dedup key includes the lead).
|
||||||
|
|
||||||
|
### Theming — PWA dark mode
|
||||||
|
|
||||||
|
- [ ] **THEME-01**: A user can switch the PWA between **Light / Dark / System**, the choice is persisted, there is **no flash-of-wrong-theme** on load, and the dark theme renders cleanly across every route (calendar, lists, admin, settings sheet, login) including the Schedule-X calendar component's `--sx-color-*` variables.
|
||||||
|
|
||||||
|
### Setup — zero-manual-setup database bootstrap
|
||||||
|
|
||||||
|
- [ ] **SETUP-05**: On boot, the app **auto-creates and migrates the full schema** against a bring-your-own MariaDB — the operator supplies only DB credentials and a database name — running drizzle migrations programmatically (`drizzle-orm/mysql2/migrator`, never `drizzle-kit push`), **idempotently and concurrency-safely** (migration-lock sentinel), completing **before requests are served**, so no manual `db:migrate` step is required.
|
||||||
|
|
||||||
|
### Test — dev/mock provider (full-app exercise without a live calendar)
|
||||||
|
|
||||||
|
- [ ] **TEST-03**: A **mock/in-memory `CalendarProvider`** lets the dev-bypass user and the Playwright CI harness exercise the **full app** — calendar create/edit/delete, reminders, and lists — with **no live calendar account**, producing deterministic seed data covering recurring, all-day, past, and future events.
|
||||||
|
|
||||||
|
### Dependencies — apply CI-surfaced updates
|
||||||
|
|
||||||
|
- [ ] **DEP-01**: Apply the **outdated/vulnerable dependency upgrades** surfaced by CI (`pnpm audit` / `pnpm outdated`), respecting the existing version pins and constraints (ESLint held at 9.x until `eslint-plugin-react` supports 10; Drizzle/drizzle-kit kept coupled; Schedule-X minor-only and not bumped before dark mode is locked), with **all CI gates green** after the update.
|
||||||
|
|
||||||
|
## Future Requirements (deferred, not in v1.2)
|
||||||
|
|
||||||
|
- **Additional calendar providers** beyond Fastmail + Google (the PROV-01 abstraction makes them cheaper to add later).
|
||||||
|
- **Google Calendar webhook push** (`events.watch`) for lower-latency sync — the existing 5-min ctag/poll model is sufficient for a two-person household; evaluate only if latency becomes a problem.
|
||||||
|
- **Wizard re-run / reconfigure** flow after first setup (carried from v1.1 backlog).
|
||||||
|
- **Android PWA install walkthrough** verified on a real device (carried from v1.0).
|
||||||
|
- **Tablet / touchscreen UX** — explicitly reserved for **v1.3**.
|
||||||
|
|
||||||
|
## Out of Scope (explicit exclusions)
|
||||||
|
|
||||||
|
- **Modern visual styling refresh** (backlog 999.21) — current UX is satisfactory; this is a separate future redesign track, not v1.2.
|
||||||
|
- **Per-event Google color (`colorId`) honoring** — FamilySync uses its own per-calendar colors for consistency; Google's per-event colors are ignored.
|
||||||
|
- **CalDAV/JMAP for Google** — Google integration uses the Google Calendar API + OAuth2; no CalDAV bridge.
|
||||||
|
- **Notification-preferences UI / reminder snooze** — the per-event multi-reminder selector (CAL-18) is sufficient for a 2-member household.
|
||||||
|
- **`googleapis` monolith** — use the scoped `@googleapis/calendar` + `google-auth-library` to avoid the ~50 MB footprint.
|
||||||
|
- **`drizzle-kit push` at boot** — emits a false destructive diff on populated MariaDB 11; boot migration uses the `migrate()` API only.
|
||||||
|
- **Bulk `pnpm up`** for DEP-01 — selective, pin-aware updates only.
|
||||||
|
|
||||||
|
## Traceability
|
||||||
|
|
||||||
|
Maps each REQ-ID to its phase. v1.2 phases continue prior numbering (v1.1 ended at Phase 20) → v1.2 starts at **Phase 21**.
|
||||||
|
|
||||||
|
| REQ-ID | Phase | Status |
|
||||||
|
| -------- | -------- | ----------- |
|
||||||
|
| SETUP-05 | Phase 21 | Not started |
|
||||||
|
| PROV-01 | Phase 22 | Not started |
|
||||||
|
| CAL-18 | Phase 23 | Not started |
|
||||||
|
| TEST-03 | Phase 24 | Not started |
|
||||||
|
| CAL-16 | Phase 25 | Not started |
|
||||||
|
| CAL-17 | Phase 25 | Not started |
|
||||||
|
| PROV-02 | Phase 25 | Not started |
|
||||||
|
| PROV-03 | Phase 26 | Not started |
|
||||||
|
| PROV-04 | Phase 26 | Not started |
|
||||||
|
| THEME-01 | Phase 27 | Not started |
|
||||||
|
| DEP-01 | Phase 28 | Not started |
|
||||||
|
|
||||||
|
**Coverage:** 11/11 requirements mapped to exactly one phase — no orphans, no duplicates.
|
||||||
|
|
||||||
|
| Phase | Requirements |
|
||||||
|
| ----- | ------------ |
|
||||||
|
| Phase 21 — Zero-Setup DB Bootstrap | SETUP-05 |
|
||||||
|
| Phase 22 — Provider Abstraction | PROV-01 |
|
||||||
|
| Phase 23 — Multiple Reminders Per Event | CAL-18 |
|
||||||
|
| Phase 24 — Dev/Mock Provider | TEST-03 |
|
||||||
|
| Phase 25 — Google Calendar Provider | CAL-16, CAL-17, PROV-02 |
|
||||||
|
| Phase 26 — Self-Service Provider Onboarding | PROV-03, PROV-04 |
|
||||||
|
| Phase 27 — PWA Dark Mode | THEME-01 |
|
||||||
|
| Phase 28 — Dependency Updates | DEP-01 |
|
||||||
+133
-230
@@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
- ✅ **v1.0 MVP** — Phases 1–6 (shipped 2026-06-10) — see [`milestones/v1.0-ROADMAP.md`](milestones/v1.0-ROADMAP.md)
|
- ✅ **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–20 (shipped 2026-06-18) — see [`milestones/v1.1-ROADMAP.md`](milestones/v1.1-ROADMAP.md)
|
- ✅ **v1.1 Operability & Polish** — Phases 7–20 (shipped 2026-06-18) — see [`milestones/v1.1-ROADMAP.md`](milestones/v1.1-ROADMAP.md)
|
||||||
|
- 🟡 **v1.2 Multi-Provider, Theming & Zero-Setup** — Phases 21–28 (in progress)
|
||||||
> Next milestone not yet defined — start with `/gsd-new-milestone`.
|
|
||||||
|
|
||||||
## Phases
|
## Phases
|
||||||
|
|
||||||
@@ -45,6 +44,111 @@ Full phase detail archived in [`milestones/v1.1-ROADMAP.md`](milestones/v1.1-ROA
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### 🟡 v1.2 Multi-Provider, Theming & Zero-Setup (Phases 21–28)
|
||||||
|
|
||||||
|
- [ ] **Phase 21: Zero-Setup DB Bootstrap** - App auto-creates/migrates its schema on boot against a bring-your-own MariaDB — no manual `db:migrate`
|
||||||
|
- [ ] **Phase 22: Provider Abstraction** - Refactor Fastmail/CalDAV behind a `CalendarProvider` interface with no Fastmail regression; widen credential uniqueness to `(user_id, provider_type)`
|
||||||
|
- [ ] **Phase 23: Multiple Reminders Per Event** - Up to 5 independent reminders per event, serialized to N VALARMs, other-client alarms preserved
|
||||||
|
- [ ] **Phase 24: Dev/Mock Provider** - In-memory provider lets the dev user + Playwright exercise the full app with no live calendar account
|
||||||
|
- [ ] **Phase 25: Google Calendar Provider** - Connect a Google account via OAuth; Google events read + write + reminders at Fastmail parity
|
||||||
|
- [ ] **Phase 26: Self-Service Provider Onboarding** - Members connect/reconnect their own provider (Fastmail app-password / Google OAuth) without admin help, with a one-tap reconnect banner
|
||||||
|
- [ ] **Phase 27: PWA Dark Mode** - Light/Dark/System theme switch, persisted, no flash-of-wrong-theme, clean across every route incl. Schedule-X
|
||||||
|
- [ ] **Phase 28: Dependency Updates** - Apply CI-surfaced outdated/vulnerable upgrades, pin-aware and per-ecosystem, with all CI gates green
|
||||||
|
|
||||||
|
## Phase Details
|
||||||
|
|
||||||
|
### Phase 21: Zero-Setup DB Bootstrap
|
||||||
|
**Goal**: A bring-your-own MariaDB (creds + DB name only) becomes a working FamilySync schema with no manual migration step — the app migrates itself on boot, idempotently and concurrency-safely, before it serves any request.
|
||||||
|
**Depends on**: Nothing (touches only `index.ts` startup + `db/migrations`)
|
||||||
|
**Requirements**: SETUP-05
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. Pointing the app at an empty MariaDB (DB credentials + database name supplied) and starting it produces the full, current schema with no manual `db:migrate` / `docker exec` step.
|
||||||
|
2. Restarting an already-migrated app is a no-op — no duplicate-migration error, no destructive diff, and existing data is untouched.
|
||||||
|
3. Two app instances starting against the same fresh database concurrently both come up cleanly — the migration runs exactly once (migration-lock sentinel), the loser waits rather than racing.
|
||||||
|
4. If the database is unreachable or migration fails, the app refuses to start (fatal) rather than serving on a partial schema.
|
||||||
|
5. The first request the setup wizard receives already has a complete schema — no setup-wizard call ever hits a missing table.
|
||||||
|
**Plans**: TBD
|
||||||
|
|
||||||
|
### Phase 22: Provider Abstraction (CalDAV only, Fastmail unchanged)
|
||||||
|
**Goal**: All calendar read, write-back, and reminder operations flow through one `CalendarProvider` seam, with the existing Fastmail/CalDAV broker wrapped behind it verbatim — so future providers plug in without touching the poller, outbox worker, or scheduler, and the live Fastmail path behaves identically.
|
||||||
|
**Depends on**: Phase 21 (schema auto-migrates before the integration suite runs)
|
||||||
|
**Requirements**: PROV-01
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. The existing Fastmail calendar continues to read, write (create/edit/delete, recurring + whole-series), and remind with no observable change — every v1.1 outbox/poller/scheduler integration test passes unchanged against the new interface.
|
||||||
|
2. Optimistic-202, create-before-delete on moves, per-uid exactly-once, the `isDraining` drain guard, and RRULE/VALARM preservation on edit all still hold (no per-provider drain locks introduced).
|
||||||
|
3. The poller and outbox worker select the implementation per member credential via a `createProvider(cred)` factory keyed on `provider_type`, with `caldav` as the default branch.
|
||||||
|
4. The `member_credentials` uniqueness constraint is widened to `UNIQUE(user_id, provider_type)` (migrated, no data loss), unblocking one credential per provider per user, and the token-storage shape (separate `provider_tokens` table vs. extended `member_credentials`) is decided and recorded.
|
||||||
|
**Plans**: TBD
|
||||||
|
|
||||||
|
### Phase 23: Multiple Reminders Per Event
|
||||||
|
**Goal**: A user can set several independent reminders on one event (up to 5), each firing once at its own lead, serialized to the connected provider's native model, without clobbering reminders authored in another client.
|
||||||
|
**Depends on**: Phase 22 (the CalDAV provider's write path accepts a reminder-leads array)
|
||||||
|
**Requirements**: CAL-18
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. In the event form a user can add and remove multiple reminder rows (up to 5); on edit, all existing reminders pre-populate.
|
||||||
|
2. Each reminder fires independently and exactly once — the scheduler dedup key includes the lead (`uid:dtstartMs:lead`), so two reminders on the same event don't collapse or double-fire.
|
||||||
|
3. Saving an event writes one VALARM per chosen lead to Fastmail (multiple sub-components in the VCALENDAR); reminders authored in another client are preserved on edit (preserve-vs-replace driven by a remindersChanged signal).
|
||||||
|
4. Existing single-reminder events keep their reminder after the schema migration (the old `reminder_lead_minutes` value is migrated into the new `reminder_leads` JSON array before the old column is dropped).
|
||||||
|
**Plans**: TBD
|
||||||
|
|
||||||
|
### Phase 24: Dev/Mock Provider (full-app exercise without a live calendar)
|
||||||
|
**Goal**: The dev-bypass user and the Playwright CI harness can exercise the entire app — calendar create/edit/delete, reminders, and lists — with no real calendar account connected, against deterministic seed data.
|
||||||
|
**Depends on**: Phase 22 (the `CalendarProvider` interface + factory); benefits from Phase 23 (reminder model) for reminder coverage
|
||||||
|
**Requirements**: TEST-03
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. Under `DEV_AUTH_BYPASS` (dev user, `provider_type='mock'` / no credential row), the calendar populates with seeded events — recurring, all-day, past, and future — with no live provider and no network I/O.
|
||||||
|
2. The dev user can create, edit, and delete events and set reminders end-to-end through the UI; the mock provider absorbs the outbox writes and reflects them back.
|
||||||
|
3. The Playwright harness exercises calendar CRUD hermetically (no live Fastmail/Google account), so CI can validate the full flow.
|
||||||
|
4. The mock path is strictly dev-only (same hard production guard as the existing dev bypass) and never activates in a production image.
|
||||||
|
**Plans**: TBD
|
||||||
|
|
||||||
|
### Phase 25: Google Calendar Provider
|
||||||
|
**Goal**: A member can connect a Google account and have their Google calendar reach full Fastmail parity — events appear in the unified color-coded view (recurrence + all-day correct), and create/edit/delete (including recurring + whole-series) and reminders write back to Google.
|
||||||
|
**Depends on**: Phase 22 (provider interface), Phase 23 (reminder serialization contract), Phase 21 (token table exists after migrate-on-boot)
|
||||||
|
**Requirements**: CAL-16, CAL-17, PROV-02
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. After connecting a Google account, that calendar's events appear in the unified color-coded calendar — recurring events expand correctly (Google recurrence/cancelled-instance model reconciled to the app's RRULE/EXDATE model) and all-day events are not shifted.
|
||||||
|
2. A user can create, edit, and delete events on the connected Google calendar from the app — including recurring-event create and whole-series delete — written back to Google.
|
||||||
|
3. Reminders set in the app serialize to Google `overrides` (and parse back), capped at 5, preserving other-client alarms.
|
||||||
|
4. The OAuth refresh token is stored AES-256-GCM encrypted (never in `app_config`), via the `UNIQUE(user_id, provider_type)` model; access tokens refresh inline and a Google `syncToken` 410 triggers a safe full re-sync without overwriting un-drained local writes.
|
||||||
|
5. The Google OAuth callback is mounted pre-auth (distinct path + distinct state cookie, redirect URI from `EXTERNAL_BASE_URL`) and round-trips correctly through the Pangolin hostname.
|
||||||
|
**Plans**: TBD
|
||||||
|
**UI hint**: yes
|
||||||
|
|
||||||
|
### Phase 26: Self-Service Provider Onboarding
|
||||||
|
**Goal**: A member can connect, re-enter, or reconnect their own provider credential using each provider's native mechanism — Fastmail app-password entry, Google "Connect Google" OAuth — without an admin acting for them, and a clear in-calendar banner makes a broken connection a one-tap fix.
|
||||||
|
**Depends on**: Phase 25 (the Google OAuth authorize/callback must exist before the UI redirects to it)
|
||||||
|
**Requirements**: PROV-03, PROV-04
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. A member sees which providers they have connected and can self-service connect their own credential — Fastmail via an app-password form (live-validated), Google via a single "Connect Google Calendar" button — with no admin involvement.
|
||||||
|
2. When a provider connection expires or is revoked, the user sees a clear banner in the calendar view (not buried in settings), not a silent failure.
|
||||||
|
3. From that banner the user can reconnect in one tap by re-running the provider's auth flow, without removing and re-adding the account.
|
||||||
|
4. A member can disconnect a provider, which removes its credential/tokens and its synced calendars + events.
|
||||||
|
**Plans**: TBD
|
||||||
|
**UI hint**: yes
|
||||||
|
|
||||||
|
### Phase 27: PWA Dark Mode
|
||||||
|
**Goal**: A user can switch the PWA between Light / Dark / System, the choice persists, and the dark theme renders cleanly everywhere with no flash-of-wrong-theme on load.
|
||||||
|
**Depends on**: Nothing (pure frontend; parallelizable with Phases 22–26)
|
||||||
|
**Requirements**: THEME-01
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. A theme toggle in Settings lets the user pick Light / Dark / System; the choice is persisted (per-device localStorage) and survives reload.
|
||||||
|
2. On load there is no flash-of-wrong-theme — the resolved theme is applied before first paint (inline pre-paint script reading the stored preference).
|
||||||
|
3. The dark theme renders cleanly across every route — calendar, lists, admin, settings sheet, and login — including the Schedule-X calendar component's `--sx-color-*` variables and the per-member color lanes.
|
||||||
|
4. "System" tracks the OS `prefers-color-scheme` and updates live when the OS theme changes; the iOS `theme-color` meta reflects the active theme.
|
||||||
|
**Plans**: TBD
|
||||||
|
**UI hint**: yes
|
||||||
|
|
||||||
|
### Phase 28: Dependency Updates
|
||||||
|
**Goal**: The outdated/vulnerable dependency upgrades that CI surfaces are applied — selectively and pin-aware — leaving the app on current, non-CVE-bearing dependencies with every CI gate green.
|
||||||
|
**Depends on**: Phase 27 (Schedule-X is not bumped until dark mode is locked); runs last so all other v1.2 work is already merged
|
||||||
|
**Requirements**: DEP-01
|
||||||
|
**Success Criteria** (what must be TRUE):
|
||||||
|
1. The CI dependency report's outdated/vulnerable packages are upgraded (no bulk `pnpm up` / `audit --fix`), per-ecosystem and pin-aware — ESLint held at 9.x until `eslint-plugin-react` supports 10, Drizzle orm + drizzle-kit bumped together, Schedule-X minor-only.
|
||||||
|
2. After the updates, all CI gates pass — lint, typecheck, unit, API integration, the Playwright harness, audit, and image hygiene are green.
|
||||||
|
3. No runtime regression to the calendar, lists, push, provider, or theming paths after the bumps (existing tests + harness still pass).
|
||||||
|
**Plans**: TBD
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
| Phase | Milestone | Plans Complete | Status | Completed |
|
| Phase | Milestone | Plans Complete | Status | Completed |
|
||||||
@@ -69,240 +173,36 @@ Full phase detail archived in [`milestones/v1.1-ROADMAP.md`](milestones/v1.1-ROA
|
|||||||
| 18. Auto Timezone Detection | v1.1 | 4/4 | Complete | 2026-06-14 |
|
| 18. Auto Timezone Detection | v1.1 | 4/4 | Complete | 2026-06-14 |
|
||||||
| 19. Local Auth (No-OIDC Mode) | v1.1 | 5/5 | Complete | 2026-06-17 |
|
| 19. Local Auth (No-OIDC Mode) | v1.1 | 5/5 | Complete | 2026-06-17 |
|
||||||
| 20. Admin Member Editor & Declutter | v1.1 | 3/3 | Complete | 2026-06-18 |
|
| 20. Admin Member Editor & Declutter | v1.1 | 3/3 | Complete | 2026-06-18 |
|
||||||
|
| 21. Zero-Setup DB Bootstrap | v1.2 | 0/? | Not started | - |
|
||||||
|
| 22. Provider Abstraction | v1.2 | 0/? | Not started | - |
|
||||||
|
| 23. Multiple Reminders Per Event | v1.2 | 0/? | Not started | - |
|
||||||
|
| 24. Dev/Mock Provider | v1.2 | 0/? | Not started | - |
|
||||||
|
| 25. Google Calendar Provider | v1.2 | 0/? | Not started | - |
|
||||||
|
| 26. Self-Service Provider Onboarding | v1.2 | 0/? | Not started | - |
|
||||||
|
| 27. PWA Dark Mode | v1.2 | 0/? | Not started | - |
|
||||||
|
| 28. Dependency Updates | v1.2 | 0/? | Not started | - |
|
||||||
|
|
||||||
|
## Dependencies & Critical Path (v1.2)
|
||||||
|
|
||||||
|
```
|
||||||
|
21 (DB Bootstrap) ──► 22 (Provider Abstraction) ──┬─► 23 (Multiple Reminders) ──┐
|
||||||
|
│ ├─► 25 (Google) ──► 26 (Onboarding)
|
||||||
|
└─► 24 (Mock Provider) ───────┘
|
||||||
|
27 (Dark Mode) — independent, parallelizable with 22–26
|
||||||
|
28 (Dependency Updates) — last; after 27 (Schedule-X hold) and all other v1.2 work
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Critical path:** 21 → 22 → 23 → 25 → 26, then 28 last.
|
||||||
|
- **Parallelizable:** 24 after 22 (alongside 23/25); 27 anytime.
|
||||||
|
- **HIGH-risk gate:** Phase 22 must not regress the live Fastmail path — golden-path integration tests pass unchanged before 23/24/25 build on it.
|
||||||
|
|
||||||
## Backlog
|
## Backlog
|
||||||
|
|
||||||
### Phase 999.1: Treat Fastmail as one calendar provider; framework supports adding more providers (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Abstract the calendar backend behind a provider interface so Fastmail/CalDAV is one implementation among potentially many. Shipping with a single provider is fine, but the broker, sync, and event-expansion layers should be structured so additional providers (e.g. other CalDAV hosts, Google Calendar, generic ICS feeds) can be added without rework. Captures the "provider" seam as an explicit architectural concern.
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 6/6 plans complete
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.4: Per-event reminder configuration (VALARM authoring + scheduler honors it) (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] End-to-end per-event reminders — let the user choose *when* (or whether) to be reminded per event, and make the push scheduler honor that choice instead of a hardcoded lead.
|
|
||||||
|
|
||||||
**Half A — author the VALARM (event form):** The event create/edit form has no UI to set a reminder ("remind me 10 min / 1 hour / 1 day before", or **no reminder**), so the written `.ics` carries no `VALARM` and no reminder can fire — in native clients or via web push. Add a reminder selector (including an explicit "none"), serialize chosen offsets as `VALARM` (TRIGGER) on write-back, and parse existing `VALARM`s on read so edits preserve them. Feeds the Phase 5 web-push requirement (push needs reminder data to notify about).
|
|
||||||
|
|
||||||
**Half B — scheduler honors the provider's value (NEW, surfaced 2026-06-10):** Today `apps/api/src/broker/reminderScheduler.ts` runs a **hardcoded 15-minute** scan for shared timed events (`index.ts:139` "starting in ~15 min"; reminderScheduler header "15-min reminder scan") and never reads the event's actual alarm. So every reminder fires 15 min before regardless of what the event (or the calendar provider) specifies, and an event with **no** alarm still gets a 15-min push. Change the scheduler to read each event's `VALARM` `TRIGGER` (the value written in Half A / set in Fastmail or another native client) and fire at that lead — and fire **nothing** when the event has no alarm. The current fixed 15-min window/dedup logic (catch-up scan, per-uid exactly-once — see quick 260610-hbu) must be generalized to a variable per-event lead.
|
|
||||||
|
|
||||||
**Boundary:** preserve the reminder scheduler's resilience guarantees (catch-up on a missed tick, per-uid exactly-once dedup). This makes the lead per-event/variable rather than constant; it is not a rewrite of the scan/dedup design.
|
|
||||||
|
|
||||||
**Severity:** medium — feature gap surfaced during Phase 03 Gate 2 testing; Half B surfaced 2026-06-10. Tags: phase-03, phase-05, calendar, write-back, reminders, valarm, push, scheduler, phase-05-dependency.
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 11 (Per-Event Reminders) — CAL-13/CAL-14/NOTIF-04/05/06.** Backlog entry retained for history.
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
- **View/update per-member Fastmail app passwords** (stored encrypted via `APP_PASSWORD_ENCRYPTION_KEY`, existing crypto path) — rotate or re-enter a member's credential and re-trigger sync.
|
|
||||||
- **Designate which synced calendar is the "shared" calendar** by toggling `calendars.is_shared` from the UI. Today this is a manual DB write: e.g. `UPDATE calendars SET is_shared=1 WHERE id=<row>` — done by hand on 2026-06-10 to mark the "FamilySync" calendar (id 10) shared after the poller synced it (D-16). The admin should pick the shared calendar from a list of synced collections instead of relying on a backend process. (The poller's upsert already leaves `is_shared` untouched, so a UI-set flag persists.)
|
|
||||||
|
|
||||||
**Context:** Motivated by the manual D-16 resolution (2026-06-10). **Related:** 999.5 (per-member first-login app-password onboarding) — this is the ongoing admin-managed counterpart; and 999.11 (initial setup wizard) — bootstrap-time vs. ongoing config. Tags: admin, settings, calendar, app-passwords, D-16.
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 10 (Admin Role & Settings) — ADMIN-01/ADMIN-02/ADMIN-03.** Backlog entry retained for history.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.11: Initial setup wizard — first-run config of env vars, app passwords, DB connection (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Add a first-run **setup wizard** that walks the administrator through defining all bootstrap configuration instead of hand-editing `.env` / `docker-compose.yml`:
|
|
||||||
|
|
||||||
- **App environment variables:** OIDC client id/secret/issuer/redirect URI + external URL, session signing secret (`OIDC_AUTH_SECRET`), `APP_PASSWORD_ENCRYPTION_KEY`, and the **VAPID keypair** (subject + public + private).
|
|
||||||
- **MariaDB connection:** host/port/user/password/db, with a connectivity test.
|
|
||||||
- **First Fastmail app password** for the initial member, encrypted on save.
|
|
||||||
|
|
||||||
Wizard should **validate inputs before completing** — e.g. VAPID private key decodes to 32 bytes AND pairs with the public key, OIDC discovery resolves, DB connects, app-password reaches CalDAV.
|
|
||||||
|
|
||||||
**Context:** Motivated by setup friction observed 2026-06-10 — a VAPID private key truncated on paste into `.env` silently broke push (`setVapidDetails failed — 32 bytes`), and `DB_HOST` / dev overrides must currently be set by hand. A guided + validated wizard would have caught these. **Related:** 999.10 (ongoing admin Settings) and 999.5 (member onboarding). Tags: onboarding, setup, install, env, vapid, mariadb, oidc.
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 12 (Initial Setup Wizard) — SETUP-01/02/03/04.** Backlog entry retained for history.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.12: Assistant-driven mobile-browser UI testing (mobile viewport + authed PWA) (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Give the assistant a way to validate UI/UX changes in a **mobile** browser experience, not just desktop Chromium. Today `playwright-cli` drives a desktop viewport, and the prod stack enforces OIDC (Authelia) so the authed PWA can't be reached headlessly — which is exactly why a string of mobile-only defects this milestone (silent Android notifications, the dead "How to enable" link, iOS/Android session-cookie persistence, install/standalone behaviour) could only be found by the operator on real devices, not by the assistant.
|
|
||||||
|
|
||||||
**What this needs (any subset):**
|
|
||||||
|
|
||||||
- **Mobile viewport + UA emulation** in the browser harness (e.g. Playwright device descriptors — iPhone/Pixel viewport, touch, mobile user-agent) so layout, tap targets, and responsive behaviour can be checked.
|
|
||||||
- **An authenticated entry path for automated runs** so the assistant can reach the real PWA past Authelia — e.g. a reusable saved storage-state/cookie, a test-only bypass on a non-prod host, or driving the Authelia login once and reusing the session. (Note: this overlaps the existing `DEV_AUTH_BYPASS`, but that only works on the host-side dev stack, not the prod-mode PWA that has the real service worker. A mobile, authed, SW-enabled target is the gap.)
|
|
||||||
- Optionally: a documented way to point the harness at the Pangolin HTTPS URL with a persisted session, and/or remote-debug a real device.
|
|
||||||
|
|
||||||
**Boundary:** genuinely device-only behaviour (iOS-Safari standalone push, real APNs/FCM delivery, OS notification-channel importance) still needs a human — this item is about everything SHORT of that (responsive layout, tap flows, in-page notification UI states, auth redirects) which a mobile-emulated authed browser *could* cover but currently can't.
|
|
||||||
|
|
||||||
**Context:** Surfaced 2026-06-10 during Phase 5 UAT — repeated mobile-only bugs were caught only by the operator because the assistant had no mobile, authenticated browser to test in. **Related:** [[feedback-playwright-verify]] (use playwright-cli over manual verification — this extends it to mobile/authed). Tags: testing, playwright, mobile, pwa, oidc, dx.
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 7 (Mobile Test Harness) — TEST-01/TEST-02.** v1.1 scopes the `DEV_AUTH_BYPASS` dev-build path; the prod-SW authed-mobile target stays deferred. Backlog entry retained for history.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.13: Reduce event write-back latency to the calendar provider (outbox drain) (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Calendar create/edit/delete writes are enqueue-only (`calendarOutbox`, 202 optimistic-accept; D-12/D-05 — no Fastmail call in the route) and flushed to Fastmail by `runOutboxDrain` on a **15-second `setInterval`** (`apps/api/src/broker/outboxWorker.ts`). So a change can take up to ~15s to land in Fastmail (and longer to reflect back in the app, which depends on the separate 5-min poller). Reduce that perceived sync delay so edits feel near-immediate.
|
|
||||||
|
|
||||||
**Options to weigh when picking this up:**
|
|
||||||
|
|
||||||
- **Event-driven drain (preferred):** trigger an outbox drain immediately after a successful enqueue (in-process signal, or Redis pub/sub which is already available) so the write fires within ~1s instead of waiting for the next tick — keep the 15s `setInterval` as a fallback/retry sweep. Must preserve the existing per-row etag/412 handling and the rapid-successive-edit ordering (see outboxWorker comments ~L312 — each edit carries its enqueue-time etag).
|
|
||||||
- **Shorter interval:** simplest, but more idle DB polling; a floor (e.g. 3–5s) trades latency for load.
|
|
||||||
- **Faster read-back too:** the user also sees latency from the 5-min poller reflecting the change back. Consider invalidating/short-poll after a local write, or optimistic UI already covering it — confirm whether the perceived delay is the write (15s) or the read-back (5min).
|
|
||||||
|
|
||||||
**Boundary:** the optimistic 202 + outbox durability design (create-before-delete, drain concurrency guard, fresh-etag-before-PUT) must be preserved — this is a latency tune, not a rewrite of the write path.
|
|
||||||
|
|
||||||
**Context:** Surfaced 2026-06-10. Tags: calendar, write-back, outbox, latency, redis, performance.
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 9 (Faster Write-Back) — CAL-15.** In-process EventEmitter chosen (not Redis); the drain is single-process by design. Backlog entry retained for history.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.14: Gitea CI — full regression on PR to main + build/publish Docker image (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] The repo is committed against a self-hosted Gitea instance with a registered Actions runner, but there is no CI yet (no `.gitea/workflows/` or `.github/workflows/`). Two things should run automatically: (1) **full regression** on every PR targeting `main` — gating the merge; (2) **build the app's Docker image and publish it** to the Gitea container registry.
|
|
||||||
|
|
||||||
**Options / decisions to make when picking this up:**
|
|
||||||
|
|
||||||
- **Test scope:** "full regression" = lint + typecheck + unit + the API integration tests. Integration tests need a real MariaDB (see [[api-integration-test-db]]) — the workflow must spin up a MariaDB service container, bind it, and set `DB_HOST=127.0.0.1` + `.env` creds. The PWA build/test also runs.
|
|
||||||
- **Monorepo:** pnpm workspace (`apps/api`, `apps/pwa`, shared). Cache the pnpm store.
|
|
||||||
- **Docker images:** only `apps/api/Dockerfile` exists today — there is no PWA Dockerfile yet. Decide one image (API) vs. also building/serving the PWA. Tag scheme + when to publish (only on merge to `main`? on tags? per-PR?).
|
|
||||||
- **Registry auth:** push to the Gitea registry using the runner's Gitea-provided token or a dedicated package-write token.
|
|
||||||
- Gitea Actions are GitHub-Actions-compatible syntax but run on the self-hosted runner — confirm runner labels and available images, and that Actions is enabled, before authoring.
|
|
||||||
|
|
||||||
**Likely shape:** a `.gitea/workflows/ci.yml` — `on: pull_request` (to `main`) → install (pnpm), lint, typecheck, unit, API integration vs. a `mariadb` service container, PWA build; `on: push` to `main`/tag → `docker build apps/api/Dockerfile`, login, push tagged image.
|
|
||||||
|
|
||||||
**Context:** Promoted from STATE.md pending todo (`.planning/todos/pending/2026-06-10-gitea-ci-regression-and-docker-publish.md`), surfaced 2026-06-10. Tags: tooling, ci, gitea, docker, mariadb, monorepo.
|
|
||||||
|
|
||||||
> **Promoted into v1.1 Phase 8 (Gitea CI) — CI-01/CI-02.** v1.1 also extends CI-01 to run the Phase 7 mobile harness as a UI-regression step (CI brings up the dev stack in the runner). Backlog entry retained for history.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.15: Desktop e2e coverage — add a Desktop Playwright profile + desktop-safe specs (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] The Playwright harness (`apps/pwa/playwright.config.ts`) defines only **mobile** device profiles — `iphone` (iPhone 14 / WebKit) and `pixel` (Pixel 7 / Chromium), both with touch and a mobile viewport. The Phase 8 CI regression gate runs `pnpm test:e2e`, so it currently validates the **mobile experience only**. Add desktop coverage so the regression gate exercises the desktop layout/flows as well.
|
|
||||||
|
|
||||||
**Options / decisions to make when picking this up:**
|
|
||||||
|
|
||||||
- **Add a Desktop profile:** a new `desktop` project in `playwright.config.ts` (e.g. `devices['Desktop Chrome']`, no `hasTouch`, wide viewport). Optionally a Desktop WebKit/Safari profile too — but the family's Apple member is already covered on mobile Safari via `iphone`; Desktop Chrome is likely sufficient for a shared/wall browser.
|
|
||||||
- **Spec-compat pass (the real work):** the existing e2e specs were authored for mobile — they may assume touch gestures, a mobile nav/drawer, or mobile-only layout. Each spec needs review/adjustment so it passes (or is appropriately skipped) on a no-touch, wide-viewport desktop. This is harness/spec work, not CI plumbing.
|
|
||||||
- **Gating choice:** decide whether desktop runs block the merge immediately, or run advisory (non-blocking) until the specs are confirmed desktop-safe.
|
|
||||||
|
|
||||||
**Boundary:** Phase 8 deliberately reused the Phase 7 harness **unchanged** (CI owns only stack bring-up + readiness waits, not spec content), which is why this was deferred. Once a Desktop project is added to the config, Phase 8 CI picks it up automatically via `pnpm test:e2e` — no CI changes needed beyond whatever runtime/wait the desktop profile requires.
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.16: Wire a real linter (ESLint) so the CI lint gate actually fails on violations (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] The Phase 8 CI `fast-checks` job runs `pnpm lint`, but **no linter exists** in the repo — the root `lint` script is `pnpm -r --if-present lint`, which finds no package-level lint script and exits 0. The lint gate is a hollow placeholder that can never fail. Wire up a real linter so it runs and gates merges on lint violations. (`typecheck`/tsc already gates type errors meanwhile.)
|
|
||||||
|
|
||||||
**Options / decisions to make when picking this up:**
|
|
||||||
|
|
||||||
- **Tooling:** ESLint flat config (`eslint.config.js`) with `typescript-eslint`; add React + react-hooks plugins for `apps/pwa`. Add `eslint` (+ plugins) as devDeps and a `lint` script to `apps/api` and `apps/pwa` — `pnpm -r --if-present lint` then picks them up automatically, no CI change needed.
|
|
||||||
- **Rule strictness:** pick a baseline (recommended vs strict-type-checked). Stricter = more upfront violations to fix.
|
|
||||||
- **Violation cleanup (the real work):** the first run surfaces existing violations across both apps. Decide per-rule: fix, downgrade to warn, or disable. The gate must end green.
|
|
||||||
- **Gating choice:** blocking on merge immediately, or advisory (warn-only) until the codebase is clean.
|
|
||||||
|
|
||||||
**Boundary:** Phase 8 deliberately scoped lint wiring out (CI-plumbing-only); it shipped the gate slot wired to auto-activate once a package `lint` script lands. This item is that follow-up.
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.18: Update dependencies as found during CI (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] When the CI dependency-audit gate (Phase 16) surfaces outdated or vulnerable packages, bump them rather than letting the report accumulate. Establish a lightweight, recurring "act on the CI dependency report" loop so the two-person household app doesn't drift onto stale/CVE-bearing deps. Scope is the upkeep workflow (review → bump → verify gate green), not a one-time audit.
|
|
||||||
|
|
||||||
**Context:** Captured 2026-06-13 during Phase 10 work. Companion to the audit *reporting* shipped in Phase 16 (CI Dependency Audit) — that phase makes outdated/vulnerable deps *visible*; this item is the standing follow-through to *resolve* what it finds. Tags: ci, dependencies, maintenance, security, upkeep.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.19: Dev user exercises full app functionality without syncing to a real calendar (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Let the `DEV_AUTH_BYPASS` dev user (currently hardcoded `DEV_USER` id 1 in `apps/api/src/auth/devBypass.ts`) exercise the full app — create/edit/delete events, set per-event reminders, manage lists — against a local/in-app calendar store, WITHOUT requiring a connected Fastmail/CalDAV provider and WITHOUT writing anything to a real calendar. Today the dev user has no `member_credentials` row and no `calendars`, so `writable-calendars` is empty and `POST /api/events/create` returns `422 "No writable calendar found for user"` — making hands-on UAT of event/reminder features impossible in dev. Options to explore: seed the dev user a fake local calendar + short-circuit the outbox/CalDAV write path under dev-bypass (no Fastmail round-trip), or a dev-only in-memory calendar provider. Must stay strictly dev-only (same hard `NODE_ENV !== 'production'` guard) and never ship in production images.
|
|
||||||
|
|
||||||
**Context:** Captured 2026-06-14 during Phase 11 (Per-Event Reminders) UAT. The reminder picker and backend were verified via automated tests + a route-mocked playwright smoke, but the operator could not manually create an event to see reminders end-to-end because no provider is connected in the dev DB (`needsProviderSetup: true`). This is a recurring dev-testability friction (see MEMORY: "Dev user 1 has no calendars"). Tags: dev-tooling, dev-bypass, testability, calendars, outbox, uat.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.20: PWA dark mode / theming — ship a full dark theme + light/dark/system switch (BACKLOG)
|
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] Ship a complete dark theme for the PWA plus a light/dark/system theme switch. **Phase 17 lays the token-architecture groundwork** — it restructures `apps/pwa/src/styles/tokens.css` from a single light `:root` into a themeable semantic-token layer that can be swapped via `data-theme` / `prefers-color-scheme`, with light staying the default and only-shipped theme. This backlog item is the follow-through that consumes that seam: author the actual dark palette values (including the Schedule-X `--sx-color-*` calendar overrides at the bottom of tokens.css), wire `prefers-color-scheme`, add a persisted in-app toggle in the /admin or Settings surface (light / dark / system), and verify both themes render cleanly across every route (calendar, lists, admin, settings sheet, login) via `playwright-cli` + the Phase 7 `layout.spec` profiles.
|
|
||||||
|
|
||||||
**Context:** Deferred out of Phase 17 (2026-06-17) during `/gsd-discuss-phase 17` to keep that phase scoped to phone-layout polish + branding assets. Phase 17's token restructure is the explicit enabling groundwork, so this should be cheap to pick up afterward. Related: Phase 17 (UI Optimization & Polish — the groundwork), 999.21 (modern styling refresh). Tags: pwa, theming, dark-mode, tokens, accessibility, settings, prefers-color-scheme.
|
|
||||||
|
|
||||||
**Requirements:** TBD
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
|
||||||
|
|
||||||
### Phase 999.21: PWA modern visual styling refresh — contemporary look across the app (BACKLOG)
|
### Phase 999.21: PWA modern visual styling refresh — contemporary look across the app (BACKLOG)
|
||||||
|
|
||||||
**Goal:** [Captured for future planning] A broader "more modern, visually appealing" styling pass across the PWA — beyond the bounded in-system polish of Phase 17. Candidate scope: a contemporary refresh of high-visibility surfaces (login, calendar shell, event form, lists, admin), revisiting elevation/shadows, radii, spacing rhythm, typography scale, and control states, potentially reworking specific component layouts. Explicitly **flagged for a future milestone**, not v1.1 — it is a visual-overhaul track with real redesign risk and should be scoped/sequenced on its own rather than bolted onto a polish phase. Best sequenced after the Phase 17 token groundwork and 999.20 (dark mode) so the refresh is theme-aware from the start.
|
**Goal:** [Captured for future planning] A broader "more modern, visually appealing" styling pass across the PWA — beyond the bounded in-system polish of Phase 17. Candidate scope: a contemporary refresh of high-visibility surfaces (login, calendar shell, event form, lists, admin), revisiting elevation/shadows, radii, spacing rhythm, typography scale, and control states, potentially reworking specific component layouts. Explicitly **flagged for a future milestone**, not v1.2 — it is a visual-overhaul track with real redesign risk and should be scoped/sequenced on its own rather than bolted onto a polish phase. Best sequenced after the Phase 17 token groundwork and Phase 27 (dark mode) so the refresh is theme-aware from the start.
|
||||||
|
|
||||||
**Context:** Deferred out of Phase 17 (2026-06-17) during `/gsd-discuss-phase 17`. The user scoped Phase 17 to layout polish + branding (logo/favicon/icon assets) + theme-token groundwork, and routed the open-ended styling refresh here for a future milestone to avoid an unbounded redesign inside a polish phase. Related: Phase 17 (the polish baseline), 999.20 (dark mode / theming). Tags: pwa, ui, styling, redesign, design-system, future-milestone.
|
**Context:** Deferred out of Phase 17 (2026-06-17) during `/gsd-discuss-phase 17`. The user scoped Phase 17 to layout polish + branding (logo/favicon/icon assets) + theme-token groundwork, and routed the open-ended styling refresh here for a future milestone to avoid an unbounded redesign inside a polish phase. Related: Phase 17 (the polish baseline), Phase 27 / 999.20 (dark mode / theming). Tags: pwa, ui, styling, redesign, design-system, future-milestone.
|
||||||
|
|
||||||
**Requirements:** TBD
|
**Requirements:** TBD
|
||||||
**Plans:** 0 plans
|
**Plans:** 0 plans
|
||||||
@@ -311,3 +211,6 @@ Plans:
|
|||||||
|
|
||||||
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
- [ ] TBD (promote with /gsd-review-backlog when ready)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Backlog items 999.1 (provider abstraction), 999.5 (self-service onboarding), 999.18 (CI dependency updates), 999.19 (dev-user full-app exercise), and 999.20 (dark mode) were promoted into v1.2 Phases 22, 26, 28, 24, and 27 respectively. Historical promotion provenance for v1.0/v1.1 backlog items (999.1/4/5/10/11/12/13/14/15/16) is preserved in `milestones/v1.1-ROADMAP.md`._
|
||||||
|
|||||||
+21
-20
@@ -1,20 +1,16 @@
|
|||||||
---
|
---
|
||||||
gsd_state_version: 1.0
|
gsd_state_version: 1.0
|
||||||
milestone: v1.1
|
milestone: v1.2
|
||||||
milestone_name: Operability & Polish
|
milestone_name: Multi-Provider, Theming & Zero-Setup
|
||||||
current_phase: null
|
status: planning
|
||||||
status: Awaiting next milestone
|
last_updated: "2026-06-19T14:30:00.000Z"
|
||||||
stopped_at: v1.1 milestone shipped & archived
|
last_activity: 2026-06-19
|
||||||
last_updated: "2026-06-19T01:58:30.566Z"
|
|
||||||
last_activity: 2026-06-18
|
|
||||||
last_activity_desc: Milestone v1.1 completed and archived
|
|
||||||
progress:
|
progress:
|
||||||
total_phases: 20
|
total_phases: 8
|
||||||
completed_phases: 20
|
completed_phases: 0
|
||||||
total_plans: 57
|
total_plans: 0
|
||||||
completed_plans: 57
|
completed_plans: 0
|
||||||
percent: 100
|
percent: 0
|
||||||
current_phase_name: Awaiting next milestone
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -24,14 +20,16 @@ current_phase_name: Awaiting next milestone
|
|||||||
See: .planning/PROJECT.md (updated 2026-06-18 after v1.1 milestone)
|
See: .planning/PROJECT.md (updated 2026-06-18 after v1.1 milestone)
|
||||||
|
|
||||||
**Core value:** One color-coded family calendar (shared + personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store
|
**Core value:** One color-coded family calendar (shared + personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store
|
||||||
**Current focus:** Planning next milestone — run `/gsd-new-milestone`
|
**Current focus:** Phase 21 — Zero-Setup DB Bootstrap (v1.2 roadmap created; awaiting plan)
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: Milestone v1.1 complete
|
Phase: 21 of 28 (Zero-Setup DB Bootstrap) — first v1.2 phase
|
||||||
Plan: —
|
Plan: — (not yet planned)
|
||||||
Status: Awaiting next milestone
|
Status: Ready to plan
|
||||||
Last activity: 2026-06-19 — Milestone v1.1 completed and archived
|
Last activity: 2026-06-19 — Completed quick task 260619-jp4: added app screenshots + README Features section
|
||||||
|
|
||||||
|
Progress: [░░░░░░░░░░] 0% (0/8 v1.2 phases)
|
||||||
|
|
||||||
### ✅ Resolved Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action)
|
### ✅ Resolved Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action)
|
||||||
|
|
||||||
@@ -210,6 +208,8 @@ Recent decisions affecting current work:
|
|||||||
|
|
||||||
### Roadmap Evolution
|
### Roadmap Evolution
|
||||||
|
|
||||||
|
- **v1.2 roadmap created (2026-06-19):** 8 phases (21–28), continuing v1.1 numbering (last phase 20). 11/11 requirements mapped to exactly one phase, no orphans, no duplicates. Dependency-ordered per research ARCHITECTURE.md "Dependency-Ordered Build Sequence": **21 Zero-Setup DB Bootstrap** (SETUP-05, no deps, unlocks auto-migrate) → **22 Provider Abstraction** (PROV-01, HIGH-risk, CalDAV-only/Fastmail unchanged, carries the `UNIQUE(user_id, provider_type)` migration) → **23 Multiple Reminders** (CAL-18, dep 22) and **24 Dev/Mock Provider** (TEST-03, dep 22) → **25 Google Calendar Provider** (CAL-16/17/PROV-02, deps 22/23/21) → **26 Self-Service Onboarding** (PROV-03/04, dep 25). **27 PWA Dark Mode** (THEME-01) is independent/parallelizable with 22–26. **28 Dependency Updates** (DEP-01) is last — after 27 (Schedule-X hold) and all other v1.2 work. Backlog promotions: 999.1→Provider Abstraction (22), 999.20→Dark Mode (27), 999.5→Self-Service Onboarding (26), 999.19→Dev/Mock Provider (24), 999.18→Dependency Updates (28). DEP-01 kept a standalone closing phase (not folded) to preserve its hard "last, after dark-mode-locked" ordering. Critical path: 21→22→23→25→26, then 28.
|
||||||
|
|
||||||
- Phase 6 added (2026-06-07): UX Polish — all-day visual distinction, event-form date/recurrence behavior, recurring-series edit, auth-flow smoothing. Candidate scope pulls from backlog 999.2/999.3/999.6/999.7/999.8/999.9.
|
- Phase 6 added (2026-06-07): UX Polish — all-day visual distinction, event-form date/recurrence behavior, recurring-series edit, auth-flow smoothing. Candidate scope pulls from backlog 999.2/999.3/999.6/999.7/999.8/999.9.
|
||||||
- Phase 6 complete (2026-06-10): all 6 plans executed + 2 phase-level UX fixes (AppNav persistence + BottomTabBar desktop hide). Residual device-only checkpoints documented above.
|
- Phase 6 complete (2026-06-10): all 6 plans executed + 2 phase-level UX fixes (AppNav persistence + BottomTabBar desktop hide). Residual device-only checkpoints documented above.
|
||||||
- Backlog reviewed (2026-06-10, /gsd-review-backlog): removed 6 stale duplicates (999.2/3/6/7/8/9 — already promoted into Phase 6) from the Backlog section + deleted the 999.2 dir; kept 999.1/4/5/10/11/12/13; added 999.14 (Gitea CI, promoted from STATE pending todo); archived stale kickoff-new-project todo.
|
- Backlog reviewed (2026-06-10, /gsd-review-backlog): removed 6 stale duplicates (999.2/3/6/7/8/9 — already promoted into Phase 6) from the Backlog section + deleted the 999.2 dir; kept 999.1/4/5/10/11/12/13; added 999.14 (Gitea CI, promoted from STATE pending todo); archived stale kickoff-new-project todo.
|
||||||
@@ -259,6 +259,7 @@ Recent decisions affecting current work:
|
|||||||
| 260613-ndv | Isolate local apps/api integration tests to a dedicated `familysync_test` DB so test runs stop polluting the dev `familysync` DB. New CI-gated vitest globalSetup root-provisions (CREATE DATABASE + GRANT) + migrates + truncate-resets `familysync_test` each run; `vitest.config.ts` forces `DB_NAME=familysync_test` for local workers (no-op under CI, so CI's `familysync` service DB + db:migrate are untouched). Verified: dev `familysync` users stays 3 across a run, `familysync_test` resets (186→93, not doubled), 244/244 tests pass (flaky list_shares timeout gone), typecheck 0. Branch off main. | 2026-06-13 | 07d5161 | Verified | [260613-ndv-wire-apps-api-integration-tests-to-a-ded](./quick/260613-ndv-wire-apps-api-integration-tests-to-a-ded/) |
|
| 260613-ndv | Isolate local apps/api integration tests to a dedicated `familysync_test` DB so test runs stop polluting the dev `familysync` DB. New CI-gated vitest globalSetup root-provisions (CREATE DATABASE + GRANT) + migrates + truncate-resets `familysync_test` each run; `vitest.config.ts` forces `DB_NAME=familysync_test` for local workers (no-op under CI, so CI's `familysync` service DB + db:migrate are untouched). Verified: dev `familysync` users stays 3 across a run, `familysync_test` resets (186→93, not doubled), 244/244 tests pass (flaky list_shares timeout gone), typecheck 0. Branch off main. | 2026-06-13 | 07d5161 | Verified | [260613-ndv-wire-apps-api-integration-tests-to-a-ded](./quick/260613-ndv-wire-apps-api-integration-tests-to-a-ded/) |
|
||||||
| 260618-smr | Remove unused Redis service and all references — Redis confirmed unused at runtime (no ioredis/redis client import, no `REDIS_*` env, not a dependency in any package.json). Dropped the `redis` service from both compose files and cleaned all references in CLAUDE.md, README.md, and docs/* + e2e config. Kept the in-memory-vs-Redis design-rationale comments (D-12/D-18) in listEmitter/reminderScheduler/linkNonceStore/localAuth. `docker compose config` parses clean (0 redis); `format:check` green. Branch off main. | 2026-06-18 | 0b42666 | Verified | [260618-smr-remove-unused-redis-service-and-referenc](./quick/260618-smr-remove-unused-redis-service-and-referenc/) |
|
| 260618-smr | Remove unused Redis service and all references — Redis confirmed unused at runtime (no ioredis/redis client import, no `REDIS_*` env, not a dependency in any package.json). Dropped the `redis` service from both compose files and cleaned all references in CLAUDE.md, README.md, and docs/* + e2e config. Kept the in-memory-vs-Redis design-rationale comments (D-12/D-18) in listEmitter/reminderScheduler/linkNonceStore/localAuth. `docker compose config` parses clean (0 redis); `format:check` green. Branch off main. | 2026-06-18 | 0b42666 | Verified | [260618-smr-remove-unused-redis-service-and-referenc](./quick/260618-smr-remove-unused-redis-service-and-referenc/) |
|
||||||
| 260618-tg2 | Persistent CI dependency caches — point all 4 CI `pnpm install` steps at a host-mounted `/pnpm-store` (`--store-dir /pnpm-store --prefer-offline`) and persist Playwright browsers via `PLAYWRIGHT_BROWSERS_PATH=/ms-playwright` on the harness job; added BuildKit `--mount=type=cache` to all 3 Dockerfile install stages + `DOCKER_BUILDKIT=1` on the publish build. Avoids `actions/cache` (D-PROBE-04 timeout). In-repo only — requires act_runner `config.yaml` `container.options` host mounts (manual host change). Verdaccio deferred. Branch off main. | 2026-06-18 | 6e93e24 | Verified | [260618-tg2-persistent-ci-dependency-caches-pnpm-sto](./quick/260618-tg2-persistent-ci-dependency-caches-pnpm-sto/) |
|
| 260618-tg2 | Persistent CI dependency caches — point all 4 CI `pnpm install` steps at a host-mounted `/pnpm-store` (`--store-dir /pnpm-store --prefer-offline`) and persist Playwright browsers via `PLAYWRIGHT_BROWSERS_PATH=/ms-playwright` on the harness job; added BuildKit `--mount=type=cache` to all 3 Dockerfile install stages + `DOCKER_BUILDKIT=1` on the publish build. Avoids `actions/cache` (D-PROBE-04 timeout). In-repo only — requires act_runner `config.yaml` `container.options` host mounts (manual host change). Verdaccio deferred. Branch off main. | 2026-06-18 | 6e93e24 | Verified | [260618-tg2-persistent-ci-dependency-caches-pnpm-sto](./quick/260618-tg2-persistent-ci-dependency-caches-pnpm-sto/) |
|
||||||
|
| 260619-jp4 | Add six app screenshots (Calendar, Lists, List detail, Login, Setup wizard, Admin) to `docs/screenshots/` and rebuild the README intro into a "Features" section that expands the description, one screenshot per capability. Captured via playwright-cli (Chromium) against the live dev stack with demo data mocked per-browser via `route` interception — nothing written to the real Fastmail account. prettier + md:lint green. Branch: gsd/v1.2-multi-provider-theming-zero-setup. | 2026-06-19 | 23dc3cd | | [260619-jp4-add-app-screenshots-to-readme](./quick/260619-jp4-add-app-screenshots-to-readme/) |
|
||||||
|
|
||||||
## Deferred Items
|
## Deferred Items
|
||||||
|
|
||||||
@@ -284,4 +285,4 @@ Resume file: .planning/phases/20-admin-member-editor-form-declutter/20-UI-SPEC.m
|
|||||||
|
|
||||||
## Operator Next Steps
|
## Operator Next Steps
|
||||||
|
|
||||||
- Start the next milestone with /gsd-new-milestone
|
- v1.2 roadmap created (Phases 21–28). Plan the first phase with `/gsd-plan-phase 21`.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"firecrawl": false,
|
"firecrawl": false,
|
||||||
"exa_search": false,
|
"exa_search": false,
|
||||||
"git": {
|
"git": {
|
||||||
"branching_strategy": "phase",
|
"branching_strategy": "milestone",
|
||||||
"create_tag": true,
|
"create_tag": true,
|
||||||
"phase_branch_template": "gsd/phase-{phase}-{slug}",
|
"phase_branch_template": "gsd/phase-{phase}-{slug}",
|
||||||
"milestone_branch_template": "gsd/{milestone}-{slug}",
|
"milestone_branch_template": "gsd/{milestone}-{slug}",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user