Files
familysync/.planning/REQUIREMENTS.md
T
Lucas BergerandClaude Opus 4.8 ff06a8479b docs(phase-11): complete phase execution (5/5 plans, CAL-13/14 + NOTIF-04/05/06)
One deferred human check: live Fastmail VALARM round-trip + push (untestable in dev, backlog 999.19).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 08:31:31 -04:00

8.3 KiB
Raw Blame History

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

  • 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.
  • 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_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 AutheliaDEV_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) Complete
ADMIN-01 Phase 10 (Admin Role & Settings) Complete
ADMIN-02 Phase 10 (Admin Role & Settings) Complete
ADMIN-03 Phase 10 (Admin Role & Settings) Complete
CAL-13 Phase 11 (Per-Event Reminders) Complete
CAL-14 Phase 11 (Per-Event Reminders) Complete
NOTIF-04 Phase 11 (Per-Event Reminders) Complete
NOTIF-05 Phase 11 (Per-Event Reminders) Complete
NOTIF-06 Phase 11 (Per-Event Reminders) Complete
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).