Files
familysync/.planning/phases/11-per-event-reminders/11-VALIDATION.md
T

93 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 11
slug: per-event-reminders
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-06-13
---
# Phase 11 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution.
> Source: `11-RESEARCH.md` § Validation Architecture.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | Vitest (vite-native, same config as frontend) |
| **Config file** | `apps/api/vitest.config.ts` |
| **Quick run command** | `pnpm --filter @familysync/api exec vitest run tests/broker/vevent.test.ts tests/broker/reminderScheduler.test.ts` |
| **Full suite command** | `pnpm --filter @familysync/api exec vitest run` |
| **Estimated runtime** | ~1030 seconds (quick); full suite longer |
---
## Sampling Rate
- **After every task commit:** Run quick command (`vevent.test.ts` + `reminderScheduler.test.ts`)
- **After every plan wave:** Run full API suite
- **Before `/gsd-verify-work`:** Full API suite green + Playwright smoke (allDay toggle swaps presets; edit-mode picker loads correct value)
- **Max feedback latency:** ~30 seconds
---
## Per-Task Verification Map
> Per-task rows are populated during planning once plan/task IDs exist. The
> requirement → behavior → test-command coverage below is sourced from
> `11-RESEARCH.md` § "Phase Requirements → Test Map" and must each be claimed
> by at least one task's `<automated>` verify.
| Requirement | Behavior | Test Type | Automated Command | File Exists |
|-------------|----------|-----------|-------------------|-------------|
| CAL-13 | Timed VALARM emits DURATION trigger `-PTNmM` (no `VALUE=TEXT`) | unit | `vitest run tests/broker/vevent.test.ts` | ✅ extend |
| CAL-13 | All-day VALARM emits absolute DATE-TIME trigger | unit | `vitest run tests/broker/vevent.test.ts` | ✅ extend |
| CAL-13 | allDay toggle swaps preset list; resets to None on toggle | browser | `playwright-cli` | ❌ W0 |
| CAL-14 | No picker change on edit → rawVevent VALARM preserved in PUT | unit | `vitest run tests/broker/outboxWorker.test.ts` | ✅ extend |
| CAL-14 | Off-list single relative VALARM → `offlist` classification | unit | `vitest run tests/broker/vevent.test.ts` | ✅ extend |
| CAL-14 | Absolute trigger → `custom` classification | unit | `vitest run tests/broker/vevent.test.ts` | ✅ extend |
| CAL-14 | Two VALARMs → `custom` classification | unit | `vitest run tests/broker/vevent.test.ts` | ✅ extend |
| NOTIF-04 | Scheduler fires at T-leadMinutes (e.g. T-30 for 30-min lead) | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend |
| NOTIF-04 | Humanized body: 30→"30 min", 60→"1 hour", 1440→"1 day", 10080→"7 days" | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend |
| NOTIF-05 | `null` reminderLeadMinutes → no push | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend |
| NOTIF-05 | `0` lead + `allDay=false` → no push | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend |
| NOTIF-06 | All-day `0` lead fires 9 AM local (UTC computation correct) | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ❌ W0 |
| NOTIF-06 | Rescheduled event (new dtstart) re-fires — `uid:dtstartMs` dedup | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend |
| NOTIF-06 | Same event fires exactly once across 3 ticks — `uid:dtstartMs` dedup | unit | `vitest run tests/broker/reminderScheduler.test.ts` | ✅ extend (current test is uid-only) |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Wave 0 Requirements
- [ ] `tests/broker/vevent.test.ts` — extend: VALARM serialization (DURATION trigger type, all-day absolute DATE-TIME, preserve round-trip, classifier I/O none/preset/offlist/custom)
- [ ] `tests/broker/reminderScheduler.test.ts` — extend: variable-lead fire, all-day 9 AM UTC, `uid:dtstartMs` dedup re-fire on reschedule + once-across-ticks, NULL-vs-0 semantics, humanized body
- [ ] `computeAlertInstantUtc` — unit tests at DST boundaries (spring-forward and fall-back dates)
- [ ] Playwright smoke — EventForm create mode allDay-toggle preset swap; edit mode loads correct picker value
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| All-day VALARM trigger honored by Fastmail / Apple Calendar | CAL-13 (best-effort interop) | Requires live Fastmail + Apple Calendar accounts; not driveable headlessly | Create an all-day event with a "1 day before" reminder in FamilySync; open in Apple Calendar / Fastmail web and confirm an alarm shows (exact fire time is best-effort — scheduler is push ground truth) |
---
## 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
- [ ] No watch-mode flags
- [ ] Feedback latency < 30s
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending