Skip to content

Alert Inventory and Mapping

Last updated: 2026-04-02 09:30 EDT Status: G1 baseline + G2 pump/cgm mapping + non-CGM background local notification routing + in-app Alert Center + persisted alert lifecycle hooks + app-derived CGM urgent-low review alert Owner: BionicLoop engineering

Purpose

This document is the G1 baseline inventory and normalization map for alerts across:

  • OmniBLE pump integration
  • G7SensorKit CGM integration
  • Runtime/algorithm safety policy
  • App-level safety and workflow policy

It defines what alert sources exist today and how they should be normalized for consistent UI, escalation, and verification.

Current Implementation Baseline

  • Runtime now includes a normalized alert domain model across BionicLoop/Runtime/AppAlertSupport.swift and BionicLoop/Runtime/AppAlertDomainSupport.swift, with deterministic prioritization in AppAlertPrioritizer.
  • For a trigger-to-presentation review of the implemented alert path, use Alert Flow Review.
  • AppAlertCenter is implemented and active for pump signal-loss alerting:
  • de-duplication key: pump:ALERT-PUMP-SIGNAL-LOSS
  • debounce: 5 minutes
  • clear behavior: auto-clear when signal-loss condition clears
  • Real Omni pump alerts are now normalized into AppAlertCenter through LoopKit alert callbacks in AppPumpManagerDelegate.issueAlert / retractAlert:
  • lowReservoir -> ALERT-PUMP-LOW-RESERVOIR
  • userPodExpiration -> ALERT-PUMP-EXPIRING
  • podExpiring, podExpireImminent -> ALERT-PUMP-EXPIRED
  • finishSetupReminder -> ALERT-PUMP-SETUP-INCOMPLETE
  • suspendInProgress -> ALERT-PUMP-SUSPEND-IN-PROGRESS
  • suspendEnded -> ALERT-PUMP-SUSPEND-ENDED
  • timeOffsetChangeDetected -> ALERT-PUMP-TIME-OFFSET-DETECTED
  • pod alarm/fault notifications -> ALERT-PUMP-FAULT
  • incompatible pod text/signals -> ALERT-PUMP-INCOMPATIBLE
  • ALERT-PUMP-EXPIRING parses relative time text into countdown metadata (deadline) when available.
  • ALERT-PUMP-EXPIRED maps to post-expiration delivery-stop countdown metadata (deadline at service stop) with distinct message/action wording.
  • State-driven synthesis from pump expiresAt on status refresh/startup now emits:
  • ALERT-PUMP-EXPIRING when expiresAt is within 1 hour and still in future.
  • ALERT-PUMP-EXPIRED when expiresAt has passed and pod still has remaining service time after nominal expiration.
  • This avoids relaunch misses and prevents expired states being shown as long-horizon expiring reminders.
  • Real G7 state-driven alerts are now normalized into AppAlertCenter through AppCGMManagerDelegate:
  • unreliable/non-OK AlgorithmState (for example warmup, temporary issue) -> ALERT-CGM-UNAVAILABLE
  • failed/expired lifecycle/state -> ALERT-CGM-FAILED-OR-EXPIRED
  • trustworthy live glucose <55 mg/dL -> ALERT-CGM-URGENT-LOW
  • state-driven mapping is authoritative for CGM safety states; LoopKit issueAlert fallback mapping is retained for lifecycle compatibility and persistence plumbing, but does not escalate failed/expired from message-only keyword text.
  • urgent-low review is app-derived from live G7 data for clinician review/telemetry, not Dexcom-app acknowledgement.
  • Runtime-derived algorithm stepping interruption alerting is now implemented:
  • armed loop + no successful algorithm step for 15 minutes -> ALERT-ALGORITHM-STEPPING-INTERRUPTED
  • interruption deadline is measured from lastSuccessfulRunAt, or from session start / first-step wait state before the first successful step exists
  • each successful step refreshes the deadline and replaces any pending interruption notification
  • interruption alerting clears when successful stepping resumes or when the loop is disarmed/reset
  • interruption state is recomputed on app foreground from persisted runtime/session state
  • this alert remains distinct from G7 state-driven ALERT-CGM-UNAVAILABLE and ALERT-CGM-FAILED-OR-EXPIRED
  • alert messaging may include current blocker detail (No CGM, No Pod, signal loss, other runtime gate) while preserving stronger source-native alert precedence
  • Runtime now also uses guarded pump reconnect as a secondary wake source when CGM-driven stepping stalls:
  • reconnect wake is sourced from pump-status recovery, not raw BLE connect
  • only after the first anchored step exists
  • only when accepted CGM receipt age is > 5 minutes
  • current due step only; no step 0, no cadence re-anchor, no backlog replay
  • Home now renders active alerts in a prioritized vertical carousel through HomeAlertBannerView + carousel container in BionicLoop/Features/Home/HomeAlertCenterView.swift:
  • ordering: severity first, then recency.
  • multiplicity visibility: explicit index/count and visual paging indicator when activeAlerts.count > 1.
  • Pump signal-loss state from PumpStatusObserver and loop state is mapped into AppAlertCenter by HomeView.
  • Home/runtime state now also synthesizes ALERT-PUMP-NO-ACTIVE-POD when loop is armed, session anchor exists, and no active pod is present.
  • de-duplication key: pump:ALERT-PUMP-NO-ACTIVE-POD
  • debounce: 5 minutes
  • clear behavior: auto-clear once active pod is restored
  • Auth/session state now synthesizes ALERT-AUTH-LOGIN-REQUIRED when loop session remains active while user is unauthenticated.
  • de-duplication key: app:ALERT-AUTH-LOGIN-REQUIRED
  • clear behavior: clears when user re-authenticates (or no active loop session remains)
  • Clock-sync state now synthesizes ALERT-APP-CLOCK-SKEW when UTC drift check reports abs(skew_seconds) > 600.
  • de-duplication key: app:ALERT-APP-CLOCK-SKEW
  • warning cadence: no more than once per 24 hours
  • network/unavailable UTC checks do not raise user-facing warning alerts
  • High-priority alert background channel is now implemented in AppAlertCenter for non-CGM alerts:
  • UserNotificationAppAlertScheduler routes non-CGM actionable/safetyCritical alerts to local notifications when app state is background.
  • per-alert dedupe key is respected with cooldown:
    • actionable: 5 minutes
    • safetyCritical: 60 seconds
  • notification authorization is primed at app startup via BionicLoopApp.
  • retracting an alert clears the matching pending/delivered OS notification so resolved conditions are not left visible in Notification Center.
  • CGM alerts do not schedule BionicLoop OS notifications; the Dexcom app remains the source of truth for CGM alarming.
  • tapping a delivered alert routes to context: pump alerts -> Pod modal, CGM alerts -> CGM modal, non-device alerts -> Home.
  • safety-critical alerts (ackState == requiresAcknowledge) expose explicit acknowledge action in Home banner; acknowledge clears active alert and matching OS notification state.
  • In-app Alert Center is now implemented and reachable from Home (bell badge/button) and from Home Settings:
  • active normalized alerts list (severity + timestamp sorted; UI displays date + time)
  • recently-cleared alerts timeline (displayed in 10-item pages with More / Less; resets to first 10 on close)
  • acknowledge action for requiresAcknowledge alerts
  • App-level alert persistence is now implemented:
  • AppAlertCenter persists active alerts and recently-cleared alert timeline across relaunch (recently-cleared retention capped at 100 entries).
  • Alert issuing/persistence hooks:
  • Pump alert issue/retract is implemented in BionicLoop/Integrations/Pump/AppPumpManagerDelegate.swift.
  • CGM alert issue/retract mapping is implemented in BionicLoop/Integrations/CGM/AppCGMManagerDelegate.swift (plus authoritative state-driven mapping from G7CGMManager updates).
  • CGM fallback mapping uses source identifiers (with unavailable/warmup/temporary precedence) and intentionally avoids failed/expired escalation from free-text payload keywords.
  • LoopKit PersistedAlertStore callbacks are now implemented in both delegates, including issued/unretracted lookup and retraction recording.
  • persisted unretracted pump/cgm alerts are restored into AppAlertCenter on delegate attach so alert lifecycle survives app relaunch.
  • CGM state-driven availability/failure alerts (ALERT-CGM-UNAVAILABLE, ALERT-CGM-FAILED-OR-EXPIRED) are rebuilt from live G7CGMManager state on attach and are not replayed from persisted LoopKit alert payloads (prevents stale relaunch replays).
  • on no-active-pod restore/sync, only non-critical pod-tied alerts auto-retract (for example expiring/setup/suspend/time-offset/low-reservoir); ALERT-PUMP-FAULT and ALERT-PUMP-INCOMPATIBLE are retained until explicit lifecycle retraction/acknowledgement.
  • Time-sensitive alert text refresh:
  • AppAlertCenter refreshes active countdown-backed alert text on a minute interval.
  • Current countdown-backed production mappings are ALERT-PUMP-EXPIRING and ALERT-PUMP-EXPIRED.
  • Minute text refresh updates in-app alert content only and does not emit additional background notifications.

Canonical Normalized Alert Model (G1 Baseline)

Field Type Description
source enum Event origin: pump, cgm, runtime, app.
sourceCode string Source-native identifier (for example PodCommsError.podFault, AlgorithmState.expired).
normalizedCode string Stable app-wide code (for example ALERT-PUMP-SIGNAL-LOSS).
severity enum informational, actionable, safetyCritical.
title string Short user-visible title.
message string User-visible detail text.
recommendedAction string Explicit action guidance.
timestamp date Event occurrence time.
ackState enum none, autoClears, requiresAcknowledge.
dedupeKey string Stable de-duplication key for repeated transient events.

Severity Classes (G1)

Severity Intended UX Channel
informational Non-blocking in-app message/state text.
actionable Prominent in-app banner/state and explicit action guidance.
safetyCritical Blocking/high-priority alert path; must not be masked by lower-severity alerts.

Source Inventory

Pump (OmniBLE)

Pump lifecycle alerts (PumpManagerAlert):

  • lowReservoir
  • userPodExpiration
  • podExpiring
  • podExpireImminent
  • finishSetupReminder
  • suspendInProgress
  • suspendEnded
  • unexpectedAlert
  • timeOffsetChangeDetected

Pump comm/fault errors (PodCommsError) include:

  • comm availability: podNotConnected, noResponse, noPodsFound, tooManyPodsFound
  • active-delivery conflicts: unfinalizedBolus, unfinalizedTempBasal
  • hard fault/incompatible states: podFault, activationTimeExceeded, podIncompatible

CGM (G7SensorKit)

CGM reliability/health sources:

  • AlgorithmState.hasReliableGlucose controls reliable glucose availability.
  • unreliable states include warmup/temporary issue/expired/failed/unknown.
  • AlgorithmError.unreliableState surfaces user-facing unavailability.

Runtime and App Policy

Current policy surfaces that should be normalized:

  • meal unavailable reasons (loopOff, missingProfile, noPump, awaitingFirstStep, tooEarly, tooLate, slotConflict, pumpDelivering, missingFreshGlucose, signalLoss, requestInProgress, uncertainPreviousMeal)
  • loop skip reasons (missingFreshGlucose, pumpStatusUnavailable, stepNotDue, cannotBorrow, mealSlotConflict)
  • pump signal-loss policy state (lastPumpSignalLossAt and report window)

Initial Source-to-Normalized Mapping (Proposed)

Source Source code / trigger Normalized code Severity Recommended action
pump PodCommsError.podNotConnected, noResponse, transient comm errors ALERT-PUMP-SIGNAL-LOSS actionable Keep phone near pod and verify Bluetooth; app retries reconnect.
pump Loop armed + established session (firstSuccessfulStepAt) + hasActivePod == false ALERT-PUMP-NO-ACTIVE-POD safetyCritical Start Pod setup and pair a new pod.
pump PodCommsError.podFault, activationTimeExceeded, podIncompatible ALERT-PUMP-FAULT safetyCritical Stop relying on loop delivery and follow pod replacement/fault workflow.
pump PumpManagerAlert.lowReservoir ALERT-PUMP-LOW-RESERVOIR actionable Prepare and replace pod soon.
pump PumpManagerAlert.userPodExpiration ALERT-PUMP-EXPIRING actionable Replace pod before delivery interruption window (countdown text refreshes every minute while active).
pump PumpManagerAlert.podExpiring, podExpireImminent ALERT-PUMP-EXPIRED safetyCritical Pod is already expired; replace now before delivery stops (countdown text refreshes every minute while active).
pump incompatible pod alert text/signals ALERT-PUMP-INCOMPATIBLE safetyCritical Pair a compatible pod and complete setup again.
pump PumpManagerAlert.finishSetupReminder ALERT-PUMP-SETUP-INCOMPLETE actionable Complete pod setup flow.
pump PumpManagerAlert.suspendInProgress ALERT-PUMP-SUSPEND-IN-PROGRESS actionable Delivery is suspended. Resume delivery when appropriate.
pump PumpManagerAlert.suspendEnded ALERT-PUMP-SUSPEND-ENDED actionable Suspension ended. Confirm delivery state.
pump PumpManagerAlert.timeOffsetChangeDetected ALERT-PUMP-TIME-OFFSET-DETECTED actionable Review and sync pump time in Pump settings.
cgm unreliable glucose (hasReliableGlucose == false) or temporary/warmup identifier fallback ALERT-CGM-UNAVAILABLE informational Informational BionicLoop status only; use the Dexcom app as the source of truth for CGM alarming.
cgm state-driven expired/failed lifecycle or algorithm state (not message-only keyword text) ALERT-CGM-FAILED-OR-EXPIRED informational Informational BionicLoop status only; follow Dexcom-app alarming and replace/recover the sensor.
cgm trustworthy live G7 reading <55 mg/dL with fresh timestamp ALERT-CGM-URGENT-LOW safetyCritical Confirm participant safety and treat low glucose per protocol. This is a BionicLoop clinician-review alert keyed to the Dexcom urgent-low threshold, not Dexcom-app acknowledgement.
runtime armed loop + no successful step execution for >15 minutes regardless of current blocker ALERT-ALGORITHM-STEPPING-INTERRUPTED actionable Loop stepping is interrupted. Review current blocker (No CGM, No Pod, signal loss, or other runtime gate) and restore stepping.
runtime step 0 blocked for missing fresh in-range CGM ALERT-LOOP-AWAITING-FIRST-STEP actionable Wait for next valid CGM reading to establish anchor.
runtime skipReason == pumpStatusUnavailable or unknown pump status ALERT-LOOP-COMMAND-BLOCKED actionable Loop may compute, but command application is blocked until pump recovers.
app meal unavailable tooEarly/tooLate/requestInProgress ALERT-MEAL-NOT-AVAILABLE-NOW informational Follow shown retry timing/state text.
app unauthenticated + active loop session ALERT-AUTH-LOGIN-REQUIRED actionable Log in to restore remote monitoring; local loop continuity remains active.
app UTC drift check exceeds 10 minutes (abs(skew_seconds) > 600) ALERT-APP-CLOCK-SKEW actionable Check Date & Time settings and enable Set Automatically.

Planned Meal-Safety Alert Additions

These are planned hardening paths, not current implemented baseline.

Source Source code / trigger Normalized code Severity Recommended action
runtime/app meal request accepted but pump command outcome remains unresolved across timeout/comms interruption/relaunch ALERT-MEAL-OUTCOME-UNCERTAIN actionable Do not re-announce the meal yet. Review current pump status/history and follow guided recovery flow until outcome is reconciled.

Debounce and Dedupe Baseline (G1)

  • Use dedupeKey = source + normalizedCode + deviceIdentifier.
  • Debounce transient comm-loss transitions before user-facing escalation where safe.
  • Do not debounce safetyCritical fault/expired/failed alerts.
  • Repeated actionable alerts should coalesce while active and refresh timestamp/message only.

Acknowledge/Clear Baseline (G1)

  • informational: auto-clear on state resolution.
  • actionable: auto-clear on state resolution unless protocol requires explicit acknowledge.
  • safetyCritical: require explicit acknowledge or explicit replacement/recovery state transition; must not auto-clear solely from hasActivePod == false.
  • Current production required-ack alerts are pump fault, pump incompatible, and app-derived ALERT-CGM-URGENT-LOW. CGM availability/failure alerts remain informational and auto-clear on live-state recovery.

G1 Completion for Workstream G

  • [x] Canonical multi-source alert inventory documented.
  • [x] Normalized alert model fields defined.
  • [x] Source-to-normalized mapping baseline documented.
  • [x] Informational vs actionable vs safety-critical classification defined.

Open Implementation Gaps

  • Severity-based channels remain partial: Home top-banner + in-app Alert Center + local background notifications for non-CGM alerts are implemented; CGM availability/failure alerts remain informational in-app only and rely on the Dexcom app for alarming, while ALERT-CGM-URGENT-LOW is in-app only with no OS notification channel. Protocol-specific blocking flows and richer escalation surfaces remain pending.
  • Debounce/coalescing behavior is implemented for signal loss; broader cross-source debounce/coalescing rules remain to be wired.
  • Foreground presentation exists in Home top-banner and Home/Settings -> Alert Center; dedicated standalone alert inbox/navigation entry remains optional future UX work.
  • Runtime-derived Algorithm Stepping Interrupted alerting is implemented with a 15-minute threshold keyed to absent successful execution rather than CGM receipt alone; remaining work is real-device/background validation of reconnect fallback and interruption recovery timing.

Traceability