Skip to content

Software Design Description (SDD)

Status: Final draft prepared for handoff (pending review)
Version: 1.12 Owner: BionicLoop engineering Prepared by: BionicLoop engineering Reviewer: ____
Approver: ____
Decision date: ____
Effective date: ____
Baseline freeze SHA: ____
Last updated: 2026-06-18

Revision History

Version Date Author Summary of Changes
0.1 2026-04-05 Engineering Initial controlled design draft
0.9 2026-04-06 BionicLoop engineering Added handoff-ready document-control metadata and software-handoff disposition language
0.91 2026-04-08 BionicLoop engineering Added fallback-event review-state persistence notes for ongoing offline-fallback feasibility scaffolding
0.92 2026-04-08 BionicLoop engineering Added pump basal-schedule seam for masked offline-fallback feasibility scaffolding
0.93 2026-04-08 BionicLoop engineering Clarified that programmed basal-schedule replacement is a disruptive masked-fallback arm/disarm operation that is blocked during active bolus, active temp basal, or non-steady basal-delivery states
0.94 2026-04-08 BionicLoop engineering Clarified that masked-fallback schedule refresh uses a controlled reprogram-and-immediate-remask maintenance sequence and that unknown basal-delivery state blocks disruptive schedule replacement
0.95 2026-04-08 BionicLoop engineering Added active masked-fallback maintenance lifecycle details, active-session gating, and reset/disarm restore behavior for the feasibility branch
0.96 2026-04-08 BionicLoop engineering Clarified 20-minute masked-fallback renewal-window behavior, deferred maintenance semantics, and pre-step renewal before a new bolus-bearing step
0.97 2026-04-14 BionicLoop engineering Added reconciliation-required blocked-state design notes after offline mask expiry for the active masked-fallback feasibility branch
0.98 2026-04-14 BionicLoop engineering Added reconnect restore/disarm retry notes and explicit fresh re-arm behavior after successful masked-fallback recovery
0.99 2026-04-14 BionicLoop engineering Updated reconnect recovery design to preserve the existing session/cadence anchor and mark successful feasibility-path recovery as same-session unreconciled resume
1.00 2026-04-15 BionicLoop engineering Added basal-only reconnect evidence, pod total-delivery baseline capture, modeled-vs-pump-reported fallback review, and pre-step missing-fallback arm details for the active feasibility branch
1.01 2026-04-15 BionicLoop engineering Added confirmed/corrected basal-only reconnect replay design details, persisted replay-plan state, and replay-specific review detail in Home timeline surfaces
1.02 2026-04-15 BionicLoop engineering Added dedicated cloud fallback telemetry emission from structured fallback review state for BionicScout timeline and dosing overlays
1.03 2026-04-15 BionicLoop engineering Clarified that replayed internal steps now persist into local per-step telemetry / CSV export as distinct replay-marked rows
1.04 2026-04-27 BionicLoop engineering Clarified that fallback replay uses pump-reported delivered-insulin delta only; modeled fallback exposure is logging/review evidence when pump reconciliation is unavailable
1.05 2026-05-06 BionicLoop engineering Added Swift-maintained q5 nominal-basal profile persistence, safety-track four-bucket fallback schedule generation, schedule-aware exposure modeling, and profile/schedule cloud telemetry details
1.06 2026-06-02 BionicLoop engineering Reframed masked-fallback recovery as pump-delta missed-step algorithm replay with unavailable CGM input, tightened pump-evidence credibility, restored IDE schedule-refresh cadence, and added repeated no-active-pod alerting
1.07 2026-06-08 BionicLoop engineering Added persisted-schedule-weighted replay allocation for credible same-pod pump delta, including partial-slot / midnight weighting and zero-weight suppression rules
1.08 2026-06-12 BionicLoop engineering Added generalized issued-dose attribution design for interrupted meal, correction, mixed, and basal bolus commands
1.09 2026-06-13 BionicLoop engineering Hardened issued-dose attribution design for delivered-unit credibility, replacement-pod live-step input scrubbing, and automatic resume blocking under active holds
1.10 2026-06-15 BionicLoop engineering Updated different/new-pod issued-dose design to assumed-delivered replay/live attribution, nonblocking replacement-pod dosing, legacy hold clearance, and deferred adaptation-forget consideration
1.11 2026-06-17 BionicLoop engineering Added explicit meal-progress pod-replacement escape design with user-abandoned unavailable-pod evidence
1.12 2026-06-18 BionicLoop engineering Added retired/expired/no-active-pod masked-fallback recovery design that records modeled fallback exposure as assumed delivered when the old pod cannot provide connected reconciliation evidence.

1. Design Intent

This design implements a CGM-triggered closed-loop runtime with:

  • deterministic cadence state
  • degraded execution behavior when inputs are unavailable
  • explicit gating for pump command application
  • traceable per-step telemetry

2. Major Components

  • SDD-APP-001: LoopRuntimeEngine (BionicLoop/Runtime/LoopRuntimeEngine.swift) handles wake handling, availability logic, session control, and telemetry orchestration.
  • SDD-APP-002: LoopSessionStore owns app-layer runtime persistence reads/writes for loop armed state and runtime state snapshots.
  • SDD-APP-003: LoopWorkScheduler owns CGM-timestamp trigger dedupe and arm/reset semantics for runtime wake dispatch.
  • SDD-APP-004: LoopAlertMediator owns pump signal-loss tracking and report gating in app-layer state.
  • SDD-APP-005: LoopTelemetryWriter owns app-layer telemetry-store writes and reconciliation calls.
  • SDD-APP-006: LoopRuntimeWorkExecutor owns coordinator doWork execution snapshot sequencing (latest reading capture, operation execution, completion timestamp, state snapshot).
  • SDD-APP-007: DeviceClockSyncMonitor (BionicLoop/App/AuthSessionNetworking.swift) owns UTC midpoint drift checks (GET /v1/time/utc), retry policy, 24-hour foreground check gating, timezone/significant-time-change trigger handling, and lifecycle telemetry context projection.
  • SDD-AUTH-001: App auth-session boundary is split into AppAuthSessionManager (secure token persistence, restore, refresh, sign-out clear) and AuthenticatedAPIClient (bearer-auth request composition + one-time refresh retry on unauthorized response), implemented in BionicLoop/App/AuthSessionNetworking.swift. Recovered/refreshed token sets are validated for telemetry-ingest scope before persistence.
  • SDD-AUTH-002: Email/password recovery boundary is implemented in CognitoPasswordRecoveryService (ForgotPassword, ConfirmForgotPassword) plus unauthenticated route/state wiring (.forgotPassword) and NoAuthForgotPasswordView for reset-code request, confirmation, resend, and user-feedback messaging.
  • SDD-CORE-001: LoopRuntimeCoordinator (BionicLoopCore/.../Runtime/LoopRuntimeCoordinator.swift) handles due-step logic, input assembly, algorithm invocation, and command application.
  • SDD-ALG-001: RealBUDosingAlgorithm + Algo2015Bridge map runtime input into C bridge structs and receive output/state.
  • SDD-PUMP-001: PumpServiceAdapter + PumpStatusObserver handle status refresh, command execution, delivery reconciliation, home-status projection, and auto-polling while deliveryState == delivering. For ongoing masked offline-fallback feasibility work, the pump abstraction now also carries a domain-level programmed basal-schedule seam (PumpBasalSchedule) so runtime can read and rewrite the pod's current basal schedule without taking a direct dependency on OmniBLE schedule types. On DASH, replacing the programmed basal schedule is a disruptive arm/disarm operation rather than a neutral setter: the OmniBLE implementation cancels current delivery before applying the new schedule, so the app seam now treats schedule replacement as an interrupting operation and blocks it unless pump delivery is in a known safe connected state (no active bolus, no nonzero temp basal, not suspended/resuming/suspending, and no unknown basal-delivery state). The active masked-fallback feasibility path now uses that seam in two explicit maintenance operations only: performMaskedFallbackMaintenance(...) replaces the programmed fallback schedule and immediately reapplies the 0.0 U/hr / 30 minute mask, while performMaskedFallbackDisarm(...) restores the original programmed schedule without reapplying the mask. Runtime arms masked fallback immediately for a new active algorithm session once a valid fallback candidate is available from the current or latest successful step. If the candidate is first produced by the current step, LoopRuntimeCoordinator invokes a pre-pump-command maintenance hook after primary/secondary algorithm calculation but before applying that same step's pump recommendation; this lets step 0 program the fallback schedule and 0.0 U/hr mask before a bolus command can place the pod into bolus-in-progress. The hook receives the same refreshed pump status used by pump-command classification and skips fallback maintenance when that status is unavailable or unknown, so it cannot bypass the normal pump availability/unknown-state gate. If a valid candidate is already persisted but no fallback is currently armed, the existing pre-step maintenance pass first refreshes pump status and only arms fallback before the next loop execution when that status is idle; unavailable, unknown, delivering, or failed refresh leaves maintenance deferred without pod mutation. If the initial arm is cleanly blocked before any schedule mutation, runtime leaves fallback explicitly unarmed, records a failure instead of pretending ordinary deferred masked maintenance is active, and lets the current step's normal pump command proceed through its own safety gates; the same execution cycle does not then run post-execution masked-fallback maintenance for a second first-arm attempt. If the programmed fallback schedule may have been written but the immediate mask fails, is blocked, or is uncertain, PumpServiceAdapter normalizes that post-schedule mask error as remaskFailed, runtime persists reconciliation-required recovery context, and the current step's normal pump command is blocked before additional pod mutation. Runtime now maintains Swift-side q5 nominal-basal profiles from primary and secondary/safety algorithm scalar outputs; fallback programming prefers the secondary/safety profile, imputes missing 5-minute slots from observed values, averages four local-time six-hour buckets, rounds each bucket through the pump service, and programs the resulting four-entry PumpBasalSchedule (0, 21600, 43200, 64800 seconds) under the mask. If no q5 profile is available, runtime falls back to the latest rounded scalar safety-track nominal-basal candidate. Runtime refreshes the programmed fallback schedule no more often than the configured refresh interval when the desired schedule changes, preserves recoverable restore context on session reset/disarm until the original programmed schedule is successfully restored or explicit restore failure is recorded, and blocks the next algorithm start while unresolved restore-failed masked-fallback recovery remains in persisted state. For an already-masked fallback, steady-state connected behavior does not rewrite the mask every step: runtime tracks maskExpiresAt, enters a renewal window when 20 minutes or less remain on the active 30 minute mask, and performs pre-step renewal before the next loop execution only after a fresh idle pump-status refresh. If bolus, unknown/unavailable status, refresh failure, or other blocked pump state prevents renewal while the current mask is still active, runtime records deferred maintenance rather than immediate failure; PumpServiceAdapter now normalizes both device-state and late communication unfinalizedBolus outcomes on the temp-basal renewal path into that deferred-maintenance result while the current mask still has time remaining. If a remask failure occurs after an existing masked fallback renewal or schedule-refresh path and may leave fallback exposure uncertain, runtime records the exposure start (maskExpiresAt for renewal uncertainty, or current time for post-refresh remask failure) and enters reconciliation-required recovery rather than continuing ordinary stepping. If successful step execution stalls or deferral continues long enough for the active mask to expire, the pod is allowed to resume the programmed fallback schedule and runtime records that activation for later reconciliation rather than immediately remasking. Once maskExpiredOffline is recorded, runtime also persists a reconciliation-required recovery state and suppresses ordinary loop execution, manual BG dispatch, meal announce availability, and new arm until reconnect recovery resolves. While that state remains pending, runtime attempts connected restore/disarm of the original programmed basal schedule on runtime start, app foreground, or pump reconnect. If restore succeeds while the loop remained armed, runtime clears the persisted masked-fallback state without resetting the current session, preserves the existing cadence anchor, and resolves basal-only reconnect evidence against the persisted fallback context. Exact cached-schedule equality is no longer a hard prerequisite for corrected recovery: if reconnect finds a different cached programmed schedule in local OmniBLE manager state, recovery may still record a corrected result and proceed with pump-delta reconciliation, but only when the persisted blackout context, proven same-pod continuity, a fresh connected pod-total-delivery baseline, and close agreement between modeled and pump-reported delivered insulin together provide credible basal-only evidence. App-log diagnostics capture both the persisted fallback schedule and the cached reconnect schedule from manager state for later review; that cached schedule is explicitly not treated as authoritative pod truth. Confirmed or corrected recovery persists pending pump-delta reconciliation metadata, records the resolved disarmed fallback event with reconciliation metadata, and lets LoopRuntimeCoordinator allocate the same-pod pump-reported delivered-insulin delta across missed 5-minute primary/secondary algorithm replay steps using the persisted programmed fallback schedule as the weighting model. Each missed replay step uses CGM=-1, that step's delivered-insulin allocation including explicit 0 U input when no pump allocation belongs to that step, and no pump command application; the resumed live step then runs with actual refreshed pump status rather than a duplicate aggregate recovered-delivery input. If a positive pump delta cannot be weighted because persisted fallback schedule weights are unavailable or zero, runtime suppresses replay rather than fabricating an allocation; if the authoritative pump delta is 0 U, the replay plan may still preserve the missed-step range so coordinator replay can emit 0 U algorithm inputs. Pending replay metadata is single-use: if the live due step no longer matches, the plan is malformed, no session anchor exists, or the plan no longer contains missed steps before the current due step, coordinator emits a skipped replay trace and clears the pending plan instead of retrying it against later wakes. Ambiguous, different/new pod, unknown pod identity, or history-discontinuous recovery records a resolved disarmed event stamped as resumed_without_reconciliation and permits only the current due 5-minute step without replay. If the operator explicitly reset or turned the loop off first, successful recovery restores the original programmed schedule but keeps the loop off. The reconnect path now also exposes a basal-only makeFallbackBasalReconciliationSnapshot(...) seam carrying a stable pod identity, the cached programmed basal schedule from manager state, recent automatic temp-basal evidence, and pod cumulative delivered-insulin measurements so recovery can compare modeled fallback exposure against pump-reported delivered insulin since the last connected same-pod fallback baseline. Core now contains pure FallbackBasalExposureReconciliation, FallbackBasalPodContinuity, and FallbackBasalReplayPlan support types, and runtime/coordinator use them both for recovery classification/review and for no-command pump-delta missed-step algorithm replay when reconnect evidence is confirmed/corrected and same-pod; modeled exposure and replay weighting now integrate the persisted fallback schedule across six-hour bucket boundaries, partial replay slots, and local-day midnight wrap rather than multiplying the entire blackout by one scalar rate. Current audible-command behavior remains split by the existing OmniBLE command categories: the automatic 0.0 U/hr mask temp basal follows automatic-command beep preference rules, while fallback schedule arm/refresh/disarm still reuse the manual/profile basal-schedule programming path and therefore may emit the normal DASH confidence beep unless pod confidence beeps are disabled or the pod is silenced. A silent maintenance-specific basal-schedule write path remains deferred backlog work.
  • Retired/expired/no-active pod exception: when masked-fallback recovery is pending and PumpStatusObserver proves the previous pump session no longer has an active pod, or the observed time is beyond hard pod service stop, runtime may synthesize an assumed disarmed recovery event without connected schedule restore. The event uses assumed_delivered_per_clinical_policy, sets actual delivered fallback units equal to modeled schedule exposure for the bounded fallback-active interval, queues the normal no-command replay plan, clears the recovery state, and keeps the existing algorithm session. UI and telemetry label this as assumed delivered, not confirmed/corrected pump reconciliation.
  • Replay eligibility is bounded to missed slots whose delivery interval overlaps confirmed fallback-active time. Missing slots that occurred after pump communication loss but before offline fallback activation remain absent from replay, not synthetic 0 U rows; explicit 0 U replay rows are preserved only inside the fallback-active interval.
  • SDD-CGM-001: G7ViewModel + CGM subsystem adapters provide CGM state ingestion and wake trigger source; telemetry cgm.state.changed is emitted from refreshed/current callback state to avoid stale has_sensor transitions, and cgm.connection.changed derives status_text from refreshed lifecycle state before emit.
  • SDD-BG-001: Manual BG entry path spans Home/UI entry, runtime bgCheck wake dispatch, single-pending-candidate BG state, next-eligible-step consumption logic, and telemetry source tagging (manualBG). The same runtime entrypoint now also checks persisted masked-fallback recovery state and refuses bgCheck dispatch while reconnect reconciliation is required.
  • SDD-CLIN-001: Clinical settings boundary spans offline clinical-unlock-gated settings presentation, clinician-only control hosting (Subject ID, Weight, Start Algo, Reset Algo), runtime-facing pregnancy configuration projection (target, meal upfront, TMAX), a clinician-controlled participant target-access profile (Pregnancy / Standard), and the participant-facing target-change approval-capture sheet hosted in general settings.
  • SDD-LOG-001: LoopTelemetryStore stores per-step records for UI + export, and CloudTelemetryReporter emits authenticated telemetry envelopes with correlation metadata (event_id, session_id, subject_id) plus auth_user_sub derived from Cognito ID-token sub (fallback UNSET only when unavailable); loop-step payloads carry explicit execution timestamp step_executed_at (execution semantics) in addition to envelope created_at (ingest creation time). Loop command telemetry mapping is schema-stable: loop.command.blocked is emitted only when a recommendation existed but was blocked, while pump.command.result remains pump-result schema only and emits on delivery-result deltas only (no unchanged refresh re-emission). For ongoing offline-fallback feasibility work, the same local telemetry store also persists structured fallback-basal review events published from runtime state so Home Recent Dose Steps can show fallback rate, source (instant vs nominal), safety target, duration, reconciliation status, pod-continuity result, restore/remask failure detail, modeled fallback delivery, and pump-reported delivered insulin since the last proven same-pod connected fallback baseline separately from ordinary step rows without adding blocking export work to the main actor. That local fallback event store now also acts as the app-side cloud emitter seam for a dedicated authenticated loop.fallback.event family: when a fallback event is newly created or materially updated, LoopTelemetryStore emits a structured payload carrying stable fallback_event_id, lifecycle kind, occurrence timestamps (issued_at, optional reconciled_at), selected/reconciled fallback rates, modeled delivery, pump-reported delivered insulin, pod_continuity, and reconciliation summary detail. Arm and schedule-refresh fallback events additionally attach the exact programmed schedule entries plus the safety q5 profile metadata used for that programming operation (profile_source_track, profile_kind, observed/imputed slot counts, timezone fields, and profile_rates_288); routine mask renewals omit the 288-rate array to avoid high-volume telemetry churn. Because a single logical fallback event can be updated later with reconciliation detail, the cloud contract treats fallback_event_id as the stable backend projection key while each emitted envelope keeps its own unique transport event_id. Executed step rows now retain both the primary algorithm telemetry snapshot and the secondary/safety algorithm telemetry snapshot plus each track's recommendation, so Recent Dose Steps can review both tracks' step count, suggested dose, nominal basal, and instant basal alongside the delivered dose row. When confirmed/corrected reconnect recovery occurs, LoopRuntimeCoordinator allocates the credible same-pod pump-reported fallback delivery delta into no-command missed-step primary/secondary algorithm replay rows with CGM=-1 and schedule-weighted per-step pump-delivery input, then records the resumed live step with actual refreshed pump status and no duplicate recovered-delivery input. Reconnect diagnostics now also emit app-log evidence for every masked-fallback recovery attempt, including the persisted fallback schedule, the cached programmed schedule from local OmniBLE manager state, the original pre-fallback schedule, cached-schedule match state, cached-schedule source labeling, baseline/observed pod identity, pod-continuity result, modeled-vs-pump-reported delivered insulin totals, and no-replay reason when replay cannot be safely allocated, so backend review can determine whether schedule divergence reflects stale cached reconnect state, an incorrect schedule-programming command, zero/unavailable replay weights, or replacement/unknown pod continuity. The masked-fallback event lifecycle now includes explicit armed, maintenance_deferred, mask_renewed, schedule_checked_unchanged, schedule_refreshed, mask_expired_offline, disarmed, remask_failed, and restore_failed transitions. Outbox flushing is loss-avoidant: if flush is requested while a flush is active, a deferred follow-up flush is queued and executed immediately after the active pass completes (with full-flush escalation when any deferred request requires non-high-priority delivery). Client cloud-log threshold resolution uses precedence remote override (active, TTL-bound) -> local threshold -> default error; local threshold is exposed in debug builds via HomeSettingsView and persisted through CloudLogUploadPolicy.
  • Confirmed/corrected reconnect replay telemetry is limited to fallback-active missed slots. Pre-activation disconnected gaps remain visible by absence rather than replay rows; 0 U replay rows are emitted only for fallback-active slots with no pump allocation.
  • SDD-SIM-001: Deterministic simulation harness (medium-fidelity) uses protocol-conformant mock CGM/pump services plus a virtual clock/scenario runner to exercise runtime logic without BLE transport dependency.
  • SDD-ALERT-001: Alert normalization/presentation layer is implemented as an app-level normalized model (AppAlert* types + AppAlertCenter) with deterministic precedence (severity, then recency), dedupe keys, and debounced signal-loss handling. Current live UI presentation includes a Home vertical active-alert carousel (prioritized and multiplicity-aware), plus a Home bell entry to Alert Center (active + recently cleared), with secondary access from Settings. G1 source inventory/mapping baseline is documented in Alert Inventory and Mapping.
  • SDD-QA-001: Algo2015 verification infrastructure uses deterministic replay vectors plus instrumented coverage collection, mandatory static-analysis lane execution on formal runs, and conditional MISRA policy linkage (report+deviations or explicit N/A rationale) to generate reproducible TV-ALG-* evidence artifacts for IDE submission, as specified in Algo2015 Verification Plan.

3. Runtime Sequence (Nominal)

  1. New CGM timestamp arrives.
  2. LoopRuntimeEngine triggers coordinator doWork.
  3. Coordinator computes expected step from anchor + interval.
  4. Coordinator builds algorithm input:
  5. CGM (or unavailable -1 by policy).
  6. Pump fields (or unavailable values if pump not available).
  7. Algorithm returns recommendation and updated internal state blob.
  8. If pump status is available and recommendation passes gates, command is attempted.
  9. Telemetry record is persisted with input/output/command fields.
  10. Pump status refresh reconciles delivered/requested units.

4. Key Design Policies

  • SDD-POL-001 (Cadence): anchored 5-minute slot model.
  • SDD-POL-002 (CGM): step 0 strict gate, step >0 degraded allowed.
  • SDD-POL-003 (Pump): unavailable pump permits algorithm step but blocks command application.
  • SDD-POL-004 (Meal announce): explicit pump-ready gating (active pod present, known, and not delivering), established first-step cadence anchor requirement, borrow-window gating for pre-due execution, and current-due-step execution when request arrives after slot is due/missed. Unavailable-reason precedence reports noPump before signalLoss when there is no active pod, and a persisted masked-fallback reconciliation-required state blocks meal availability with reconnect/recovery-required guidance until that recovery resolves. Home/meal presentation logic revalidates meal availability when the app refreshes to active while the composer is visible and again immediately before submit, closing stale-composer paths by replacing them with the current unavailable presentation instead of dispatching a now-invalid meal request. This is the accepted current software handoff baseline for late-submit meal behavior.
  • SDD-POL-005 (Reset): full session reset clears persisted runtime + algorithm + timeline session data.
  • SDD-POL-006 (Home status): state precedence is deterministic and runtime-derived; age-based states use cadence phase (nextDueAt from anchor + step index) so borrowed meal steps do not falsely age status before the next due boundary. Current thresholds: Active through nextDueAt + 2m, Aging through nextDueAt + 15m, then Stale.
  • SDD-POL-007 (Modal setup cancellation): startup/setup modals for CGM and Pod provide direct dismiss behavior via explicit Cancel, including persisted-manager/no-active-pod Pod setup.
  • SDD-POL-009 (Meal composer lifecycle safety): if app scene transitions to background while meal composer is presented, the composer is auto-cancelled/dismissed.
  • SDD-POL-010 (BG check): manual BG submit triggers bgCheck with no borrowing, uses a single pending BG candidate (replace-on-new submit), applies candidate to the next eligible step, and expires candidate if not consumed on that immediate next step.
  • SDD-POL-011 (BG safety gate): manual BG submit path checks loop-armed state plus persisted masked-fallback recovery state in LoopRuntimeEngine and shall not dispatch bgCheck while disarmed or while reconnect reconciliation is required.
  • SDD-POL-012 (BG step-0 guard): until step-0 BG rescue is approved, manual BG submit is rejected if firstSuccessfulStepAt is not established.
  • SDD-POL-013 (Clinical settings policy): clinician configuration access is gated by ClinicalUnlockVerifier and KeychainClinicalUnlockStateStore. The app verifies subject-scoped single-use unlock codes offline after verifier material is provisioned, stores verifier material/local state per subject in the Keychain, burns the matched counter before exposing clinician controls, and keeps the clinician controls unlocked only until the provisioned expiration timestamp or explicit manual lock. The app-side material provisioning path validates the verifier material before storage, rejects subject/material mismatches, preserves the last accepted counter when material is refreshed, clears active unlock expiration on refresh, and resets local failed-attempt/lockout state so refreshed material does not silently preserve an open clinician session. The verifier normalizes pasted/grouped ASCII numeric input, computes the HMAC code using the current provisioned secret version, searches only the configured future counter lookahead, and rejects malformed, non-ASCII digit, reused/lower-counter, wrong-subject, beyond-lookahead, and unsupported-version codes. Manual lock writes the cleared unlock expiration back to secure storage; if storage persistence fails, HomeSettingsView keeps clinician controls visibly unlocked and shows the storage-failure message instead of presenting a false locked state. Production master secrets are not embedded in app source; DEBUG UI-test provisioning uses the public contract test secret only under UI_TEST_MODE and exercises the same app-side provisioning path. Operational provisioning is human-typed: ClinicalUnlockProvisioningCode (BionicLoopCore) decodes the BionicScout-issued one-time provisioning code (Crockford base32 of a 30-byte payload carrying format version, secret version, code digits, offline lookahead, unlock duration, a 4-byte subject binding check, the 16-byte per-subject derived device secret, and a 2-byte checksum; input normalization maps I/L to 1 and O to 0 and strips separators), and ClinicalUnlockProvisioningInstaller installs the decoded material through ClinicalUnlockSessionController.provision. HomeClinicalSettingsFormView presents provisioning as an operator-facing "Clinical Unlock Setup" section only while clinician controls are locked: when material is installed for the current subject it collapses to a ready confirmation (subject + code-set version, with an explicit "Redo Device Setup" action); otherwise it shows a guided form with "Set Up Automatically" (authenticated fetch) as the primary action and the typed setup code behind a manual-entry disclosure, and the locked unlock-code field shows a set-up-required hint. On successful installation HomeSettingsView adopts the provisioned subject ID only when the app has no subject configured yet (bootstrap) and never silently changes a non-empty configured subject. ClinicalUnlockAutoProvisioner performs the fetch through AuthenticatedAPIClient (GET /v1/subjects/{subject_id}/clinical-unlock-material, subject-device token with automatic refresh), maps auth/claim failures to operator-readable messages (notSignedIn, subjectNotClaimed), rejects responses whose subject_id differs from the requested subject, and installs the fetched provisioning code through the identical ClinicalUnlockProvisioningInstaller path so typed and fetched material share one validation surface. The device stores only the per-subject derived secret, so compromise of one device's Keychain material exposes only that subject's unlock codes. Clinical control values are validated to allowed ranges (target 90...130 step 10, meal upfront 75/90, TMAX 40...70 step 5) and mapped into algorithm/runtime configuration without changing existing start/reset semantics. HomeSettingsView persists a target-access profile (Pregnancy, Standard) alongside the clinical config; participant-facing settings derive their available target buttons from that profile (Pregnancy -> 90/100/110, Standard -> 110/120/130). The clinician target selector now uses that same profile subset and normalizes any inherited out-of-profile draft target to the nearest allowed value when the profile changes. When a previously conflicting subject identifier is corrected and the save/claim flow succeeds, HomeSettingsView explicitly retracts the app-level ALERT-SUBJECT-ID-CONFLICT alert so operator-facing alert state matches the resolved persisted configuration. Separately, Home now performs a throttled silent re-claim check for the currently persisted subject identifier when the conflict alert is still active and the operator launches, foregrounds, or reopens settings; if the backend claim now succeeds, Home retracts the stale conflict alert without requiring a settings edit/resave round-trip. This investigational single-device unlock control remains distinct from broader production role-based authorization.
  • SDD-POL-014 (CGM display masking for stale/unreliable data): G7ViewModel applies a display-staleness cutoff of 11 minutes and checks hasReliableGlucose on the latest G7 reading. If stale or unreliable, display accessors emit masked glucose text (-- mg/dL / --), suppress trend-arrow rendering, and do not append unreliable points into chart-history persistence.
  • SDD-POL-015 (Auth/runtime decoupling): authentication/session state shall not stop or reset local loop runtime state. Auth gates cloud/protected operations and onboarding surfaces, while therapy runtime continuity is preserved unless user explicitly performs runtime reset/start-stop actions.
  • SDD-POL-016 (Auth re-entry with continuity fallback): app launch attempts silent token-session restore/refresh when persisted auth state is authenticated; on restore success, authenticated UX remains uninterrupted. If auth state changes to unauthenticated while restore is in flight (for example explicit user sign-out), launch reconciliation preserves the current unauthenticated state and does not auto-reauthenticate. When restore fails (or user is unauthenticated) and local runtime indicates active algorithm state, login UX exposes explicit Home bypass. In bypass mode, Home shows a persistent auth-recovery alert (ALERT-AUTH-LOGIN-REQUIRED) with direct Log In action until re-authentication completes.
  • SDD-POL-017 (Clinical/settings telemetry): Clinical Settings save-review flow emits ui.critical telemetry with stable event/action mapping: state_viewed on review presentation, submit on Save, cancel on review dismissal, and blocked on invalid/locked/no-change save attempts. Save telemetry includes changed-field list and old/new clinical values (subject, weight_lbs, target_mgdl, target_range_profile, meal_upfront_percent, tmax_minutes). Participant target-change approval capture emits its own stable ui.critical lifecycle (state_viewed, submit, cancel, blocked) with requested target, current target, target-access profile, approving staff name, and approval timestamp.
  • SDD-POL-018 (Clock-sync safety telemetry): App lifecycle telemetry (app.lifecycle.launched, app.lifecycle.foregrounded) includes timezone + UTC-check context fields (device_timezone_id, device_utc_offset_seconds, clock_check_result, optional skew/rtt/timestamp). UTC checks run on launch, timezone/significant-time-change notifications, and foreground only when last successful check is older than 24 hours. If absolute skew exceeds 600 seconds, app raises actionable ALERT-APP-CLOCK-SKEW warning no more than once per 24 hours; unavailable checks do not show user warnings.
  • SDD-POL-019 (CGM boundary display + chart scaling): CGM value rendering maps <=39 mg/dL to LOW and >=401 mg/dL to HIGH across Home/G7 primary value surfaces and scrub value presentation; Home inline CGM chart uses bounded stepped maxima (300, 350, 400 mg/dL) based on observed peak values so out-of-range excursions remain visible without unbounded axis drift.
  • SDD-POL-020 (algorithm stepping interruption monitoring): LoopRuntimeEngine monitors for stalled loop execution while armed using persisted runtime/session state rather than CGM-receipt time alone. The interruption deadline is computed from lastSuccessfulRunAt + 15 minutes; if no successful step exists yet, the baseline is session start / first-step wait state. AppAlertCenter clears any prior pending interruption notification and schedules a replacement local-notification deadline at the current interruption deadline; if the deadline passes while still current, it raises actionable in-app alert state. Monitoring is recomputed on app foreground and refreshed after each successful step. The alert clears when successful stepping resumes or when the loop is disarmed/reset.
  • SDD-POL-021 (reconnect fallback execution): current cadence remains anchored to firstSuccessfulStepAt with the existing early execution lead (27 seconds). Pump reconnect is treated as a secondary wake source rather than a cadence source: app runtime listens for pump-status recovery (not raw BLE connect), only after an anchored session exists, only for step >0, only when the accepted CGM receipt is older than the approved freshness limit (>5 minutes), and only when the current due slot has not already executed. The reconnect path may execute only the current due step, shall not replay missed slots, shall not re-anchor the schedule, and shall remain suppressed once fresh CGM receipts resume so CGM retains priority as the primary loop trigger.
  • SDD-POL-022 (algorithm stepping interrupted alert policy): the normalized runtime interruption alert is ALERT-ALGORITHM-STEPPING-INTERRUPTED, keyed to lack of successful algorithm execution rather than lack of CGM receipt alone. Trigger basis is loop armed plus no successful step for >15 minutes, measured from the most recent successful executed step or, if none exists yet, from session arm time / first-step wait state. The alert issues even when the immediate blocker is not CGM-specific (for example no active pod, pump signal loss, or another execution gate), clears on the next successful step or loop disarm/reset, and exposes blocker/root-cause detail without masking stronger source-native CGM/pump alerts.
  • SDD-POL-023 (meal-request durability baseline): LoopRuntimeState persists pending meal-request state across relaunch using submit time, the concrete execution step accepted by LoopRuntimeCoordinator, a pending-state discriminator (awaitingExecution, uncertainCommandOutcome), and the correlated meal flow identifier. On startup/foreground availability checks, runtime reconciles that state against persisted step history (lastExecutedStep) and, for uncertain outcomes, against pump-delivery evidence (lastDelivery.requestTimeStep) after pump attachment. While the pending request remains unresolved, mealAnnouncementAvailability() returns requestInProgress or uncertainPreviousMeal and duplicate meal entry is blocked until reconciliation or session reset/disarm.
  • SDD-POL-024 (meal outcome handling): Home/UI and runtime telemetry split meal flow into pre-submit interaction plus outcome-driven submitted, accepted, success, blocked, uncertain, and resolved paths. HomeView keeps the composer in a submitting state while LoopRuntimeEngine.announceMeal() awaits coordinator completion, dismisses only on executed success, presents explicit blocked messaging when runtime returns a blocked/rejected outcome, and presents explicit uncertain-delivery guidance when pump command outcome is unresolved. Home captures the lastExecutedStep observed when the meal composer opens and passes that snapshot with submit; LoopRuntimeCoordinator compares it against current cadence state and returns mealSlotConflict if another step has already advanced, so runtime maps the result to explicit slot-conflict retry guidance instead of silently reassigning the meal into a new borrowed step. Runtime now emits correlated accepted telemetry after coordinator acceptance and resolved telemetry when the request clears immediately, reconciles after uncertainty, or is cleared by session reset, using persisted replay state for the meal flow identifier + target step until those lifecycle events are emitted so cloud review can close the lifecycle across relaunch/terminate windows. Loop-command telemetry carries explicit command_outcome semantics so uncertain delivery is not flattened into generic blocked state.
  • SDD-POL-025 (participant target-change approval flow): The general settings target control is a constrained participant-facing action, not a direct free-form clinical config editor. HomeRegularTargetChangeApprovalPolicy first validates that the requested target belongs to the clinician-selected profile subset and that the request would change the applied target. Valid requests open an approval-capture sheet that blocks dismissal until the operator either cancels or records both approving staff name and approximate approval time. Only after successful validation does the app persist the updated target into the shared clinical config store and update the live runtime-facing target binding. Invalid approval-capture attempts emit blocked telemetry and keep the prior target unchanged.
  • SDD-POL-026 (CGM urgent-low review alert): AppCGMManagerDelegate.syncG7Alerts(for:) derives ALERT-CGM-URGENT-LOW from current live G7 data when latestReading.hasReliableGlucose == true, the reading timestamp is within the trusted freshness window, and glucose < 55 mg/dL. The alert is app-derived for clinician review and cloud telemetry, not Dexcom-app alarm acknowledgement. AppAlertCenter keeps the alert active after acknowledge by stamping acknowledgedAt rather than clearing it immediately, so Home / Alert Center can display reviewed state while the urgent-low condition remains active. The active urgent-low alert auto-clears only when a later trustworthy reading is >=55 mg/dL; stale, unreliable, or missing readings leave the active episode in place. Repeated live-state upserts preserve existing acknowledgedAt for the active episode, and a later new <55 mg/dL episode reissues as unacknowledged after recovery clears the prior episode. CGMAlertPersistenceStore persists acknowledged dedupe state so reviewed active urgent-low episodes survive app reset / delegate reattach until glucose recovery clears the episode. As with all CGM-derived alerts, this path never schedules OS local notifications.
  • SDD-POL-027 (meal cancel-delivery path): When the operator opens meal announce while pump bolus delivery is already in progress, HomeActionCoordinator routes the flow into a dedicated cancel-delivery presentation on Home instead of exposing a second meal submit path. HomeView reveals a destructive slider-backed Cancel Delivery action beneath the Manual BG / Let's Eat controls, and that action can now remain visible automatically while an active meal-announcement bolus is still in progress, even without a second tap on Let's Eat, by deriving the active cancelable state from current pump delivery state plus the matching meal step telemetry. When PumpStatus.lastDelivery is unavailable during reconnect/status-thin windows, Home falls back to the latest still-delivering meal step record so the cancel affordance is not hidden solely because detailed delivery accounting has not yet arrived. The cancel action calls PumpStatusObserver.cancelBolusDelivery(), which delegates to PumpService.cancelBolus() and converts the canceled dose into actual requested/delivered units. On success, Home shows an orange partial-delivery summary in the normal alert-summary region above the chart so the operator can see how much insulin was already delivered before cancellation before later reopening meal announce. That summary now carries cancel time plus meal type/size context derived from the matching meal step telemetry, so the operator can distinguish which meal was interrupted without reopening the composer. The summary is retained until both at least one later algorithm step has executed and at least 5 minutes have elapsed, preventing the warning from disappearing immediately after the next step. The Home insulin chart derives interrupted-delivery styling directly from existing step telemetry (requestedUnits vs deliveredUnits), but active in-progress meal delivery remains rendered in the normal meal-dose color while the pump still reports .delivering; LoopTelemetryStore now marks a successfully issued bolus as delivering immediately until the first pump refresh arrives, so a just-started meal bolus does not flash yellow while waiting for hardware status. Caution color is reserved for actual interrupted/partial delivery once delivery has been stopped or otherwise ended short. When cancel succeeds, PumpStatusObserver explicitly reconciles the shared LoopTelemetryStore step record with the cancel-returned delivered amount and forces the corresponding local delivery state back to idle, so the chart bar height reflects actual delivered insulin even if the immediate post-cancel status refresh or lagging LoopKit event history still overstates the completed amount. Later pump-status observer refreshes continue reconciling the shared LoopTelemetryStore record for the same step until delivery completes, so an in-progress partial snapshot does not remain yellow on the chart after a later refresh proves full delivery completed. The chart render compactor preserves .delivering when adjacent bars collapse onto one display pixel so compaction cannot strip active-delivery styling. The pump adapter preserves the delivered amount in PumpStatus.lastDelivery so the next algorithm step consumes actual partial-delivery evidence rather than assuming the full original bolus completed, and when LoopKit event history lags a later pod status refresh it now floors delivered insulin to the pod-reported requested - bolusNotDelivered amount instead of regressing a completed bolus back into partial-delivery state.
  • SDD-POL-028 (operator-controlled algorithm session lifecycle): LoopRuntimeEngine treats algorithm start, stop, reset, and replacement session creation as explicit operator actions only. Recovery code paths may preserve or clear blocked fallback state, retry pump schedule restoration, suppress replay, continue the existing cadence slot, and emit unreconciled telemetry, but they shall not silently stop/start/reset the algorithm or create a replacement algorithm session. For masked-fallback recovery, same physical pod continuity is established by persisting the fallback pod identity with the connected delivery baseline and comparing it with the recovery snapshot; different/new pod, unknown identity, or delivery-counter/history discontinuity suppresses replay while preserving the existing session/cadence state if the loop remained armed. If the previous pod is known inactive/retired or past hard service-stop before restore evidence can be collected, the recovery path preserves the existing algorithm session and records assumed modeled fallback delivery instead of creating a new session or holding the user in an unrecoverable blocked state.
  • SDD-POL-029 (issued-dose attribution replay): LoopRuntimeCoordinator persists PendingIssuedDoseAttribution for any applied or uncertain automatic bolus command, not only meal announces. The attribution records request step, first attribution step, requested units, request/completion timing, component split (basal, correction, meal, or mixed), flow ID, and pod identity when the pump adapter can expose it. On later refresh, PumpServiceAdapter restores the pending request metadata before status reconciliation so relaunch does not lose the request step or requested-unit match. If the pump still reports active delivery on the same pod, coordinator skips the due step. If matching completed delivery evidence returns after missed steps, coordinator requires same request step, requested-unit tolerance, same pod identity, and delivered units in the credible range of 0...requested units before replaying the missed primary and secondary/safety algorithm steps before the live step, injecting the actual delivered amount only into the first missed attribution step, using CGM=-1 for synthetic history, and emitting no pump commands during replay. If persisted matching evidence exists and no historical replay rows are needed because the live due step equals the first attribution step, coordinator builds the live-step pump-status input from the persisted evidence before clearing the attribution. When reconnect recovery returns a same-pod cumulative pod-total delivery delta while a pending issued-dose attribution exists, FallbackBasalExposureReconciler partitions the known pending issued-dose requested amount from the pump total only if the total covers the request, the pending request has reached its expected physical completion time, pod identity matches, and the residual delta credibly matches modeled fallback exposure. The partition is stored as IssuedDoseReconciliationEvidence.source == partitioned_pump_delta; FallbackBasalEvent.actualDeliveredUnits remains the fallback-basal residual used for replay, and FallbackBasalEvent.pumpTotalDeliveredUnits carries the raw pump counter delta for telemetry/operator review. If partitioning is not credible, reconnect recovery remains ambiguous/unresolved, no split is guessed, and same-pod raw pump totals remain in pumpTotalDeliveredUnits while actualDeliveredUnits stays empty so UI/telemetry cannot claim residual fallback delivery. If fallback-basal replay is also pending, issued-dose replay is bounded before the first fallback replay row or merged into that first row when the attribution step overlaps, avoiding duplicate step replay; fallback merge uses the same delivered-unit credibility rule and otherwise keeps only the fallback-basal delivery row. If evidence is absent, mismatched, non-credible, or missing pod identity while the original pod has not been proven replaced, the pending attribution remains and live advancement is skipped. If a different/new pod identity is observed first, coordinator records IssuedDoseReconciliationFailure.reason == different_or_new_pod, preserves the algorithm session, assumes the original issued dose was delivered, feeds the requested units as delivered into the first eligible attribution step by replay or live-step input, clears the old-pod pending attribution after that consumption, strips unrelated replacement-pod lastDelivery from the resumed live algorithm input, and permits later insulin-adding automatic commands on the replacement pod. Legacy persisted activeIssuedDoseReconciliationHold values with different_or_new_pod_unresolved are cleared/nonblocking under this clinical-policy disposition. Active issued-dose holds remain available only for true pending/held clinical-disposition cases. A possible future algorithm-adaptation control, pending team signoff, is to forget or discount larger unconfirmed assumed-delivered doses after about 4 hours; that is not implemented in this slice because Algo2015 adaptation state is opaque and needs algorithm-owner design. When Home presents an unresolved meal-delivery progress modal for a matching meal-linked pending attribution and the original pod is known unavailable/service-stopped/different or has not produced matching evidence by expected physical completion plus two minutes, MealDeliveryPodReplacementEscapePolicy exposes a Replace Pod action. The action uses LoopRuntimeEngine.reconciledUserAbandonedMealIssuedDoseEvidenceState(...) to persist assumed-delivered evidence with IssuedDoseReconciliationFailure.reason == user_abandoned_unavailable_pod, clears only the meal-progress UI fields, leaves PendingIssuedDoseAttribution for coordinator replay/live-step consumption, emits the meal resolved telemetry path as uncertain/reconciled, and then routes to pod setup. If legacy/partial state lacks pod identity, coordinator accepts this assumed-delivered evidence only for the user-confirmed unavailable-pod path and only when request step, requested units, delivered-unit bounds, and non-active pump status match. The policy rejects correction-only or basal-only pending attributions and hides the action once matching reconciliation evidence exists.
  • SDD-POL-008 (Alert policy, partial): multi-source alert normalization model drives pump and CGM alert mapping plus pump signal-loss debounce (5 minutes), no-active-pod debounce (5 minutes), repeating safety-critical notification attempts every 30 minutes while the condition remains true, dedupe, deterministic alert precedence, and auto-clear behavior. Home active-alert presentation uses a vertical carousel ordered by severity then recency, with multiplicity indicator and step-through controls when multiple alerts are active. High-priority non-CGM alerts additionally route to background local notifications through AppAlertCenter with per-alert dedupe and severity cooldown (actionable: 5 minutes, safetyCritical: 60 seconds), and resolved alerts clear matching pending/delivered OS notifications. CGM availability/failure alerts are informational in-app status only and never schedule background local notifications; the FDA-cleared Dexcom application remains the source of truth for CGM alarming. Notification-tap routing maps alert category to app context (pump -> Pod modal, cgm -> CGM modal, non-device -> Home); the cgm route remains defined for in-app navigation/preview support even though production CGM alerts do not schedule OS notifications. Safety-critical alerts expose explicit acknowledge action on Home banner/carousel and in Alert Center (ackState == requiresAcknowledge); current production required-ack alerts are pump fault, pump incompatible, and app-derived ALERT-CGM-URGENT-LOW. OS-notification clear calls are idempotent on every retract attempt (including already-cleared keys) to cover async schedule/retract races; alert lifecycle telemetry still emits alert.notification.cleared only when an active alert transitions to removed. App-level active/recently-cleared alert state is persisted and restored across relaunch; pump/cgm delegate PersistedAlertStore hooks are implemented for issued/unretracted/retracted lifecycle lookup. On no-active-pod restore/sync cleanup, only non-critical pod-tied alerts are auto-retracted; ALERT-PUMP-FAULT and ALERT-PUMP-INCOMPATIBLE are retained until explicit lifecycle retraction or acknowledge closure. Time-sensitive alert countdown text (ALERT-PUMP-EXPIRING and ALERT-PUMP-EXPIRED) is carried as countdown metadata and refreshed by AppAlertCenter on a minute interval while active so displayed remaining time stays current. Pod expiration/expired alerting is sourced from both delegate issue/retract callbacks and state-driven synthesis from pump expiresAt on status refresh/startup to avoid relaunch misses and misclassification. For CGM, failed/expired classification is state-driven (lifecycleState/algorithmState) while delegate fallback keyword mapping prioritizes unavailable-temporary states and does not escalate failed/expired from message-only payload text; the only app-derived CGM required-ack path is the urgent-low review alert defined in SDD-POL-026. Runtime-derived ALERT-ALGORITHM-STEPPING-INTERRUPTED is step-based rather than CGM-receipt-based, carries blocker detail, routes notification-tap navigation to Home, and remains distinct from pump-native alerts while CGM state remains visible as informational context. Remaining work is expanded severity channels/escalation surfaces and real-device/background validation. Mapping baseline and source inventory are maintained in Alert Inventory and Mapping.

5. Data Persistence

  • SDD-DATA-001: runtime cadence state is persisted in UserDefaultsLoopRuntimeStateStore.
  • SDD-DATA-002: algorithm state blob is persisted in UserDefaultsAlgoStateStore.
  • SDD-DATA-003: step telemetry is persisted in LoopTelemetryStore; ongoing offline-fallback feasibility scaffolding additionally allows the shared runtime state + telemetry path to persist both the latest structured fallback-basal review event and the masked-fallback maintenance state needed to safely continue connected maintenance (originalProgrammedBasalSchedule, currently programmed fallback schedule/rate, safety target, last known pod identity, last known pod total-delivery baseline/timestamp, last mask-renew time, last schedule-refresh time, and recent maintenance outcome/failure detail). The same persisted runtime state now also carries maskedFallbackRecoveryState and pendingFallbackBasalReplayPlan (a legacy field name now used for pump-delta reconciliation metadata) so offline mask expiry or restore failure can survive relaunch, drive reconnect/start/foreground restore retry, continue blocking arm/step/meal/BG flows until reconnect recovery resolves, and then support confirmed/corrected no-command missed-step algorithm replay or explicit ambiguous logging-only resume after successful disarm. Runtime state also persists pending issued-dose attribution, partitioned issued-dose reconciliation evidence derived from a credible same-pod pump delta, the most recent issued-dose reconciliation failure, and any active issued-dose reconciliation hold needed for true pending/held clinical-disposition cases; known different/new-pod issued-dose cases now record the failure disposition and consume the attribution through assumed-delivered replay/live input without preserving a blocking hold. Runtime state also persists separate primary and secondary/safety q5 nominal-basal profiles built from scalar nominalBasal output at each algorithm step, with up to seven observations per local-time slot and imputation for unobserved slots. Resolved fallback review events now persist modeled expected fallback delivery, actual fallback-basal residual delivery only when same-pod continuity and delivery-counter credibility are proven, raw pump-total delivered-insulin delta when available, pod-continuity metadata, and reconciliation metadata (replayedFirstStep, replayedLastStep, replayedDeliveredUnits, retained as legacy field names for pump-delta review) so Home review can distinguish pump-delta-reconciled recovery from ambiguous logging-only recovery. Confirmed/corrected recovery persists missed-step algorithm replay telemetry rows from credible same-pod pump-reported delivered-insulin evidence, with unavailable CGM (-1), persisted-schedule-weighted per-step delivery input, and no catch-up pump commands; modeled expected delivery remains review-only when pump evidence is unavailable, weak, from a different/new pod, from unknown identity, or cannot be safely allocated because positive pump delta has no usable schedule weight. Retired/expired/no-active pod fallback recovery is persisted as a separate assumed-delivered disposition: modeled expected delivery is copied into actual delivered units only with assumed_delivered_per_clinical_policy, and replay/review telemetry must preserve that source so assumed fallback accounting cannot be mistaken for pump-counter-confirmed delivery.
  • pendingFallbackBasalReplayPlan.firstReplayStep / lastReplayStep denote the fallback-active replay interval, not the full disconnected gap. The stored plan includes explicit 0 U deliveries only for replay-eligible fallback-active slots.
  • SDD-DATA-004: device managers are persisted via app delegates for reconnect behavior.
  • SDD-DATA-005: alert lifecycle state is persisted in both AppAlertCenter (active + recently-cleared timeline, capped at 100 entries) and delegate-level LoopKit PersistedAlertStore implementations (issued/retracted records for pump/cgm sources).
  • SDD-DATA-006: clinical settings are persisted in a versioned model with deterministic defaults and migration behavior; weight is normalized lbs (UI) -> kg (runtime input). The persisted clinical config now also carries the participant target-access profile, with migration default/inference rules so legacy saved target values load into either Pregnancy or Standard deterministically.

6. Requirement Allocation

SRS ID Design Element(s)
SRS-RUN-001..006 SDD-APP-001, SDD-CORE-001, SDD-POL-001, SDD-POL-021, SDD-PUMP-001
SRS-ALG-001..007 SDD-ALG-001, SDD-QA-001
SRS-CGM-001..005 SDD-CORE-001, SDD-POL-002, SDD-POL-020, SDD-CGM-001, SDD-APP-003, SDD-SIM-001
SRS-BG-001..012 SDD-APP-001, SDD-CORE-001, SDD-BG-001, SDD-POL-010, SDD-POL-011, SDD-POL-012, SDD-LOG-001
SRS-CLIN-001..012 SDD-CLIN-001, SDD-POL-013, SDD-POL-017, SDD-POL-025, SDD-DATA-006
SRS-PUMP-001..010 SDD-PUMP-001, SDD-POL-003, SDD-POL-029, SDD-SIM-001, SDD-DATA-003
SRS-MEAL-001..006 SDD-APP-001, SDD-CORE-001, SDD-POL-004
SRS-MEAL-007..011 SDD-APP-001, SDD-PUMP-001, SDD-LOG-001, SDD-POL-023, SDD-POL-024, SDD-POL-027
SRS-STATE-001..005 SDD-DATA-001..005, SDD-POL-005, SDD-POL-028, SDD-POL-029, SDD-PUMP-001
SRS-LOG-001..009 SDD-LOG-001, SDD-SIM-001, SDD-POL-017, SDD-POL-018, SDD-POL-024, SDD-POL-025, SDD-APP-007, SDD-POL-028
SRS-UI-001..008 SDD-APP-001, SDD-POL-006, SDD-POL-007, SDD-POL-009, SDD-POL-014, SDD-POL-018, SDD-POL-019, SDD-CGM-001
SRS-VAL-001 SDD-CLIN-001, SDD-DATA-006
SRS-ALERT-001..017 SDD-ALERT-001, SDD-POL-008, SDD-POL-020, SDD-POL-022, SDD-POL-026, SDD-DATA-005
SRS-SEC-001..002 SDD-LOG-001 + Docs/Quality/CybersecurityPlan.md (current software handoff scope)
SRS-SEC-003..004, SRS-SEC-006..009 SDD-AUTH-001, SDD-AUTH-002, SDD-POL-015, SDD-POL-016 + Docs/Quality/CybersecurityPlan.md (documented implementation; deferred from current software handoff package)

7. Design Constraints

  • iOS background execution is opportunistic; runtime requires robust skip/degraded behavior.
  • External device SDK behavior (OmniBLE, G7SensorKit) constrains event cadence and status timing.
  • Closed-loop safety policy requires avoiding manual bolus path exposure.

Security/auth scope note:

  • SDD-AUTH-001, SDD-AUTH-002, SDD-POL-015, and SDD-POL-016 document current implementation boundaries because the code exists in the repository.
  • For the current engineering software handoff package, those auth/provider/session-continuity design elements are documented implementation context only.
  • Closure claims for SRS-SEC-003..009 remain deferred and are not being asserted through the current handoff package.

8. BG Design and Pending Decisions

8.1 Functional flow

  1. User opens BG entry flow and submits BG with explicit confirmation.
  2. Runtime validates session state and computes expectedStep.
  3. Submitted BG must be within 20...600 mg/dL.
  4. Runtime creates/updates one pending BG candidate:
  5. if due step has not executed yet, target that due step;
  6. if due step already executed, target immediate next step.
  7. if a candidate already exists, replace with newer BG submission.
  8. Runtime dispatches doWork(cause: bgCheck) without borrowing future slots.
  9. On execution, coordinator consumes pending BG candidate only when step index matches candidate target and candidate freshness is valid.
  10. Coordinator builds algorithm input with:
  11. BGval from submitted manual BG.
  12. CGM input from normal CGM mapping policy (may remain -1).
  13. Algorithm executes once for due step.
  14. Command application follows normal pump safety policy.
  15. Telemetry records manualBG source, value, timestamps, and execution outcome.
  16. If candidate target step is missed without consumption, runtime discards candidate as too old.

8.2 Guards and invariants

  • No future-slot borrowing is allowed for bgCheck.
  • Exactly one pending BG candidate is kept at a time.
  • New BG submit replaces existing pending candidate before consumption.
  • Pending BG candidate is single-step scoped and expires if not consumed on immediate next target step.
  • Manual BG freshness is required at execution time.
  • Pump-unavailable behavior remains unchanged from degraded-mode policy.
  • Before first successful anchored step exists, manual BG submit is rejected (step-0 BG rescue disabled by current policy).

8.3 Pending decisions before implementation lock

  • Final manual BG freshness window.
  • Step-0 BG-rescue policy (SRS-BG-008) is deferred; current implementation remains CGM-only at step 0.

9. Simulation Strategy (Medium now, High later)

9.1 Medium-fidelity harness (in-scope)

  • Architecture:
  • Scenario runner emits deterministic event timelines into runtime ports.
  • Mock services conform to production protocols (CGMService, PumpService) so runtime/coordinator logic remains unmodified.
  • Virtual clock controls step-slot progression (300s cadence, early window, skip/catch-up timing).
  • Scenario event classes:
  • CGM readings (value, timestamp, reliability, availability transitions)
  • pump status transitions (idle, delivering, unknown, disconnect/reconnect)
  • command-result events (success/failure/reconciliation data)
  • normalized alert issue/retract events
  • Required outputs:
  • executed step index and skip reason
  • algorithm input/output snapshot
  • command-application decision
  • alert center active/recent lifecycle state

9.2 High-fidelity emulator (future)

  • Target:
  • BLE/session-level emulation for Omni/G7 transport behavior (timing jitter, ACK patterns, session-restore edge cases).
  • Positioning:
  • not a near-term gating dependency; added after medium-fidelity harness is stable.

9.3 Safety/testing contribution

  • Improves hazard-path reproducibility for race conditions that are hard to force on demand with real devices.
  • Expands deterministic pre-merge coverage for gating failures (step timing, degraded inputs, command-block behavior, alert escalation).
  • Produces traceable verification artifacts that can be mapped to RA/SRS/TV with lower variance than ad hoc manual runs.
  • Complements, but does not replace, hardware-in-the-loop verification for true transport and physical-delivery behavior.