Plans 08-03 (harness UI-regression job) and 08-04 (publish job) are not yet built; CI-01's definition includes the harness step (08-03). Revert phase 8 and CI-01 from complete back to in-progress.
8.2 KiB
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_KEYpath) — 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
- CI-01: Every pull request targeting
mainruns 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 withDEV_AUTH_BYPASSin the runner and runs the harness specs headlessly against the authed PWA) — and the result gates the merge. - 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
- TEST-01: The assistant can drive the PWA in a mobile-emulated viewport (device profile + mobile UA + touch) for automated UI/layout verification.
- TEST-02: Automated runs reach the authenticated PWA via the existing
DEV_AUTH_BYPASSon 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_BYPASSonly 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) | In progress |
| CI-02 | Phase 8 (Gitea CI) | Pending |
| 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).