Skip to content

Use LoopKit's value types and bridge to LoopAlgorithm - #2635

Merged
ps2 merged 4 commits into
next-devfrom
plain-numbers
Nov 1, 2026
Merged

ps2 merged 4 commits into
next-devfrom
plain-numbers

Conversation

@ps2

@ps2 ps2 commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Part of decoupling LoopKit and LoopAlgorithm (so Trio can use LoopKit without the algorithm). It depends on:

With those, Loop is the one place that converts between LoopKit's types and the algorithm's.

What changes

  • Loop/Models/AlgorithmBridge.swift:
    • StoredGlucoseSample and StoredCarbEntry conform to the algorithm's protocols.
    • LoopKit insulin models are wrapped for the algorithm.
    • Schedules convert to mg/dL; ISF, targets and the suspend threshold go in as sensitivityMgdlPerUnit, targetMgdl and suspendThresholdMgdl.
    • Algorithm results convert back to LoopKit values for storage, display, the watch and the extension context.
  • Common/LoopKitTypeAliases.swift: makes unqualified names like GlucoseEffect mean LoopKit's in each target. Algorithm types are written LoopAlgorithm.X. The struct is now Algorithm, so calls are Algorithm.run(input:).
  • Where LoopAlgorithm is linked: once, in LoopCore. The widget, watch app and LoopCore's own code no longer import it; 15 files in the app still do.
  • LoopCore constants: gains the two constants the UI used from LoopAlgorithm (input recency, maximum carb absorption time).
  • Current Setup: names the slow retrospective correction experiment that the LoopAlgorithm update brings in (Guard + Runway, second version; slow retrospective correction experiment (off by default) LoopAlgorithm#16). Loop doesn't turn it on.

Testing

  • New end-to-end mmol/L test (testMmolSettingsDoseTheSameAsMgdl). With ISF, targets, suspend threshold and glucose all in mmol/L, the algorithm receives mg/dL values and Loop doses exactly as in the mg/dL test.
  • Moved tests: two override tests moved from LoopKit; they check LoopKit's override schedules flowing into the algorithm. The dose annotation and IOB tests went to Take plain numbers in fixed units at the public API LoopAlgorithm#17 instead.
  • LoopTests: 415 pass. The full LoopWorkspace build has all 19 plugins.
  • Simulator run, on the prototype of this change before rebasing: closed loop with mock CGM and pump (automatic boluses, temp basals, carbs, the bolus screen). I saved the input of 17 algorithm runs and replayed each through the previous LoopAlgorithm. Forecasts, effects, IOB/COB and recommendations matched the app's output within 1e-9.

LoopKit no longer depends on LoopAlgorithm, and LoopAlgorithm takes plain
numbers in fixed units (LoopKit/LoopAlgorithm#17). Loop is now the one place
that converts between the two.

- AlgorithmBridge.swift: StoredGlucoseSample and StoredCarbEntry conform to
  the algorithm's protocols; LoopKit insulin models are wrapped for the
  algorithm; schedules convert to mg/dL; algorithm results convert back to
  LoopKit values for storage, display and the watch.
- LoopKitTypeAliases.swift makes the unqualified value-type names mean
  LoopKit's in each target; algorithm types are written LoopAlgorithm.X.
- LoopAlgorithm is linked once, in LoopCore; the widget, watch app and
  LoopCore's own code no longer import it.
- LoopCore gets the two constants the UI used from LoopAlgorithm.
- Name the new slow retrospective correction experiment in Current Setup.
- Tests: an end-to-end test that mmol/L settings dose the same as mg/dL, and
  the dose annotation, IOB and override tests moved from LoopKit.
- The automatic dose direction maps by switch instead of rawValue, so a case
  added on either side fails to compile instead of crashing the loop.
- Test that mmol/L settings give the same manual bolus recommendation and
  bolus preview as mg/dL with a pending carb entry.
- Test that LoopCore's copies of the input recency interval and maximum carb
  absorption time match LoopAlgorithm's.
- Fix the indentation of a predictGlucose call.
ps2 added a commit to LoopKit/LoopAlgorithm that referenced this pull request Nov 1, 2026
These test this package's dose annotation and insulin on board, so they
belong here rather than in LoopKit/Loop#2635. They read LoopKit's pump event
dose fixtures through a small test-only loader, with LoopKit's Walsh curve
and daily basal schedule expansion ported for the tests.
They test LoopAlgorithm's dose annotation and insulin on board, so they now
live there with their fixtures (LoopKit/LoopAlgorithm#17). The override tests
stay here because they cross from LoopKit's override schedules into the
algorithm.
ps2 added a commit to LoopKit/LoopAlgorithm that referenced this pull request Nov 1, 2026
* Move the dose history annotation and IOB tests from Loop

These test this package's dose annotation and insulin on board, so they
belong here rather than in LoopKit/Loop#2635. They read LoopKit's pump event
dose fixtures through a small test-only loader, with LoopKit's Walsh curve
and daily basal schedule expansion ported for the tests.

* Note the limits of the test basal schedule expansion
# Conflicts:
#	Loop/Managers/LoopDataManager.swift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant