resetType('duration') + setValue(ICAL.Duration) to prevent VALUE=TEXT on TRIGGER
resetType('date-time') + setValue(ICAL.Time) for absolute DATE-TIME VALARM trigger
Intl.DateTimeFormat-based UTC offset probe at 9 AM (not midnight) for DST-correct computation
ICAL.parse try/catch safe-default pattern for T-11-01 tamper mitigation
created
modified
apps/api/src/broker/vevent.ts
apps/api/tests/broker/vevent.test.ts
D-VALARM-PROBE: computeAlertInstantUtc probes UTC offset at naive-9AM-UTC (not midnight) so DST transitions before 9 AM (spring-forward at 2 AM) use the post-transition offset — single-pass Intl computation, no iteration needed
D-ICAL-INSTANCEOF: classifyValarms uses instanceof ICAL.Time (not getParameter('value')) to distinguish absolute vs relative TRIGGER — more robust per A3/A4 assumption log since getParameter returns undefined for default-type DURATION triggers
duration_minutes
completed_date
tasks_completed
files_modified
7
2026-06-14
3
2
Phase 11 Plan 01: VALARM Serialization + Classification Layer Summary
VALARM pure-function layer: five exported units covering timed DURATION trigger, all-day absolute DATE-TIME trigger, none/preset/offlist/custom classification, component extraction for preserve-on-edit, and DST-correct 9 AM-local→UTC computation — all TDD RED-first, 37/37 tests green.
Tasks Completed
Task
Description
Commit
RED
Failing tests for all 5 units + buildVeventString VALARM emission
Set([0,5,10,15,30,60,120,1440,2880,10080]) — D-01/D-02 preset list
Interface Extensions
NewEventParams in vevent.ts (after existing dtstamp field):
reminderLeadMinutes?: number|null;// null = no VALARM; 0 = same-day all-day; positive = timed lead
valarms?: ICAL.Component[];// pre-parsed preserve-on-edit components (D-08/CAL-14)
allDayAlertInstantUtc?: Date;// 9 AM local on alert day in UTC (for buildAllDayValarm)
Key TRIGGER Assertions in Tests
buildTimedValarm(30) → ICS contains TRIGGER:-PT30M, does NOT contain VALUE=TEXT
buildTimedValarm(120) → ICS matches /TRIGGER:-P(?:T2H|T120M)/, no VALUE=TEXT
2. [Rule 1 - Bug] DST probe at midnight vs at 9 AM
Found during: Task 3 GREEN — spring-forward (2026-03-08) and fall-back (2026-11-01) tests failed
Issue: Original getUtcOffsetMsForDate computed the UTC offset at alertDate midnight. On spring-forward day (DST change at 2 AM), midnight is still in EST (UTC-5), but 9 AM is in EDT (UTC-4). Using the midnight offset gave the wrong UTC instant.
Fix: Replaced the midnight-based offset computation with a probe at alertDateUtcMs + 9h (naive 9 AM UTC), then asked Intl what local date+time that corresponds to and computed the adjustment. The probe is inherently near 9 AM so it captures the post-transition offset on DST days.
None. All five units are fully implemented and tested. No placeholder values or TODO markers in the produced code.
Threat Flags
None. This plan adds no new network endpoints, auth paths, or schema changes. The only external-input path (classifyValarms/extractValarms parsing stored rawVevent) is wrapped in try/catch per T-11-01 mitigation, matching the existing extractRruleString idiom.