docs(03): finalize phase plan (8 plans, verified)

This commit is contained in:
Lucas Berger
2026-06-05 17:08:16 -04:00
parent 9dd08d28d1
commit 93302cf942
7 changed files with 696 additions and 57 deletions
@@ -75,7 +75,7 @@ five RED test files.
New symbols introduced across Phase 3 (excluded from drift verification):
- DB: `calendarOutbox` table (`calendar_outbox`), `calendarEvents.objectUrl` column (`object_url`)
- Backend files: `apps/api/src/broker/vevent.ts` (`buildVeventString`, `NewEventParams`), `apps/api/src/broker/write.ts` (`createCalendarEvent`, `updateCalendarEvent`, `deleteCalendarEvent`), `apps/api/src/broker/outboxWorker.ts` (`runOutboxDrain`, `startOutboxWorker`, `RRULE_PRESETS`)
- Backend routes: `POST /api/events/create`, `PATCH /api/events/:uid/edit`, `DELETE /api/events/:uid`, `GET /api/events/sync-status`
- Backend routes: `POST /api/events/create`, `PATCH /api/events/:uid/edit`, `DELETE /api/events/:uid`, `GET /api/events/sync-status`, `GET /api/events/writable-calendars`
- Frontend files: `apps/pwa/src/components/EventForm.tsx`, `apps/pwa/src/components/InstallPrompt.tsx`, `apps/pwa/src/components/SyncStateToast.tsx`, `apps/pwa/src/components/DeleteConfirmationDialog.tsx`
- Frontend client fns: `createEvent`, `updateEvent`, `deleteEvent`, `fetchSyncStatus`, `fetchWritableCalendars`
- Zustand keys: `eventFormOpen`, `eventFormMode`, `eventFormUid`, `deleteDialogOpen`, `deleteDialogUid`, `lastSyncedUid`
@@ -170,7 +170,7 @@ New symbols introduced across Phase 3 (excluded from drift verification):
- vevent.test.ts: buildVeventString produces VCALENDAR with VEVENT for a timed event (DTSTART with Z/UTC); for an all-day event a DATE value (no time component, no TZID) per D-13; with rruleString produces an RRULE property (CAL-04, CAL-07).
- write.test.ts: createCalendarEvent calls client.createCalendarObject with `${uid}.ics` filename; updateCalendarEvent passes etag into the calendarObject (If-Match); deleteCalendarEvent passes etag; each returns the raw Response (mock client).
- outboxWorker.test.ts: runOutboxDrain transitions pending→done on mock 204; pending→failed on mock 412 (and triggers re-sync, no retry); pending→backoff (nextAttemptAt advanced, attemptCount++) on mock 500; pending→dead at MAX_ATTEMPTS; edit-as-move emits a create row processed BEFORE the linked delete row (D-04/D-07/D-08).
- events.test.ts (extend existing): POST /api/events/create returns 202 + inserts a pending outbox row; PATCH /api/events/:uid/edit returns 202 + inserts row with etag; DELETE /api/events/:uid returns 202 + inserts delete row; GET /api/events/sync-status?uid= returns the outbox status; create rejects writing to a calendar not owned by the user with 403 (D-03 / V4 access control).
- events.test.ts (extend existing): POST /api/events/create returns 202 + inserts a pending outbox row; PATCH /api/events/:uid/edit returns 202 + inserts row with etag; DELETE /api/events/:uid returns 202 + inserts delete row; GET /api/events/sync-status?uid= returns the outbox status; GET /api/events/writable-calendars returns the member's writable set (own personal + shared `isShared=1`) and NEVER another member's read-only personal calendar (different userId, isShared=false) — D-03 / V4; create rejects writing to a calendar not owned by the user with 403 (D-03 / V4 access control).
- InstallPrompt.test.tsx: isIOSSafariNonStandalone() returns true for a mock iOS Safari non-standalone UA and false in standalone; useAndroidInstallPrompt sets canInstall=true when a mock beforeinstallprompt event dispatches.
</behavior>
<action>
@@ -182,6 +182,7 @@ New symbols introduced across Phase 3 (excluded from drift verification):
<acceptance_criteria>
- All five test files exist.
- `pnpm --filter @familysync/api test -- broker/vevent` reports failures or unresolved imports (RED — implementation not present).
- The events.test.ts scaffold includes a `writable-calendars` describe block (`grep -c "writable-calendars" apps/api/tests/routes/events.test.ts` ≥1).
- The existing GET /api/events describe block is still present in events.test.ts (`grep -c "GET /api/events" apps/api/tests/routes/events.test.ts` ≥1).
</acceptance_criteria>
<done>Five RED test files exist and fail because their target modules are unimplemented; existing tests preserved.</done>