# Reminders & Notifications Architecture

!!! abstract "State Contract"
    The notification system is a reactive consumer of the `EventCache`'s `time-tick` stream. It maintains zero persistent state of its own, relying entirely on the canonical event data, runtime deduplication, and centralized trigger evaluations.

---

## The Notification Pipeline

1.  **Subscription**: The `NotificationManager` subscribes to the high-frequency `time-tick` event from the `EventCache` (ticking every 60 seconds).
2.  **Lookahead Filtering**: On every tick, the manager filters the cache for events starting within the next 48 hours to minimize processing overhead.
3.  **Centralized Trigger Evaluation**:
    *   The manager calculates a trigger time for each occurrence using the public helper `getTriggerTime(occurrence: EnrichedOFCEvent)`.
    *   **Custom Priority**: If an event has a `notify` property in its metadata (e.g. `notify: 15`), the trigger point is computed as `start.minus({ minutes: event.notify.value })`.
    *   **Default Fallback**: If no custom value exists and global default reminders are enabled, the trigger point is computed as `start.minus({ minutes: defaultReminderMinutes })`.
4.  <span id="mutex-and-fcr-takeover"></span>**Mutex and FCR Takeover**:
    *   Before firing local OS notifications or launching the interactive snooze/dismiss modal, the manager checks if the [FCR Reminder Companion](fcr-reminder-architecture.md) is enabled (`fcrReminderCompanion.enabled`).
    *   If enabled, the native toast notification is bypassed (returning immediately), allowing the background daemon to handle alerting. For details on how the daemon takes over active alerting, see the [Alert Mutex (Obsidian Bypass)](fcr-reminder-architecture.md#alert-mutex-obsidian-bypass) specification.
5.  **Deduplication**: To prevent "notification storms" (especially during startup or timezone shifts), every locally triggered notification is keyed by `sessionId::type::triggerTime`. Once a key is added to the runtime `notifiedEvents` set, it cannot trigger again in the current session.

---

## Destructive Snooze Implementation

The decision to use a destructive snooze (modifying source data) rather than a runtime-only snooze was made to solve the **Multi-Device Conflict** problem.

### Rationale
If snooze was runtime-only, snoozing on a Desktop would not prevent a mobile device (or a Google Calendar notification) from firing at the original time. By modifying the source YAML or Google Event, the "snooze" state is synchronized across the entire ecosystem.

### Logic
*   **Time Shift**: For events without custom `notify` values, the `startTime` is incremented.
*   **Threshold Shift**: For events with `notify` values, the `notify` integer is decremented.

---

## Provider Alarm Contract

Full Calendar keeps two reminder concepts separate:

*   `notify` is the Obsidian/local reminder value consumed by `NotificationManager`.
*   `alarms` is provider-owned reminder data that syncs to remote calendar services.

Providers that can persist provider alarms set `CalendarProviderCapabilities.supportsAlarms`. The edit modal reads that capability and shows the provider reminder control only for those calendars.

Provider mappings:

*   CalDAV serializes `alarms` as ICS `VALARM` components.
*   Google serializes `alarms` as popup reminder overrides.
*   Outlook serializes `alarms` as reminder minutes.

Recurring instance overrides inherit `notify` and `alarms` from the master event unless the override explicitly supplies its own values. For CalDAV, override alarm edits are written by replacing the matching recurrence override component in the same `.ics` resource as the recurring master.

---

## Startup Safety (Recency Cutoff)

The manager implements a **5-minute recency cutoff**. If a reminder's trigger point was more than 5 minutes in the past (e.g., you open Obsidian at 14:05 for a 14:00 event with a 10-minute reminder), the notification is suppressed. This prevents a "spam" of missed notifications when starting the app after a long break.

---

[FCR Reminder Companion Architecture](fcr-reminder-architecture.md) · [Event Cache](../../system/eventcache.md) · [Timezone Architecture](../../system/features/timezone-architecture.md) · [API Architecture](../../system/api-architecture.md)
