100 lines
6.0 KiB
Markdown
100 lines
6.0 KiB
Markdown
---
|
|
phase: 3
|
|
slug: event-write-back-pwa-install
|
|
status: draft
|
|
nyquist_compliant: true
|
|
wave_0_complete: false
|
|
created: 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
|
|
|
|
- [x] All tasks have `<automated>` verify or a Wave 0 RED dependency (created in 03-01 Task 4)
|
|
- [x] Sampling continuity: no 3 consecutive tasks without automated verify
|
|
- [x] Wave 0 covers all MISSING references (five RED files in 03-01)
|
|
- [x] No watch-mode flags
|
|
- [x] Feedback latency < 40s
|
|
- [x] `nyquist_compliant: true` set in frontmatter
|
|
|
|
**Approval:** approved 2026-06-05
|