fix(11-05): WR-01 positive-duration TRIGGER classifies as custom (no Math.abs)

classifyValarms: check sign of dur.toSeconds() before preset lookup.
Positive value = alarm fires after event (RFC 5545 TRIGGER:+PT15M or
TRIGGER;RELATED=END:PTNm) → return {kind:'custom'} for preserve path.
Compute leadMinutes as -seconds/60 (was Math.abs) for negative triggers.

Prevents alarm direction inversion: +PT15M was being stored as 15-min-before
lead and re-fired at dtstartUtc-15min — the opposite of the original intent.
This commit is contained in:
Lucas Berger
2026-06-14 08:15:31 -04:00
parent d18aba7816
commit bc605e6a42
+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 };