Skip to content

Developer Change Plan

This document outlines where code changes are intended to land as the integration work continues. It is meant for internal engineering coordination.

Core Loop + Algorithm

  • BionicLoopCore/Sources/BionicLoopCore/Algorithms/
  • Extend RealBUDosingAlgorithm to map additional inputs/outputs as the C++ API evolves.
  • Translation logic for meal announcements is implemented (MealAnnouncement -> Algo2015 mealTime / mealSize).
  • Add translation logic for pregnancy targets.
  • BionicLoopCore/Sources/BionicLoopCore/Runtime/
  • Enhance LoopRuntimeCoordinator scheduling, retry logic, and offline fallback tracking.
  • Persist last‑run timestamps and loop health state.

Marjorie I/O Parity Workstream

  • Detailed comparison and extracted requirements are documented in:
  • Docs/Analysis/Marjorie_AlgorithmIO_GapAnalysis.md
  • Priority implementation slices:
  • Add canonical per-step telemetry persistence (input + output + applied delivery) as a single source of truth.
  • Expand bridge/output mapping to include key fields needed for audit and chart parity.
  • Resolve policy decision for pump-unavailable step execution (skip vs execute-with-unavailable flags).
  • Add test matrix coverage for degraded CGM/pump states and step continuity.

Algorithm Update Plan (Detailed)

  • Input mapping:
  • Build a single Swift “input model” that mirrors the C++ structs (glucose history, insulin history, device status, user weight, target selection, meal announcements).
  • Centralize unit conversions (mg/dL, U/hr, minutes) and keep them in one file to prevent drift.
  • Output mapping:
  • Translate the C++ recommendation into DosingRecommendation with explicit 5‑minute dosing output (bolus/micro‑dose), plus any flags or status codes.
  • Preserve algorithm status details for logging and UI visibility (e.g., “insufficient data”, “using fallback basal”, “safety limited”).
  • State handling:
  • Maintain a stable per‑subject state (subject ID, last run time, persistent algorithm state if required by the C++ API).
  • Add a versioned “algorithm state blob” store if the C++ interface needs to persist internal state across app restarts.
  • Error handling:
  • Normalize error codes from the C++ layer into a small set of Swift errors; log full raw codes for debugging.
  • Ensure the loop fails closed (no dosing) on malformed inputs.
  • Testing strategy:
  • Add a golden‑input test harness that runs fixed inputs through the C++ wrapper and checks for deterministic outputs.
  • Add “missing data” tests (no CGM, stale CGM, missing pump status).
  • Add regression tests for target selection and meal sizing changes.

Algorithm Changes for Protocol Requirements

  • Pregnancy targets:
  • Add lower targets (90/100 mg/dL) to the target selection logic and surface them in the UI and input mapping.
  • Meal fraction:
  • Update meal controller fraction to 90% for pregnancy configuration.
  • Ensure the change is configurable per study phase if needed.
  • Initialization:
  • Enforce weight‑only initialization (no carb ratios or correction factors).
  • Ensure default tmax remains fixed at 65 minutes.
  • CGM downtime rules:
  • Accept fingerstick BG as CGM input during CGM downtime.
  • Use weight‑based basal early, then adaptive basal after >= 24 hours of history.

Loop Timing + Step Semantics (doWork Plan)

Operational cadence assumptions (current hardware behavior)

  • CGM cadence assumption: new G7 glucose values arrive approximately every 5 minutes.
  • Pump transport cadence assumption: Omnipod DASH BLE disconnect/reconnect occurs approximately every 3 minutes (pod-driven).
  • Implication:
  • Current runtime execution is triggered by new CGM readings only.
  • Pump reconnects are transport/state events and must not independently advance algorithm step.
  • If both G7 and DASH are out of BLE range, the app has no reliable wake/data path and cannot safely execute algorithm steps.

Legacy BUMarjorie timing behavior to mirror

  • MasterController computes next step wall-clock as:
  • timeOfNextStep = firstStepTime + algorithmTime * 300s
  • Work is driven by doWork and split into:
  • Device reads (CGM, insulin pump, glucagon pump)
  • Algorithm execution scheduling
  • A single cycle trigger uses an early window:
  • It schedules once when timeUntilStep <= 300s and last schedule is older than 273s (300 - 27).
  • Practical effect: one device/algorithm scheduling pass per 5-minute cycle, approximately 27 seconds before nominal boundary.
  • Step skipping is wall-clock based:
  • If late beyond grace, inferred step is floor((now - firstStepTime) / 300s) + 1.
  • algorithmTime is advanced to inferred step before next run.
  • AlgorithmController only increments step after a successful run:
  • Input uses current algorithmTime as inputData.time.
  • After success, state saves and algorithmTime = algorithmTime + 1.
  • Step 0 is blocked until either CGM or BG exists.
  • BLE/device-driven wake behavior exists through device notifications:
  • DeviceController.deviceStateChanged triggers scheduleWorkNow.
  • This supports "run when awakened by BLE" even without always-on silent audio.

Current BionicLoop behavior (implemented)

  • Runtime path:
  • LoopRuntimeEngine in app layer receives G7 state updates and calls LoopRuntimeCoordinator.doWork(cause: .cgmUpdate).
  • Start Algo arms runtime and waits for the next new G7 reading timestamp.
  • Step 0 executes on that next new reading.
  • Timing/cadence:
  • LoopRuntimeCoordinator computes due step with early window:
    • expectedStep = floor((now + 27s - firstSuccessfulStepAt) / 300s)
  • firstSuccessfulStepAt, lastExecutedStep, and related runtime fields are persisted in UserDefaultsLoopRuntimeStateStore.
  • Duplicate wake attempts within an already-executed step index are skipped with stepNotDue.
  • Gate behavior:
  • Step 0 requires fresh usable CGM (missingFreshGlucose on failure).
  • Step > 0 can execute with unavailable CGM input (CGM_VALUE_NONE, -1) when CGM is stale/missing/out of range.
  • Pump status refresh failure executes with unavailable pump input (deliveryState = unknown) and blocks command application.
  • A single in-flight guard prevents concurrent execution overlap.
  • Home loop badge age states (Active/Aging/Stale) are based on lastSuccessfulRunAt (algorithm cadence), not raw CGM timestamp age.
  • Meal announce behavior:
  • LoopRuntimeCoordinator.announceMeal(...) supports early execution of future step slots.
  • Borrow is only allowed when:
    • current slot is not due yet (expectedStep < nextStep)
    • pump availability is known (deliveryState != unknown)
    • pump is not currently delivering
    • next scheduled slot is within 2 * 300s from now
  • Availability checks treat missing cached pump status (nil) as a cache-miss, not immediate signal loss; signal-loss gating is driven by policy state or explicit unknown status.
  • On successful borrow, execution runs immediately as nextStep and consumes that slot.
  • If request occurs after slot is due/missed (expectedStep >= nextStep), execution uses current due step (expectedStep) with meal input.
  • Borrow remains rejected with cannotBorrow for pump delivering/unknown, no anchor, or out-of-window pre-due requests.
  • tooLate wait messaging is bounded to the immediate due-step boundary (no forced extra +5 minute delay).
  • This due/missed meal behavior is provisional and requires explicit team review before release lock.
  • Step synchronization:
  • Runtime computes expectedStep.
  • RealBUDosingAlgorithm is synchronized to expectedStep before recommendation generation.
  • Bridge call still increments step by one after successful algorithmRunStep.
  • Session control:
  • Manual test bolus UI is removed.
  • Reset Algo performs full fresh-session reset:
    • clears persisted algorithm state (UserDefaultsAlgoStateStore)
    • clears persisted runtime cadence state (UserDefaultsLoopRuntimeStateStore)
    • clears pump adapter request/delivery carry-over metadata
    • clears persisted recent-step timeline used by UI

Pump-unavailable policy alignment (BUMarjorie parity review)

  • Legacy BUMarjorie behavior (documented in Docs/Analysis/BUMarjorie.md):
  • Step executes even when pump is unavailable.
  • Algorithm input marks pump unavailable and uses invalid request/delivery fields.
  • Delivery is skipped if unavailable, but step/time still advances.
  • Current BionicLoop behavior:
  • doWork skips entirely when pump status refresh fails (pumpStatusUnavailable).
  • Decision/work item:
  • Policy selected and implemented: POLICY-B (run step with unavailable pump input and gate delivery path only).
  • Implementation tasks after policy decision:
  • Keep step executed separate from delivery applied in persisted runtime telemetry.
  • Ensure next-step input carries last request/delivery fields only when they are valid.
  • Keep reconnect/recovery path deterministic and visible in logs/UI.
  • Required tests:
  • Pump unavailable at step boundary.
  • Reconnect before next step with valid status read.
  • Delivery command failure after successful status read.
  • Step counter behavior under each policy.
  • CGM sanitization safety: <39 and >401 map to CGM_VALUE_NONE (-1) before algorithm input.

Remaining implementation plan (no silent-audio dependency)

  • Keep doWork CGM-driven for study closed-loop operation unless requirements change.
  • Add explicit fallback mode state machine (degraded/offline) and user status messaging.
  • Add fingerstick BG fallback path for step 0 when protocol behavior is enabled.
  • Evaluate additional wake paths (BGTask, reconnect-triggered run attempts) only after policy review.

BG check doWork policy (planned, now in scope)

  • Goal:
  • Allow manual BG entry to drive algorithm execution when CGM-triggered execution is not viable.
  • Trigger model:
  • Add a dedicated wake cause (bgCheck) from UI submit action.
  • bgCheck executes the current due step (expectedStep) only.
  • No borrowing semantics for BG check:
    • never execute a future slot early,
    • never consume nextStep ahead of schedule.
  • Algorithm input mapping:
  • Map manual fingerstick value into algorithm BG field (BGval / bridge BG input field).
  • Preserve CGM field behavior independently (CGM may still be -1 when unavailable/unusable).
  • Persist source metadata so telemetry can distinguish manual BG vs CGM driven inputs.
  • Edge-case execution requirements:
  • Case A: No CGM wake events (sensor not waking app, BLE out-of-range, etc.).
    • BG submit must trigger doWork so cadence can progress when policy allows.
  • Case B: CGM wake events occur, but CGM values are unusable for algorithm input (stale/out-of-range/invalid).
    • BG submit must still trigger doWork using BG input path.
  • Case C: Step already executed for current index.
    • BG submit returns stepNotDue; no duplicate run.
  • Case D: Step 0 with no fresh usable CGM.
    • BG path must define whether step 0 is allowed with BG-only input and be explicitly tested/approved.
  • Safety/gating:
  • Keep existing pump-ready gating and command-application gating.
  • BG entry does not override pump unavailable command block behavior.
  • Test matrix additions:
  • bgCheck executes due step when no CGM wake.
  • bgCheck executes due step when CGM wake exists but CGM is unusable.
  • bgCheck does not borrow future slot.
  • bgCheck does not duplicate same-step execution.
  • bgCheck telemetry captures BG source and value correctly.

Step increment and catch-up policy (implemented)

  • Source of truth is persisted algorithm state (timeStep) plus runtime anchor metadata.
  • expectedStep is derived from wall-clock anchor and early window (+27s lead).
  • If runtime is late, coordinator catches up by synchronizing algorithm step before running.
  • If expectedStep <= lastExecutedStep, algorithm execution is skipped for idempotency.
  • On successful algorithm call, bridge increments algorithm step by one.

Testable requirements for timing behavior

  • TIMING-01: Step 0 gate
  • Given no fresh CGM, step 0 is not executed and step counter does not advance.
  • TIMING-01a: Step > 0 degraded CGM execution
  • Given stale/missing/out-of-range CGM and step index > 0, runtime executes using CGM_VALUE_NONE (-1) input.
  • TIMING-02: Single-step increment
  • One successful run advances step exactly by 1.
  • TIMING-03: Wall-clock catch-up
  • If app wakes after missing >= 1 cycle, step aligns to inferred wall-clock step before next run.
  • TIMING-04: No duplicate execution in one cycle
  • Multiple wake events inside same cycle do not cause extra step increments.
  • TIMING-05: CGM-driven trigger
  • A new G7 reading timestamp triggers doWork attempt within bounded latency.
  • TIMING-06: Restart continuity
  • After app quit/relaunch, scheduler restores prior step timeline and does not reset to step 0.
  • TIMING-07: Freshness gate (step 0)
  • If latest CGM exceeds configured max age before first successful step, run is skipped and reason is logged.
  • TIMING-08: Delivery feedback continuity
  • Next-step input contains prior request time/units requested/units delivered when available.
  • TIMING-09: Observability
  • Each attempt logs: cause, expected step, persisted step, run/skip reason, and post-run step.
  • TIMING-10: Pump reconnect non-trigger
  • Pump reconnect events alone do not trigger algorithm execution.
  • TIMING-11: Foreground non-trigger
  • App foreground/active transition alone does not trigger algorithm execution.
  • TIMING-12: CGM-step coupling
  • With CGM values at ~5-minute cadence, algorithm executes at most once per 5-minute step index.
  • TIMING-13: Reset fresh start
  • After Reset Algo, next run starts at step 0 with no carried-over request/delivery metadata.
  • TIMING-14: Out-of-range hard gate
  • Step 0 still requires usable CGM; for step > 0, unavailable CGM and unavailable pump execute in degraded mode with no command application.
  • TIMING-15: Fallback transition (planned)
  • After configured outage threshold (for example >= 15 minutes without valid CGM + successful run), loop transitions to offline/degraded mode and emits user-visible status.
  • TIMING-16: Fallback recovery (planned)
  • Once connectivity/data gates recover, loop exits offline/degraded mode and resumes normal step scheduling without resetting step timeline.
  • TIMING-17: Meal borrow window and consumption
  • Two immediate meal borrows are allowed when within 10 minutes of future slots, third is rejected.
  • A borrowed step does not execute again at its original wall-clock slot.
  • TIMING-18: Meal borrow rejection paths
  • Borrow is rejected for pre-due out-of-window requests, pump delivering, pump unknown, or missing anchor.
  • TIMING-19: Meal announce on due/missed slot (provisional)
  • If meal announce is requested after a slot is due/missed, runtime executes meal on current due step (expectedStep) rather than rejecting for late borrow.
  • Requires team/clinical sign-off before release behavior lock.
  • TIMING-20: Delivery-state auto-clear visibility
  • While pump reports delivering, status is polled and Home/meal gating clear to non-delivering state without user entering pump settings.
  • TIMING-21: BG check due-step execution only
  • BG-triggered doWork executes only current due step (expectedStep) and never borrows a future slot.
  • TIMING-22: BG check rescue path for CGM wake failure
  • If CGM wake does not occur, BG entry can still trigger execution (subject to other gates).
  • TIMING-23: BG check rescue path for invalid CGM data
  • If CGM wake occurs but CGM input is unusable, BG entry can still trigger execution with BG input mapping.

OmniBLE Integration

  • BionicLoopCore/Sources/BionicLoopCore/Ports/
  • Keep PumpService interface stable for OmniBLEPumpManager adapter use.
  • BionicLoop/Integrations/Pump/
  • Maintain PumpServiceAdapter and reconciliation of pump history using dosesForStorage() after reconnect.
  • BionicLoop/Features/Pump/
  • Wire Pump UI entry points, pairing flow, and status display.

G7 Integration

  • BionicLoopCore/Sources/BionicLoopCore/Ports/
  • Keep CGMService interface stable for G7SensorKit adapter use.
  • BionicLoop/Integrations/CGM/
  • Maintain CGM manager adoption/restore flow and sensor lifecycle edge handling.
  • BionicLoop/Features/CGM/
  • Continue onboarding/settings UI integration and edge‑case handling.

Offline/Fallback Behavior

  • BionicLoopCore/Sources/BionicLoopCore/Runtime/
  • Add offline mode state machine (trigger at >= 15 min without valid CGM + algorithm).
  • Define and enact offline basal via DASH.
  • BionicLoop/Features/Home/
  • Show offline mode banner and recovery messaging.

User Alerts (Planned)

  • BionicLoop/Runtime/
  • Add alert normalization service that consumes runtime/policy events and produces canonical alert objects.
  • BionicLoop/Integrations/Pump/
  • Map Omni alert/state events into normalized alert types with severity and dedupe keys.
  • BionicLoop/Integrations/CGM/
  • Map G7 alert/state events (sensor reliability, disconnect duration, critical glucose conditions) into normalized alert types.
  • BionicLoop/Features/Home/ and settings/modals
  • Add consistent alert presentation surfaces and acknowledgment/clear interactions.
  • Cross-cutting
  • Add alert precedence/debounce policy and trace to SRS-ALERT-* and TV-ALERT-*.

Persistence + State Restore

  • BionicLoopCore/Sources/BionicLoopCore/Persistence/
  • Persist loop state, last successful run, and last known device status.
  • BionicLoop/Integrations/
  • Restore device sessions on app launch and avoid unnecessary re‑pairing.

Home Charts (Scout Reuse Plan)

Legacy chart code review summary

  • Source reviewed:
  • /Users/jcostik/Scout/evan2020/Charts/EGVChart.swift
  • /Users/jcostik/Scout/evan2020/Charts/InsulinChart.swift
  • Keep:
  • Lightweight SwiftUI rendering (no heavy chart framework dependency).
  • Drag-to-scrub interaction pattern.
  • Time-axis labels and glucose visual zones from EGVChart.
  • Refactor:
  • Remove dependency on Scout ChartData/Egv model.
  • Remove legacy interpolation assumptions tied to remote API payload shape.
  • Convert insulin bars from equal-spacing to time-based x positioning so insulin and CGM align on the same timeline.

Current implementation status

  • Implemented in HomeView:
  • Shared-range CGM + insulin overlay chart with aligned x-axis placement by timestamp.
  • Time window selector (4h, 8h, 12h, 24h).
  • Scrub pills above chart (CGM, Dose) driven by chart scrub position.
  • Dose scrub output includes 0.0U cases and time output even when no nearby insulin step exists.
  • Recent step timeline moved into Bionic Loop Settings -> Recent Dose Steps.
  • Styling/interaction state now implemented:
  • Dashed rounded plot border with matching corner radius.
  • Left/right y-axis labels anchored to chart container edges.
  • Continuous scrub line tracking across chart area.

Single source of truth for Home chart/list data

  • Implemented:
  • LoopTelemetryStore in app layer as Home chart/list projection source.
  • Persist rolling CGM and step telemetry windows in UserDefaults JSON.
  • Runtime now writes executed-step telemetry from DoWorkResult and reconciles with pump status updates.
  • G7 view model ingests both current reading and persisted history into telemetry store.
  • Canonical records:
  • CGMPoint: timestamp, glucose mg/dL, trend (optional), source (live, backfill).
  • DosePoint: algorithm step, requested units, delivered units, request timestamp, delivered timestamp, status.
  • StepContext: step, CGM used by algorithm (value + timestamp), loop cause.
  • Home UI (charts + step list) reads from this store.
  • Runtime/pump/CGM adapters write to this store; Home must not compute independent state copies.

Ingestion paths

  • CGM stream:
  • Capture readings in AppCGMManagerDelegate.cgmManager(_:hasNew:) from CGMReadingResult.
  • Persist every valid sample used for display, not just the latest sample.
  • Algorithm/runtime stream:
  • On executed step, write StepContext (step index, CGM used, execution timestamp, wake cause).
  • Pump stream:
  • Continue reconciling delivery via PumpStatusObserver + PumpServiceAdapter.
  • Upsert DosePoint by step ID so requested vs delivered can evolve as pod finalizes bolus.

UI composition plan

  • Replace current "Recent Dose Steps" ad-hoc rendering with projection from telemetry store.
  • Add two Home chart components backed by the same projection:
  • EGVChartView (ported/refactored from Scout EGVChart).
  • InsulinDoseChartView (ported/refactored from Scout InsulinChart with time-based x-axis).
  • Keep text list under charts for quick debugging:
  • Step, requested/delivered units, step timestamp, CGM value/timestamp used.
  • Scrubbing behavior:
  • CGM chart scrub shows glucose + absolute timestamp.
  • Insulin chart scrub shows delivered/requested units + step + absolute timestamp.

Acceptance requirements (testable)

  • CHART-01: Single-source projection
  • Home chart data and Home step list are generated from the same telemetry store snapshot.
  • CHART-02: CGM history persistence
  • After app relaunch, Home chart still renders recent CGM points without requiring a new read first.
  • CHART-03: Dose reconciliation update
  • When pod delivery status transitions (in-progress -> finalized), existing step point is updated in place (no duplicate step rows).
  • CHART-04: Step reset behavior
  • Reset Algo clears telemetry tied to prior session; next session begins with step 0 and fresh chart/list history.
  • CHART-05: Time-axis alignment
  • CGM and insulin charts place points/bars by timestamp, not by array index.
  • CHART-06: Zero-dose visibility
  • Step list/chart still include executed steps with 0.0U delivery so skipped/no-dose behavior is visible.

Rollout order

  1. Done: Add telemetry store models and persistence schema.
  2. Done: Move Home chart/list reads to telemetry store snapshot.
  3. Done: Wire ingestion from CGM + loop runtime + pump reconciliation.
  4. Done: Add store unit tests (LoopTelemetryStoreTests) and coordinator telemetry tests.
  5. Pending: Add UI smoke tests for range switching and scrub behavior with sparse CGM/dose data.

Testing

  • BionicLoopTests/
  • Add unit tests for loop scheduling, offline fallback, and reconciliation logic.
  • BionicLoopUITests/
  • Add onboarding flow smoke tests for G7 and DASH.

Docs

  • Docs/Architecture/Architecture.md and Docs/Requirements/Requirements.md should be updated alongside any new scheduling or device‑integration behavior.