Phase 11: Per-Event Reminders (CAL-13/14, NOTIF-04/05/06) #19

Merged
luckberg merged 41 commits from gsd/phase-11-per-event-reminders into main 2026-06-14 14:06:45 -04:00
Showing only changes of commit bc605e6a42 - Show all commits
+9 -2
View File
@@ -181,11 +181,18 @@ export function classifyValarms(rawVevent: string): AlarmClassification {
const firstValue = triggerProp.getFirstValue() as unknown; const firstValue = triggerProp.getFirstValue() as unknown;
if (firstValue instanceof ICAL.Time) return { kind: 'custom' }; if (firstValue instanceof ICAL.Time) return { kind: 'custom' };
// Relative DURATION trigger — extract lead minutes // Relative DURATION trigger — extract lead minutes.
const dur = firstValue as ICAL.Duration; const dur = firstValue as ICAL.Duration;
if (!dur || typeof dur.toSeconds !== 'function') return { kind: 'custom' }; if (!dur || typeof dur.toSeconds !== 'function') return { kind: 'custom' };
const leadMinutes = Math.round(Math.abs(dur.toSeconds()) / 60); const seconds = dur.toSeconds();
// WR-01 (Phase 11 Plan 05): positive seconds = alarm fires AFTER the event start
// (RFC 5545 TRIGGER:+PT15M or TRIGGER;RELATED=END:PT15M). This is a post-event alarm
// and cannot be expressed as a before-event lead. Classify as custom so the preserve
// path keeps the original VALARM rather than inverting the alarm direction.
if (seconds > 0) return { kind: 'custom' };
const leadMinutes = Math.round(-seconds / 60);
return PRESET_MINUTES.has(leadMinutes) return PRESET_MINUTES.has(leadMinutes)
? { kind: 'preset', leadMinutes } ? { kind: 'preset', leadMinutes }
: { kind: 'offlist', leadMinutes }; : { kind: 'offlist', leadMinutes };