# Requirements: FamilySync — v1.1 "Operability & Polish" **Defined:** 2026-06-10 **Milestone:** v1.1 (continues from v1.0 MVP, shipped 2026-06-10) **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.1 makes that app **configurable, administrable, and maintainable** without hand-editing env files or the database. REQ-IDs continue v1.0 numbering (CAL ≤12, NOTIF ≤3 already used). New categories: ADMIN, SETUP, CI, TEST. ## v1.1 Requirements Each requirement maps to exactly one roadmap phase (see Traceability). ### Calendar — Per-event reminders & write-back latency - [ ] **CAL-13**: User can choose a reminder lead time when creating or editing an event from a preset list (None / 5m / 10m / 15m / 30m / 1h / 2h / 1d / 2d), with **"None" as the default**; the choice is serialized as a VALARM on the event written back to Fastmail. - [ ] **CAL-14**: Editing an event **preserves any existing reminder/VALARM** set in another client (Fastmail or native) — reminders are never silently stripped on round-trip. - [ ] **CAL-15**: A created, edited, or deleted event reaches Fastmail within ~2 seconds (event-driven outbox drain) instead of up to ~15s, while preserving the optimistic-202 accept and all outbox durability guarantees (create-before-delete ordering, drain concurrency guard, fresh-etag-before-PUT, per-uid exactly-once). ### Notifications — Variable-lead reminder scheduling - [ ] **NOTIF-04**: An event reminder push fires at the event's **chosen lead time**, not a hardcoded 15-minute lead. - [ ] **NOTIF-05**: An event with **no reminder set produces no reminder push** (no default 15-min fire). - [ ] **NOTIF-06**: An all-day event's reminder fires at a sensible local time (9 AM on the alert day), not at midnight, and reminder delivery remains exactly-once across catch-up scans and rescheduled events. ### Administration — Settings section (role-gated) > Role-agnostic design: ship operator-only (`is_admin`), but the role check is member-count-agnostic so more admins can be added later without rework. - [ ] **ADMIN-01**: An admin can view household members and update (rotate / re-enter) a member's Fastmail app password from the UI; the credential is validated against CalDAV before saving and stored encrypted (existing `APP_PASSWORD_ENCRYPTION_KEY` path) — the password is never displayed, logged, or echoed. - [ ] **ADMIN-02**: An admin can designate which synced calendar is the shared family calendar (set `calendars.is_shared`) from the UI, replacing the manual DB write. - [ ] **ADMIN-03**: Admin Settings routes and UI are gated by a role check; a non-admin member cannot reach or invoke them. ### Setup — First-run configuration wizard - [ ] **SETUP-01**: On first run (no admin/credentials configured), the operator is guided through a setup wizard to define bootstrap configuration (app/external URL, OIDC client, session secret, encryption key, VAPID keypair, MariaDB connection, first member's Fastmail app password) instead of hand-editing `.env` / `docker-compose.yml`. - [ ] **SETUP-02**: The wizard **validates each input before completing** — DB connectivity test, VAPID private key decodes to 32 bytes and pairs with the public key, OIDC discovery resolves, and the Fastmail app password reaches CalDAV (PROPFIND). - [ ] **SETUP-03**: The wizard generates secrets (session secret, encryption key, VAPID keypair) for the operator to copy into env; secrets are **never written to the database or returned in a response body**. - [ ] **SETUP-04**: Once setup is complete, the setup endpoints are no longer accessible (guard checked on every invocation, not only at startup). ### CI — Gitea continuous integration - [x] **CI-01**: Every pull request targeting `main` runs full regression — lint, typecheck (both apps), unit tests, API integration tests against a MariaDB service container, **and the Phase 7 mobile Playwright harness as a UI-regression step (CI brings up the API + PWA dev servers + MariaDB service container with `DEV_AUTH_BYPASS` in the runner and runs the harness specs headlessly against the authed PWA)** — and the result gates the merge. - [x] **CI-02**: On merge to `main`, the API Docker image is built and published to the Gitea container registry. ### Test — Mobile-emulated authed browser harness - [x] **TEST-01**: The assistant can drive the PWA in a **mobile-emulated viewport** (device profile + mobile UA + touch) for automated UI/layout verification. - [x] **TEST-02**: Automated runs reach the **authenticated** PWA via the existing `DEV_AUTH_BYPASS` on the host-side dev stack (no manual login, no Authelia/OIDC mocking). Targets the dev build; real prod-service-worker mobile testing is out of scope (see below). The harness specs are also consumed by Phase 8 (Gitea CI) as the PR UI-regression step. ## Future Requirements (deferred, not in v1.1) - **Multiple reminders per event** (2× VALARM) — stretch; deferred to v1.2. - **Self-service provider onboarding** (backlog 999.5) — a member adds their *own* Fastmail app password on first login. v1.1 covers this admin-managed (ADMIN-01); self-service deferred. - **Calendar provider abstraction** (backlog 999.1) — provider interface so Fastmail is one of several backends. - **Android PWA install walkthrough** verified on a real device (carried from v1.0). - **Wizard re-run / reconfigure** flow after first setup. ## Out of Scope (explicit exclusions) - **Notification-preferences UI, reminder snooze** — over-build for a 2-member household; the per-event selector (CAL-13) is sufficient. - **Admin audit log, health dashboard, user-management/CRUD** — scope creep for a tiny self-hosted app. - **Real-device iOS push / standalone CI** — remains a human/device gate, as in v1.0; the mobile harness covers responsive layout + authed flows only, not iOS-Safari-standalone behavior. - **Mobile testing against the prod service-worker build past real Authelia** — `DEV_AUTH_BYPASS` only reaches the dev build (no real SW). A reusable Authelia storage-state to drive the prod-SW PWA is deferred; not worth the complexity for v1.1's layout/flow goal. - **Redis pub/sub for the outbox drain** — the drain is single-process by design; an in-process EventEmitter is correct. (Redis stays for list SSE.) - **node-cron** — silently skips ticks in the long-lived process; schedulers stay on `setInterval`. - **drizzle-kit push** — emits a false destructive diff on populated MariaDB; migrations use generate+migrate. ## Traceability Maps each REQ-ID to its phase. v1.1 phases continue v1.0 numbering (v1.0 ended at Phase 6) → v1.1 starts at Phase 7. **Coverage: 17/17 v1.1 requirements mapped, no orphans, no duplicates.** | REQ-ID | Phase | Status | |--------|-------|--------| | TEST-01 | Phase 7 (Mobile Test Harness) | Complete | | TEST-02 | Phase 7 (Mobile Test Harness) | Complete | | CI-01 | Phase 8 (Gitea CI) | Complete | | CI-02 | Phase 8 (Gitea CI) | Complete | | CAL-15 | Phase 9 (Faster Write-Back) | Pending | | ADMIN-01 | Phase 10 (Admin Role & Settings) | Pending | | ADMIN-02 | Phase 10 (Admin Role & Settings) | Pending | | ADMIN-03 | Phase 10 (Admin Role & Settings) | Pending | | CAL-13 | Phase 11 (Per-Event Reminders) | Pending | | CAL-14 | Phase 11 (Per-Event Reminders) | Pending | | NOTIF-04 | Phase 11 (Per-Event Reminders) | Pending | | NOTIF-05 | Phase 11 (Per-Event Reminders) | Pending | | NOTIF-06 | Phase 11 (Per-Event Reminders) | Pending | | SETUP-01 | Phase 12 (Initial Setup Wizard) | Pending | | SETUP-02 | Phase 12 (Initial Setup Wizard) | Pending | | SETUP-03 | Phase 12 (Initial Setup Wizard) | Pending | | SETUP-04 | Phase 12 (Initial Setup Wizard) | Pending | **DB foundation note:** The v1.1 schema migration (`users.is_admin`, `calendar_events.reminder_lead_minutes`, `app_config` table) is not a standalone requirement — it is carried by **Phase 10 (Admin Role & Settings)** (which owns is_admin + app_config) and consumed by **Phase 11 (Per-Event Reminders)** (reminder_lead_minutes) and **Phase 12 (Initial Setup Wizard)** (app_config.setup_complete). Folded per ARCHITECTURE.md ordering rather than created as a migration-only phase. This makes Phase 10 the head of the admin chain (10 → 11, 10 → 12).