Files
familysync/.planning/milestones/v1.0-phases/02-calendar-display/02-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

13 KiB
Raw Blame History

phase, slug, status, nyquist_compliant, wave_0_complete, created
phase slug status nyquist_compliant wave_0_complete created
02 calendar-display ready true false 2026-06-04

Phase 02 — Validation Strategy

Per-phase validation contract for feedback sampling during execution.


Test Infrastructure

Property Value
Framework vitest
Config (API) apps/api/vitest.config.ts (environment: node — exists, Phase 1)
Config (PWA) apps/pwa/vitest.config.ts (environment: jsdom — created in Plan 01 Task 1)
Quick run command (API) cd apps/api && pnpm test -- <test-file>
Quick run command (PWA) cd apps/pwa && pnpm test -- <test-file>
Full suite command pnpm -r test (runs both workspaces)
Type gate pnpm exec tsc --noEmit per workspace
Estimated runtime ~25 seconds full suite (no live network; broker mocked)

PWA harness (vitest + jsdom + @testing-library/react + @testing-library/jest-dom) is installed in Plan 01 Task 1 — until that task completes, all PWA test rows are blocked on the harness (❌ W0).


Sampling Rate

  • After every task commit: Run the task's quick run command (the <automated> in that task)
  • After every plan wave: Run pnpm -r test
  • Before /gsd-verify-work: pnpm -r test green + tsc --noEmit clean in both workspaces
  • Max feedback latency: 30 seconds

Per-Task Verification Map

Task ID Plan Wave Requirement Threat Ref Secure Behavior Test Type Automated Command File Exists Status
02-01-01 01 1 CAL-07 T-02-SC schema + RED stubs + fixtures (no behavior yet) unit/grep grep -q has_rrule + ICAL.parse fixtures (3 grep/node checks) creates RED stubs pending
02-01-02 01 1 CAL-02 T-02-01 dev-bypass hard-disabled when NODE_ENV=production unit cd apps/api && pnpm test -- tests/auth/devBypass.test.ts W0 (this task creates it) pending
02-01-03 01 1 CAL-07 T-02-02 live MariaDB has has_rrule + is_shared (no false-green) integration node SHOW COLUMNS calendar_events/calendars N/A (DB assertion) pending
02-02-01 02 2 CAL-07 T-02b-04 DST wall-clock preserved; all-day no shift; EXDATE excluded unit cd apps/api && pnpm test -- tests/broker/expand.test.ts RED stub from 02-01 pending
02-02-02 02 2 CAL-02, CAL-07 T-02b-01/02 start/end zod-validated before SQL; 90-day cap; color+isShared join unit cd apps/api && pnpm test -- tests/routes/events.test.ts RED stub from 02-01 pending
02-02-03 02 2 CAL-02 operator marks shared calendar (is_shared=1) manual human-verify checkpoint (see Manual-Only) N/A pending
02-03-01 03 2 CAL-02 T-02c-SC token layer + Schedule-X var overrides; Temporal-first import grep/node grep --color-shared-family + node require deps N/A (style/deps) pending
02-03-02 03 2 CAL-02 firstDayOfWeek 0→7; per-member + 'shared' config unit cd apps/pwa && pnpm test -- src/lib/colorUtils.test.ts src/lib/calendarConfig.test.ts RED stub (calendarConfig) from 02-01 pending
02-03-03 03 2 CAL-07 T-02c-02 all-day→PlainDate guard; calendarId routed by isShared/ownerUserId unit cd apps/pwa && pnpm test -- src/lib/hydrateEvents.test.ts RED stub from 02-01 pending
02-04-01 04 3 CAL-02, CAL-03 T-02d-01 Schedule-X renders real windowed occurrences; 4 views; token-only grep/type grep ScheduleXCalendar/hydrateEvents + tsc --noEmit N/A (wired in 04) pending
02-04-02 04 3 CAL-03 T-02d-01 render smoke: 4 views + timed+all-day through hydrate→eventsService unit cd apps/pwa && pnpm test -- src/components/CalendarShell.test.tsx W0 (this task creates it) pending
02-05-01 05 4 CAL-03 T-02e-01 popover renders fields as text (no dangerouslySetInnerHTML); Escape closes unit cd apps/pwa && pnpm test -- src/components/EventDetailPopover.test.tsx W0 (this task creates it) pending
02-05-02 05 4 CAL-02, CAL-03 T-02e-02 legend/nav/toolbar + skeleton/empty/error; EventProof removed grep/type grep SkeletonCalendar/EmptyState + tsc --noEmit N/A (wired in 05) pending
02-05-03 05 4 CAL-02, CAL-03, CAL-07 visual + functional verification of 4 success criteria manual human-verify checkpoint (see Manual-Only) N/A pending

Status: pending · green · red · ⚠️ flaky

Tasks without a unit-test <automated> (02-01-01 grep/fixture, 02-01-03 DB, 02-03-01 grep/deps, 02-04-01 grep/type, 02-05-02 grep/type) each carry an automated grep/node/tsc check, and none of them appear in 3-consecutive sequence without a unit test between them: the expand/events/colorUtils/hydrateEvents/popover/smoke unit tests interleave every wave.


Wave 0 Requirements

Wave 0 = Plan 01 Task 1, which creates the failing-but-present test stubs and fixtures the later waves turn green:

  • apps/api/tests/broker/expand.test.ts — RED stub for expandOccurrences (CAL-07). Contract: weekly America/New_York RRULE across the March 2026 DST boundary keeps 10:00 local wall-clock on both sides; all-day birthday → allDay:true + 'YYYY-MM-DD' start; EXDATE-excluded occurrence absent. Fails at import (../../src/broker/expand.js not yet built).
  • apps/api/tests/routes/events.test.ts — RED stub for windowed /api/events (CAL-02). Contract: occurrences carry color + isShared; bad/oversized params → 400.
  • apps/pwa/src/lib/hydrateEvents.test.ts — RED stub (CAL-07). Contract: all-day→Temporal.PlainDate; timed→Temporal.ZonedDateTime; Schedule-X calendarId = occ.isShared ? 'shared' : String(occ.ownerUserId) (shared occurrence → 'shared'; personal occurrence → String(ownerUserId)).
  • apps/pwa/src/lib/calendarConfig.test.ts — RED stub (CAL-02). Contract: WEEK_START_DAY=0 → Schedule-X firstDayOfWeek=7.
  • apps/api/tests/fixtures/{weekly-dst,allday-birthday,exdate-series}.ics — fixture corpus; weekly-dst.ics carries a full VTIMEZONE (STANDARD + DAYLIGHT) for the DST assertion.
  • apps/api/tests/auth/devBypass.test.ts — created and made green within Plan 01 Task 2 (not a cross-wave RED stub).
  • apps/pwa/vitest.config.ts + PWA test deps (vitest, @testing-library/react, @testing-library/jest-dom, jsdom) — installed in Plan 01 Task 1; without this the PWA RED stubs cannot run.
  • apps/pwa/src/components/CalendarShell.test.tsx and apps/pwa/src/components/EventDetailPopover.test.tsx — created in-wave by Plan 04/05 (not Wave 0 stubs; depend on components built in the same plan).

Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
Designate the shared-family calendar (calendars.is_shared=1) CAL-02 Which calendar is "shared-family" cannot be derived deterministically from data (broker exposes "Calendar" + "USA Holidays"; members' calendars arrive under their own credential) — operator must designate it. A different identification rule (e.g. displayName-pattern matching) is a plan revision, not a resume-from-checkpoint. Plan 02 Task 3: list calendars, UPDATE calendars SET is_shared=1 WHERE id=<chosen>, re-list, curl /api/events confirms isShared:true + color #F25C7A on marked rows.
DST boundary correctness in the rendered UI CAL-07 Automated expand.test.ts asserts the wall-clock contract, but visual confirmation that Schedule-X paints the occurrence at the right hour across March 2026 requires a human eye on the grid. Plan 05 Task 3 step 4: find a recurring event, navigate across the March 2026 DST boundary, confirm time does not jump ±1 hour.
All-day event renders as a full-day banner with no date shift CAL-07 PlainDate guard is unit-tested, but the actual Schedule-X all-day banner placement (correct date, no off-by-one) is a render-path visual check. Plan 05 Task 3 step 5: find an all-day event (birthday/holiday), confirm it appears as a full-day banner on the correct date, not a day early/late.
Color-coded ownership legible at a glance + legend decode CAL-02 "Reads at a glance" is a subjective slick-constraint judgment. Plan 05 Task 3 steps 23: confirm each member's events render in their color, shared in rose, legend decodes ownership.
Phone bottom-sheet popover + agenda default view CAL-03 Responsive breakpoint behavior (D-05) needs a real phone-width render. Plan 05 Task 3 step 7: resize to phone width, confirm default view is Agenda and popover is a bottom sheet.

Validation Sign-Off

  • All tasks have <automated> verify or Wave 0 dependencies
  • Sampling continuity: no 3 consecutive tasks without automated verify
  • Wave 0 covers all MISSING references (expand, events, hydrateEvents, calendarConfig stubs + PWA harness)
  • No watch-mode flags (vitest run / pnpm test -- <file>, never --watch)
  • Feedback latency < 30s
  • nyquist_compliant: true set in frontmatter

Approval: approved 2026-06-04