Files
familysync/.planning/milestones/v1.0-phases/03-event-write-back-pwa-install/03-VALIDATION.md
T
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

8.1 KiB

phase, slug, status, nyquist_compliant, wave_0_complete, created
phase slug status nyquist_compliant wave_0_complete created
3 event-write-back-pwa-install draft true false 2026-06-05

Phase 3 — Validation Strategy

Per-phase validation contract for feedback sampling during execution. Sourced from 03-RESEARCH.md §Validation Architecture.


Test Infrastructure

Property Value
Framework (API) Vitest 4.x, environment: node
Framework (PWA) Vitest 4.x + jsdom + @testing-library/react
Config (API) apps/api/vitest.config.ts
Config (PWA) apps/pwa/vitest.config.ts
Quick run (API) pnpm --filter @familysync/api test
Quick run (PWA) pnpm --filter @familysync/pwa test
Full suite pnpm test (from repo root — runs both apps)
Estimated runtime ~20-40 seconds (mocked DB + CalDAV; no network)

Sampling Rate

  • After every task commit: Run the filtered quick command for the app touched (pnpm --filter @familysync/api test -- <path> or pnpm --filter @familysync/pwa test -- <name>).
  • After every plan wave: Run pnpm test (full suite, both apps).
  • Before /gsd-verify-work: Full suite must be green.
  • Max feedback latency: ~40 seconds (full suite).

Per-Task Verification Map

Req ID Behavior Plan Wave Test Type Automated Command File Exists Status
CAL-04 buildVeventString → VCALENDAR for a timed event (DTSTART UTC) 02 2 unit pnpm --filter @familysync/api test -- broker/vevent W0 (03-01) pending
CAL-04 buildVeventString → all-day event uses DATE not DATETIME (D-13) 02 2 unit same W0 (03-01) pending
CAL-04 POST /api/events/create → 202 + inserts pending outbox row 03 2 unit (mocked DB) pnpm --filter @familysync/api test -- routes/events W0 (03-01) pending
CAL-05 PATCH /api/events/:uid/edit → 202 + inserts row with etag 03 2 unit same W0 (03-01) pending
CAL-06 DELETE /api/events/:uid → 202 + inserts delete row 03 2 unit same W0 (03-01) pending
CAL-07 buildVeventString with rruleString → RRULE property 02 2 unit pnpm --filter @familysync/api test -- broker/vevent W0 (03-01) pending
CAL-04/05/06 Outbox worker: pending→done (204), pending→failed (412), pending→backoff (500), pending→dead (max attempts) 02/04 2 unit pnpm --filter @familysync/api test -- broker/outboxWorker W0 (03-01) pending
CAL-04/05/06 GET /api/events/sync-status returns outbox status (D-09) 03 2 unit pnpm --filter @familysync/api test -- routes/events W0 (03-01) pending
CAL-04/05/07 GET /api/events/writable-calendars returns D-03 set; never another member's read-only personal (V4) 03 2 unit pnpm --filter @familysync/api test -- routes/events W0 (03-01) pending
D-08 412 → conflict (not retry), mark failed, trigger re-sync 04 2 unit pnpm --filter @familysync/api test -- broker/outboxWorker W0 (03-01) pending
D-04 Edit-as-move emits DELETE+CREATE pair; create runs first 04 2 unit same W0 (03-01) pending
D-03/V4 create rejects write to non-owned/non-shared calendar (403) 03 2 unit pnpm --filter @familysync/api test -- routes/events W0 (03-01) pending
PWA-01 vite.config.ts produces valid manifest.webmanifest with required fields 06 3 smoke (build output) pnpm --filter @familysync/pwa build + manifest field check W0 (03-01) pending
PWA-02 isIOSSafariNonStandalone() true on mock iOS Safari non-standalone UA 06 3 unit pnpm --filter @familysync/pwa test -- InstallPrompt W0 (03-01) pending
PWA-02 useAndroidInstallPrompt sets canInstall=true on beforeinstallprompt 06 3 unit (mock event) same W0 (03-01) pending

Status: pending · green · red · ⚠️ flaky


Wave 0 Requirements

The Wave 0 RED test scaffold is created in plan 03-01 Task 4 (these files import not-yet-existing modules so they fail RED until later waves implement them):

  • apps/api/tests/broker/vevent.test.ts — CAL-04, CAL-07 (VEVENT builder, DATE/DATETIME split, RRULE)
  • apps/api/tests/broker/write.test.ts — tsdav call shapes, etag/If-Match, response interpretation
  • apps/api/tests/broker/outboxWorker.test.ts — outbox state machine (done/failed/backoff/dead), edit-as-move ordering (D-04/D-07/D-08)
  • apps/api/tests/routes/events.test.ts — EXTEND existing: POST /create, PATCH /edit, DELETE /:uid, GET /sync-status, GET /writable-calendars, 403 ownership (preserve existing GET /api/events block)
  • apps/pwa/src/components/InstallPrompt.test.tsx — iOS detection, Android beforeinstallprompt capture (PWA-02)

Existing test files (broker/sync, routes/events GET block, auth/devBypass) remain in place.


Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
SW navigateFallbackDenylist excludes /callback PWA-01 Requires a real production build + SW registration over HTTPS Verify against prod build; confirm /callback not intercepted by SW
iOS standalone PWA login completes without leaving standalone Gate 2 Requires a physical iPhone, installed PWA, Authelia OIDC round-trip Follow docs/deployment.md Gate 2 checklist (Plan 07)

Validation Sign-Off

  • All tasks have <automated> verify or a Wave 0 RED dependency (created in 03-01 Task 4)
  • Sampling continuity: no 3 consecutive tasks without automated verify
  • Wave 0 covers all MISSING references (five RED files in 03-01)
  • No watch-mode flags
  • Feedback latency < 40s
  • nyquist_compliant: true set in frontmatter

Approval: approved 2026-06-05