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.swiftandBionicLoop/Runtime/AppAlertDomainSupport.swift, with deterministic prioritization inAppAlertPrioritizer. - For a trigger-to-presentation review of the implemented alert path, use Alert Flow Review.
AppAlertCenteris 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
AppAlertCenterthrough LoopKit alert callbacks inAppPumpManagerDelegate.issueAlert/retractAlert: lowReservoir->ALERT-PUMP-LOW-RESERVOIRuserPodExpiration->ALERT-PUMP-EXPIRINGpodExpiring,podExpireImminent->ALERT-PUMP-EXPIREDfinishSetupReminder->ALERT-PUMP-SETUP-INCOMPLETEsuspendInProgress->ALERT-PUMP-SUSPEND-IN-PROGRESSsuspendEnded->ALERT-PUMP-SUSPEND-ENDEDtimeOffsetChangeDetected->ALERT-PUMP-TIME-OFFSET-DETECTED- pod alarm/fault notifications ->
ALERT-PUMP-FAULT - incompatible pod text/signals ->
ALERT-PUMP-INCOMPATIBLE ALERT-PUMP-EXPIRINGparses relative time text into countdown metadata (deadline) when available.ALERT-PUMP-EXPIREDmaps to post-expiration delivery-stop countdown metadata (deadlineat service stop) with distinct message/action wording.- State-driven synthesis from pump
expiresAton status refresh/startup now emits: ALERT-PUMP-EXPIRINGwhenexpiresAtis within 1 hour and still in future.ALERT-PUMP-EXPIREDwhenexpiresAthas 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
AppAlertCenterthroughAppCGMManagerDelegate: - 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
issueAlertfallback 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-UNAVAILABLEandALERT-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 inBionicLoop/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
PumpStatusObserverand loop state is mapped intoAppAlertCenterbyHomeView. - Home/runtime state now also synthesizes
ALERT-PUMP-NO-ACTIVE-PODwhen 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-REQUIREDwhen 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-SKEWwhen UTC drift check reportsabs(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
AppAlertCenterfor non-CGM alerts: UserNotificationAppAlertSchedulerroutes non-CGMactionable/safetyCriticalalerts 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
requiresAcknowledgealerts - App-level alert persistence is now implemented:
AppAlertCenterpersists 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 fromG7CGMManagerupdates). - CGM fallback mapping uses source identifiers (with unavailable/warmup/temporary precedence) and intentionally avoids failed/expired escalation from free-text payload keywords.
- LoopKit
PersistedAlertStorecallbacks are now implemented in both delegates, including issued/unretracted lookup and retraction recording. - persisted unretracted pump/cgm alerts are restored into
AppAlertCenteron 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 liveG7CGMManagerstate 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-FAULTandALERT-PUMP-INCOMPATIBLEare retained until explicit lifecycle retraction/acknowledgement. - Time-sensitive alert text refresh:
AppAlertCenterrefreshes active countdown-backed alert text on a minute interval.- Current countdown-backed production mappings are
ALERT-PUMP-EXPIRINGandALERT-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):
lowReservoiruserPodExpirationpodExpiringpodExpireImminentfinishSetupRemindersuspendInProgresssuspendEndedunexpectedAlerttimeOffsetChangeDetected
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.hasReliableGlucosecontrols reliable glucose availability.- unreliable states include warmup/temporary issue/expired/failed/unknown.
AlgorithmError.unreliableStatesurfaces 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 (
lastPumpSignalLossAtand 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
safetyCriticalfault/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 fromhasActivePod == 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-LOWis 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 Interruptedalerting is implemented with a15-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
- Risk: RA-011
- Requirements: SRS-ALERT-001..016
- Design: SDD-ALERT-001, SDD-POL-008, SDD-POL-026
- Verification plan: TV-ALERT-001..015