From d03e977360e9794f7fb500c82e7db5b64298446f Mon Sep 17 00:00:00 2001 From: LoopKit Developer Date: Thu, 12 Mar 2026 15:50:39 -0500 Subject: [PATCH 01/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] 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/47] =?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/47] =?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/47] 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/47] 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/47] 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/47] 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" } }, {