diff --git a/apps/api/src/broker/vevent.ts b/apps/api/src/broker/vevent.ts index 5d45a4f..77de856 100644 --- a/apps/api/src/broker/vevent.ts +++ b/apps/api/src/broker/vevent.ts @@ -181,11 +181,18 @@ export function classifyValarms(rawVevent: string): AlarmClassification { const firstValue = triggerProp.getFirstValue() as unknown; 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; 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) ? { kind: 'preset', leadMinutes } : { kind: 'offlist', leadMinutes };