From d03e977360e9794f7fb500c82e7db5b64298446f Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 15:50:39 -0500 Subject: [PATCH 01/58] Update submodule URLs to loopkitdev forks; add sync doc - Point all 18 submodule URLs to https://github.com/loopkitdev/* forks - Minizip and TrueTime.swift retain LoopKit origin (no forks) - Add docs/tidepool-sync-2026-03-10.md (Tidepool sync documentation) --- .gitmodules | 36 +- AmplitudeService | 2 +- CGMBLEKit | 2 +- G7SensorKit | 2 +- LibreTransmitter | 2 +- LogglyService | 2 +- Loop | 2 +- LoopKit | 2 +- LoopOnboarding | 2 +- LoopSupport | 2 +- .../xcshareddata/swiftpm/Package.resolved | 12 +- MinimedKit | 2 +- NightscoutRemoteCGM | 2 +- NightscoutService | 2 +- OmniBLE | 2 +- OmniKit | 2 +- RileyLinkKit | 2 +- TidepoolService | 2 +- dexcom-share-client-swift | 2 +- docs/tidepool-sync-2026-03-10.md | 669 ++++++++++++++++++ 20 files changed, 714 insertions(+), 37 deletions(-) create mode 100644 docs/tidepool-sync-2026-03-10.md diff --git a/.gitmodules b/.gitmodules index 1308e60c03..3637ab97e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,21 @@ [submodule "Loop"] path = Loop - url = https://github.com/LoopKit/Loop.git + url = https://github.com/loopkitdev/Loop.git [submodule "LoopKit"] path = LoopKit - url = https://github.com/LoopKit/LoopKit.git + url = https://github.com/loopkitdev/LoopKit.git [submodule "CGMBLEKit"] path = CGMBLEKit - url = https://github.com/LoopKit/CGMBLEKit.git + url = https://github.com/loopkitdev/CGMBLEKit.git [submodule "dexcom-share-client-swift"] path = dexcom-share-client-swift - url = https://github.com/LoopKit/dexcom-share-client-swift.git + url = https://github.com/loopkitdev/dexcom-share-client-swift.git [submodule "RileyLinkKit"] path = RileyLinkKit - url = https://github.com/LoopKit/RileyLinkKit + url = https://github.com/loopkitdev/RileyLinkKit [submodule "NightscoutService"] path = NightscoutService - url = https://github.com/LoopKit/NightscoutService.git + url = https://github.com/loopkitdev/NightscoutService.git [submodule "Minizip"] path = Minizip url = https://github.com/LoopKit/Minizip.git @@ -24,37 +24,37 @@ url = https://github.com/LoopKit/TrueTime.swift.git [submodule "LoopOnboarding"] path = LoopOnboarding - url = https://github.com/LoopKit/LoopOnboarding.git + url = https://github.com/loopkitdev/LoopOnboarding.git [submodule "AmplitudeService"] path = AmplitudeService - url = https://github.com/LoopKit/AmplitudeService.git + url = https://github.com/loopkitdev/AmplitudeService.git [submodule "LogglyService"] path = LogglyService - url = https://github.com/LoopKit/LogglyService.git + url = https://github.com/loopkitdev/LogglyService.git [submodule "OmniBLE"] path = OmniBLE - url = https://github.com/LoopKit/OmniBLE.git + url = https://github.com/loopkitdev/OmniBLE.git [submodule "NightscoutRemoteCGM"] path = NightscoutRemoteCGM - url = https://github.com/LoopKit/NightscoutRemoteCGM.git + url = https://github.com/loopkitdev/NightscoutRemoteCGM.git [submodule "LoopSupport"] path = LoopSupport - url = https://github.com/LoopKit/LoopSupport + url = https://github.com/loopkitdev/LoopSupport [submodule "G7SensorKit"] path = G7SensorKit - url = https://github.com/LoopKit/G7SensorKit.git + url = https://github.com/loopkitdev/G7SensorKit.git [submodule "TidepoolService"] path = TidepoolService - url = https://github.com/LoopKit/TidepoolService.git + url = https://github.com/loopkitdev/TidepoolService.git [submodule "OmniKit"] path = OmniKit - url = https://github.com/LoopKit/OmniKit.git + url = https://github.com/loopkitdev/OmniKit.git [submodule "MinimedKit"] path = MinimedKit - url = https://github.com/LoopKit/MinimedKit.git + url = https://github.com/loopkitdev/MinimedKit.git [submodule "MixpanelService"] path = MixpanelService - url = https://github.com/LoopKit/MixpanelService + url = https://github.com/loopkitdev/MixpanelService [submodule "LibreTransmitter"] path = LibreTransmitter - url = https://github.com/LoopKit/LibreTransmitter.git + url = https://github.com/loopkitdev/LibreTransmitter.git diff --git a/AmplitudeService b/AmplitudeService index 5a7e8c69f5..f094a60c2d 160000 --- a/AmplitudeService +++ b/AmplitudeService @@ -1 +1 @@ -Subproject commit 5a7e8c69f545bd8a2347dd35f68c7ac95ec4492b +Subproject commit f094a60c2d223601d36be077387591d21c53b2a8 diff --git a/CGMBLEKit b/CGMBLEKit index 134396b961..c8dbdd4aab 160000 --- a/CGMBLEKit +++ b/CGMBLEKit @@ -1 +1 @@ -Subproject commit 134396b96170d410b18f9699b92409bc6d35aedb +Subproject commit c8dbdd4aab35427dc8b7807b6e6f7ad9b856f4fd diff --git a/G7SensorKit b/G7SensorKit index 0c09305008..c86b4d4195 160000 --- a/G7SensorKit +++ b/G7SensorKit @@ -1 +1 @@ -Subproject commit 0c093050084b63d7af0dc99307dba09239eb3180 +Subproject commit c86b4d4195fcb045f06e3082e5cf00cded81f70e diff --git a/LibreTransmitter b/LibreTransmitter index 12dec3ce19..6c0e4aa667 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit 12dec3ce191afe761fd68a70172ef75638319e20 +Subproject commit 6c0e4aa66758decbe6a2537164cd2e3d36de70a5 diff --git a/LogglyService b/LogglyService index 8a7691cd74..eb04e1b7be 160000 --- a/LogglyService +++ b/LogglyService @@ -1 +1 @@ -Subproject commit 8a7691cd7497ffacf4fd8caa4a174fb860e42297 +Subproject commit eb04e1b7be192753e9f37b790b9159f8174cbc3e diff --git a/Loop b/Loop index c044b52fb3..79eee6d5cb 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit c044b52fb30f3986a7b55c6d5cb1c587c5f9a4ee +Subproject commit 79eee6d5cbb24915b6101fac476df4b4e0d7a40a diff --git a/LoopKit b/LoopKit index 58cd228556..e003e6b69e 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 58cd22855637a86fd1776d1735fec4cb2b92a460 +Subproject commit e003e6b69e61d8ba73be3e19b564c1f655fb29c9 diff --git a/LoopOnboarding b/LoopOnboarding index 60f57a77c9..7bfc5fdf8c 160000 --- a/LoopOnboarding +++ b/LoopOnboarding @@ -1 +1 @@ -Subproject commit 60f57a77c9450df17c39f475542795e72f261136 +Subproject commit 7bfc5fdf8c323fce9c1fedcf52ecbd19afea137d diff --git a/LoopSupport b/LoopSupport index 6d7d3e293e..4538579519 160000 --- a/LoopSupport +++ b/LoopSupport @@ -1 +1 @@ -Subproject commit 6d7d3e293ebc5f8b7d9f9e6b0326bf6641da8680 +Subproject commit 4538579519b80a33422097ee7d3425130be49ead diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 85b387d04a..5d6f9b843a 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "7645108625333b4ec60e0e439db0c0dc8a91ad0942d36797c6b66208a9082ea2", + "originHash" : "9fa433ef5fce7eff885b44f4dea36e033d61f148853051ee0494bf4e79200676", "pins" : [ { "identity" : "amplitude-ios", @@ -82,6 +82,15 @@ "version" : "2.0.0" } }, + { + "identity" : "loopalgorithm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tidepool-org/LoopAlgorithm", + "state" : { + "branch" : "main", + "revision" : "13cb4b45258cee5be1eb2ad941b374dde53de551" + } + }, { "identity" : "mixpanel-swift", "kind" : "remoteSourceControl", @@ -167,7 +176,6 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/LoopKit/ZIPFoundation.git", "state" : { - "branch" : "stream-entry", "revision" : "c67b7509ec82ee2b4b0ab3f97742b94ed9692494" } } diff --git a/MinimedKit b/MinimedKit index 942996e3f5..c13c039344 160000 --- a/MinimedKit +++ b/MinimedKit @@ -1 +1 @@ -Subproject commit 942996e3f53c4875553c9827aeee1799a8dbf434 +Subproject commit c13c039344decf762b988062af9f2a7c8ec752eb diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 512c9dc6d8..87e3898b01 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 512c9dc6d81b54ea9cc9a3a6681a888ea5ac91dc +Subproject commit 87e3898b01d84e0ad7f42dc6d4dc23b3f377c4d2 diff --git a/NightscoutService b/NightscoutService index d21abadedf..1ea3e43b4e 160000 --- a/NightscoutService +++ b/NightscoutService @@ -1 +1 @@ -Subproject commit d21abadedf3cb25fa4732223f3954a22050ee532 +Subproject commit 1ea3e43b4e93926f8d5670c17b2541ace65e53a8 diff --git a/OmniBLE b/OmniBLE index 3782c584fe..536efd9244 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 3782c584fe800116b6e60754e3be3cd818e033ee +Subproject commit 536efd9244d894bb67a552168fe6243dcdf124fd diff --git a/OmniKit b/OmniKit index 1446be89bf..0e3cf2e0c8 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 1446be89bfab23ac021d3e22f03b34bda8ce30cf +Subproject commit 0e3cf2e0c89b27db466da4bc43087e9be82c2f5d diff --git a/RileyLinkKit b/RileyLinkKit index 8dad76d152..b9a52dccc2 160000 --- a/RileyLinkKit +++ b/RileyLinkKit @@ -1 +1 @@ -Subproject commit 8dad76d15295e13e091be74f6f47dbca5f0eb022 +Subproject commit b9a52dccc2b5f7c69b344770b5c65da4862f907c diff --git a/TidepoolService b/TidepoolService index a10f9d3ba0..08b57560b6 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit a10f9d3ba097daae85de61d4a5bca063f34d64dc +Subproject commit 08b57560b6585fdb04d3e245d00899a0d50ff45e diff --git a/dexcom-share-client-swift b/dexcom-share-client-swift index 8c4f0edfe9..29409f34c6 160000 --- a/dexcom-share-client-swift +++ b/dexcom-share-client-swift @@ -1 +1 @@ -Subproject commit 8c4f0edfe9356463c66a2e5dff9d00794291ebfd +Subproject commit 29409f34c6fb127eec683e9133975c9fde0e015c diff --git a/docs/tidepool-sync-2026-03-10.md b/docs/tidepool-sync-2026-03-10.md new file mode 100644 index 0000000000..2569b7f625 --- /dev/null +++ b/docs/tidepool-sync-2026-03-10.md @@ -0,0 +1,669 @@ +# Tidepool → LoopKit DIY Sync — 2026-03-10 + +**Branch:** `tidepool-sync/2026-03-10` (all 18 repos) +**Build status:** ✅ Compiles clean (verified 2026-03-12) + +This document describes the changes introduced by syncing the Tidepool fork of Loop +back into DIY, the conflicts encountered during that merge, and the +decisions made to resolve them. + +--- + +## Table of Contents + +1. [New Features from Tidepool](#1-new-features-from-tidepool) +2. [Conflicts Encountered](#2-conflicts-encountered) +3. [Conflict Resolution Decisions](#3-conflict-resolution-decisions) +4. [DIY Features Preserved](#4-diy-features-preserved) +5. [Post-Merge Fixes](#5-post-merge-fixes) +6. [Testing Notes](#6-testing-notes) + +--- + +## 1. New Features from Tidepool + +### LoopAlgorithm — A New Architecture for DIY + +#### Background: What Changed and Why + +DIY Loop previously embedded algorithm logic directly +inside `LoopKit` as an inline copy (`LoopKit/LoopKit/LoopAlgorithm/`), and effects +calculations were distributed across the data store layer — `CarbStore` computed carb +effects, `DoseStore` computed insulin effects, and `GlucoseStore` computed glucose momentum +and retrospective correction. `LoopDataManager` orchestrated these by calling each store +asynchronously and stitching the results together. The central `LoopAlgorithm` type was +a Swift `actor` — a stateful, async object. + +This sync introduces the **LoopAlgorithm Swift Package** as an actual dependency, replacing +that architecture with something fundamentally different. + +#### The New Design: Functional and Stateless + +The package reconceives the algorithm as a **pure function**: given a complete snapshot of +the user's current state, produce a complete output. No stores, no CoreData, no HealthKit, +no async, no side effects. + +``` +AlgorithmOutput = LoopAlgorithm.run(input: AlgorithmInput) +``` + +`LoopAlgorithm` is now declared as a `caseless enum` — it cannot be instantiated. All +methods are static. There is no mutable state. The same input will always produce the +same output. + +**Input (`AlgorithmInput` protocol)** is a value-type snapshot containing everything the +algorithm needs: + +| Field | Description | +|-------|-------------| +| `glucoseHistory` | Recent CGM readings | +| `doses` | Insulin delivery history (basal + boluses) | +| `carbEntries` | Active carb entries | +| `basal` | Scheduled basal rate timeline | +| `sensitivity` | ISF timeline | +| `carbRatio` | ICR timeline | +| `target` | Glucose target range timeline | +| `suspendThreshold` | Low glucose suspend threshold | +| `maxBolus` | Safety limit | +| `maxBasalRate` | Safety limit | +| `maxActiveInsulinMultiplier` | Max IOB cap as multiple of maxBolus (default 2×) | +| `carbAbsorptionModel` | Parabolic, linear, or piecewise linear | +| `recommendationType` | Temp basal, automatic bolus, or manual | +| `useIntegralRetrospectiveCorrection` | IRC vs standard RC | +| `gradualTransitionsThreshold` | Threshold for gradual effect ramping | + +**Output (`AlgorithmOutput`)** is a complete value type containing everything produced: + +| Field | Description | +|-------|-------------| +| `recommendationResult` | `Result` — temp basal and/or bolus | +| `predictedGlucose` | Full glucose prediction curve | +| `effects` | Broken-out insulin, carb, momentum, RC, counteraction effects | +| `dosesRelativeToBasal` | Each dose expressed relative to scheduled basal | +| `activeInsulin` | Current IOB | +| `activeCarbs` | Current COB | + +The `effects` field (`LoopAlgorithmEffects`) exposes every intermediate calculation — +insulin effect curve, carb effect curve, carb absorption status per entry, retrospective +correction, glucose momentum, insulin counteraction velocities, and retrospective glucose +discrepancies. Previously these intermediate values were scattered across store callbacks +and ephemeral local variables inside `LoopDataManager`. + +#### Why This Matters for DIY + +**Testability.** Because the algorithm is a pure function over value types, tests require +no mocks, no CoreData stack, no async machinery. A test is just: + +```swift +let input = AlgorithmInputFixture(...) // or decoded from JSON +let output = LoopAlgorithm.run(input: input) +XCTAssertEqual(output.predictedGlucose, expected) +``` + +Fixtures can be serialized to and from JSON, making it trivial to capture a real-world +scenario and turn it into a regression test. The package ships with JSON fixture files +for glucose math, carb math, and dosing scenarios. + +**Portability.** Removing HealthKit and CoreData as dependencies means the algorithm can +run anywhere Swift runs — on a server, in a command-line tool, in a simulation, or in +tests — without needing a full iOS stack underneath it. + +**Clarity.** Previously, understanding what went into a dosing decision required tracing +through multiple async callbacks across multiple store classes. Now the entire decision +lives in one call with explicit inputs and outputs. + +#### What the Package Replaced vs. What Was Already There + +| Aspect | Before (DIY inline) | After (LoopAlgorithm package) | +|--------|--------------------|-----------------------------| +| `LoopAlgorithm` type | `public actor` (stateful, async) | `public enum` (no state, static methods only) | +| Entry point | `generatePrediction(input:startDate:) throws` | `run(input:) -> AlgorithmOutput` (non-throwing; errors in Result) | +| Effects calculation | Distributed across `CarbStore`, `DoseStore`, `GlucoseStore` | All in `LoopAlgorithm.generatePrediction(...)` static methods | +| Error handling | `throws` propagated through async chain | `Result<_, Error>` in output struct | +| Effects exposed | 5 fields (insulin, carbs, RC, momentum, counteraction) | 8 fields (adds carbStatus, retrospective discrepancies, total RC magnitude) | +| Error cases | 2 (`missingGlucose`, `incompleteSchedules`) | 7 (granular: glucose too old, incomplete timeline, sensitivity window, etc.) | +| Unit types | `HKUnit` / `HKQuantity` (HealthKit) | `LoopUnit` / `LoopQuantity` (custom, no HealthKit import) | +| Dependencies | HealthKit, CoreData | None (pure Swift) | +| Testability | Required mocked async stores | Pure value types; JSON fixture files included | + +#### Specific Changes in This Sync's LoopAlgorithm Version + +On top of the architectural shift, the version of LoopAlgorithm synced here includes +several algorithmic and API improvements made by Tidepool since the package was established: + +| Change | Details | +|--------|---------| +| **Gradual effect transitions** | Insulin and carb effects ramp up gradually at the start of absorption rather than stepping immediately to full effect, producing smoother prediction curves (PR #23) | +| **Configurable max active insulin multiplier** | `maxActiveInsulinMultiplier` on `AlgorithmInput` caps max IOB as a multiple of `maxBolus`; defaults to 2× (LOOP-5502, PR #22) | +| **Carb absorption model selection** | `carbAbsorptionModel` field on input allows runtime selection of parabolic, linear, or piecewise-linear absorption modeling (PR #21) | +| **AutomaticDoseRecommendation backward decode fix** | Decoding old stored recommendations without `basalAdjustment` field no longer crashes (PR #20) | +| **ISF fix during mid-absorption** | Corrects which ISF value is used during active carb absorption for more accurate corrections (PR #19) | +| **TempBasalRecommendation direction** | `.direction` field (increase / decrease / unchanged) added to `TempBasalRecommendation`; enables directional UI feedback (LOOP-5295, PR #18) | +| **Swift 6 Sendable conformances** | `AbsoluteScheduleValue` and other types marked `Sendable` throughout (PR #14) | +| **Glucose math tests moved here** | `GlucoseMathTests.swift` and JSON fixtures moved from LoopKit into this package where they belong (PR #24) | + +### Loop (App) — Presets Overhaul + +Presets have been substantially redesigned. The changes span the data model, safety +guardrails, manager architecture, UI, and Watch app. This is the most user-visible +change in this sync. + +#### Before: Override Presets in DIY Loop + +In prior DIY Loop, override presets were simple `TemporaryScheduleOverridePreset` objects +stored in `LoopSettings.overridePresets`. Each preset had a name, symbol, correction range, +insulin sensitivity multiplier, and duration. There was no preset type system — all presets +were generic overrides. Users could set a target range and/or an ISF multiplier, activate +a preset from the toolbar, and it would run until it expired or was manually cancelled. +There were no guardrails specific to presets, no required training, no scheduling, and no +safety mitigations for extreme settings. + +#### After: SelectablePreset Type System + +Presets are now modeled as a typed enum, `SelectablePreset`, with three distinct cases: + +```swift +enum SelectablePreset { + case preMeal(range: ClosedRange) + case activity(ActivityPreset) + case custom(TemporaryPreset) +} +``` + +Each case has different capabilities enforced at the type level: + +| Capability | Pre-Meal | Activity | Custom | +|-----------|---------|----------|--------| +| Adjust correction range | ✅ | ✅ | ✅ | +| Adjust insulin needs (ISF multiplier) | ❌ | ✅ | ✅ | +| Set duration | ❌ (ends when carbs entered) | ✅ | ✅ | +| Indefinite duration | ❌ | ❌ | ✅ | +| Rename | ❌ | ❌ | ✅ | +| Schedule (day/time) | ❌ | ✅ | ✅ | +| Delete | ❌ | ❌ | ✅ | + +#### Activity Presets — Evidence-Based Defaults + +A new `.activity` case provides four pre-defined activity presets with evidence-based +default insulin reduction values. These appear in the Presets list for all users and +can be customized but not deleted: + +| Activity | Default Target Range | Default Insulin Needs | +|----------|--------------------|-----------------------| +| Jogging | 150–170 mg/dL | 21% of normal | +| Biking | 150–170 mg/dL | 23% of normal | +| Walking | 150–170 mg/dL | 23% of normal | +| Strength Training | 150–170 mg/dL | 37% of normal | + +The `insulinNeedsScaleFactor` (e.g. 0.21 for jogging) scales all three delivery parameters +simultaneously — basal rate, carb ratio, and ISF — so that "need 21% of normal insulin" +is expressed as a single unified control rather than three separate adjustments. + +When a user modifies an activity preset from its defaults, a "modified" indicator is shown +in the UI (`isModifiedFromDefault`). + +#### New Safety Feature: High Insulin Needs Mitigation (LOOP-5439) + +When a preset's `insulinNeedsScaleFactor` exceeds 165% (the upper recommended guardrail +bound), a safety mitigation is automatically applied: the effective correction range is +clamped to a minimum of **110 mg/dL**, regardless of what the user set. + +```swift +// TemporaryScheduleOverride.effectiveCorrectionRangeDuring(scheduledRange:) +if veryHighInsulinNeeds { + return range.clampedTo(atLeast: highInsulinNeedsMitigationCorrectionRangeLimit) // 110 mg/dL +} +``` + +This prevents the dangerous combination of very high insulin delivery AND a very low +correction target. Even if a user sets a correction range of 80 mg/dL while also +setting insulin needs to 180%, the system will use 110 mg/dL as the effective floor. +The UI makes this behavior visible to the user. + +#### Preset Guardrails + +| Setting | Absolute Bounds | Warning | Recommended | +|---------|----------------|---------|-------------| +| Insulin Needs | 15%–200% | 15%–190% | 15%–165% | +| Custom preset correction range | Suspend threshold–250 mg/dL | Suspend threshold–180 mg/dL | — | +| Pre-meal correction range | Suspend threshold–130 mg/dL | Dynamic (based on scheduled range) | — | + +The pre-meal guardrail is dynamic: the recommended upper bound is capped at the lower +bound of the user's current correction range schedule, encouraging a pre-meal target +that is meaningfully lower than their normal target. Pre-meal maximum is hard-capped +at 130 mg/dL absolute. + +Guardrail violations are surfaced inline during editing with color-coded warnings +(yellow = outside recommended, red = outside absolute) and a `GuardrailWarning` view +shown before saving. + +#### TemporaryPresetsManager — Separated from LoopDataManager + +Preset lifecycle management has been extracted from `LoopDataManager` into a dedicated +`TemporaryPresetsManager` (`@MainActor @Observable`). It owns: + +- The active `scheduleOverride` (with `didSet` observers for activation/deactivation events) +- `presetHistory` (a `TemporaryScheduleOverrideHistory` for IOB/ISF history reconstruction) +- Override intent observer (Siri shortcut handling) +- Preset scheduling and reminder alerts +- `basalRateScheduleApplyingOverrideHistory`, `insulinSensitivityScheduleApplyingOverrideHistory`, + `carbRatioScheduleApplyingOverrideHistory` — used by the algorithm to reconstruct what + the effective schedules were over the past few hours, accounting for any presets that were + active during that window + +#### Safety Alert: Indefinite Preset Reminder + +When a custom preset is started with indefinite duration, a repeating alert fires every +24 hours reminding the user it is still active: + +> *"[Preset Name] has been active for more than 24 hours. Make sure you still want it +> enabled, or turn it off."* + +This alert is time-sensitive (interrupts Focus modes) and repeats until the preset is +deactivated. When the preset is deactivated, the alert is retracted. + +#### Preset Scheduling + +Presets can now be scheduled to start at a specific time, optionally repeating on +selected days of the week (`PresetScheduleRepeatOptions`: Sunday through Saturday as +an `OptionSet`). When a scheduled preset's start time approaches, a time-sensitive +alert fires asking the user whether to start it: + +> *"Would you like to start your [Preset Name] preset? This will end any active preset."* + +The alert offers "Don't Start" (dismiss) and "Yes, Start Now" (activates the preset). + +#### Required Training Before Creating Presets + +New users must complete a required in-app training sequence (`PresetsTraining`) before +they can create custom presets. The training is gated by `PresetsTrainingCompletion`, +which tracks progress through 5 chapters persisted in `UserDefaults`: + +1. **Customizing Presets** — How overall insulin %, correction range, and duration work +2. **Illness** — How to use presets when sick (illness typically increases insulin needs) +3. **Daily Activities** — How to use presets for non-exercise activities +4. **Exercise** — How to use presets for exercise (aerobic vs. anaerobic differences) +5. **Training Complete** — Summary + +If a user attempts to create a new preset without completing training, an alert blocks +the action and offers to start the training flow. The training can be reviewed at any +time from the Presets screen. (Debug builds allow skipping chapters via `allowDebugFeatures`.) + +#### Presets UI + +The Presets screen (`PresetsView`) is now a dedicated full-screen view accessible from +Settings, with: + +- **Active preset card** — shown at the top when a preset is running, with expected end + time and tap-to-manage +- **All Presets list** — sortable by name, last used, or date created (ascending/descending) +- **Training card** — shown until training is complete; blocks the "+" create button +- **Presets Performance History** — a history view showing past preset activations +- **Review Training** option in the Support section + +Individual preset cards (`PresetCard`) display the preset's icon, name, duration, insulin +needs, and correction range, with guardrail-aware color coding. + +#### Other Loop (App) Changes + +| Feature | Details | +|---------|---------| +| **Active Preset Banner on CarbEntryView** | Shows the active override preset while entering carbs (LOOP-5432) | +| **DeeplinkView widget component** | Replaces `SystemActionLink.swift`; cleaner implementation of widget deep-link action buttons | +| **roundBasalRate utility** | `DeviceDataManager.roundBasalRate(unitsPerHour:)` added for consistent basal rate rounding (LOOP-5558) | +| **schedulePresets storage** | `NSUserDefaults` now persists schedule presets (LOOP-4754) | +| **defaultEnvironment key** | New `NSUserDefaults` key for Tidepool service environment selection (LOOP-5153) | +| **iOS 17+ onChange API** | `onChange(of:)` calls updated to the two-parameter form throughout views | +| **XCTest environment guard** | `AppDelegate` skips full initialization when running under XCTest | +| **Async/await in RemoteDataServicesManager** | `uploadCgmEventData` migrated from callback-based to `async/await Task` pattern | +| **"Save as Favorite" conditional display** | CarbEntryView only shows "Save as favorite" when no existing favorite food is selected | +| **GeometryReader removed from BolusEntryView** | Cleaner layout; fixes safe area issues | + +### Pump Drivers (OmniKit / OmniBLE / MinimedKit) + +| Feature | Details | +|---------|---------| +| **Decision ID on pump events** | `decisionId: UUID?` added to dose entries and pump events across OmniKit, OmniBLE, and MinimedKit — links pump commands to the algorithm decision that triggered them (LOOP-5295) | +| **Pump inoperable state** | `inSignalLoss` and `isInoperable` properties added to LibreTransmitter, OmniBLE, and OmniKit for consistent inoperable-device reporting (LOOP-4801) | +| **acknowledgeAlert async migration** | OmniBLE and OmniKit: `acknowledgeAlert` migrated from completion-handler to `async throws` | + +### Services + +| Feature | Details | +|---------|---------| +| **TidepoolService: decisionId** | `DoseEntry` now carries `decisionId` for Tidepool upload correlation | +| **NightscoutService: decisionId** | Same `decisionId` support added for Nightscout upload | + +--- + +## 2. Conflicts Encountered + +### LoopAlgorithm +**No conflicts.** Because DIY Loop had never incorporated this package before, there were +no diverging commits on the DIY side — the repo had been tracking `LoopKit/LoopAlgorithm` +main without any local modifications. The merge was a clean fast-forward: Tidepool was +29 commits ahead, all of which applied without conflict. + +--- + +### LoopKit +**16 conflicts** across Swift source and project files. + +| File | Conflict Type | +|------|--------------| +| `LoopKit.xcodeproj/project.pbxproj` | File references, deployment target, `.strings` vs `.xcstrings` | +| Multiple algorithm files in `LoopKit/LoopAlgorithm/` | HealthKit → LoopUnit type migration | +| Various type definition files | API additions on both sides | + +--- + +### Loop (App) +**33 conflicts** across 30+ files — the most complex repo in the sync. + +| Category | Count | Files | +|----------|-------|-------| +| Modify/delete | 3 | `SystemActionLink.swift`, `FavoriteFoodDetailView.swift`, `Main.strings` | +| Swift source | 22 | Managers, ViewControllers, ViewModels, Views, Core/Tests | +| xcschemes | 7 | All scheme files | +| project.pbxproj | 1 | Build settings, file references | + +**Deepest conflict: `LoopDataManager.swift`** — 7 conflict hunks due to a fundamental architectural divergence: +- Tidepool migrated to Swift Concurrency (`@MainActor async/await`, `Task {}`) +- DIY added Live Activity support and retained `dataAccessQueue`-based threading + +--- + +### Peripheral Repos (9 repos) +**pbxproj-only conflicts** — no Swift source conflicts. + +| Repo | Conflict | +|------|----------| +| CGMBLEKit | project.pbxproj | +| G7SensorKit | project.pbxproj | +| dexcom-share-client-swift | project.pbxproj | +| NightscoutRemoteCGM | project.pbxproj | +| RileyLinkKit | project.pbxproj | +| LoopSupport | project.pbxproj | +| LoopOnboarding | project.pbxproj | +| AmplitudeService | project.pbxproj | +| LogglyService | project.pbxproj | + +**Swift source conflicts in plugin repos:** + +| Repo | Files | Conflict | +|------|-------|---------| +| OmniKit | `OmnipodPumpManager.swift`, `PodCommsSessionTests.swift` | decisionId, async acknowledgeAlert | +| OmniBLE | `OmniBLEPumpManager.swift`, `PodState.swift` | decisionId, slot6SuspendTimeExpired guard, async acknowledgeAlert | +| MinimedKit | `MinimedPumpManager.swift` | decisionId, async Task, updateLastEventDates | +| LibreTransmitter | `LibreTransmitterManagerV3.swift` | inSignalLoss/isInoperable properties | +| TidepoolService | `DoseEntry+Tidepool.swift` | decisionId, import LoopAlgorithm | +| NightscoutService | `NightscoutService.swift` | decisionId, RemoteNotificationResponseManager | + +--- + +## 3. Conflict Resolution Decisions + +### LoopAlgorithm: HealthKit → LoopUnit Migration + +**Conflict:** Tidepool replaced `HKUnit`/`HKQuantity` HealthKit types throughout the algorithm +with custom `LoopUnit`/`LoopQuantity` types. LoopKit DIY's inline algorithm copy still used +HealthKit types. + +**Decision:** Accept Tidepool's type migration in full. `LoopUnit` and `LoopQuantity` are +functionally equivalent and improve portability. The inline LoopKit algorithm code was updated +to match. The `HKUnit.swift` and `HKQuantity.swift` extensions were removed. + +--- + +### Loop: `LoopDataManager.swift` — Concurrency Migration + +**Conflict:** The deepest conflict in the sync. Tidepool restructured `LoopDataManager` around +Swift Concurrency — replacing `dataAccessQueue.async` blocks with `Task { @MainActor in }` and +`await`-based calls. DIY had added `LiveActivityManager` integration woven into the same +`dataAccessQueue` paths, and retained `lockedSettings`/`mutateSettings()`/`loop()`/`loopInternal()`/`finishLoop()`. + +**Decision:** +- Adopt Tidepool's `Task { @MainActor in await updateDisplayState() }` pattern as the new + threading model throughout +- Remove DIY's `dataAccessQueue`, `lockedSettings`, `mutateSettings()`, `loop()`, + `loopInternal()`, and `finishLoop()` chain — Tidepool's async pattern replaces this +- Inject DIY's Live Activity calls into Tidepool's new `updateDisplayState()` call sites + (hunks 3, 4, 5), so live activity updates fire from the same points the old queue callbacks did +- Adopt Tidepool's new init parameters (`analyticsServicesManager`, `carbAbsorptionModel`, + `usePositiveMomentumAndRCForManualBoluses`, `dosingStrategySelectionEnabled`) and move + all stored property assignments before the `overrideIntentObserver` closure to satisfy + Swift's init-before-capture requirement + +--- + +### Loop: `SystemActionLink.swift` — Deleted (replaced by DeeplinkView) + +**Conflict:** Tidepool deleted `SystemActionLink.swift` and replaced it with `DeeplinkView.swift`. +DIY had made improvements to `SystemActionLink` (widget rendering mode, active preset colors). + +**Decision:** Accept the deletion and take `DeeplinkView.swift`. It provides equivalent +deeplink functionality. DIY's widget tinting improvements should be ported to `DeeplinkView` +if verified missing. + +--- + +### Loop: `FavoriteFoodDetailView.swift` — Deleted (moved) + +**Conflict:** Tidepool moved the file to `Loop/Views/Favorite Foods/FavoriteFoodDetailView.swift`. +DIY had added `String(localized:)` annotations to the original location. + +**Decision:** Accept the move. The `Favorite Foods/` subfolder version already exists in DIY's +tree. DIY's localization annotations should be verified against the new location. + +--- + +### Loop: `Main.strings` — Keep Deleted + +**Conflict:** DIY deleted `Main.strings` when migrating to `.xcstrings` string catalogs. +Tidepool modified `Main.strings` (never migrated). + +**Decision:** Keep the deletion. DIY's `.xcstrings` format is the forward path. + +--- + +### Loop: `AppDelegate.swift` — Keep Both + +**Conflict:** DIY added diagnostic logging setup; Tidepool added an XCTest environment guard +(skips full initialization during unit tests). + +**Decision:** Keep both — they are independent and complementary additions. + +--- + +### Loop: `DeviceDataManager.swift` — Keep Both + +**Conflict:** DIY added a diagnostic report function with submodule SHAs (`b6e88416`); Tidepool +added `roundBasalRate(unitsPerHour:)` (`184ea75a`). Both additions were at adjacent but +non-overlapping locations. + +**Decision:** Keep both — distinct features, no overlap. + +--- + +### Loop: `SettingsView.swift` — Keep Both + +**Conflict:** DIY had a Favorite Foods sheet; Tidepool added a Presets sheet. + +**Decision:** Keep both sheets — separate features serving different user needs. + +--- + +### Loop: `NSUserDefaults.swift` — Keep Both + +**Conflict:** DIY added `liveActivity` key (`LiveActivitySettings`); Tidepool added +`defaultEnvironment` key. + +**Decision:** Keep both — entirely separate features (Live Activity vs Tidepool service config). + +--- + +### Loop: `WidgetBackground.swift` — Take Tidepool's + +**Conflict:** Tidepool uses `Color.widgetBackground` extension; DIY used `Color("WidgetBackground")` +string-based lookup. + +**Decision:** Take Tidepool's — the extension is safer (compile-time vs. runtime string lookup). + +--- + +### Loop: `ContentMargin.swift` — Keep DIY's + +**Conflict:** Both sides had identical implementation; only the copyright year differed. + +**Decision:** Keep DIY's copyright date. + +--- + +### OmniBLE: `slot6SuspendTimeExpired` Guard — Keep DIY's + +**Conflict:** Tidepool's version of OmniBLE removed the `slot6SuspendTimeExpired` safety check +that prevents acknowledging a pod alert when the pod is suspended. + +**Decision:** Preserve DIY's guard. This is a safety-critical check: if the pod is suspended, +the pod should continue beeping until the user resumes it. Silently acknowledging in this state +could mask a dangerous condition. + +--- + +### MinimedKit: `updateLastEventDates` — Keep DIY's + +**Conflict:** Tidepool's MinimedKit removed `updateLastEventDates()`, a function DIY uses to +track cannula age and insulin age for display in the UI. + +**Decision:** Preserve DIY's implementation. Cannula/insulin age tracking is a meaningful DIY +feature with no equivalent in Tidepool's version. + +--- + +### NightscoutService: `RemoteNotificationResponseManager` — Keep DIY's + +**Conflict:** Tidepool's NightscoutService version removed `RemoteNotificationResponseManager`, +which handles feedback notifications for remote commands sent via Nightscout. + +**Decision:** Preserve DIY's implementation. Remote command feedback is a core DIY feature +(remote bolus, temp basal, etc.) that has no Tidepool equivalent. + +--- + +### TidepoolService: `import LoopAlgorithm` — Removed + +**Conflict:** Tidepool's `TidepoolService` imports `LoopAlgorithm` as a Swift package. DIY does +not include the `LoopAlgorithm` package in the workspace — the algorithm is embedded inline +inside `LoopKit`. + +**Decision:** Remove `import LoopAlgorithm` from `TidepoolService`. In DIY, the types it +provided come from `LoopKit` which is already imported. + +--- + +### All Repos: project.pbxproj + +Applied consistent rules across all 18 repos: + +| Setting | Decision | Rationale | +|---------|----------|-----------| +| `IPHONEOS_DEPLOYMENT_TARGET` | Take higher value (17.0) | Both sides are raising it; take the further-along value | +| `LOCALIZATION_PREFERS_STRING_CATALOGS` | Keep `YES` | DIY's xcstrings migration | +| `.strings` file references | Drop Tidepool's | DIY deleted these files; re-adding references causes build errors | +| `.xcstrings` references | Keep DIY's | Current localization format | +| `XCRemoteSwiftPackageReference "LoopAlgorithm"` | Omit | DIY embeds algorithm inline in LoopKit | +| New Swift file references | Keep both sides' additively | Both sides added files; all should be included | +| PBXGroup `children`/`files` duplicates | Deduplicate after merge | "Keep both" strategy on adjacent group entries can produce duplicates | +| Bundle IDs (`com.loopkit.*` / `com.tidepool.*`) | Keep both | Apply to different build targets | + +--- + +## 4. DIY Features Preserved + +| Feature | Repo | Notes | +|---------|------|-------| +| **Live Activity** (`LiveActivityManager`) | Loop | Woven into Tidepool's new `updateDisplayState()` async calls | +| **Diagnostic report with submodule SHAs** | Loop | Kept alongside Tidepool's `roundBasalRate` addition | +| **Favorite Foods sheet** | Loop | Kept alongside Tidepool's new Presets sheet | +| **liveActivity NSUserDefaults key** | Loop | Kept alongside Tidepool's `defaultEnvironment` key | +| **slot6SuspendTimeExpired safety guard** | OmniBLE | Safety-critical; not present in OmniKit (different pod hardware) | +| **updateLastEventDates** | MinimedKit | Cannula/insulin age tracking | +| **RemoteNotificationResponseManager** | NightscoutService | Remote command feedback | +| **`.xcstrings` localization** | All repos | DIY's Xcode 15+ string catalog format | +| **Community CGM integrations** | CGMBLEKit, LibreTransmitter, etc. | Open-source CGM drivers not in Tidepool | +| **Open-source pump drivers** | OmniKit, OmniBLE, MinimedKit, RileyLinkKit | All preserved | + +--- + +## 5. Post-Merge Fixes + +After the mechanical merge, the following additional fixes were required to achieve a +clean build: + +### `LoopDataManager.swift` — Init Property Ordering (Loop) + +**Problem:** Swift requires all stored properties to be initialized before `self` can be +captured in a closure. The `overrideIntentObserver` closure captured `self`, but several +stored properties (`analyticsServicesManager`, `carbAbsorptionModel`, +`usePositiveMomentumAndRCForManualBoluses`, `automationHistory`, +`publishedMostRecentGlucoseDataDate`, `dosingStrategySelectionEnabled`, +`publishedMostRecentPumpDataDate`) were assigned *after* the closure in the init. + +**Fix:** Moved all stored property assignments before the `overrideIntentObserver` closure. + +--- + +### `NSUserDefaults.swift` — Missing Closing Braces (Loop) + +**Problem:** The merge introduced a structural artifact in the `liveActivity` computed property +— the closing `}}` for the `set {` block was missing. + +**Fix:** Restored the two missing closing braces manually. + +--- + +### `Loop.xcodeproj/project.pbxproj` — Structural Corruption (Loop) + +**Problem:** The pbxproj resolver's "keep both" strategy on a `PBXVariantGroup` conflict +(conflict 36) produced a combined brace depth of +2 instead of 0, making the file +unparseable by Xcode. + +**Root cause:** Both sides of the conflict had `brace_balance = +1`. Naively keeping both +produced `+2`, breaking the file's structure. + +**Fix:** +1. Re-ran `git merge-file` on origin/dev vs merge-base vs tidepool/dev to get clean + conflict markers +2. Applied improved resolver: checks `brace_count(ours) + brace_count(theirs)` before + keeping both; takes OURS when the combined balance would be non-zero +3. Deduplicated 7 duplicate lines in PBXGroup `children`/`files` lists (artifact of + "keep both" on adjacent group entries) +4. Validated with `xcodebuild -project Loop.xcodeproj -list` ✅ + +--- + +## 6. Testing Notes + +### Critical paths to verify + +- [ ] **Live Activity** — glucose, dosing, and carb update notifications all trigger live activity updates +- [ ] **Widget action buttons** — carbs, bolus, pre-meal, preset deeplinks work; colors correct in tinted/accented mode (DeeplinkView replaced SystemActionLink) +- [ ] **Favorite Foods** — detail view labels localized; "Save as favorite" only shown when no food selected +- [ ] **Settings** — both Favorite Foods and Presets sheets accessible +- [ ] **Bolus entry** — safe area layout correct (GeometryReader removed) +- [ ] **Presets training** — training flow completes; "+" button blocked until training done +- [ ] **Activity presets** — all 4 activity types appear; defaults are correct; "modified" indicator shows when changed +- [ ] **High insulin needs mitigation** — correction range clamped to ≥ 110 mg/dL when insulin needs > 165% +- [ ] **Indefinite preset reminder** — 24-hour alert fires when indefinite preset is active; retracts on deactivation +- [ ] **Preset scheduling** — scheduled presets alert at the correct time; "Yes, Start Now" activates correctly +- [ ] **Pre-meal guardrail** — max 130 mg/dL hard cap; recommended upper bound matches correction range lower bound +- [ ] **Active preset banner** — displays correctly on CarbEntryView when override active +- [ ] **Algorithm** — glucose predictions, ISF during carb absorption, max IOB multiplier +- [ ] **TempBasalRecommendation direction** — direction field populated in recommendations +- [ ] **Pump decision IDs** — OmniKit, OmniBLE, Minimed dose entries carry decisionId +- [ ] **Remote commands** — Nightscout remote bolus/basal feedback notifications work +- [ ] **Cannula/insulin age** — displays correctly for Minimed users (updateLastEventDates) +- [ ] **OmniBLE pod suspend safety** — suspended pod continues beeping; alert not silently acked +- [ ] **Diagnostic report** — support report includes submodule SHAs +- [ ] **App expiry alerts** — TestFlight and provisioning profile expiry handled correctly +- [ ] **Unit tests** — `LoopAlgorithmTests`, `LoopKitTests`, `LoopTests` suites pass From 24f9d53336c6833d078827f3db5a65f700ad70e2 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 16:24:20 -0500 Subject: [PATCH 02/58] Update sync doc: preset scheduling history, alert permissions accuracy --- docs/tidepool-sync-2026-03-10.md | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/tidepool-sync-2026-03-10.md b/docs/tidepool-sync-2026-03-10.md index 2569b7f625..bbfd35fe17 100644 --- a/docs/tidepool-sync-2026-03-10.md +++ b/docs/tidepool-sync-2026-03-10.md @@ -155,8 +155,17 @@ stored in `LoopSettings.overridePresets`. Each preset had a name, symbol, correc insulin sensitivity multiplier, and duration. There was no preset type system — all presets were generic overrides. Users could set a target range and/or an ISF multiplier, activate a preset from the toolbar, and it would run until it expired or was manually cancelled. -There were no guardrails specific to presets, no required training, no scheduling, and no -safety mitigations for extreme settings. + +DIY did have a limited scheduling concept: the UIKit `AddEditOverrideTableViewController` +exposed a **Start Time** row that allowed setting a future start time for an override. +If `startDate > Date()`, the override was queued as a one-shot future event and shown in +a "SCHEDULED PRESET" section in `OverrideSelectionViewController`. There was no +day-of-week recurrence, no system alert to prompt the user at the scheduled time, and +no persistence of the schedule on the preset definition itself — the delay was set +per-activation, not stored on the preset. + +There were no guardrails specific to presets, no required training, and no safety +mitigations for extreme settings. #### After: SelectablePreset Type System @@ -262,16 +271,31 @@ When a custom preset is started with indefinite duration, a repeating alert fire This alert is time-sensitive (interrupts Focus modes) and repeats until the preset is deactivated. When the preset is deactivated, the alert is retracted. -#### Preset Scheduling +#### Preset Scheduling — Significantly Expanded -Presets can now be scheduled to start at a specific time, optionally repeating on -selected days of the week (`PresetScheduleRepeatOptions`: Sunday through Saturday as -an `OptionSet`). When a scheduled preset's start time approaches, a time-sensitive -alert fires asking the user whether to start it: +DIY Loop already had a basic scheduling concept: a **one-shot future start time** +that could be set per-activation in the old UIKit override editor. That delayed the +activation of an override to a future time, but the schedule was not stored on the +preset, could not repeat, and generated no alert to remind the user. + +The new scheduling system stores the schedule on the preset definition itself and adds +full day-of-week recurrence. Presets can now be scheduled to start at a specific time, +optionally repeating on selected days of the week (`PresetScheduleRepeatOptions`: +Sunday through Saturday as an `OptionSet`). When a scheduled preset's start time +approaches, a time-sensitive system alert fires asking the user whether to start it: > *"Would you like to start your [Preset Name] preset? This will end any active preset."* The alert offers "Don't Start" (dismiss) and "Yes, Start Now" (activates the preset). +The schedule is re-armed after each activation to fire again at the next scheduled time. + +| Aspect | Old DIY | New (Tidepool) | +|--------|---------|----------------| +| Schedule stored on preset | ❌ (set per-activation) | ✅ | +| One-shot future start | ✅ | ✅ | +| Day-of-week recurrence | ❌ | ✅ (any combination of days) | +| System alert at scheduled time | ❌ | ✅ (time-sensitive) | +| User confirm/dismiss in alert | ❌ | ✅ | #### Required Training Before Creating Presets @@ -313,6 +337,7 @@ needs, and correction range, with guardrail-aware color coding. | **roundBasalRate utility** | `DeviceDataManager.roundBasalRate(unitsPerHour:)` added for consistent basal rate rounding (LOOP-5558) | | **schedulePresets storage** | `NSUserDefaults` now persists schedule presets (LOOP-4754) | | **defaultEnvironment key** | New `NSUserDefaults` key for Tidepool service environment selection (LOOP-5153) | +| **Granular alert permission warnings** | `AlertPermissionsChecker` (existing since 2021) updated: single "safety notifications off" alert replaced with 5 granular cases distinguishing `notificationsDisabled`, `criticalAlertsDisabled`, `timeSensitiveDisabled`, and combinations thereof — each with tailored messaging | | **iOS 17+ onChange API** | `onChange(of:)` calls updated to the two-parameter form throughout views | | **XCTest environment guard** | `AppDelegate` skips full initialization when running under XCTest | | **Async/await in RemoteDataServicesManager** | `uploadCgmEventData` migrated from callback-based to `async/await Task` pattern | From 5e98b2205f87cbe5ab5a72b1aa1c86d530800e4d Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 17:21:29 -0500 Subject: [PATCH 03/58] Update submodule refs after filter-branch author rewrite; add workspace docs - Update all 17 submodule commit pointers to reflect rewritten history (filter-branch changed SHAs when fixing author to LoopKit Developer) - Add Loop build fix commit (LoopDataManager init ordering) - Add LOOPKIT_SYNC_PROCESS.md, SYNC_PROGRESS.md, sync-docs/ --- AmplitudeService | 2 +- CGMBLEKit | 2 +- G7SensorKit | 2 +- LOOPKIT_SYNC_PROCESS.md | 440 +++++++++++++++++++++++++++++++++++++ LibreTransmitter | 2 +- LogglyService | 2 +- Loop | 2 +- LoopKit | 2 +- LoopOnboarding | 2 +- LoopSupport | 2 +- MinimedKit | 2 +- NightscoutRemoteCGM | 2 +- NightscoutService | 2 +- OmniBLE | 2 +- OmniKit | 2 +- RileyLinkKit | 2 +- SYNC_PROGRESS.md | 71 ++++++ TidepoolService | 2 +- dexcom-share-client-swift | 2 +- sync-docs/Loop.md | 158 +++++++++++++ sync-docs/LoopAlgorithm.md | 143 ++++++++++++ 21 files changed, 829 insertions(+), 17 deletions(-) create mode 100644 LOOPKIT_SYNC_PROCESS.md create mode 100644 SYNC_PROGRESS.md create mode 100644 sync-docs/Loop.md create mode 100644 sync-docs/LoopAlgorithm.md diff --git a/AmplitudeService b/AmplitudeService index f094a60c2d..ac96a29fb4 160000 --- a/AmplitudeService +++ b/AmplitudeService @@ -1 +1 @@ -Subproject commit f094a60c2d223601d36be077387591d21c53b2a8 +Subproject commit ac96a29fb464de334b0b94e8b6dcb15578ba8e9e diff --git a/CGMBLEKit b/CGMBLEKit index c8dbdd4aab..6b702067d4 160000 --- a/CGMBLEKit +++ b/CGMBLEKit @@ -1 +1 @@ -Subproject commit c8dbdd4aab35427dc8b7807b6e6f7ad9b856f4fd +Subproject commit 6b702067d40394bc39c9b0874cb2e9189e7ff69c diff --git a/G7SensorKit b/G7SensorKit index c86b4d4195..4b850b09e1 160000 --- a/G7SensorKit +++ b/G7SensorKit @@ -1 +1 @@ -Subproject commit c86b4d4195fcb045f06e3082e5cf00cded81f70e +Subproject commit 4b850b09e192df7bc3fd94317ebfe7935ccbe929 diff --git a/LOOPKIT_SYNC_PROCESS.md b/LOOPKIT_SYNC_PROCESS.md new file mode 100644 index 0000000000..e671d54f1e --- /dev/null +++ b/LOOPKIT_SYNC_PROCESS.md @@ -0,0 +1,440 @@ +# LoopKit ↔ Tidepool Sync Process + +**Purpose:** A repeatable process an developer can follow to merge changes from Tidepool's fork of the Loop +ecosystem back into the LoopKit DIY repos, resolving conflicts with full contextual understanding. + +**Last updated:** 2026-03-10 (added Golden Rule; clarified .strings vs .xcstrings handling) + +--- + +## Background & Architecture + +The Loop ecosystem consists of two parallel development streams: + +- **LoopKit (DIY/open source):** The community-maintained fork at `github.com/LoopKit/*`. + Organized as a set of git submodules in `LoopWorkspace`. +- **Tidepool:** A company building a supported version of Loop at `github.com/tidepool-org/*`. + Tidepool's repos are **forks** of the LoopKit repos, with additional clinical/regulatory features. + +Changes flow in both directions over time, but syncing is typically done in the direction: +**tidepool-org → LoopKit**, bringing Tidepool's upstream improvements back to DIY, and then DIY -> Tidepool + +--- + +## ⭐ Golden Rule: Prefer Tidepool, Protect DIY + +**When in doubt about a conflict resolution, prefer Tidepool's version — but never at the cost of +breaking or removing DIY functionality.** + +More specifically: + +- **Tidepool changes win** for: algorithm improvements, bug fixes, new clinical features, API + changes, architecture decisions, test coverage, Swift version upgrades. +- **LoopKit DIY wins** for: anything that is *exclusively* a DIY capability — community + translations, open-source-only build paths, non-Tidepool signing/bundle IDs, features that + only exist in DIY and would be silently deleted by taking Tidepool's version. +- **Keep both** when a DIY feature and a Tidepool feature occupy the same code area but serve + different purposes and can coexist (e.g. an added Tidepool service upload alongside an existing + Nightscout upload). +- **Never silently drop** a DIY feature. If Tidepool's version removes something DIY users + depend on, document it explicitly in the per-repo sync log and flag it for human review (⚠️) + rather than quietly taking Tidepool's side. + +**Practical decision tree for any conflicting hunk:** + +1. Is this a pure algorithm/logic change? → Take Tidepool's. +2. Does Tidepool's version remove a capability DIY users have? → Keep both if possible; if not, + flag for human review. +3. Is this cosmetic (formatting, style, ordering)? → Take Tidepool's; not worth fighting over. +4. Is this a build/project setting (deployment target, signing, bundle ID)? → See the + "project.pbxproj" section below for specific rules. +5. Is this a Tidepool-only backend integration (e.g. Coastal, Tidepool upload)? → Keep it; + it doesn't harm DIY users and removing it creates future conflicts. + +--- + +### ⚠️ Key Architectural Divergence: LoopAlgorithm + +The most important structural difference between the two streams: + +- **Tidepool** extracted the core Loop algorithm into a standalone Swift Package: + `tidepool-org/LoopAlgorithm` (a fork of `LoopKit/LoopAlgorithm`). + Tidepool's `LoopKit` repo declares it as a SwiftPM dependency (in `Package.resolved`). +- **LoopKit DIY** still embeds the algorithm code inline inside `LoopKit/LoopKit/LoopAlgorithm/`. + +This means when syncing `LoopKit`: +- Algorithm changes Tidepool made via their `LoopAlgorithm` package need to be found by + looking at `tidepool-org/LoopAlgorithm` commits AND `tidepool-org/LoopKit` commits. +- Conflicts in `LoopAlgorithm.swift`, `LoopPredictionOutput.swift`, etc. inside LoopKit + may reflect changes that Tidepool now maintains in their separate package. +- **LoopAlgorithm must be synced first** (as a standalone package repo) before syncing LoopKit. + +--- + +## Repository Map + +All repos below are submodules of `LoopWorkspace`, except `LoopAlgorithm` which is standalone. + +| Repo | LoopKit branch | Tidepool fork | Notes | +|------|---------------|---------------|-------| +| **LoopAlgorithm** | `main` | `tidepool-org/LoopAlgorithm` | ⚠️ Standalone Swift Package. Sync FIRST. Not in sync.swift. | +| LoopKit | `dev` | `tidepool-org/LoopKit` | ⚠️ Complex. References LoopAlgorithm as package (Tidepool) vs inline (DIY). | +| Loop | `dev` | `tidepool-org/Loop` | Most complex. Many source conflicts. Sync LAST. | +| TidepoolService | `dev` | `tidepool-org/TidepoolService` | Tidepool-specific service; sync carefully | +| OmniBLE | `dev` | `tidepool-org/OmniBLE` | Pump driver; test after merge | +| OmniKit | `main` | `tidepool-org/OmniKit` | Pump driver; test after merge | +| MinimedKit | `main` | `tidepool-org/MinimedKit` | Pump driver; test after merge | +| NightscoutService | `dev` | `tidepool-org/NightscoutService` | Service layer | +| LibreTransmitter | `main` | `tidepool-org/LibreTransmitter` | CGM driver | +| G7SensorKit | `main` | `tidepool-org/G7SensorKit` | CGM driver | +| CGMBLEKit | `dev` | `tidepool-org/CGMBLEKit` | CGM driver | +| dexcom-share-client-swift | `dev` | `tidepool-org/dexcom-share-client-swift` | CGM client | +| RileyLinkKit | `dev` | `tidepool-org/RileyLinkKit` | Radio hardware | +| LoopOnboarding | `dev` | `tidepool-org/LoopOnboarding` | Onboarding UI | +| LoopSupport | `dev` | `tidepool-org/LoopSupport` | Support utilities | +| AmplitudeService | `dev` | `tidepool-org/AmplitudeService` | Analytics service | +| LogglyService | `dev` | `tidepool-org/LogglyService` | Logging service | +| NightscoutRemoteCGM | `dev` | `tidepool-org/NightscoutRemoteCGM` | CGM source | +| MixpanelService | `main` | `tidepool-org/MixpanelService` | Analytics service | + +**Not synced:** `Minizip`, `TrueTime.swift` (third-party libs, no Tidepool fork) + +--- + +## Recommended Sync Order + +**Core → App → Plugins (Peripheral)** + +The correct order is *not* simply "foundational to dependent" in a build-graph sense. +It is **core architectural decisions first**, so that by the time you reach the peripheral +plugins you already understand what the core changed — making those conflicts easier to +read and resolve coherently. + +In practice, a conflict in a pump driver that looks like "Tidepool changed the DoseEntry +type" only makes sense once you've already seen that LoopKit changed `DoseEntry` in the +core repo. If you do plugins first, you're resolving conflicts blind. + +### Tier 1 — Core (do these first) +1. **LoopAlgorithm** — standalone package; establishes the algorithm API everything else uses +2. **LoopKit** — foundational types (`DoseEntry`, `GlucoseValue`, `Guardrail`, etc.); all plugins depend on it + +### Tier 2 — App (do before plugins) +3. **Loop** — the top-level app; resolving this *before* plugins means you understand which + app-level API changes the plugins are expected to match, rather than discovering surprises later + +### Tier 3 — Plugins / Peripheral (do last, in any order) +4. **CGM drivers**: CGMBLEKit, G7SensorKit, dexcom-share-client-swift, NightscoutRemoteCGM, LibreTransmitter +5. **Pump drivers**: RileyLinkKit, OmniKit, OmniBLE, MinimedKit +6. **Services**: TidepoolService, NightscoutService, AmplitudeService, LogglyService, MixpanelService +7. **Support/Onboarding**: LoopSupport, LoopOnboarding + +> **Note:** pbxproj-only conflicts in peripheral repos can be batched and resolved +> mechanically at any point since they don't require architectural context. Swift source +> conflicts in peripheral repos should wait until Tier 1 + 2 are done. + +--- + +## Setup (One-Time Per Workspace Clone) + +For each repo in the sync list, add the Tidepool remote if not already present: + +```bash +cd LoopWorkspace/ +git remote add tidepool https://github.com/tidepool-org/.git +git fetch tidepool +``` + +For LoopAlgorithm (standalone — clone separately): +```bash +cd LoopWorkspace # or wherever you keep it +git clone https://github.com/LoopKit/LoopAlgorithm.git +cd LoopAlgorithm +git remote add tidepool https://github.com/tidepool-org/LoopAlgorithm.git +git fetch tidepool +``` + +--- + +## Per-Repo Sync Process + +Repeat these steps for each repo, in the order listed above. + +### Step 1 — Choose a sync branch name + +Use a consistent name across all repos for this sync run, e.g.: +``` +tidepool-sync/YYYY-MM-DD +``` + +### Step 2 — Create sync branch + +```bash +cd LoopWorkspace/ +git checkout origin/ # e.g. origin/dev or origin/main +git checkout -b tidepool-sync/YYYY-MM-DD +``` + +### Step 3 — Attempt merge + +```bash +git merge --no-edit tidepool/ +``` + +If the merge succeeds with no conflicts → commit and move to Step 9. +If there are conflicts → continue to Step 4. + +### Step 4 — Identify conflict files + +```bash +git diff --name-only --diff-filter=U +``` + +Categorize conflicts: +- **project.pbxproj** → See "Xcode Project File Conflicts" section below +- **Swift source files** → See "Source Code Conflicts" section below +- **Other** (yml, strings, etc.) → Research case by case + +### Step 5 — Research each conflict (Source Files) + +For each conflicting Swift file: + +**a) Understand what each side changed:** +```bash +MERGE_BASE=$(git merge-base HEAD tidepool/) + +# What did LoopKit change in this file since the merge base? +git log --oneline $MERGE_BASE..origin/ -- +git diff $MERGE_BASE..origin/ -- + +# What did Tidepool change? +git log --oneline $MERGE_BASE..tidepool/ -- +git diff $MERGE_BASE..tidepool/ -- +``` + +**b) Find related GitHub issues and PRs:** + +For each commit hash found above, search for it on GitHub: +- `https://github.com/LoopKit//commit/` +- Look at the PR that merged it (GitHub shows "merged via PR #NNN") +- Read the PR description and linked issues +- Also search for the LOOP-XXXX ticket numbers in commit messages at: + `https://github.com/LoopKit//issues` and + `https://github.com/tidepool-org//issues` + +**c) For LoopAlgorithm-related conflicts in LoopKit:** +- Check if the same change exists in `tidepool-org/LoopAlgorithm` +- The Tidepool version of a function in LoopKit may be forwarding to their package; + the DIY version keeps it inline. Preserve the inline version while incorporating + any algorithmic improvements from the package version. + +### Step 6 — Resolve conflicts + +**General principles:** Follow the ⭐ Golden Rule (see top of document) — prefer Tidepool's +version, but never silently remove DIY functionality. + +**For algorithm changes:** Take Tidepool's. Their test coverage is usually more thorough +and the algorithmic direction is the right one. Check the `LoopAlgorithm` sync doc to +understand if a conflict here is related to the HealthKit→LoopUnit migration. + +**For UI changes:** Take Tidepool's unless it removes a DIY-only UI path. Regulatory/clinical +UI additions from Tidepool are fine to include — they add capability without breaking DIY. + +**For Tidepool-specific features** (Tidepool Service uploads, Coastal integration, etc.): +Keep them — they don't break DIY users, and removing them creates future conflicts. + +**For DIY-only features** (e.g. community CGM integrations, Nightscout, open-source pump +drivers not supported by Tidepool): Protect these. They are the reason DIY exists. + +**Never silently drop** either side's work without a note in the sync doc. + +After resolving each file: +```bash +git add +``` + +### Step 7 — Resolve Xcode Project File Conflicts (project.pbxproj) + +The `.pbxproj` is a structured text file. Conflicts here are almost always about: +- **Object version** (LoopKit likely bumped for newer Xcode) +- **File references** (new Swift files added by either side) +- **Build settings** (deployment targets, signing, feature flags) +- **Localization** (LoopKit uses `.xcstrings`; Tidepool may still use `.strings`) + +**Approach:** +```bash +# See what LoopKit changed in the project file since merge base +git diff $MERGE_BASE..origin/ -- .xcodeproj/project.pbxproj + +# See what Tidepool changed +git diff $MERGE_BASE..tidepool/ -- .xcodeproj/project.pbxproj +``` + +Then look at the actual conflict markers in the file: +```bash +grep -n "<<<<<<\|=======\|>>>>>>>" .xcodeproj/project.pbxproj +``` + +**Common resolutions:** +- `objectVersion`: Keep LoopKit's (higher = newer Xcode format) +- `IPHONEOS_DEPLOYMENT_TARGET`: Take the *higher* of the two values (both are raising it) +- New file references added by LoopKit (e.g. `.xcstrings`): Keep them +- New file references added by Tidepool (new Swift files, mapping models, etc.): Keep them +- Tidepool's `.strings` localization file references: **Drop them** (DIY deleted these; see Pattern below) +- Tidepool's `XCRemoteSwiftPackageReference "LoopAlgorithm"`: **Omit from DIY** (DIY embeds it inline) +- LoopKit bundle IDs (`com.loopkit.*`): Keep LoopKit's +- Tidepool bundle IDs (`com.tidepool.*`): Keep Tidepool's (they're for different targets) +- Signing/provisioning settings: Keep LoopKit's for shared targets + +After resolving: +```bash +git add .xcodeproj/project.pbxproj +``` + +### Step 8 — Commit + +```bash +git commit -m "Merge tidepool/dev into tidepool-sync/YYYY-MM-DD + +Resolved conflicts: +- : +- : + +See sync-docs/.md for full context." +``` + +### Step 9 — Document in per-repo log + +Update `sync-docs/.md` with: +- Merge base commit hash +- LoopKit and Tidepool tip commit hashes +- For each resolved conflict: + - The file path + - Relevant commit hashes from each side + - Links to GitHub PRs/issues + - What each side was trying to do + - How it was resolved and why + - Any features that need testing as a result +- Any open questions or items requiring human review + +### Step 10 — Update SYNC_PROGRESS.md + +Mark the repo as done (✅) or blocked (❌) in the progress table. +Note any cross-repo dependencies discovered (e.g. "LoopKit change requires matching Loop change"). + +### Step 11 — Push and create PR (when ready) + +```bash +git push origin tidepool-sync/YYYY-MM-DD +# Then open a PR on GitHub: tidepool-sync/YYYY-MM-DD → +``` + +--- + +## Special Case: LoopAlgorithm + +LoopAlgorithm lives at `LoopKit/LoopAlgorithm` (not a submodule of LoopWorkspace) and +`tidepool-org/LoopAlgorithm` is a fork of it. + +**Key questions before syncing:** +1. Is the DIY `LoopAlgorithm` used as a package by Loop/LoopKit, or still embedded inline? + - If package: sync just like any other repo + - If inline (current DIY state): sync algorithm changes need to flow into LoopKit's + `LoopKit/LoopAlgorithm/` subdirectory AND the standalone LoopAlgorithm package + +2. What is the current pinned version of `tidepool-org/LoopAlgorithm` in Tidepool's LoopKit? + Check `LoopKit.xcodeproj/.../Package.resolved` on `tidepool/dev`. + +3. Clone LoopAlgorithm separately: + ```bash + git clone https://github.com/LoopKit/LoopAlgorithm.git + cd LoopAlgorithm + git remote add tidepool https://github.com/tidepool-org/LoopAlgorithm.git + git fetch tidepool + ``` + +4. Follow the same per-repo sync process above. +5. After resolving LoopAlgorithm, **also check** whether any of the same changes need to be + applied to the inline copy in `LoopKit/LoopKit/LoopAlgorithm/` (if DIY hasn't adopted the package yet). + +--- + +## Common Patterns to Watch For + +### Pattern: Tidepool adds LoopAlgorithm package dependency +In Tidepool's `LoopKit`, the `project.pbxproj` will have: +``` +XCRemoteSwiftPackageReference "LoopAlgorithm" +repositoryURL = "https://github.com/tidepool-org/LoopAlgorithm"; +``` +**Resolution for DIY:** Omit this package reference. Keep the inline `LoopAlgorithm/` code. +However, DO bring in any algorithmic logic changes from the inline vs. package versions. + +### Pattern: Deployment target bumps +Tidepool regularly bumps `IPHONEOS_DEPLOYMENT_TARGET`. DIY follows at its own pace. +**Resolution:** Take the higher value unless there's a specific reason not to. +Check LoopKit's own dev branch to see what they've already committed to. + +### Pattern: Localization format migration +LoopKit DIY migrated from `.strings` files to `.xcstrings` (Xcode 15+ string catalogs). +Tidepool does not maintain translations and remains on `.strings`. + +**Resolution:** Keep LoopKit's `.xcstrings` format. **Never re-add Tidepool's `.strings` +file references** — Tidepool's `.strings` references belong to files that DIY deliberately +deleted when migrating to string catalogs. Re-adding them would cause build errors +("file not found") because the actual `.strings` files no longer exist in DIY's tree. + +In practice, when resolving `project.pbxproj` conflicts: +- Drop any `PBXFileReference` entries for `*.strings` that came from Tidepool's side +- Drop any `PBXBuildFile` entries referencing those same `.strings` files +- Drop any group `children = (...)` entries pointing to `.strings` files from Tidepool +- Keep DIY's `*.xcstrings` references +- Keep all of Tidepool's non-translation additions (new Swift files, mapping models, etc.) + +### Pattern: Tidepool-specific regulatory features +Features like Coastal integration, FDA submission mode, specific clinical guardrails. +**Resolution:** Keep them. They add capability without breaking DIY. Only omit if they +require Tidepool backend infrastructure that simply won't exist in DIY. + +### Pattern: Bundle identifier differences +`com.loopkit.*` (LoopKit) vs `com.tidepool.*` (Tidepool). +**Resolution:** Keep both — they apply to different build targets/schemes. + +### Pattern: HKUnit.swift removal +Both sides removed the `HKUnit.swift` extension file (HealthKit unit helpers were moved). +This should auto-merge or be a trivially clean conflict. If not, take the removal. + +--- + +## Testing Checklist After Merge + +After completing all repos, test these critical paths before opening PRs: + +- [ ] **Glucose display:** CGM data flows and displays correctly +- [ ] **Insulin delivery:** Bolus and basal commands work (OmniBLE/OmniKit/Minimed) +- [ ] **Loop algorithm:** Closed loop prediction and dosing recommendations +- [ ] **Remote services:** Nightscout upload/download, Tidepool service +- [ ] **Onboarding:** Fresh install and therapy settings configuration +- [ ] **Watch app:** Complication and status display (if applicable) +- [ ] **Widgets:** Lock screen / home screen widgets (if applicable) +- [ ] **Build:** All targets compile cleanly with no warnings promoted to errors + +--- + +## Tracking Files + +| File | Purpose | +|------|---------| +| `SYNC_PROGRESS.md` | Master status table, notes on blocked items | +| `sync-docs/.md` | Per-repo conflict log with full context and links | +| `LOOPKIT_SYNC_PROCESS.md` | This file — the process itself | + +--- + +## Reference Links + +- LoopKit org: https://github.com/LoopKit +- Tidepool org: https://github.com/tidepool-org +- LoopAlgorithm (LoopKit): https://github.com/LoopKit/LoopAlgorithm +- LoopAlgorithm (Tidepool): https://github.com/tidepool-org/LoopAlgorithm +- LoopWorkspace: https://github.com/LoopKit/LoopWorkspace +- Original sync script: `LoopWorkspace/Scripts/sync.swift` diff --git a/LibreTransmitter b/LibreTransmitter index 6c0e4aa667..bc017fba25 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit 6c0e4aa66758decbe6a2537164cd2e3d36de70a5 +Subproject commit bc017fba254e3e8e18efc7ddb326e951c7f246e1 diff --git a/LogglyService b/LogglyService index eb04e1b7be..86253bd32a 160000 --- a/LogglyService +++ b/LogglyService @@ -1 +1 @@ -Subproject commit eb04e1b7be192753e9f37b790b9159f8174cbc3e +Subproject commit 86253bd32a46b7257e7c671acb96aadc65f8722a diff --git a/Loop b/Loop index 79eee6d5cb..71e06d6ab8 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 79eee6d5cbb24915b6101fac476df4b4e0d7a40a +Subproject commit 71e06d6ab88396dd7e2d0f069d7bbf254130a583 diff --git a/LoopKit b/LoopKit index e003e6b69e..12ed30ae5b 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit e003e6b69e61d8ba73be3e19b564c1f655fb29c9 +Subproject commit 12ed30ae5ba993a78707e41089d05056e152e2ff diff --git a/LoopOnboarding b/LoopOnboarding index 7bfc5fdf8c..eece785c7b 160000 --- a/LoopOnboarding +++ b/LoopOnboarding @@ -1 +1 @@ -Subproject commit 7bfc5fdf8c323fce9c1fedcf52ecbd19afea137d +Subproject commit eece785c7b18d835e3da82f32776e385ac0f72ea diff --git a/LoopSupport b/LoopSupport index 4538579519..de1dae3aec 160000 --- a/LoopSupport +++ b/LoopSupport @@ -1 +1 @@ -Subproject commit 4538579519b80a33422097ee7d3425130be49ead +Subproject commit de1dae3aecada9dd8e2b48d2ee8a8df26939998e diff --git a/MinimedKit b/MinimedKit index c13c039344..fcba4a7d36 160000 --- a/MinimedKit +++ b/MinimedKit @@ -1 +1 @@ -Subproject commit c13c039344decf762b988062af9f2a7c8ec752eb +Subproject commit fcba4a7d369f624748da201c80a231750b8a7f12 diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 87e3898b01..45a1782dc0 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 87e3898b01d84e0ad7f42dc6d4dc23b3f377c4d2 +Subproject commit 45a1782dc09c11c8f9b54a6eefc6ceb8f7b8abe6 diff --git a/NightscoutService b/NightscoutService index 1ea3e43b4e..5ba403c210 160000 --- a/NightscoutService +++ b/NightscoutService @@ -1 +1 @@ -Subproject commit 1ea3e43b4e93926f8d5670c17b2541ace65e53a8 +Subproject commit 5ba403c21072312f8cf4236f7fef3f6c011716dc diff --git a/OmniBLE b/OmniBLE index 536efd9244..d8b6b1560a 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 536efd9244d894bb67a552168fe6243dcdf124fd +Subproject commit d8b6b1560af9c972dab885480b22f14205b1c376 diff --git a/OmniKit b/OmniKit index 0e3cf2e0c8..a5897b9c58 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 0e3cf2e0c89b27db466da4bc43087e9be82c2f5d +Subproject commit a5897b9c58bfc70ab75734246946e795a5b1bd10 diff --git a/RileyLinkKit b/RileyLinkKit index b9a52dccc2..bc1f77b333 160000 --- a/RileyLinkKit +++ b/RileyLinkKit @@ -1 +1 @@ -Subproject commit b9a52dccc2b5f7c69b344770b5c65da4862f907c +Subproject commit bc1f77b333319d9c06aff47d89dd0d9feb765b50 diff --git a/SYNC_PROGRESS.md b/SYNC_PROGRESS.md new file mode 100644 index 0000000000..362551fe03 --- /dev/null +++ b/SYNC_PROGRESS.md @@ -0,0 +1,71 @@ +# Tidepool → LoopKit Sync Progress + +**Sync branch:** `tidepool-sync/2026-03-10` +**Source:** `tidepool-org//dev` (or `main`) +**Target:** `LoopKit//dev` (or `main`) +**Started:** 2026-03-10 +**Completed:** 2026-03-10 +**Last updated:** 2026-03-10 + +**Process doc:** [LOOPKIT_SYNC_PROCESS.md](LOOPKIT_SYNC_PROCESS.md) + +--- + +## Status: ✅ ALL REPOS COMPLETE + +18 repos synced, 0 conflicts remaining, ready for compile test & PR. + +--- + +## Full Repo Status + +Work proceeded in **Core → App → Plugins** order (see process doc for rationale). + +| # | Repo | Base | Key Conflicts | Status | Doc | +|---|------|------|---------------|--------|-----| +| 1 | **LoopAlgorithm** | `main` | none (fast-forward) | ✅ Done | [doc](sync-docs/LoopAlgorithm.md) | +| 2 | **LoopKit** | `dev` | 16 conflicts: HK→LoopUnit types, pbxproj, .strings cleanup | ✅ Done | [doc](sync-docs/LoopKit.md) | +| 3 | **Loop** | `dev` | 33 conflicts + Swift Concurrency migration | ✅ Done | [doc](sync-docs/Loop.md) | +| 4 | **TidepoolService** | `dev` | pbxproj + DoseEntry (`import LoopAlgorithm` removed, `decisionId`) | ✅ Done | — | +| 5 | **NightscoutService** | `dev` | pbxproj + NightscoutService (RemoteNotificationResponseManager preserved, `decisionId` added) | ✅ Done | — | +| 6 | **MinimedKit** | `main` | pbxproj + MinimedPumpManager (`decisionId`, async Task, `updateLastEventDates` preserved) | ✅ Done | — | +| 7 | **LibreTransmitter** | `main` | pbxproj + `inSignalLoss`/`isInoperable` properties added | ✅ Done | — | +| 8 | **OmniBLE** | `dev` | pbxproj + OmniBLEPumpManager (6 hunks) + PodState | ✅ Done | — | +| 9 | **OmniKit** | `main` | pbxproj + OmnipodPumpManager (4 hunks) + PodCommsSessionTests | ✅ Done | — | +| 10 | **CGMBLEKit** | `dev` | pbxproj only | ✅ Done | — | +| 11 | **G7SensorKit** | `main` | pbxproj only | ✅ Done | — | +| 12 | **dexcom-share-client-swift** | `dev` | pbxproj only | ✅ Done | — | +| 13 | **NightscoutRemoteCGM** | `dev` | pbxproj only | ✅ Done | — | +| 14 | **RileyLinkKit** | `dev` | pbxproj only | ✅ Done | — | +| 15 | **LoopSupport** | `dev` | pbxproj only | ✅ Done | — | +| 16 | **LoopOnboarding** | `dev` | pbxproj only | ✅ Done | — | +| 17 | **AmplitudeService** | `dev` | pbxproj only | ✅ Done | — | +| 18 | **LogglyService** | `dev` | pbxproj only | ✅ Done | — | +| — | **MixpanelService** | `main` | already in sync, noop | ✅ Noop | — | + +--- + +## Next Steps + +1. **Compile test** — open `LoopWorkspace.xcworkspace` in Xcode and build +2. **Fix any compile errors** that surface (type mismatches, missing parameters, etc.) +3. **Push branches** — requires `GH_TOKEN` with repo write access +4. **Open PRs** — one per repo, `tidepool-sync/2026-03-10` → `dev` (or `main`) + +--- + +## Key Architectural Decisions + +See [LOOPKIT_SYNC_PROCESS.md](LOOPKIT_SYNC_PROCESS.md) for the full Golden Rule and process. + +| Decision | Detail | +|---|---| +| No `import LoopAlgorithm` in TidepoolService | DIY gets types from LoopKit; LoopAlgorithm not in workspace | +| `decisionId: UUID?` added everywhere | LOOP-5295: pump events now carry a decision ID | +| `pumpInoperable` state | LOOP-4801: LibreTransmitter, OmniBLE, OmniKit all got `inSignalLoss`/`isInoperable` | +| `acknowledgeAlert` → `async throws` | OmniBLE + OmniKit: migrated from completion to async | +| OmniBLE `slot6SuspendTimeExpired` guard | DIY safety: don't ack if pod suspended; pod beeps until resumed | +| `updateLastEventDates` preserved in MinimedKit | DIY cannula/insulin age tracking | +| `RemoteNotificationResponseManager` preserved | DIY feature: remote command feedback notifications | +| Live Activity wired via `updateDisplayState()` | LoopDataManager Concurrency migration complete | +| `.strings` refs stripped from all pbxprojs | Tidepool doesn't maintain translations; DIY uses `.xcstrings` | diff --git a/TidepoolService b/TidepoolService index 08b57560b6..c3229fa940 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit 08b57560b6585fdb04d3e245d00899a0d50ff45e +Subproject commit c3229fa940303675a545d7acaef2f0519ae07dd1 diff --git a/dexcom-share-client-swift b/dexcom-share-client-swift index 29409f34c6..a4b6a4b9e6 160000 --- a/dexcom-share-client-swift +++ b/dexcom-share-client-swift @@ -1 +1 @@ -Subproject commit 29409f34c6fb127eec683e9133975c9fde0e015c +Subproject commit a4b6a4b9e6eb2446a5c6112f66c7d98919cbf9ef diff --git a/sync-docs/Loop.md b/sync-docs/Loop.md new file mode 100644 index 0000000000..57292038ca --- /dev/null +++ b/sync-docs/Loop.md @@ -0,0 +1,158 @@ +# Loop Sync Log + +**Repo:** https://github.com/LoopKit/Loop +**Tidepool fork:** https://github.com/tidepool-org/Loop +**Sync date:** 2026-03-10 +**Sync branch:** `tidepool-sync/2026-03-10` +**Base branch:** `dev` + +--- + +## Merge Summary + +- Merge base: `55cf35a9` +- 33 conflicts across 30+ files +- Categories: 3 modify/delete, 22 Swift source, 7 xcschemes, 1 pbxproj + +--- + +## Modify/Delete Resolutions + +### `Loop Widget Extension/Components/SystemActionLink.swift` — DELETED +- **Tidepool** deleted it in commit `c22b37f4` ("Code cleanup"), replacing with `DeeplinkView.swift` +- **DIY** had improved it: added `@available(iOS 16.1, *)`, `widgetRenderingMode`, fixed active preset colors +- **Resolution:** Take deletion. `DeeplinkView.swift` auto-merged cleanly and provides the same + deeplink functionality. DIY's widget tinting improvements should be ported to `DeeplinkView` + if they're missing. +- ⚠️ **Test:** Widget action buttons (carbs, bolus, pre-meal, preset) — verify colors in accented/tinted mode + +### `Loop/Views/FavoriteFoodDetailView.swift` — DELETED (moved) +- **Tidepool** moved it to `Loop/Views/Favorite Foods/FavoriteFoodDetailView.swift` in commit + `2c914f87` ("Renaming/organizing favorite foods") +- **DIY** had updated it with `String(localized:)` annotations on the same fields +- **Resolution:** Take deletion. The `Favorite Foods/` subfolder version already exists in DIY's + tree and `FavoriteFoodsView.swift` references it correctly. DIY's localization updates need + to be checked against the Favorite Foods subfolder version. +- ⚠️ **Test:** Favorite Foods detail view — verify all field labels are localized + +### `Loop/en.lproj/Main.strings` — KEPT DELETED +- DIY deleted it in `cfff59a5` (migrated to `.xcstrings` format) +- Tidepool modified it (never migrated to xcstrings) +- **Resolution:** Keep deleted per the standard .strings policy + +--- + +## Swift Source Resolutions + +### Widget Files + +| File | Resolution | Notes | +|------|-----------|-------| +| `ContentMargin.swift` | Kept DIY's copyright date | Same impl, only date differed | +| `WidgetBackground.swift` | Took Tidepool's | `Color.widgetBackground` extension cleaner than `Color("WidgetBackground")` | +| `SystemStatusWidget.swift` | Took Tidepool's | Uses `DeeplinkView` + `.containerRelativeBackground()`; removed `@available(iOS 16.1, *)` guard (no longer needed) | + +### Managers + +| File | Hunks | Resolution | Notes | +|------|-------|-----------|-------| +| `AppDelegate.swift` | 1 | Kept both | DIY: logging; Tidepool: XCTest environment guard (skip full init in unit tests) | +| `StoredAlert.swift` | 2 | Took Tidepool's | Whitespace/empty hunks only | +| `AppExpirationAlerter.swift` | 2 | Took Tidepool's | Indentation of `#if targetEnvironment(simulator)` only | +| `DeviceDataManager.swift` | 1 | Kept both | DIY: diagnostic report with submodule SHAs (`b6e88416`); Tidepool: `roundBasalRate(unitsPerHour:)` function (`184ea75a`) — different code at adjacent location | +| `RemoteDataServicesManager.swift` | 1 | Took Tidepool's | Migrated `uploadCgmEventData` from callback to `async/await Task` | + +### LoopDataManager ⚠️ NEEDS COMPILE TEST + +This file has the deepest architectural conflict. Tidepool migrated to Swift Concurrency +(`@MainActor async/await`) while DIY added Live Activity support and kept `dataAccessQueue`. + +| Hunk | DIY | Tidepool | Resolution | +|------|-----|---------|-----------| +| 1 | `liveActivityManager: LiveActivityManagerProxy?` property | `lastReservoirValue` computed property | **Kept both** | +| 2 | LiveActivityManager init + overrideIntentObserver setup | Different init params (`analyticsServicesManager`, `carbAbsorptionModel`, etc.) | **Kept both** — ⚠️ likely compile errors; needs human review | +| 3 | `dataAccessQueue.async` + cache invalidation + liveActivity update | `Task { @MainActor in await updateDisplayState() }` | **Tidepool's Task + liveActivity injected** | +| 4 | `dataAccessQueue.async` + glucose effect clear + liveActivity | `Task { @MainActor }` + `restartGlucoseValueStalenessTimer()` | **Tidepool's Task + liveActivity injected** | +| 5 | `dataAccessQueue.async` + insulin effect clear + liveActivity | `Task { @MainActor in await updateDisplayState() }` | **Tidepool's Task + liveActivity injected** | +| 6 | `lockedSettings`, `settings`, `mutateSettings()` + liveActivity calls | `Task { await updateDisplayState() }` | **Kept both** — ⚠️ structural divergence; needs human review | +| 7 | Complete `loop()`→`loopInternal()`→`finishLoop()`→`update()` chain | `await dosingDecisionStore.storeDosingDecision()` (async) | **Kept both** — ⚠️ DIY loop chain critical; Tidepool async store; needs human review | + +**Key questions for human review:** +- Did Tidepool replace `lockedSettings`/`mutateSettings()` with a different pattern? If so, all callers need updating. +- Does Tidepool's `updateDisplayState()` need to also call `liveActivityManager?.update()`? +- Is the DIY `loop()`/`loopInternal()` chain still intact or did Tidepool restructure it? + +### View Controllers + +| File | Resolution | Notes | +|------|-----------|-------| +| `CarbAbsorptionViewController.swift` | Took Tidepool's | Cleaner async do/catch pattern for carb review | +| `InsulinDeliveryTableViewController.swift` | Took Tidepool's | `pumpEvent.dose` + `String(describing:)` format | +| `StatusTableViewController.swift` | Took Tidepool's | More complete `.inProgress` switch handling | + +### View Models + +| File | Resolution | Notes | +|------|-----------|-------| +| `CarbEntryViewModel.swift` | Took Tidepool's | `CarbMath.dateAdjustmentPast` (from LoopAlgorithm pkg) vs `LoopConstants.maxCarbEntryPastTime` — same value, different source | +| `SimpleBolusViewModel.swift` | Took Tidepool's | Explicit `NSLocalizedString("–", comment:...)` | + +### Views + +| File | Resolution | Notes | +|------|-----------|-------| +| `BolusEntryView.swift` | Took Tidepool's | Removed `GeometryReader` wrapper; updated `onChange(of:)` to iOS 17+ two-param API | +| `CarbEntryView.swift` | Took Tidepool's | "Save as favorite" button only shown when `selectedFavoriteFood == nil` | +| `ManualEntryDoseView.swift` | Took Tidepool's | Minor indentation + iOS 17+ `onChange` API | +| `SettingsView.swift` | Kept both | DIY's `favoriteFoods` sheet + Tidepool's `presets` sheet added | + +### Core / Tests + +| File | Resolution | Notes | +|------|-----------|-------| +| `NSUserDefaults.swift` | Kept both | DIY: `liveActivity` key (`LiveActivitySettings`); Tidepool: `defaultEnvironment` key — separate features | +| `AlertStoreTests.swift` | Took Tidepool's | Updated JSON encoding assertions for new Alert format | +| `StoredAlertTests.swift` | Took Tidepool's | Updated JSON expectations | + +### xcschemes (all 7) + +Took Tidepool's — updated Xcode scheme versions. + +--- + +## project.pbxproj + +- Standard resolver applied: merged both sides' file references, dropped Tidepool's `.strings` refs, kept DIY's `.xcstrings`, took Tidepool's deployment target + +--- + +## Features Added by This Merge (Tidepool → DIY) + +- **Active Preset Banner** on CarbEntryView (LOOP-5432) +- **DeeplinkView** widget component (replaces SystemActionLink) +- **roundBasalRate** in DeviceDataManager (LOOP-5558) +- **schedulePresets** storage in NSUserDefaults (LOOP-4754) +- **defaultEnvironment** key for Tidepool service (LOOP-5153) +- **iOS 17+ onChange API** updates throughout views +- **XCTest environment guard** in AppDelegate (skip full init when testing) +- **Async Task pattern** in RemoteDataServicesManager + +## DIY Features Preserved + +- **Live Activity** (`LiveActivityManager`, `liveActivityManager?.update()` calls woven into Tidepool's Task blocks) +- **Diagnostic report with submodule SHAs** in DeviceDataManager +- **FavoriteFoods** sheet in SettingsView +- **liveActivity** NSUserDefaults key + `LiveActivitySettings` + +--- + +## Testing Notes + +- [ ] ⚠️ **Build** — `LoopDataManager.swift` hunks 2/6/7 kept both sides and will very likely need manual fixup to compile +- [ ] **Live Activity** — verify glucose, dosing, and carb notifications all trigger liveActivity updates (hunks 3/4/5) +- [ ] **Widget action buttons** — carbs, bolus, pre-meal, preset deeplinks work; colors correct in tinted mode +- [ ] **Favorite Foods** — detail view labels localized; "Save as favorite" only shown when no food selected +- [ ] **Settings** — both Favorite Foods and Presets sheets accessible +- [ ] **Bolus entry** — recommendation clears on edit; safe area layout correct (GeometryReader removed) +- [ ] **App expiry alerting** — TestFlight and profile expiry both handled independently +- [ ] **Diagnostic report** — includes submodule SHAs in support report diff --git a/sync-docs/LoopAlgorithm.md b/sync-docs/LoopAlgorithm.md new file mode 100644 index 0000000000..a5a98238ff --- /dev/null +++ b/sync-docs/LoopAlgorithm.md @@ -0,0 +1,143 @@ +# LoopAlgorithm Sync Log + +**Repo:** https://github.com/LoopKit/LoopAlgorithm +**Tidepool fork:** https://github.com/tidepool-org/LoopAlgorithm +**Sync date:** 2026-03-10 +**Sync branch:** `tidepool-sync/2026-03-10` +**Base branch:** `main` + +--- + +## Merge Summary + +**Type:** ✅ Clean fast-forward — no conflicts + +- Merge base (LoopKit/main tip): `9d24054` +- Tidepool/main tip: `13cb4b4` +- LoopKit has 0 commits not in Tidepool +- Tidepool has 29 commits not in LoopKit (across multiple PRs) + +``` +git checkout -b tidepool-sync/2026-03-10 +git merge --no-edit tidepool/main +# Result: Fast-forward, 67 files changed, 2237 insertions(+), 442 deletions(-) +``` + +--- + +## What Was Merged + +### PR #24 — Move glucose math tests from LoopKit to LoopAlgorithm +- Commit: `13cb4b4` +- https://github.com/tidepool-org/LoopAlgorithm/pull/24 +- Adds `GlucoseMathTests.swift` (435 lines) and associated fixtures +- Moves test coverage that was previously in LoopKit into this package +- **Testing impact:** Run `LoopAlgorithmTests` test suite + +### PR #23 — Gradual transitions support +- Commit: `8093b57` +- https://github.com/tidepool-org/LoopAlgorithm/pull/23 +- Adds support for gradual insulin/carb effect transitions in algorithm +- **Testing impact:** Verify glucose prediction curves match expected shapes + +### PR #22 — LOOP-5502: Allow setting of max active insulin multiplier +- Commit: `89dd58a` +- https://github.com/tidepool-org/LoopAlgorithm/pull/22 +- Adds configurable `maxActiveInsulinMultiplier` to algorithm input +- **Testing impact:** Verify max IOB calculations respect the multiplier + +### PR #21 — Carb absorption model selection updates +- Commit: `29c7b52` +- https://github.com/tidepool-org/LoopAlgorithm/pull/21 +- Updates how carb absorption model is selected/configured +- **Testing impact:** CarbMathTests; verify carb absorption curves + +### PR #20 — Fix decoding of old AutomaticDoseRecommendation structures +- Commit: `7ba61e1` +- https://github.com/tidepool-org/LoopAlgorithm/pull/20 +- Fixes backward compatibility for `AutomaticDoseRecommendation` without `basalAdjustment` +- **Testing impact:** Data migration; old stored recommendations should decode without crashing + +### PR #19 — Fix issue with mid-absorption ISF calculation +- Commit: `84a099f` +- https://github.com/tidepool-org/LoopAlgorithm/pull/19 +- Fixes insulin sensitivity factor calculation during active carb absorption +- **Testing impact:** CorrectionDosingTests; verify ISF used correctly during meal absorption + +### LOOP-5295 — Add directionality to TempBasalRecommendation +- PR: https://github.com/tidepool-org/LoopAlgorithm/pull/18 +- Adds `.direction` property to `TempBasalRecommendation` (increase/decrease/unchanged) +- Enables Loop UI to show directional feedback on temp basal changes +- **Testing impact:** Verify temp basal recommendations include direction field + +### LOOP-5280 — Display Glucose Preference by InternationalUnit +- Adds `LoopUnit` and `LoopQuantity` types to replace HealthKit dependency for glucose units +- **Testing impact:** New `LoopUnitTests.swift` added + +### Remove HealthKit Dependency & Upgrade to Swift 6 +- Removes `HKUnit.swift` and `HKQuantity.swift` extensions +- Replaces with `LoopUnit` and `LoopQuantity` (custom, no HealthKit import) +- Upgrades Swift concurrency to Swift 6 (`Sendable` annotations throughout) +- **Testing impact:** ⚠️ HIGH IMPACT — changes the unit/quantity types used in algorithm API. + Any caller of the algorithm API that used `HKUnit`/`HKQuantity` needs to be updated + to use `LoopUnit`/`LoopQuantity`. This is a breaking API change. + In LoopKit DIY: the inline `LoopKit/LoopAlgorithm/` code will need these types added, + OR LoopKit should adopt this package. + +### Remove CoreData Import +- Removed CoreData framework dependency from the package +- Package is now more portable and framework-independent + +### PR #14 — Mark AbsoluteScheduleValue as Sendable +- Adds Swift 6 `Sendable` conformance to `AbsoluteScheduleValue` + +--- + +## ⚠️ Breaking API Change: HealthKit Removal + +The most significant change is the replacement of `HKUnit`/`HKQuantity` with `LoopUnit`/`LoopQuantity`. + +**Impact on LoopKit DIY's inline algorithm code (`LoopKit/LoopKit/LoopAlgorithm/`):** +- The inline code still uses HealthKit types +- When syncing LoopKit, these new types need to either: + 1. Be introduced inline in `LoopKit/LoopAlgorithm/` as well, OR + 2. Prompt a decision to adopt the `LoopAlgorithm` package in LoopKit DIY + +This is a cross-repo dependency: **LoopAlgorithm sync must be reviewed before LoopKit sync**. +Specifically, any LoopKit conflict in `LoopAlgorithm/*.swift` files likely involves +these same HealthKit→LoopKit unit type changes. + +--- + +## Files Changed + +| Change | Files | +|--------|-------| +| Deleted | `Extensions/HKQuantity.swift`, `Extensions/HKUnit.swift` | +| Added | `LoopQuantity.swift`, `LoopUnit.swift`, `Tests/GlucoseMathTests.swift`, `Tests/LoopUnitTests.swift` | +| Added fixtures | 13 JSON test fixture files for glucose/carb math tests | +| Modified | `LoopAlgorithm.swift`, `DoseMath.swift`, `GlucoseMath.swift`, `CarbMath.swift`, `InsulinMath.swift`, `LoopPredictionInput.swift`, `AlgorithmInput.swift`, `TempBasalRecommendation.swift`, `AutomaticDoseRecommendation.swift`, and others | + +--- + +## Status + +✅ **Merged cleanly** — fast-forward, no conflicts. + +⚠️ **Action required before LoopKit sync:** +- Review the `LoopUnit`/`LoopQuantity` API change impact on LoopKit's inline algorithm code +- Decide: adopt LoopAlgorithm as a package in DIY, or update inline code to match? +- Consider whether PR needs to be opened on `LoopKit/LoopAlgorithm` → `main` + +--- + +## Testing Notes + +After this sync is pushed and integrated: +- [ ] Run full `LoopAlgorithmTests` test suite +- [ ] Verify glucose prediction accuracy (GlucoseMathTests) +- [ ] Verify ISF handling during carb absorption (CorrectionDosingTests) +- [ ] Verify carb absorption model selection +- [ ] Verify `AutomaticDoseRecommendation` backward decode compatibility +- [ ] Verify `TempBasalRecommendation` direction field populated +- [ ] Check for any callers of algorithm API still using `HKUnit`/`HKQuantity` From 1a0b850a3ef81949f1e3d190590703e36c192216 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 19:45:57 -0500 Subject: [PATCH 04/58] Update TidepoolService submodule ref (restore LoopAlgorithm imports) --- TidepoolService | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TidepoolService b/TidepoolService index c3229fa940..7b6a704055 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit c3229fa940303675a545d7acaef2f0519ae07dd1 +Subproject commit 7b6a704055bbe0a99fd7c80d7f6c7127284e585b From c9608997c51f4f8bda898e27df39602ca8b3ce3e Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 19:46:25 -0500 Subject: [PATCH 05/58] Update sync doc with TidepoolServiceKit import fix --- docs/tidepool-sync-2026-03-10.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/tidepool-sync-2026-03-10.md b/docs/tidepool-sync-2026-03-10.md index bbfd35fe17..5b4a8d5df0 100644 --- a/docs/tidepool-sync-2026-03-10.md +++ b/docs/tidepool-sync-2026-03-10.md @@ -647,6 +647,19 @@ stored properties (`analyticsServicesManager`, `carbAbsorptionModel`, --- +### `TidepoolServiceKit/Extensions/DoseEntry.swift` and `DeviceLogUploader.swift` — Missing `import LoopAlgorithm` + +**Problem:** Both files use `AbsoluteScheduleValue` (DoseEntry.swift) and `TDeviceLogEntry` +(DeviceLogUploader.swift) from the LoopAlgorithm package, but `import LoopAlgorithm` was +incorrectly removed from both files during conflict resolution. All other ~10 files in +TidepoolServiceKit correctly import LoopAlgorithm. The omission caused build failures in +Xcode (though command-line builds may succeed due to implicit module visibility from +LoopKit.xcodeproj's LoopAlgorithm SPM dependency). + +**Fix:** Restored `import LoopAlgorithm` in both files. + +--- + ### `Loop.xcodeproj/project.pbxproj` — Structural Corruption (Loop) **Problem:** The pbxproj resolver's "keep both" strategy on a `PBXVariantGroup` conflict From 530057f9de7d03ee328275ae515e905d4354479e Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 13:42:33 -0500 Subject: [PATCH 06/58] Update TidepoolKit to latest dev (4f4747ff) --- LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- TidepoolService | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5d6f9b843a..1e4885644b 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -168,7 +168,7 @@ "location" : "https://github.com/tidepool-org/TidepoolKit", "state" : { "branch" : "dev", - "revision" : "54045c2e7d720dcd8a0909037772dcd6f54f0158" + "revision" : "4f4747ff647d836c5a27cc1b9c275e5717901e83" } }, { diff --git a/TidepoolService b/TidepoolService index 7b6a704055..babbfa9e72 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit 7b6a704055bbe0a99fd7c80d7f6c7127284e585b +Subproject commit babbfa9e72c2588853b7ee8213edc538bb5483ce From 2498db2cce33312c632629cb8ab9ced07ecd8c82 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 13:55:07 -0500 Subject: [PATCH 07/58] Update mixpanel-swift to v6.0.0 (b4cb3f6e) --- LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1e4885644b..373197533b 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -97,7 +97,7 @@ "location" : "https://github.com/mixpanel/mixpanel-swift.git", "state" : { "branch" : "master", - "revision" : "c676a9737c76e127e3ae5776247b226bc6d7652d" + "revision" : "b4cb3f6e3e3084e1637c4dfe06c2dcda169ff523" } }, { From 4e4ae7c4b4ef25b3c878cd908ee7c9c1e9392b88 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 13:58:24 -0500 Subject: [PATCH 08/58] MixpanelService: bump deployment target to iOS 17.0 --- MixpanelService | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MixpanelService b/MixpanelService index b33debdac3..6bf2787e83 160000 --- a/MixpanelService +++ b/MixpanelService @@ -1 +1 @@ -Subproject commit b33debdac37d6ef3be955eebb0c42495a1f19232 +Subproject commit 6bf2787e83ddd8e68686127c90a6a964ca29437c From 577beb97760af65fa4aeaf15fb49dbfa3a4130ac Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 14:05:33 -0500 Subject: [PATCH 09/58] MixpanelService: add instance pluginIdentifier for Pluggable conformance --- MixpanelService | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MixpanelService b/MixpanelService index 6bf2787e83..36c0b767b5 160000 --- a/MixpanelService +++ b/MixpanelService @@ -1 +1 @@ -Subproject commit 6bf2787e83ddd8e68686127c90a6a964ca29437c +Subproject commit 36c0b767b5e56be3b38ad86ba60a50f232f55138 From fa192ac23237aedc6ce6f5d83a74c3b7d0437d78 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 14:35:28 -0500 Subject: [PATCH 10/58] MixpanelService: update ServiceUI conformance for new LoopKitUI API --- MixpanelService | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MixpanelService b/MixpanelService index 36c0b767b5..4a4b433c71 160000 --- a/MixpanelService +++ b/MixpanelService @@ -1 +1 @@ -Subproject commit 36c0b767b5e56be3b38ad86ba60a50f232f55138 +Subproject commit 4a4b433c7181fd8d6fa7d22840fd8466a3f6101e From a11ce414b034a4063e6e705f006555452a646f7c Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 16:25:15 -0500 Subject: [PATCH 11/58] OmniBLE: fix private(set) whitespace for Swift 6 compatibility --- OmniBLE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OmniBLE b/OmniBLE index d8b6b1560a..d5fa71f7f0 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit d8b6b1560af9c972dab885480b22f14205b1c376 +Subproject commit d5fa71f7f055ddd48a5fd314234b7425f1465199 From 3ee6fe3f747dc5924beda297a9d3b589b961fd68 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 16:33:49 -0500 Subject: [PATCH 12/58] OmniBLE: fix Swift 6 compile errors --- OmniBLE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OmniBLE b/OmniBLE index d5fa71f7f0..db9d08a86a 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit d5fa71f7f055ddd48a5fd314234b7425f1465199 +Subproject commit db9d08a86a3fdbfd57bc78a9c52a3f32b162395a From b048699c031e6d6151894eb561db66e4ac55db77 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 17:02:54 -0500 Subject: [PATCH 13/58] Package.resolved: add json-logic-swift 1.2.4 (resolved by Xcode) --- .../xcshareddata/swiftpm/Package.resolved | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 373197533b..766968a3f1 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -64,6 +64,15 @@ "version" : "1.7.1" } }, + { + "identity" : "json-logic-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/advantagefse/json-logic-swift", + "state" : { + "revision" : "9088eed1b26937fe13d248aa24d7632a51be28e2", + "version" : "1.2.4" + } + }, { "identity" : "kituracontracts", "kind" : "remoteSourceControl", From e1c6955fb0bd1ed6a71d8c4b42a15506b2ad3eda Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Wed, 25 Mar 2026 17:11:35 -0500 Subject: [PATCH 14/58] OmniKit: fix Swift 6 compile errors --- OmniKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OmniKit b/OmniKit index a5897b9c58..aa07d3e95d 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit a5897b9c58bfc70ab75734246946e795a5b1bd10 +Subproject commit aa07d3e95ded3dcb70b125a52ad4fb524f98d478 From da186a3f64f1863e87e37fac9eb6bd292eed4bad Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 9 Apr 2026 19:01:02 -0500 Subject: [PATCH 15/58] Bump submodule pins to current tidepool-sync/2026-03-10 heads Catches the superproject up to all submodule work that has accumulated on tidepool-sync/2026-03-10 since the initial sync commit, including: - Loop: restore manual dose entry "+" button on insulin delivery screen - LoopKit, NightscoutService, TidepoolService, LibreTransmitter: string catalog updates from Xcode build - OmniKit: fix reentrant lock crash in isSignalLost - Various submodules: merge upstream/{dev,main} into tidepool-sync branch --- CGMBLEKit | 2 +- G7SensorKit | 2 +- LibreTransmitter | 2 +- LogglyService | 2 +- Loop | 2 +- LoopKit | 2 +- LoopOnboarding | 2 +- LoopSupport | 2 +- MinimedKit | 2 +- NightscoutRemoteCGM | 2 +- NightscoutService | 2 +- OmniBLE | 2 +- OmniKit | 2 +- TidepoolService | 2 +- dexcom-share-client-swift | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CGMBLEKit b/CGMBLEKit index 6b702067d4..6093b71458 160000 --- a/CGMBLEKit +++ b/CGMBLEKit @@ -1 +1 @@ -Subproject commit 6b702067d40394bc39c9b0874cb2e9189e7ff69c +Subproject commit 6093b71458a8b7331dfc49ddc0794243b8cb763d diff --git a/G7SensorKit b/G7SensorKit index 4b850b09e1..32e4b88f06 160000 --- a/G7SensorKit +++ b/G7SensorKit @@ -1 +1 @@ -Subproject commit 4b850b09e192df7bc3fd94317ebfe7935ccbe929 +Subproject commit 32e4b88f06182b661b9a6ef3ce39ee19eb2d09c9 diff --git a/LibreTransmitter b/LibreTransmitter index bc017fba25..2f485e22c6 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit bc017fba254e3e8e18efc7ddb326e951c7f246e1 +Subproject commit 2f485e22c662b556aa943f08868cbe94e6be958d diff --git a/LogglyService b/LogglyService index 86253bd32a..ce3312ebc8 160000 --- a/LogglyService +++ b/LogglyService @@ -1 +1 @@ -Subproject commit 86253bd32a46b7257e7c671acb96aadc65f8722a +Subproject commit ce3312ebc80146b1ca803bdf41865d3622ebdff3 diff --git a/Loop b/Loop index 71e06d6ab8..07e729d30f 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 71e06d6ab88396dd7e2d0f069d7bbf254130a583 +Subproject commit 07e729d30fdc2516ea074dc022d5b011a2d12173 diff --git a/LoopKit b/LoopKit index 12ed30ae5b..775fcc5293 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 12ed30ae5ba993a78707e41089d05056e152e2ff +Subproject commit 775fcc529324d06fb5ef1b359884081c6654383d diff --git a/LoopOnboarding b/LoopOnboarding index eece785c7b..c80552cad3 160000 --- a/LoopOnboarding +++ b/LoopOnboarding @@ -1 +1 @@ -Subproject commit eece785c7b18d835e3da82f32776e385ac0f72ea +Subproject commit c80552cad38fccf8509082ff695329a10b8f7d8e diff --git a/LoopSupport b/LoopSupport index de1dae3aec..592f516190 160000 --- a/LoopSupport +++ b/LoopSupport @@ -1 +1 @@ -Subproject commit de1dae3aecada9dd8e2b48d2ee8a8df26939998e +Subproject commit 592f516190fdfce143a9067af10eddaaad98d8ac diff --git a/MinimedKit b/MinimedKit index fcba4a7d36..95fbc6e1d2 160000 --- a/MinimedKit +++ b/MinimedKit @@ -1 +1 @@ -Subproject commit fcba4a7d369f624748da201c80a231750b8a7f12 +Subproject commit 95fbc6e1d2aca61d34af7e8e4d9fa5f0ca1626d3 diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 45a1782dc0..68f5c4bc7d 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 45a1782dc09c11c8f9b54a6eefc6ceb8f7b8abe6 +Subproject commit 68f5c4bc7dbdab04d0da40131b313895bd6b5abc diff --git a/NightscoutService b/NightscoutService index 5ba403c210..219314ae23 160000 --- a/NightscoutService +++ b/NightscoutService @@ -1 +1 @@ -Subproject commit 5ba403c21072312f8cf4236f7fef3f6c011716dc +Subproject commit 219314ae23b2193cef6ab48fce4be0a8a81744bc diff --git a/OmniBLE b/OmniBLE index db9d08a86a..a16d31d2af 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit db9d08a86a3fdbfd57bc78a9c52a3f32b162395a +Subproject commit a16d31d2aff5294d19f566f6b846a367800bf2ca diff --git a/OmniKit b/OmniKit index aa07d3e95d..fc0c5a5ad6 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit aa07d3e95ded3dcb70b125a52ad4fb524f98d478 +Subproject commit fc0c5a5ad69dd0c248e00a1e1a485c9b7dcfd80e diff --git a/TidepoolService b/TidepoolService index babbfa9e72..61c041726a 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit babbfa9e72c2588853b7ee8213edc538bb5483ce +Subproject commit 61c041726a9525d5d2aafbe27bc39153c12b1207 diff --git a/dexcom-share-client-swift b/dexcom-share-client-swift index a4b6a4b9e6..320cc673f4 160000 --- a/dexcom-share-client-swift +++ b/dexcom-share-client-swift @@ -1 +1 @@ -Subproject commit a4b6a4b9e6eb2446a5c6112f66c7d98919cbf9ef +Subproject commit 320cc673f49d7fef19f5bae089ae0a60c7f4cde6 From 71525a18f43192ea1c0505de306af3dc31282b36 Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 23 Apr 2026 18:08:06 -0500 Subject: [PATCH 16/58] OmniBLE: bump to e9425ad (fix reentrant lock crash in isSignalLost) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel fix to OmniKit 924f10d — OmniBLE had the identical bug where isSignalLost() read state.lastPumpDataReportDate from inside a lockedState.mutate closure, causing recursive lock acquisition. --- OmniBLE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OmniBLE b/OmniBLE index a16d31d2af..e9425ad3bf 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit a16d31d2aff5294d19f566f6b846a367800bf2ca +Subproject commit e9425ad3bfc8a1cc9eb06224f831eb8300e5d6ec From 99c1e57c99f9440f5dedc33f4875022aef75c45d Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 10:47:53 -0500 Subject: [PATCH 17/58] Bump submodule pins to tidepool-sync/2026-05-11 heads Tier 1 - LoopKit: bd30c463 (409 commits from tidepool/dev; 18 source conflicts + 19 pbxproj regions resolved; DIY divergence preserved at BasalRateScheduleEditor) - Loop: 76b6b1e3 (14 commits from tidepool/dev; 3 pbxproj regions) Tier 3 - CGM drivers - CGMBLEKit: 69562e7 - G7SensorKit: d024513 - dexcom-share-client-swift: 541de2f - NightscoutRemoteCGM: b1ea9ee - LibreTransmitter: c99daf1 Tier 3 - Pump drivers - RileyLinkKit: 19f5ae8 - OmniKit: b3b6080 (preserves reentrant lock fix; adopts mutateState) - OmniBLE: 645e0fc (preserves reentrant lock fix and Pod Keep Alive suspend special case; adopts mutateState) - MinimedKit: f994d6e (preserves CAGE/IAGE tracking) Tier 3 - Services - TidepoolService: 5f6a064 (incl. DoseEntry dedupe follow-up) - NightscoutService: 1b5cded (preserves APNS response feature) - AmplitudeService: 77dae3e - LogglyService: 8e18081 - MixpanelService: unchanged (already up to date with tidepool) Tier 3 - Support/Onboarding - LoopSupport: a312dfb - LoopOnboarding: fd7e410 Package.resolved - LoopAlgorithm: tidepool-org/LoopAlgorithm bd1a879 (4 test-only commits) --- AmplitudeService | 2 +- CGMBLEKit | 2 +- G7SensorKit | 2 +- LibreTransmitter | 2 +- LogglyService | 2 +- Loop | 2 +- LoopKit | 2 +- LoopOnboarding | 2 +- LoopSupport | 2 +- LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- MinimedKit | 2 +- NightscoutRemoteCGM | 2 +- NightscoutService | 2 +- OmniBLE | 2 +- OmniKit | 2 +- RileyLinkKit | 2 +- TidepoolService | 2 +- dexcom-share-client-swift | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/AmplitudeService b/AmplitudeService index ac96a29fb4..d73d20d3a9 160000 --- a/AmplitudeService +++ b/AmplitudeService @@ -1 +1 @@ -Subproject commit ac96a29fb464de334b0b94e8b6dcb15578ba8e9e +Subproject commit d73d20d3a9c27a200ca30f9672b6d165b2114dc9 diff --git a/CGMBLEKit b/CGMBLEKit index 6093b71458..69f3a85e33 160000 --- a/CGMBLEKit +++ b/CGMBLEKit @@ -1 +1 @@ -Subproject commit 6093b71458a8b7331dfc49ddc0794243b8cb763d +Subproject commit 69f3a85e3371ec7e550151fee67bc0166fe71f69 diff --git a/G7SensorKit b/G7SensorKit index 32e4b88f06..468eefc12c 160000 --- a/G7SensorKit +++ b/G7SensorKit @@ -1 +1 @@ -Subproject commit 32e4b88f06182b661b9a6ef3ce39ee19eb2d09c9 +Subproject commit 468eefc12c936e6d14e0582f9f3bf242b57f6096 diff --git a/LibreTransmitter b/LibreTransmitter index 2f485e22c6..356824bfd8 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit 2f485e22c662b556aa943f08868cbe94e6be958d +Subproject commit 356824bfd8482dbd54e12a8553c459a698d48ede diff --git a/LogglyService b/LogglyService index ce3312ebc8..44f97c9977 160000 --- a/LogglyService +++ b/LogglyService @@ -1 +1 @@ -Subproject commit ce3312ebc80146b1ca803bdf41865d3622ebdff3 +Subproject commit 44f97c99779ba04154f541e4527e512f092f9120 diff --git a/Loop b/Loop index 07e729d30f..761eb48691 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 07e729d30fdc2516ea074dc022d5b011a2d12173 +Subproject commit 761eb48691c4f288b62525ea4609a924f01aff41 diff --git a/LoopKit b/LoopKit index 775fcc5293..c073eab151 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 775fcc529324d06fb5ef1b359884081c6654383d +Subproject commit c073eab151ac40f11898ae6bc10ff6e2980345cc diff --git a/LoopOnboarding b/LoopOnboarding index c80552cad3..da95d69d2c 160000 --- a/LoopOnboarding +++ b/LoopOnboarding @@ -1 +1 @@ -Subproject commit c80552cad38fccf8509082ff695329a10b8f7d8e +Subproject commit da95d69d2c3f35861066dbe704fadb4e4117e787 diff --git a/LoopSupport b/LoopSupport index 592f516190..b93873f4f4 160000 --- a/LoopSupport +++ b/LoopSupport @@ -1 +1 @@ -Subproject commit 592f516190fdfce143a9067af10eddaaad98d8ac +Subproject commit b93873f4f49458d5c23de6a0b98cb7893958d480 diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 766968a3f1..181c9131fd 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -97,7 +97,7 @@ "location" : "https://github.com/tidepool-org/LoopAlgorithm", "state" : { "branch" : "main", - "revision" : "13cb4b45258cee5be1eb2ad941b374dde53de551" + "revision" : "bd1a879ef5942c18630429d25294a473c03b426c" } }, { diff --git a/MinimedKit b/MinimedKit index 95fbc6e1d2..a317b3e38e 160000 --- a/MinimedKit +++ b/MinimedKit @@ -1 +1 @@ -Subproject commit 95fbc6e1d2aca61d34af7e8e4d9fa5f0ca1626d3 +Subproject commit a317b3e38e267297aae816ed47f657bc5c9c1077 diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 68f5c4bc7d..00eefaa056 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 68f5c4bc7dbdab04d0da40131b313895bd6b5abc +Subproject commit 00eefaa0561cee23ed56cb2585d8415b316ec5f1 diff --git a/NightscoutService b/NightscoutService index 219314ae23..09dc797b9a 160000 --- a/NightscoutService +++ b/NightscoutService @@ -1 +1 @@ -Subproject commit 219314ae23b2193cef6ab48fce4be0a8a81744bc +Subproject commit 09dc797b9a30682d02f20a985fec3efb79975e14 diff --git a/OmniBLE b/OmniBLE index e9425ad3bf..832b124e0d 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit e9425ad3bfc8a1cc9eb06224f831eb8300e5d6ec +Subproject commit 832b124e0d1cad4b44cfc758c689834e69d88d18 diff --git a/OmniKit b/OmniKit index fc0c5a5ad6..0d81565530 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit fc0c5a5ad69dd0c248e00a1e1a485c9b7dcfd80e +Subproject commit 0d81565530e02b9de63cdb4157e0b915a4c4e7c9 diff --git a/RileyLinkKit b/RileyLinkKit index bc1f77b333..8a3164bfef 160000 --- a/RileyLinkKit +++ b/RileyLinkKit @@ -1 +1 @@ -Subproject commit bc1f77b333319d9c06aff47d89dd0d9feb765b50 +Subproject commit 8a3164bfefeb749cff08acefb97e741467e566fe diff --git a/TidepoolService b/TidepoolService index 61c041726a..765bcb9b33 160000 --- a/TidepoolService +++ b/TidepoolService @@ -1 +1 @@ -Subproject commit 61c041726a9525d5d2aafbe27bc39153c12b1207 +Subproject commit 765bcb9b339a1c058428707d6dd8ddb0967e90a5 diff --git a/dexcom-share-client-swift b/dexcom-share-client-swift index 320cc673f4..e4c2796377 160000 --- a/dexcom-share-client-swift +++ b/dexcom-share-client-swift @@ -1 +1 @@ -Subproject commit 320cc673f49d7fef19f5bae089ae0a60c7f4cde6 +Subproject commit e4c2796377c7e32c8983006f95b23c3114e6a06f From 212bc27dc63aa6c20dd3459278fcf67cb942bfc8 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 10:48:18 -0500 Subject: [PATCH 18/58] Sync log + progress for 2026-05-11 docs/tidepool-sync-2026-05-11.md describes the merge, conflicts, and divergences. SYNC_PROGRESS.md updated to reflect the new branch and the divergences carried forward. --- SYNC_PROGRESS.md | 94 ++++++++------- docs/tidepool-sync-2026-05-11.md | 199 +++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 44 deletions(-) create mode 100644 docs/tidepool-sync-2026-05-11.md diff --git a/SYNC_PROGRESS.md b/SYNC_PROGRESS.md index 362551fe03..40b6adb132 100644 --- a/SYNC_PROGRESS.md +++ b/SYNC_PROGRESS.md @@ -1,71 +1,77 @@ # Tidepool → LoopKit Sync Progress -**Sync branch:** `tidepool-sync/2026-03-10` +**Sync branch:** `tidepool-sync/2026-05-11` **Source:** `tidepool-org//dev` (or `main`) **Target:** `LoopKit//dev` (or `main`) -**Started:** 2026-03-10 -**Completed:** 2026-03-10 -**Last updated:** 2026-03-10 +**Started:** 2026-05-11 +**Last updated:** 2026-05-11 **Process doc:** [LOOPKIT_SYNC_PROCESS.md](LOOPKIT_SYNC_PROCESS.md) +**Sync log:** [docs/tidepool-sync-2026-05-11.md](docs/tidepool-sync-2026-05-11.md) +**Previous sync:** [SYNC_PROGRESS history for 2026-03-10](#) — branch merged to dev across all 18 repos --- -## Status: ✅ ALL REPOS COMPLETE +## Status: ✅ ALL REPOS MERGED (build verification pending) -18 repos synced, 0 conflicts remaining, ready for compile test & PR. +17 repos merged, 1 noop (MixpanelService already current). All conflicts resolved; build verification in progress. --- ## Full Repo Status -Work proceeded in **Core → App → Plugins** order (see process doc for rationale). - -| # | Repo | Base | Key Conflicts | Status | Doc | -|---|------|------|---------------|--------|-----| -| 1 | **LoopAlgorithm** | `main` | none (fast-forward) | ✅ Done | [doc](sync-docs/LoopAlgorithm.md) | -| 2 | **LoopKit** | `dev` | 16 conflicts: HK→LoopUnit types, pbxproj, .strings cleanup | ✅ Done | [doc](sync-docs/LoopKit.md) | -| 3 | **Loop** | `dev` | 33 conflicts + Swift Concurrency migration | ✅ Done | [doc](sync-docs/Loop.md) | -| 4 | **TidepoolService** | `dev` | pbxproj + DoseEntry (`import LoopAlgorithm` removed, `decisionId`) | ✅ Done | — | -| 5 | **NightscoutService** | `dev` | pbxproj + NightscoutService (RemoteNotificationResponseManager preserved, `decisionId` added) | ✅ Done | — | -| 6 | **MinimedKit** | `main` | pbxproj + MinimedPumpManager (`decisionId`, async Task, `updateLastEventDates` preserved) | ✅ Done | — | -| 7 | **LibreTransmitter** | `main` | pbxproj + `inSignalLoss`/`isInoperable` properties added | ✅ Done | — | -| 8 | **OmniBLE** | `dev` | pbxproj + OmniBLEPumpManager (6 hunks) + PodState | ✅ Done | — | -| 9 | **OmniKit** | `main` | pbxproj + OmnipodPumpManager (4 hunks) + PodCommsSessionTests | ✅ Done | — | -| 10 | **CGMBLEKit** | `dev` | pbxproj only | ✅ Done | — | -| 11 | **G7SensorKit** | `main` | pbxproj only | ✅ Done | — | -| 12 | **dexcom-share-client-swift** | `dev` | pbxproj only | ✅ Done | — | -| 13 | **NightscoutRemoteCGM** | `dev` | pbxproj only | ✅ Done | — | -| 14 | **RileyLinkKit** | `dev` | pbxproj only | ✅ Done | — | -| 15 | **LoopSupport** | `dev` | pbxproj only | ✅ Done | — | -| 16 | **LoopOnboarding** | `dev` | pbxproj only | ✅ Done | — | -| 17 | **AmplitudeService** | `dev` | pbxproj only | ✅ Done | — | -| 18 | **LogglyService** | `dev` | pbxproj only | ✅ Done | — | -| — | **MixpanelService** | `main` | already in sync, noop | ✅ Noop | — | +| # | Repo | Base | Tidepool commits | Conflicts | Submodule commit | Status | +|---|------|------|------------------|-----------|------------------|--------| +| — | **LoopAlgorithm** | `main` | 4 (test-only) | — (pin bump) | pin → `bd1a879` | ✅ Done | +| 1 | **LoopKit** | `dev` | 409 | 18 source + 19 pbxproj | `bd30c463` | ✅ Done | +| 2 | **Loop** | `dev` | 14 | 0 source + 3 pbxproj | `76b6b1e3` | ✅ Done | +| 3 | **CGMBLEKit** | `dev` | 13 | pbxproj only (4) | `69562e7` | ✅ Done | +| 4 | **G7SensorKit** | `main` | 15 | pbxproj only (2) | `d024513` | ✅ Done | +| 5 | **dexcom-share-client-swift** | `dev` | 15 | pbxproj only (3, + orphan cleanup) | `541de2f` | ✅ Done | +| 6 | **NightscoutRemoteCGM** | `dev` | 13 | pbxproj only (2) | `b1ea9ee` | ✅ Done | +| 7 | **LibreTransmitter** | `main` | 14 | pbxproj only (3) | `c99daf1` | ✅ Done | +| 8 | **RileyLinkKit** | `dev` | 3 | pbxproj only (2) | `19f5ae8` | ✅ Done | +| 9 | **OmniKit** | `main` | 18 | OmnipodPumpManager (6 regions) + pbxproj | `b3b6080` | ✅ Done | +| 10 | **OmniBLE** | `dev` | 20 | OmniBLEPumpManager (8 regions) + pbxproj | `645e0fc` | ✅ Done | +| 11 | **MinimedKit** | `main` | 18 | MinimedPumpManager + 1 trivial + pbxproj | `f994d6e` | ✅ Done | +| 12 | **TidepoolService** | `dev` | 45 | DoseEntry + pbxproj | `5f6a064` | ✅ Done | +| 13 | **NightscoutService** | `dev` | 22 | NightscoutService + pbxproj | `1b5cded` | ✅ Done | +| 14 | **AmplitudeService** | `dev` | 6 | pbxproj only (2) | `77dae3e` | ✅ Done | +| 15 | **LogglyService** | `dev` | 6 | pbxproj only (2) | `8e18081` | ✅ Done | +| 16 | **LoopSupport** | `dev` | 11 | pbxproj only (2) | `a312dfb` | ✅ Done | +| 17 | **LoopOnboarding** | `dev` | 28 | pbxproj only (2) | `fd7e410` | ✅ Done | +| — | **MixpanelService** | `main` | 0 | — | unchanged | ✅ Noop | + +LoopWorkspace superproject commit: `3d4432c` ("Bump submodule pins to tidepool-sync/2026-05-11 heads"). --- ## Next Steps -1. **Compile test** — open `LoopWorkspace.xcworkspace` in Xcode and build -2. **Fix any compile errors** that surface (type mismatches, missing parameters, etc.) -3. **Push branches** — requires `GH_TOKEN` with repo write access -4. **Open PRs** — one per repo, `tidepool-sync/2026-03-10` → `dev` (or `main`) +1. **Compile test** — `xcodebuild build -workspace LoopWorkspace.xcworkspace -scheme Loop -destination 'platform=iOS Simulator,name=iPhone 17'` +2. **Fix any compile errors** that surface +3. **Push branches** to `loopkitdev/` +4. **Open PRs** — one per repo, `tidepool-sync/2026-05-11` → `dev` (or `main`) --- -## Key Architectural Decisions +## DIY Divergences Established This Sync -See [LOOPKIT_SYNC_PROCESS.md](LOOPKIT_SYNC_PROCESS.md) for the full Golden Rule and process. +| Decision | Detail | +|---|---| +| `BasalRateScheduleEditor` enforces max basal | DIY rejects tidepool/LoopKit PR #734 (LOOP-5767). Keep `maximumBasalRate: therapySettings.maximumBasalRatePerHour`, not `nil`. See [`memory/divergence_basal_max_filter.md`](../memory/divergence_basal_max_filter.md). The DIY user-reported bug (max basal not respected on OmniBLE/Dash) is what surfaced this. | +| OmniBLE/OmniKit reentrant-lock fix | DIY's `isSignalLost(at:lastPumpDataReportDate:)` signature is preserved over Tidepool's `isSignalLost(at: Date = Date())` to avoid reentrant lock crashes under rapid status polling. | +| OmniBLE Pod Keep Alive suspend special case | DIY's `slot6SuspendTimeExpired` skip-ack guard preserved during the migration to `mutateState`. | +| MinimedKit CAGE/IAGE | DIY's `updateLastEventDates(from:)` for cannula and insulin age tracking preserved; Tidepool has no equivalent. | +| NightscoutService APNS response feature | DIY's `RemoteNotificationResponseManager` + JWT-managed return notifications preserved; Tidepool's simpler version dropped. | +| OmniBLE temp basal error handling | Kept DIY's `completion(.communication(error))` style; did not adopt Tidepool's `do { ... } catch` refactor because it cannot be cleanly applied to the conflict region alone. decisionId tracking already present in DIY. | + +--- + +## Key Patterns Carried Forward from 2026-03-10 | Decision | Detail | |---|---| -| No `import LoopAlgorithm` in TidepoolService | DIY gets types from LoopKit; LoopAlgorithm not in workspace | -| `decisionId: UUID?` added everywhere | LOOP-5295: pump events now carry a decision ID | -| `pumpInoperable` state | LOOP-4801: LibreTransmitter, OmniBLE, OmniKit all got `inSignalLoss`/`isInoperable` | -| `acknowledgeAlert` → `async throws` | OmniBLE + OmniKit: migrated from completion to async | -| OmniBLE `slot6SuspendTimeExpired` guard | DIY safety: don't ack if pod suspended; pod beeps until resumed | -| `updateLastEventDates` preserved in MinimedKit | DIY cannula/insulin age tracking | -| `RemoteNotificationResponseManager` preserved | DIY feature: remote command feedback notifications | -| Live Activity wired via `updateDisplayState()` | LoopDataManager Concurrency migration complete | -| `.strings` refs stripped from all pbxprojs | Tidepool doesn't maintain translations; DIY uses `.xcstrings` | +| LoopAlgorithm as Swift Package | DIY pulls `tidepool-org/LoopAlgorithm` via workspace `Package.resolved`. Do not re-add `XCRemoteSwiftPackageReference "LoopAlgorithm"` to per-repo `.pbxproj` files. | +| `.xcstrings` over `.strings` | DIY uses Xcode 15+ string catalogs; always drop Tidepool's `.strings` PBXFileReferences and variant-group children during pbxproj conflict resolution. | +| Preserve `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` | Always keep DIY side of this XCBuildConfiguration setting. | diff --git a/docs/tidepool-sync-2026-05-11.md b/docs/tidepool-sync-2026-05-11.md new file mode 100644 index 0000000000..cb24ee94ef --- /dev/null +++ b/docs/tidepool-sync-2026-05-11.md @@ -0,0 +1,199 @@ +# Tidepool → LoopKit DIY Sync — 2026-05-11 + +**Branch:** `tidepool-sync/2026-05-11` (17 repos merged + LoopAlgorithm pin bump) +**Build status:** pending verification (in progress at time of writing) +**Previous sync:** 2026-03-10 (see [`tidepool-sync-2026-03-10.md`](tidepool-sync-2026-03-10.md)) + +This is the smaller, follow-up sync after the large 2026-03-10 rebase. +Most of the heavy architectural integration (LoopAlgorithm package extraction, +Swift Concurrency migration, HK→LoopUnit migration) landed last time. This sync +absorbs roughly 2 months of incremental Tidepool development. + +--- + +## Headline numbers + +| Repo (Tier 1+2) | Tidepool commits absorbed | +|---|---| +| LoopKit | 409 | +| Loop | 14 | + +| Repo (Tier 3) | Tidepool commits absorbed | +|---|---| +| TidepoolService | 45 | +| LoopOnboarding | 28 | +| NightscoutService | 22 | +| OmniBLE | 20 | +| OmniKit / MinimedKit | 18 each | +| G7SensorKit / dexcom-share-client-swift | 15 each | +| LibreTransmitter | 14 | +| CGMBLEKit / NightscoutRemoteCGM | 13 each | +| LoopSupport | 11 | +| AmplitudeService / LogglyService | 6 each | +| RileyLinkKit | 3 | +| MixpanelService | 0 (already up to date) | +| LoopAlgorithm (package pin) | 4 (test-only) | + +Total: ~660 Tidepool commits across the ecosystem. + +--- + +## 1. New features from Tidepool + +Most of these are completed by Tidepool in the LoopKit/Loop repos themselves. +The plugins primarily got matching protocol/API updates. + +### LoopKit + +- **Required version-update flow** (LOOP-1114) — new `LoopNotificationCategory.requiredUpdate` + and a `SupportProviding` protocol with `MockSupport` UI. +- **`isMutable` dose detection** (LOOP-5843) — `DoseStore` now uses `isMutable` rather + than time-based heuristics to determine unfinished doses. +- **Activity preset insulin-scale tuning** (LOOP-5807) — biking 0.22→0.23, strength + training 0.39→0.37 in `TemporaryScheduleOverride.defaultInsulinNeedsScaleFactor`. +- **Correction range overrides guardrail** (LOOP-5878) — `CorrectionRangeOverridesEditor` + now actually passes `viewModel.guardrail` (was always `nil`). +- **New preset UI infrastructure** — `EditPresetView`, `ReviewNewPresetView`, + `InsulinNeedsAdjustmentPreview`, and supporting types. +- **Media/Transcript support** — 7 new files under `LoopKit/Media/` (captions, transcripts, + metadata) — likely for in-app support/training video infrastructure. +- **QuantityFormatter API simplification** — removed unused `rule:` parameter from + `doubleValue()`. + +### Loop + +- **Required version-update view** — `RequiredVersionUpdateView`, paired with the LoopKit + notification category above. +- **Preset performance history** — `PresetPerformanceHistoryView` and `PresetsPerformanceHistoryViewModel`. +- **Automation history tracking** — `AutomationHistoryEntry`, `AutomationHistoryEntryTests`. +- **Media/Transcript player infrastructure** — `AudioPlayer`, `CaptionsView`, `MediaPlayerView`, + `PlayerControls`, `TranscriptView`, `VideoView` under `Loop/Views/Presets/Media Player/`. + +### Plugins + +- **OmniBLE / OmniKit** — `mutateState` API migration (replacing `setState`); `decisionId` + carried through the temp-basal path; pod-inoperable refinements. +- **MinimedKit** — same `decisionId` + protocol updates as the other pump drivers. +- **TidepoolService / NightscoutService** — `decisionId` on `DoseEntry` and + `PersistedPumpEvent`; misc protocol updates. + +--- + +## 2. Conflicts encountered & resolutions + +### LoopKit — 18 source conflicts + 19 pbxproj regions + +**Mechanical "take Tidepool" (low risk):** +DoseStore, LoopNotificationCategory, QuantityFormatter, TemporaryScheduleOverride, +LoopKitUI/SupportUI, GlucoseTherapySettingInformationView, CorrectionRangeOverridesEditor, +InsulinType (loses Lokalise detailed insulin descriptions — translations will repopulate +on next Lokalise run). + +**Keep DIY (DIY-only debug features):** +`MockKitUI/Views/MockCGMManagerSettingsView.swift` and `MockPumpManagerSettingsView.swift` +— simulator settings gated by `allowDebugFeatures`. Tidepool doesn't have these. + +**Accept Tidepool deletions (LoopAlgorithm package extraction, LOOP-4781):** +- `LoopKit/InsulinKit/ExponentialInsulinModel.swift` +- `LoopKit/LoopAlgorithm/LoopAlgorithm.swift` +- `LoopKit/LoopAlgorithm/LoopPredictionOutput.swift` + +These types now live in `tidepool-org/LoopAlgorithm` (the Swift Package DIY already +pulls via workspace `Package.resolved`). Deleting the inline copies makes DIY match +Tidepool's architecture. + +**Accept Tidepool deletion (preset UI replaced):** +`LoopKitUI/View Controllers/OverrideSelectionViewController.swift` — superseded by +Tidepool's new SwiftUI `EditPresetView` / `ReviewNewPresetView`. DIY's crash-fix +commit `3ce43ded` does not apply to the new SwiftUI flow. + +**Add/add — both sides added the same files:** +Took Tidepool's content for the new preset infrastructure +(`InsulinNeedsAdjustmentPreview`, `EditPresetView`, `ReviewNewPresetView`). + +**DIY divergence — BasalRateScheduleEditor max-basal filtering (see Section 4):** +The auto-merge silently dropped this fix; restored manually with a comment pointing +at the divergence memory. + +**pbxproj (19 regions):** +- Dropped all Tidepool `.strings` PBXFileReferences and PBXBuildFiles +- Kept DIY's `.xcstrings` references and `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` +- Kept Tidepool's new Media/Transcript and `TimeInterval+Timecode.swift` references +- Dropped references to the 4 source files deleted in this merge +- Pre-existing `XCRemoteSwiftPackageReference "LoopAlgorithm"` in HEAD was left alone + (not part of this merge; same as 2026-03-10) +- Final state: 1898 `{` / 1898 `}`, `xcodebuild -list` parses + +### Loop — 3 pbxproj regions only (no source conflicts) + +- Regions 1 + 2: both sides added a Swift file at the same insertion point — + kept both `ContentMargin.swift` (DIY) and `PresetPerformanceHistoryView.swift` (Tidepool). +- Region 3: dropped Tidepool's `.strings` refs (ru/de InfoPlist/Localizable/ckcomplication); + kept Tidepool's new Swift files (`RequiredVersionUpdateView`, `AutomationHistoryEntry`, + `AutomationHistoryEntryTests`, `LoopCircleView`). +- Cleaned up 3 orphaned variant-group children at lines ~4088, 4147, 4166 that + referenced the dropped `.strings` FileReferences. + +### Plugins (Tier 3) — pbxproj patterns + +For 11 of the 15 plugin merges, the *only* conflict was the standard +`LOCALIZATION_PREFERS_STRING_CATALOGS = YES` line in the Debug+Release +XCBuildConfiguration blocks. All resolved by keeping DIY's setting. + +Notable plugin-specific cleanup: +- **CGMBLEKit**: dropped a duplicate array-form `LD_RUNPATH_SEARCH_PATHS` entry + (string form remains below). +- **dexcom-share-client-swift**: cleaned up an orphaned `Localizable.strings` + children-reference in the `ShareClient` PBXGroup that had no PBXFileReference + declaration. +- **TidepoolService**: 4 conflict regions — kept DIY's `.xcstrings` PBXBuildFiles + and PBXFileReferences (the 4 IDs are properly wired into PBXGroup children + and PBXResourcesBuildPhase); dropped 10 Tidepool `.strings` FileReferences. + +### Plugin source conflicts (the 5 repos that had them) + +| Repo / File | Resolution | +|---|---| +| OmniKit / `OmnipodPumpManager.swift` | 3 regions keep DIY (`isSignalLost(at:lastPumpDataReportDate:)` reentrant-lock fix from commit `924f10d`); 3 regions take Tidepool (`setState` → `mutateState` Swift 6 migration). | +| OmniBLE / `OmniBLEPumpManager.swift` | 3 regions keep DIY (reentrant-lock fix `e9425ad`); region at line 2221 keep DIY (`completion(.communication(error))` error style — Tidepool's do/catch refactor cannot be cleanly applied to the conflict region alone; `decisionId` tracking already present in DIY's `setTempBasal` call); region at line 2722 **manual merge** (preserve DIY suspend-time-expired special case from Pod Keep Alive #165 + adopt Tidepool's properly-indented `try await withCheckedThrowingContinuation`); 3 regions take Tidepool (`mutateState`). | +| MinimedKit / `MinimedPumpManager.swift` | All 3 regions keep DIY — preserves CAGE/IAGE tracking (commit `ff07802`); the third region was Tidepool adding a duplicate `isInoperable` property that DIY already had elsewhere in the file. | +| MinimedKit / `MinimedKitUI/Views/MinimedPumpSettingsViewModel.swift` | Take Tidepool (trivial whitespace). | +| TidepoolService / `TidepoolServiceKit/Extensions/DoseEntry.swift` | Both sides changed import ordering; pre-staged resolution kept both → duplicate `import LoopAlgorithm`. Follow-up commit `5f6a064` deduped. | +| NightscoutService / `NightscoutServiceKit/NightscoutService.swift` | Keep DIY — preserves the 60-line APNS response feature (`RemoteNotificationResponseManager`, JWT-managed return notifications) from commit `0ca2c08`. Tidepool's version is a simpler switch with no response handling. | + +--- + +## 3. DIY divergences + +See `SYNC_PROGRESS.md` "DIY Divergences" section for the canonical list. New as of this sync: + +### BasalRateScheduleEditor max-basal filtering + +A DIY user (OmniBLE/Dash) reported being able to set basal schedule entries above +their configured `maximumBasalRatePerHour`. The cause was that DIY had inherited +Tidepool's PR #734 (LOOP-5767, "Basal schedule editor should ignore max basal rate", +merged 2026-02-27) in the 2026-03-10 sync, which changed the convenience initializer +in `BasalRateScheduleEditor` to pass `maximumBasalRate: nil`, disabling the picker-side +filter. + +DIY explicitly rejects this Tidepool change. Restored +`maximumBasalRate: therapySettingsViewModel.therapySettings.maximumBasalRatePerHour` +with an inline comment pointing at `memory/divergence_basal_max_filter.md`. On +2026-05-11 sync, auto-merge silently reverted this fix; manual restoration was needed +and a divergence comment was added to defend it on future syncs. + +--- + +## 4. Items to verify post-build + +- **OmniBLE temp basal error reporting:** kept DIY's `completion(.communication(error))` + pattern; verify error messages still surface correctly to Loop on temp basal failures. +- **Mock simulator features:** ensure simulator pump/CGM settings still render under + `allowDebugFeatures` flag in debug builds. +- **APNS responses (NightscoutService):** verify remote command feedback notifications + still flow end-to-end. +- **Basal schedule editor (OmniBLE / Dash):** verify the originally reported bug is + resolved — entries above the configured max basal should not be selectable. +- **Lokalise translations:** `InsulinType.swift` lost DIY's detailed insulin + descriptions in favor of Tidepool's simpler combined Fiasp/Lyumjev case. Next + Lokalise pull should repopulate. From 0f207ef2ef0700179a93f606bcaa00d6be57f895 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 11:12:43 -0500 Subject: [PATCH 19/58] Catch up Loop, CGMBLEKit, OmniBLE pins to origin/dev Pulls in 3 small origin/dev updates that landed after this sync started: - Loop: PR #2410 Live activity plot fixes (manual merge to preserve our post-Tidepool-sync API: kept async/await sample fetch + LoopUnit signature, adopted upstream's presetStart<=presetEnd guard and the adjustedChartStart/End helpers) - CGMBLEKit: PR #211 handle inactive CGM sensor sessions (clean merge) - OmniBLE: Lokalise translation update (clean merge) --- CGMBLEKit | 2 +- Loop | 2 +- OmniBLE | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CGMBLEKit b/CGMBLEKit index 69f3a85e33..48bbfad581 160000 --- a/CGMBLEKit +++ b/CGMBLEKit @@ -1 +1 @@ -Subproject commit 69f3a85e3371ec7e550151fee67bc0166fe71f69 +Subproject commit 48bbfad581d918c28dde08af237edc6f730f3696 diff --git a/Loop b/Loop index 761eb48691..60c30bf2ad 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 761eb48691c4f288b62525ea4609a924f01aff41 +Subproject commit 60c30bf2adde804da797e686e1ea5fbb550446ab diff --git a/OmniBLE b/OmniBLE index 832b124e0d..82a8a63479 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 832b124e0d1cad4b44cfc758c689834e69d88d18 +Subproject commit 82a8a634799db33df45e820fc15e5f9459abe412 From 932d5c3f8185d041f840974894cc00e1f78b95a7 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 12:05:13 -0500 Subject: [PATCH 20/58] Bump Loop pin: post-dose updateRemoteRecommendation for NS uploads --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 60c30bf2ad..36eba3caa1 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 60c30bf2adde804da797e686e1ea5fbb550446ab +Subproject commit 36eba3caa121f56ed258917ca28ee90fe91c9f13 From d65fff9e8f6948f5b725b4d6a211d370f0996c6a Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 12:21:11 -0500 Subject: [PATCH 21/58] Bump OmniKit + OmniBLE pins: setState compile fix --- OmniBLE | 2 +- OmniKit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OmniBLE b/OmniBLE index 82a8a63479..166ecfb11a 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 82a8a634799db33df45e820fc15e5f9459abe412 +Subproject commit 166ecfb11a35846ab9987f514807c31745e47ab3 diff --git a/OmniKit b/OmniKit index 0d81565530..52d2f41c7e 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 0d81565530e02b9de63cdb4157e0b915a4c4e7c9 +Subproject commit 52d2f41c7ee5ce42c8ffa4ea0d524f840714eb02 From 7c53ccaa42c642779135fb024019ac726aee7435 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 12:43:46 -0500 Subject: [PATCH 22/58] Bump MinimedKit pin: restore DIY CAGE/IAGE viewModel properties --- MinimedKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MinimedKit b/MinimedKit index a317b3e38e..f19f99cc0b 160000 --- a/MinimedKit +++ b/MinimedKit @@ -1 +1 @@ -Subproject commit a317b3e38e267297aae816ed47f657bc5c9c1077 +Subproject commit f19f99cc0b552fc6996412285f9a1e450a4f4961 From 6c7cd548e24eb679aee776818f92437a14da3fd3 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 12:54:58 -0500 Subject: [PATCH 23/58] Bump LoopKit pin: restore public on ResizeablePicker --- LoopKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index c073eab151..c75a9481a9 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit c073eab151ac40f11898ae6bc10ff6e2980345cc +Subproject commit c75a9481a9201025aca4eaa3adac78568cba7c76 From 83646c76a1332ae97c54848a536d4eb57948423f Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 13:13:19 -0500 Subject: [PATCH 24/58] Bump Loop + LoopKit pins: remove ResizeablePicker DIY divergence --- Loop | 2 +- LoopKit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Loop b/Loop index 36eba3caa1..e500280a12 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 36eba3caa121f56ed258917ca28ee90fe91c9f13 +Subproject commit e500280a12d2090ea46aec687384d6813b55fe0c diff --git a/LoopKit b/LoopKit index c75a9481a9..fd075aa5db 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit c75a9481a9201025aca4eaa3adac78568cba7c76 +Subproject commit fd075aa5db8111eda3f9ebd2e87dd77c98e9fe5d From 1f603e38247bbc8696c5f48667f1580c7118e7b6 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 14:18:24 -0500 Subject: [PATCH 25/58] Bump Loop pin: fix Live Activity preset SF Symbol rendering --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index e500280a12..522f8c1fda 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit e500280a12d2090ea46aec687384d6813b55fe0c +Subproject commit 522f8c1fda3dff8e395ec1a58e4c8fd6c77a6343 From 168e3f18254e0d230c26e52df5d3ff4d9a84196c Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 14:22:19 -0500 Subject: [PATCH 26/58] Bump Loop pin: top-leading alignment for Live Activity preset label --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 522f8c1fda..6f848c7e58 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 522f8c1fda3dff8e395ec1a58e4c8fd6c77a6343 +Subproject commit 6f848c7e58265455bd4bd1777bf71bd1a2c6a9ca From 429d7ade0713c0ce8e090ccde43b9f578f30e49c Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 14:28:11 -0500 Subject: [PATCH 27/58] Bump Loop pin: preset label inside plot area --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 6f848c7e58..72dc6239f1 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 6f848c7e58265455bd4bd1777bf71bd1a2c6a9ca +Subproject commit 72dc6239f1ed52c84bee8c428f9356efa7c0d3e4 From 193ef5a8ae47b69eec35f225e6619dc923dbb48b Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 May 2026 14:34:05 -0500 Subject: [PATCH 28/58] Bump Loop pin: promote Live Activity to top-level settings --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 72dc6239f1..830af33d06 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 72dc6239f1ed52c84bee8c428f9356efa7c0d3e4 +Subproject commit 830af33d06b462f46ee832d3d70497819c47ef01 From 186f32bda5e51da2f1bbed6b768823bfcbb011d4 Mon Sep 17 00:00:00 2001 From: loopkitdev Date: Sat, 16 May 2026 10:29:41 -0500 Subject: [PATCH 29/58] Bump Loop pin: diagnostic report force-unwraps watch/statusExtension managers --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 830af33d06..ce1935b54c 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 830af33d06b462f46ee832d3d70497819c47ef01 +Subproject commit ce1935b54c5bcb1fdc4c5bfbfad7d41d0ec4eb32 From e6fd8ecc3523da5fe65c5323587b8fb3fdf1512e Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 16 May 2026 12:09:53 -0500 Subject: [PATCH 30/58] Bump Loop: restore predicted carb-effect line on ICE and food-insight charts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picks up loopkitdev/Loop@4eea0b24 — widens the carbStatus.dynamicGlucoseEffects sampling window in fetchCarbAbsorptionReview and getHistoricalChartsData from `from: end` to `from: start` so the model-predicted line spans the full chart again (was only generating samples in now+1h … now+7h, outside the visible window). UI-only; no dosing-path impact. --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index ce1935b54c..4eea0b24b0 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit ce1935b54c5bcb1fdc4c5bfbfad7d41d0ec4eb32 +Subproject commit 4eea0b24b07f1ebae0b886e68a6b3cadbb23e97d From 699cddd9872f5e20ce9491b9f87719ef7abb82ad Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 16 May 2026 15:11:04 -0500 Subject: [PATCH 31/58] Bump Loop: restore submodule SHAs in diagnostic report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picks up loopkitdev/Loop@30725413 — ports Marion's #2399 submodule list into LoopAppManager.generateDiagnosticReport(). The script and BuildDetails plumbing were already in place; only the report consumer was missing after the merges. --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 4eea0b24b0..3072541390 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 4eea0b24b07f1ebae0b886e68a6b3cadbb23e97d +Subproject commit 3072541390b24502f624d7eb210ab8e837a3b0f7 From 3b68b6d928ccf9eb4962056ead4a5d58c5b31916 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 19 May 2026 20:21:14 -0500 Subject: [PATCH 32/58] Bump Loop: External Insulin log labeling/delete, presets cleanup Loop submodule changes: - Label manually-entered boluses as External Insulin in the delivery log, with a delete action in the event details screen - Fix Text wrapping in presets training content - Remove the unfinished Performance History entry point from presets Also ignore .claude in the workspace. --- .gitignore | 3 +++ Loop | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 02b598dfb4..af03cd5efe 100644 --- a/.gitignore +++ b/.gitignore @@ -18,12 +18,15 @@ xcuserdata/ *.xcscmblueprint *.xcuserstate .DS_Store +.claude ## Obj-C/Swift specific *.hmap *.ipa + ## Playgrounds *.playground playground.xcworkspace timeline.xctimeline + diff --git a/Loop b/Loop index 3072541390..e7a1e21081 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 3072541390b24502f624d7eb210ab8e837a3b0f7 +Subproject commit e7a1e210816a985c25755cf8a01334eaac7fd7ae From a85ee4541ab234fc8a615f9a0288088ffef7328d Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 11:29:31 -0500 Subject: [PATCH 33/58] Bump Loop: merge origin/dev into tidepool-sync (resolve WatchApp plist + pbxproj localization conflicts) --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index e7a1e21081..1119574eaa 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit e7a1e210816a985c25755cf8a01334eaac7fd7ae +Subproject commit 1119574eaa0ad5d7cc1c3ebd756ba6f07a448fbf From 68ce29463e228c68d4ba7d9b8508beacf97c251e Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 16:49:37 -0500 Subject: [PATCH 34/58] Bump Loop: fix WatchApp Info.plist (drop WKWatchKitApp) for watch embedding --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 1119574eaa..fd313d06dc 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 1119574eaa0ad5d7cc1c3ebd756ba6f07a448fbf +Subproject commit fd313d06dc8c2827ced2dbceaf24350c5389a0dd From 7d11a755805fc249eec6e8f145e90d4ac237b972 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 18:27:38 -0500 Subject: [PATCH 35/58] Bump Loop: Apple Health access status screen in Settings --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index fd313d06dc..a81435c2b7 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit fd313d06dc8c2827ced2dbceaf24350c5389a0dd +Subproject commit a81435c2b7dba98d7dd0902dc4ba35d69f49298e From 7fd02d60f817613f75dcae50711473004da1f770 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 19:03:05 -0500 Subject: [PATCH 36/58] Bump LoopKit: keep Loop-written doses on external HealthKit deletion --- LoopKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index fd075aa5db..75e426db14 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit fd075aa5db8111eda3f9ebd2e87dd77c98e9fe5d +Subproject commit 75e426db144dbad4a9aac82a3b695764408ab0f4 From a31116a6aa20709591f1c4edb4c7276f65538b5f Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 19:07:16 -0500 Subject: [PATCH 37/58] Bump Loop: flag-gated dose deletion from dose details (with active-insulin warning) --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index a81435c2b7..8a7dac5ff9 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit a81435c2b7dba98d7dd0902dc4ba35d69f49298e +Subproject commit 8a7dac5ff9fbf6c6137e81f74f5186a73f281f0c From d1768c3f5dbd6a95a7c006cb55e5c47d5a7c28ef Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 19:10:12 -0500 Subject: [PATCH 38/58] Bump Loop: enable dose deletion by default --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 8a7dac5ff9..1997657e98 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 8a7dac5ff9fbf6c6137e81f74f5186a73f281f0c +Subproject commit 1997657e98d39f73490590cffe3242aafb4b0d51 From a36ee937e267533683fa9a5fe6dcd207a62640e9 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 20:17:47 -0500 Subject: [PATCH 39/58] Bump Loop: clear stale Last Bolus after dose deletion --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 1997657e98..c876c5635a 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 1997657e98d39f73490590cffe3242aafb4b0d51 +Subproject commit c876c5635afca620f2dcbde815fb050a0baf629d From ffeadc06338ee86cb359d2389bf2f8811e6c723e Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 20:20:37 -0500 Subject: [PATCH 40/58] Bump Loop: don't allow deleting in-progress doses --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index c876c5635a..a3c99349fb 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit c876c5635afca620f2dcbde815fb050a0baf629d +Subproject commit a3c99349fb7f648c9a49ca266844e38beae9b1ef From 9987f2adb4c21ca6769ff994bd7dd3162c45c1bf Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 20:44:14 -0500 Subject: [PATCH 41/58] Bump LoopKit: don't crash on legacy bolus without units; note divergence --- LoopKit | 2 +- SYNC_PROGRESS.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index 75e426db14..3059bb85d1 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 75e426db144dbad4a9aac82a3b695764408ab0f4 +Subproject commit 3059bb85d1ae594c924f709295f0587ea5c89613 diff --git a/SYNC_PROGRESS.md b/SYNC_PROGRESS.md index 40b6adb132..07efac3738 100644 --- a/SYNC_PROGRESS.md +++ b/SYNC_PROGRESS.md @@ -65,6 +65,7 @@ LoopWorkspace superproject commit: `3d4432c` ("Bump submodule pins to tidepool-s | MinimedKit CAGE/IAGE | DIY's `updateLastEventDates(from:)` for cannula and insulin age tracking preserved; Tidepool has no equivalent. | | NightscoutService APNS response feature | DIY's `RemoteNotificationResponseManager` + JWT-managed return notifications preserved; Tidepool's simpler version dropped. | | OmniBLE temp basal error handling | Kept DIY's `completion(.communication(error))` style; did not adopt Tidepool's `do { ... } catch` refactor because it cannot be cleanly applied to the conflict region alone. decisionId tracking already present in DIY. | +| CachedInsulinDeliveryObject bolus-without-units | Dropped the `assertionFailure` in `CachedInsulinDeliveryObject.dose` for a `.bolus` with neither programmedUnits nor deliveredUnits — legacy rows from an upgraded DIY install can have neither and trapped debug builds on read. Falls back to 0 (release behavior). Upstream keeps the assertion (fresh installs only); re-remove on future LoopKit syncs. | --- From a7c68cfbfec864a18bafc0023869c5e9ef00a4ea Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 23:03:38 -0500 Subject: [PATCH 42/58] =?UTF-8?q?Bump=20LoopKit:=20preserve=20dose=20amoun?= =?UTF-8?q?ts=20across=20Modelv4=E2=86=92v6=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LoopKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index 3059bb85d1..d15ab455ae 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 3059bb85d1ae594c924f709295f0587ea5c89613 +Subproject commit d15ab455ae6f78cd50661a37a0b40e5249d84d13 From f1e137a671850087c04f17f3840a294854d81ce9 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 23:17:50 -0500 Subject: [PATCH 43/58] =?UTF-8?q?docs:=20note=20one-way=20upgrade=20(Core?= =?UTF-8?q?=20Data=20v4=E2=86=92v6)=20in=20sync=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document that upgrading dev→sync migrates the shared store to Modelv6 and cannot be reverted: dev ships only up to Modelv4 and can't open a v6 store, so reverting leaves the app unable to load its data store (no on-disk data loss, but dev needs a delete+reinstall, rebuilding from HealthKit). Also records that the forward migration now preserves dose amounts (value→deliveredUnits). Includes the earlier 2026-03-10 LoopAlgorithm-packaging callout. --- docs/tidepool-sync-2026-03-10.md | 15 +++++++++++++++ docs/tidepool-sync-2026-05-11.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/docs/tidepool-sync-2026-03-10.md b/docs/tidepool-sync-2026-03-10.md index 5b4a8d5df0..20a98e46c1 100644 --- a/docs/tidepool-sync-2026-03-10.md +++ b/docs/tidepool-sync-2026-03-10.md @@ -7,6 +7,21 @@ This document describes the changes introduced by syncing the Tidepool fork of L back into DIY, the conflicts encountered during that merge, and the decisions made to resolve them. +> **Update (2026-05-20) — read before relying on the LoopAlgorithm decisions below.** +> A follow-up sync landed on 2026-05-11; see +> [`tidepool-sync-2026-05-11.md`](tidepool-sync-2026-05-11.md) for that round. +> +> One major decision recorded in this doc has since been **reversed**: §3 states that +> DIY keeps LoopAlgorithm embedded *inline* inside LoopKit and *omits* the +> `XCRemoteSwiftPackageReference "LoopAlgorithm"` (see "TidepoolService: import +> LoopAlgorithm — Removed" and the pbxproj rules table). As of the 2026-05-11 sync, DIY +> instead consumes the **`tidepool-org/LoopAlgorithm` Swift package** directly — pinned +> in the workspace `Package.resolved` — and the inline `LoopKit/LoopAlgorithm/` copies +> (`LoopAlgorithm.swift`, `LoopPredictionOutput.swift`, `ExponentialInsulinModel.swift`) +> were deleted (LOOP-4781). `import LoopAlgorithm` is therefore present again where this +> doc says it was removed. Where the two docs disagree on LoopAlgorithm packaging, the +> 2026-05-11 doc is authoritative. + --- ## Table of Contents diff --git a/docs/tidepool-sync-2026-05-11.md b/docs/tidepool-sync-2026-05-11.md index cb24ee94ef..540b16d82f 100644 --- a/docs/tidepool-sync-2026-05-11.md +++ b/docs/tidepool-sync-2026-05-11.md @@ -197,3 +197,32 @@ and a divergence comment was added to defend it on future syncs. - **Lokalise translations:** `InsulinType.swift` lost DIY's detailed insulin descriptions in favor of Tidepool's simpler combined Fiasp/Lyumjev case. Next Lokalise pull should repopulate. + +--- + +## 5. Upgrading is a one-way operation (Core Data) + +**You cannot revert to `dev` after upgrading to the sync build.** + +The shared LoopKit Core Data store (glucose, dose, carb, dosing decisions — all in one +`Model.sqlite` in the app group) is migrated forward from **Modelv4** to **Modelv6** on +first launch of the sync build. `dev` only ships model versions up to **Modelv4** (it has +no v5/v6 model), and Core Data migrations are forward-only, so a `dev` build cannot open a +v6 store: `addPersistentStore` fails and `PersistenceController` lands in an `.error` state. + +Consequences of going back to `dev` after upgrading: +- `dev`'s data stores won't load — the app is non-functional (no cached glucose/dose/carb, + looping won't run). +- The v6 data is **not** wiped from disk, so reinstalling the sync build reads it again. +- To actually use `dev` again you must delete + reinstall it, which wipes the local cache; + it then rebuilds from HealthKit (the long-term history lives there, not in this store). + +This is inherent Core Data forward-migration behavior, not specific to any one change. + +**Forward migration preserves insulin data.** The v4→v6 mapping originally dropped the +old single `value` attribute (auto-generated, name-based mappings had no destination), +zeroing cached bolus/basal amounts and understating IOB. `CachedInsulinDeliveryObjectMigrationPolicy` +now copies `value` → `deliveredUnits` (and `programmedUnits` for boluses); basal *rates* +already carry over via `scheduledBasalRate`/`programmedTempBasalRate`. Note this only fixes +the forward path — installs that already migrated on a build *without* the policy have +already-dropped values that this cannot recover (they read as 0). From a775f2f2fd71debac7ba0de405730cecdd2d23cb Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 20 May 2026 23:48:00 -0500 Subject: [PATCH 44/58] Bump Loop: restore localized Intents.strings refs (ITMS-90626) --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index a3c99349fb..82193f41bf 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit a3c99349fb7f648c9a49ca266844e38beae9b1ef +Subproject commit 82193f41bf61a41a749c6bb8c8ef65d386bb2bae From 4046252069ce018520c9720f85787b25e7c1ebc2 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Thu, 21 May 2026 00:17:45 -0500 Subject: [PATCH 45/58] Bump OmniBLE/OmniKit pins: report pod faults as pump events --- OmniBLE | 2 +- OmniKit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OmniBLE b/OmniBLE index 166ecfb11a..87606c4b88 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 166ecfb11a35846ab9987f514807c31745e47ab3 +Subproject commit 87606c4b88e25cf3f991c5561645a434838d2054 diff --git a/OmniKit b/OmniKit index 52d2f41c7e..e5fad99f65 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 52d2f41c7ee5ce42c8ffa4ea0d524f840714eb02 +Subproject commit e5fad99f65f9c2a80425926715e97eba526871f1 From 14d19872781e8d7ddc697b102c2ea058d54e3cba Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Thu, 21 May 2026 10:08:25 -0500 Subject: [PATCH 46/58] Bump version to 3.15.0 --- VersionOverride.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VersionOverride.xcconfig b/VersionOverride.xcconfig index b176650a02..e7d27a9033 100644 --- a/VersionOverride.xcconfig +++ b/VersionOverride.xcconfig @@ -8,5 +8,5 @@ // Version [for DIY Loop] // configure the version number in LoopWorkspace -LOOP_MARKETING_VERSION = 3.14.0 +LOOP_MARKETING_VERSION = 3.15.0 CURRENT_PROJECT_VERSION = 57 From abad56f1cfc23cc9425f74ad5ada92afe51555f9 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Thu, 21 May 2026 18:30:23 -0500 Subject: [PATCH 47/58] Build against LoopKit/LoopAlgorithm main Repoint workspace LoopAlgorithm package to LoopKit/LoopAlgorithm (main) and bump LoopKit pin. --- LoopKit | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LoopKit b/LoopKit index d15ab455ae..c17ef4b4ea 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit d15ab455ae6f78cd50661a37a0b40e5249d84d13 +Subproject commit c17ef4b4ea5e3c8d8e57db4ffa310c06364d5cec diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 181c9131fd..11664817cf 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -94,10 +94,10 @@ { "identity" : "loopalgorithm", "kind" : "remoteSourceControl", - "location" : "https://github.com/tidepool-org/LoopAlgorithm", + "location" : "https://github.com/LoopKit/LoopAlgorithm", "state" : { "branch" : "main", - "revision" : "bd1a879ef5942c18630429d25294a473c03b426c" + "revision" : "1ca85662b1f8799758988108f46dba5f34d4889b" } }, { From 76305d9e4d633b063a74c4e41526dda86dded0c8 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Fri, 22 May 2026 11:28:56 -0500 Subject: [PATCH 48/58] Bump Loop: show carb sharing status on Apple Health page --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 82193f41bf..427deb8cf1 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 82193f41bf61a41a749c6bb8c8ef65d386bb2bae +Subproject commit 427deb8cf1d8d0567f1df7fb08fcece68c231e19 From fc9293e74ae21bf89135e4d173bd8671385e1a18 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 23 May 2026 15:14:30 -0500 Subject: [PATCH 49/58] Bump Loop: default to automatic bolus, deprecate temp basal --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 427deb8cf1..d5407fb86b 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 427deb8cf1d8d0567f1df7fb08fcece68c231e19 +Subproject commit d5407fb86b30125d52d214a9169a8016631ff11d From 1a0c51852b79a9ccb8b2ce5b1399d30576fee985 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 15:42:52 -0500 Subject: [PATCH 50/58] Bump NightscoutRemoteCGM: API Secret optional for read-only sites --- NightscoutRemoteCGM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 00eefaa056..e37dc452b3 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 00eefaa0561cee23ed56cb2585d8415b316ec5f1 +Subproject commit e37dc452b3efd82639d702d75642cdf0980555fd From b26221fdc4a5605bf72d1399a86227b9dc3757eb Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 16:50:53 -0500 Subject: [PATCH 51/58] Bump NightscoutRemoteCGM: add verification logging --- NightscoutRemoteCGM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index e37dc452b3..0d65dade56 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit e37dc452b3efd82639d702d75642cdf0980555fd +Subproject commit 0d65dade56e4bbebda2e78fff68678dd3ff6f540 From 3ee4abd3356593bceb706495b26c841441904aec Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 17:30:56 -0500 Subject: [PATCH 52/58] Bump NightscoutRemoteCGM: 'Optional' placeholder for API Secret --- NightscoutRemoteCGM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index 0d65dade56..a63d924cb2 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit 0d65dade56e4bbebda2e78fff68678dd3ff6f540 +Subproject commit a63d924cb266045882723d8bbc2c16272e50672f From a641759c1083f6b70bad739d14a2ed651446c41c Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 17:33:22 -0500 Subject: [PATCH 53/58] Bump NightscoutRemoteCGM: log periodic glucose fetches --- NightscoutRemoteCGM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index a63d924cb2..c944eeef0a 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit a63d924cb266045882723d8bbc2c16272e50672f +Subproject commit c944eeef0acf9839433528db24975799301c5d13 From f839eaa83b0f7a3ff3e614d56827675ed481f4da Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 17:45:18 -0500 Subject: [PATCH 54/58] Bump NightscoutRemoteCGM: fix glucose filtering (sort before filterDateRange) --- NightscoutRemoteCGM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightscoutRemoteCGM b/NightscoutRemoteCGM index c944eeef0a..526328e2e8 160000 --- a/NightscoutRemoteCGM +++ b/NightscoutRemoteCGM @@ -1 +1 @@ -Subproject commit c944eeef0acf9839433528db24975799301c5d13 +Subproject commit 526328e2e8d4b7dfde0a46c0c545cf8164a26bda From 8cb791ea88a36c82e1ebce91894ca38650fb5358 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 17:58:29 -0500 Subject: [PATCH 55/58] Bump LibreTransmitter: sort glucose before filterDateRange --- LibreTransmitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibreTransmitter b/LibreTransmitter index 356824bfd8..afba845f7b 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit 356824bfd8482dbd54e12a8553c459a698d48ede +Subproject commit afba845f7b43441fb72b91e49ce93dac631b99af From 980bf9c043f8f3362ddd4e7d3b81c7f16795dbc4 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 19:50:41 -0500 Subject: [PATCH 56/58] Bump LoopAlgorithm pin to main HEAD (sorted assert merged) --- LoopKit | 2 +- LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LoopKit b/LoopKit index c17ef4b4ea..12a769a593 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit c17ef4b4ea5e3c8d8e57db4ffa310c06364d5cec +Subproject commit 12a769a593e3897945b3e73658434372dc2e892c diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index 11664817cf..c25bf0911c 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -97,7 +97,7 @@ "location" : "https://github.com/LoopKit/LoopAlgorithm", "state" : { "branch" : "main", - "revision" : "1ca85662b1f8799758988108f46dba5f34d4889b" + "revision" : "2f5c630084aa0d72b8d14999e1e0f7c836b0c341" } }, { From ed1bb5f8fb9182f32e25b2b686a2a4df7fe86ef0 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 20:51:57 -0500 Subject: [PATCH 57/58] Vendor LoopAlgorithm as a submodule instead of a remote package Add LoopAlgorithm as a git submodule (tracking LoopKit/LoopAlgorithm) and reference it as a local Swift package, removing the remote package pin from the workspace. LoopAlgorithm can now be edited in place like the other workspace components. Bump LoopKit pin for the local package reference. --- .gitmodules | 3 +++ LoopAlgorithm | 1 + LoopKit | 2 +- .../xcshareddata/swiftpm/Package.resolved | 11 +---------- 4 files changed, 6 insertions(+), 11 deletions(-) create mode 160000 LoopAlgorithm diff --git a/.gitmodules b/.gitmodules index 3637ab97e8..9c28df3111 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,3 +58,6 @@ [submodule "LibreTransmitter"] path = LibreTransmitter url = https://github.com/loopkitdev/LibreTransmitter.git +[submodule "LoopAlgorithm"] + path = LoopAlgorithm + url = https://github.com/LoopKit/LoopAlgorithm.git diff --git a/LoopAlgorithm b/LoopAlgorithm new file mode 160000 index 0000000000..2f5c630084 --- /dev/null +++ b/LoopAlgorithm @@ -0,0 +1 @@ +Subproject commit 2f5c630084aa0d72b8d14999e1e0f7c836b0c341 diff --git a/LoopKit b/LoopKit index 12a769a593..130c2b408c 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 12a769a593e3897945b3e73658434372dc2e892c +Subproject commit 130c2b408cea2057c201b76d195649f3500ec936 diff --git a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved index c25bf0911c..7f9748cfe8 100644 --- a/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LoopWorkspace.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9fa433ef5fce7eff885b44f4dea36e033d61f148853051ee0494bf4e79200676", + "originHash" : "022dd750637857e01bb9d5a686f612a656b7e41972a6c0b5144ddb9bf2d2303c", "pins" : [ { "identity" : "amplitude-ios", @@ -91,15 +91,6 @@ "version" : "2.0.0" } }, - { - "identity" : "loopalgorithm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/LoopKit/LoopAlgorithm", - "state" : { - "branch" : "main", - "revision" : "2f5c630084aa0d72b8d14999e1e0f7c836b0c341" - } - }, { "identity" : "mixpanel-swift", "kind" : "remoteSourceControl", From 339b18857a3095b9b194519d3cd5be6ddf5a87e6 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 24 May 2026 21:23:48 -0500 Subject: [PATCH 58/58] Declare LoopAlgorithm as a workspace-level local package Add LoopAlgorithm to the workspace so the LoopWorkspace owns and versions it (via the submodule gitlink); projects consume the product. Bump LoopKit pin. --- LoopKit | 2 +- LoopWorkspace.xcworkspace/contents.xcworkspacedata | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index 130c2b408c..bc0b6f6b11 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 130c2b408cea2057c201b76d195649f3500ec936 +Subproject commit bc0b6f6b11d3b4c6bed8e73d34493462f9d875bc diff --git a/LoopWorkspace.xcworkspace/contents.xcworkspacedata b/LoopWorkspace.xcworkspace/contents.xcworkspacedata index a870f8b7c9..30ab15315a 100644 --- a/LoopWorkspace.xcworkspace/contents.xcworkspacedata +++ b/LoopWorkspace.xcworkspace/contents.xcworkspacedata @@ -78,6 +78,9 @@ + +