Conversation
Pins LoopKit to 522cba34 on threecee/LoopKit watchos-support: - LoopKit-watchOS framework target now builds cleanly for watchOS with WATCHOS_DEPLOYMENT_TARGET=10.0 and all 182 LoopKit sources (was 139, missing 43 files including all LoopAlgorithm components). The CI workflow commit (371cbc2f on the local watchos-support branch) is not yet pushed to origin because the active gh token lacks 'workflow' scope. Once the scope is refreshed, the 1 additional commit should be pushed and this submodule pin advanced one more step.
G7SensorKit now builds for both iOS and watchOS via the LoopWorkspace scheme `G7SensorKit`: - iOS: xcodebuild build -workspace LoopWorkspace.xcworkspace \ -scheme G7SensorKit -destination "generic/platform=iOS" BUILD SUCCEEDED. - watchOS: xcodebuild build -workspace LoopWorkspace.xcworkspace \ -scheme G7SensorKit -destination "generic/platform=watchOS" BUILD SUCCEEDED. - iOS tests: 15 tests in 3 suites, 0 failures. Port content: - Removes unused `import LoopKitUI` from G7DeviceStatus.swift. - Adds a shared G7SensorKit.xcscheme. - Adds explicit LoopKit.framework link (needed once LoopKitUI is excluded on watchOS — LoopKit was only reaching G7SensorKit transitively through the iOS-only LoopKitUI link). - Extends the G7SensorKit target's SUPPORTED_PLATFORMS to include watchos/watchsimulator, sets WATCHOS_DEPLOYMENT_TARGET = 10.0, and widens TARGETED_DEVICE_FAMILY to 1,2,4. - Stamps LoopKitUI.framework link with `platformFilter = ios` so watchOS builds skip it. - Adds .github/workflows/build.yml mirroring OmniBLE's CI pattern (clone LoopWorkspace/personal, repoint submodule, drive xcodebuild through the workspace). Out of scope (stay iOS-only): G7SensorKitTests, G7SensorKitUI, G7SensorPlugin. No watchOS test bundle was created; see .b1-g7sensorkit-testcount.txt for the rationale.
Creates SmokeTests.swift with four functions that force symbol resolution of GlucoseTrend (LoopKit), LoopAlgorithmSettings (LoopAlgorithm via LoopKit), OmniBLEPumpManager (OmniBLE), and G7CGMManager (G7SensorKit). Updates ContentView.swift to display all four smoke-test results plus a green "All linked." confirmation. Fixes framework linking so LoopKit, OmniBLE, and G7SensorKit are linked into the Watch App target (not the iOS container), enabling the workspace to build them for watchOS when the Watch App is the destination. Build verified: generic/platform=watchOS BUILD SUCCEEDED. Simulator note: watchOS Simulator build fails because OmniBLE's xcodeproj EXCLUDED_SOURCE_FILE_NAMES uses sdk=watchos* (device-only) rather than sdk=watch* (device+simulator), so PumpManagerUI files that import LoopKitUI are not excluded when building for watchsimulator. This is an OmniBLE porting bug (Phase 3 scope), not a smoke-test issue. threecee-claude
… App target The Xcode template placed the Embed Frameworks phase in the container (watchapp2-container) target, but since the Watch App is marked WKWatchOnly=YES, the container is vestigial and nothing it embeds reaches the installed app. Move the phase (and its framework entries) to the Watch App target so LoopKit, OmniBLE, and G7SensorKit ship inside the .app bundle. Verified: simctl install + launch on Apple Watch Series 11 (46mm) watchOS 26.4 simulator shows 'All linked.' in green.
…o.plist, linked frameworks
… → cache + HealthKit
threecee-claude
Picks up: - OmniBLE (b4-start..5fb011a, 4 commits): payload validity 60s → 600s + doc comment (LoopKit#6); PhoneWatchSettingsSync v2 with automaticDosing fields + protocol version bump (LoopKit#3); shared HandoffState.currentOwner extension for policy-engine wiring (LoopKit#2). - Loop (b4-start..b5e94f7a, 4 commits): iOS LoopAppManager wires settingsSyncProvider closure (closes production gap discovered during planning) + watch WatchSettingsSnapshot reads dosing flags from sync (LoopKit#3); HandoffOrchestrator wires HandoffPolicyEngine markX hooks on both sides via Combine reachability subscription with 60s debounce (LoopKit#2); review-fix renames + safety comment. After this, the watch can self-drive when the phone reports automaticDosingEnabled, the auto-revert path is live (was dead code since B.3.a), and the cached-pod-state safety gate is active. BEHAVIOR CHANGE: auto-revert from watch back to phone WILL FIRE in .automatic and .manualWithAutoRevert modes when the phone reconnects after being out of range for >=60s. Worth deliberate hardware testing on the next install. Verified: - OmniBLETests targeted (3 suites): 26/26 PASS - LoopTests targeted (3 suites): 27/27 PASS - WatchApp ExtensionTests targeted (2 suites): 19/19 PASS - Release archive: ARCHIVE SUCCEEDED (iOS, generic destination) threecee-claude
Picks up: - Loop (b6-start..HEAD, 8 commits): WatchAlgorithmDriver overrides didRecommend with 5 gates (warmingUp, automaticDosingEnabled, isAutomaticDosingAllowed, pumpManager non-nil, deliveryIsUncertain) + suppression recording + dose dispatch (was structurally absent before — comment at line 277 said "Phase 6+ will refine"). Bootstrap wires OmniBLEOwnership.pumpManager through. Race ordering hardened in HandoffOrchestrator. Driver source extracted to new shared WatchAlgorithmKit framework (multi-platform iOS+watchOS) so it can be exercised by OmniBLETests integration tests. Defensive schedule-copy in driver init catches silent-failure mode where algorithm reads schedules from doseStore but production never populated them. ClockKit gate fixed. - OmniBLE (b6-start..HEAD, 3 commits): new WatchAlgorithmEndToEndTests integration suite (4 tests, ~5 min total) exercising the full closed-loop chain against the emulated pod via PodSimulatorTestCase infrastructure — proves the watch can actually close the loop end-to-end with real BLE encryption + real pod state machine + real insulin accounting. Includes happy-path test (pod insulinDelivered increases or temp basal installs after algorithm iteration) plus 3 gate-suppression sub-tests verifying each gate prevents the dose through full production plumbing. After this, the audit gap from B.4 is closed at the integration level: the watch can actually enact doses, the gates work as designed both at unit and integration levels, the schedule-copy silent-failure mode is prevented, and the integration test proves the full chain works under realistic conditions. BEHAVIOR: After Build N+1 ships, the watch can self-drive the pod when handoff state is .watchDriver, settings sync has populated automaticDosingEnabled+true, and warmup window has cleared. Worth deliberate hardware testing on the next install (the integration test passes against the emulated pod, not yet against real hardware). Verified: - OmniBLETests targeted (4 suites): 30/30 incl. WatchAlgorithmEndToEndTests 4/4 - LoopTests targeted (3 suites): 27/27 incl. HandoffOrchestratorTests 11/11 - WatchApp ExtensionTests targeted (3 suites): 26/26 incl. WatchAlgorithmDriverTests 7/7 - Release archive: ARCHIVE SUCCEEDED (iOS, generic destination) threecee-claude
Picks up: - OmniBLE (b5-start..HEAD, 4 commits): commandsAllowed gate flag on OmniBLEOwnership + callback-based gate in OmniBLEPumpManager enactBolus/enactTempBasal (#1); claimedOwner field on PhoneWatchHeartbeat + PhoneWatchProtocol.currentVersion 2 -> 3 (LoopKit#5); HandoffStatePersistence + state machine restore-on-init (LoopKit#4); command-gate integration sub-test in WatchAlgorithmEndToEndTests. - Loop (b5-start..HEAD, 4 commits): orchestrator effect cases wire ownership.commandsAllowed flips on both iOS + watch (#1); both PhoneWatchSessionCoordinators populate claimedOwner in heartbeats + watch demotes itself silently on split-brain detection (LoopKit#5); watch pump manager construction reads from WatchSettingsCache instead of .watchSideDefault (LoopKit#7); WatchDoseRecoveryStore tripwire in WatchAlgorithmKit + ExtensionDelegate launch-time stale check; test pollution fix on iOS + watch HandoffOrchestratorTests + SettingsSyncEmissionTests + protocol version assertion bump. After this, the audit gaps from the B.4 critical review are closed: - Real protection during handoff transitions (no mid-handoff double- command races) - Split-brain detection with phone-wins arbitration - Crash recovery via persistence (HandoffState survives app restart) + dose-in-flight tripwire on watch - Watch pump manager uses real settings, not hardcoded defaults BEHAVIOR: The new gate and split-brain demotion will fire when expected (forced handoffs, transient WCSession glitches). Worth observing on hardware install of next build. Verified: - OmniBLETests targeted: all suites green incl. WatchAlgorithmEndToEndTests 5/5 - LoopTests targeted: all green (HandoffOrchestratorTests 13/13, HandoffPolicyEngineTests 11/11, SettingsSyncEmissionTests 5/5) - WatchApp ExtensionTests targeted: all green (33/33 across 4 suites) - Release archive: ARCHIVE SUCCEEDED (iOS, generic destination) threecee-claude
- WatchSettingsCache.resetForTesting() wrapped in #if DEBUG - Em-dash -> semicolon in watch split-brain log message - B.5.1 LoopKit#2 audit confirmed synced settings reach watch algorithm - Two larger B.5.1 items (timeZone field, gate widening) deferred to B.5.2 threecee-claude
Picks up: - OmniBLE (b5.2-start..HEAD, 2 commits): commandsAllowedCheck gate widened to runTemporaryBasalProgram, cancelBolus, suspendDelivery (#5b); PhoneWatchSettingsSync.timeZone: String? field + protocol bump 3 -> 4 (LoopKit#3). - Loop (b5.2-start..HEAD, 1 commit): HandoffBlockedError + StatusTableViewController handoff-blocked rendering for cancelBolus (#5b); HandoffOrchestrator + LoopAppManager populate timeZone (LoopKit#3) + observe NSSystemTimeZoneDidChangeNotification (#3b); WatchAlgorithmDriver resolves scheduleZone from sync.timeZone and threads to all 4 dailyItems schedule constructors (LoopKit#3). After this: - The 3 newly-gated pump methods short-circuit during the sub-second handoff window. cancelBolus's iOS UI renders "Handoff in progress, please try again in a moment" via HandoffBlockedError; suspendDelivery is OmniBLE-PumpManagerUI-only (out of Loop UI scope). - Watch interprets all 4 dailyItems schedules in the phone's zone; phone-side TZ changes propagate to the watch within one WCSession round-trip via the new NSSystemTimeZoneDidChangeNotification observer. Verified: - OmniBLETests targeted: all suites green (104 tests; WatchAlgorithmEndToEndTests 5/5 after retry to clear cold-start noPodsFound flake) - LoopTests targeted: 31/31 green - WatchApp ExtensionTests targeted: 36/36 green - Release archive: ARCHIVE SUCCEEDED (iOS, generic destination) threecee-claude
- Test for malformed sync.timeZone identifier fallback path - Docstring fix on notifySettingsChanged() reflecting actual callers threecee-claude
Picks up: - Loop (b7-start..HEAD, 3 commits): LoopStateView inner-circle driver indicator (iOS), StatusTableViewController subscription wiring via LoopCompletionHUDView forwarding properties, watch HUD driverDot overlay (both ActionHUDController + ChartHUDController scenes) + HUDInterfaceController subscription with willActivate/didDeactivate lifecycle. After this: - iPhone main HUD shows a small white dot inside the loop ring when the phone is driving the pod. Dot disappears when the watch takes over. - Watch HUD shows the same dot when the watch is driving. Dot pulses (~0.8s alpha cycle) on either side during the sub-second handoff transition. - No effect on closed-loop semantics or pod control — purely visual. - OmniBLE submodule untouched (still at b5.2-complete 1135438). Verified: - LoopTests targeted: 27/27 (LoopStateViewTests 7/7, HandoffOrchestratorTests 13/13, SettingsSyncEmissionTests 7/7) — all green - WatchApp ExtensionTests targeted: 27/27 (HandoffOrchestratorTests 15/15, WatchAlgorithmDriverTests 12/12) — all green - Release archive: ARCHIVE SUCCEEDED (iOS, generic destination) threecee-claude
OmniBLE: PumpStatusSnapshot + AlgorithmStateSnapshot payload types, PhoneWatchMessage.algorithmStateSnapshot enum case. Loop: AlgorithmStateSnapshotEmitter + LoopDataManager hook + LoopAppManager wiring + no-op switch arms in all four PhoneWatchMessage consumers + B.7 visual polish (driver-indicator dot size + tint). threecee-claude
…ing) Loop changes since Phase 1: - WatchAlgorithmSnapshotCache (T8) + safety+test cleanup - Watch-side coordinator dispatch into snapshot cache (T9) + tautology cleanup - WarmUpDecider 3-gate decision (T10) + boundary-test symmetry - WatchAlgorithmDriver init derives isWarmingUp from WarmUpDecision (T11) + dual-input precedence doc + DEBUG assertion - WatchAlgorithmBootstrap invokes WarmUpDecider with real freshness inputs (T12) threecee-claude
threecee-claude
threecee-claude
OmniBLE: resumeDelivery gated on commandsAllowedCheck (Codex adversarial review #1 / B.5.2 deferred LoopKit#5). manualRecoveryDismiss emits .resumeIssuingPodCommands for surviving owner (LoopKit#2). Init checks .handoffPending deadline against persistence and converts expired pending to .recovering(.restoredExpiredPending,...) (LoopKit#3). Mechanical /simplify bucket: HandoffSettings.appGroupDefaults + UserDefaults+Codable extension applied across both submodules. Tagged b8.1-complete on all three repos. Build 873 prep. threecee-claude
OmniBLE: HandoffStateMachine.init always recovers any restored .handoffPending (M3 — drops deadline check; vacuously closes M2 + M1 dir.origin cleanup). PhoneWatchProtocol.currentVersion bumped 4 → 5. Loop: live LoopSettings + dosingEnabled observer wires HandoffOrchestrator into LoopDataManager.loopAlgorithmRunner(_:settingsDidChange:) via the WatchHandoffNotifying protocol. Phone-side and watch-side settings-sync dedup. Snapshot delivery via updateApplicationContext (latest-only, intentionally overwrites) instead of transferUserInfo. 8KB warning log so B.8.4 inherits size-budget telemetry. Heartbeat encoder secondsSince1970. WatchSettingsCache.resetForTesting() DEBUG-guard regression test. Tagged b8.2-complete on all three repos. Build 874 prep — push gated on Carl's confirmation that Build 873 (B.8.1) HV-1 passed. threecee-claude
Loop: WatchSettingsCache adds CurrentValueSubject publisher + persists last-good to App Group UserDefaults (B.8.1 UserDefaults+Codable extension). WatchAlgorithmBootstrap and WatchRemoteCommandBootstrap subscribe; retryIfNeeded() gates on lastHandoffState == .watchDriver. B.8.2 #if DEBUG writeCount instrumentation removed in favor of sink-based testing. 5x NSLog -> OSLog (4 in PhoneWatchSessionCoordinator, 1 in WatchAlgorithmBootstrap). OmniBLE submodule UNTOUCHED -- pointer stays at b8.2-complete. Tagged b8.3-complete on all three repos. Build 875 prep. threecee-claude
…nciliation Loop: AlgorithmStateSnapshotEmitter populates rolling buffers from runner inputs. WatchAlgorithmDriver.applyAlgorithmStateSnapshot writes buffers to watch stores via upstream LoopKit batch-insert APIs (with NewGlucoseSample/NewCarbEntry projections; doses pass through). WatchAlgorithmDriver's .skipWarmup case hydrates inside the switch (snapshot is in scope via WarmUpDecision's associated value). Phase 6 caught + fixed a production bug: DoseStore.addDoses calls its completion handler twice; Phase 3's continuation now NSLock-guarded. Wire format: file-pointer fallback for oversized snapshots (Phase 1 measured 21.25KB; >8KB threshold). Phone writes to AppGroup/snapshot.json, sends pointer message via applicationContext. Watch reads file on pointer receipt, validates monotonic sequence. LoopAlgorithmReconciliationTests harness ships with placeholder fixtures + stub runForReconciliation body. Tests XCTSkip cleanly. Real fixtures captured manually via Phase 5a's #if DEBUG instrumentation in follow-up. OmniBLE: AlgorithmStateSnapshot.isFreshEnoughForSkipWarmup helper. PhoneWatchMessage.algorithmStateSnapshotPointer(sequence:) wire-format case + PhoneWatchProtocol.currentVersion bumped 5 to 6. Closes Codex adversarial review Loop LoopKit#3 high. Closes the umbrella B.8.x adversarial-review-hardening series. algorithm-fidelity-audit PASS verified at /tmp/algorithm-fidelity-audit-2026-05-03.md. Tagged b8.4-complete on all three repos. Build 876 prep. threecee-claude
Activates the previously-stubbed reconciliation test infrastructure:
WatchAlgorithmDriver.runForReconciliation now delegates to
LoopAlgorithm.generatePrediction (Path α — wrapper-only fixture, no
extension needed since LoopPredictionInput is self-contained); 3
real fixtures (steady-state, post-meal-carbs, predicted-hypo)
replace the placeholder {} JSON files via a new programmatic
ReconciliationFixtureGenerator (gated on
GENERATE_RECONCILIATION_FIXTURES=1).
Tests: 3/3 PASSING (no XCTSkip):
-[LoopTests.ReconciliationTests test_postMealCarbs_...] passed
-[LoopTests.ReconciliationTests test_predictedHypo_...] passed
-[LoopTests.ReconciliationTests test_steadyState_...] passed
threecee-claudeLands Stream B's commit 2b9dbbcf (rebased from 608568f7) on top of Stream A's reconciliation activation. Closes the pre-existing ExtensionDelegateApplicationContextTests.testDidReceiveApplicationContextDispatchesToHandler failure that was carrying forward from B.8.2 → B.8.3 → B.8.4. Root cause: test fixture used JSONEncoder .iso8601 dateEncodingStrategy while transport adopted .secondsSince1970 in B.8.2 (mechanical wire- format migration). Decoder threw on ISO-8601 strings; handleIncomingMessageData swallowed the error; onIncomingMessage never fired. Fix: align fixture encoder to .secondsSince1970. One-line behavioral change + 4-line wire-format-contract comment. WatchApp ExtensionTests target now fully green. threecee-claude
…cts cleanup Closes the two B.7 follow-ups deferred from the 2026-05-02 algorithm- fidelity audit. Loop submodule consolidates PredictionInputEffect to a single canonical source in LoopAlgorithmCore (deleting the on-disk orphan at Loop/Models/PredictionInputEffect.swift) and consolidates both `enabledEffects` extensions to delegate via a new canonical `PredictionInputEffect.allEnabled` static-let. Phase 1 discovery found the dual-target pbxproj wiring confusion described in the 2026-05-03 known-deviations note had already been silently resolved — both PBXBuildFile entries already pointed at LoopAlgorithmCore/PredictionInputEffect.swift, and the iOS Loop app target had no Sources entry for the type at all (iOS callers use LoopAlgorithmCore.PredictionInputEffect via existing `import LoopAlgorithmCore`). So B.8.5's only filesystem cleanup was deleting the orphan file; no production pbxproj surgery was needed. Pure structural cleanup. Algorithm-fidelity-audit PASS verified (/tmp/algorithm-fidelity-audit-2026-05-04.md). Test results: LoopTests 303/303 (1 skipped), WatchApp ExtensionTests 121/121. 3 new invariant tests in PredictionInputEffectTests assert post-cleanup invariant: `allEnabled == .all`, iOS `enabledEffects == .allEnabled`, LoopAlgorithmCore `loopAlgorithmCore_enabledEffects == .allEnabled`. Tagged b8.5-start + b8.5-complete on Loop + OmniBLE (untouched, both tags at same SHA 07d5cc7) + LoopWorkspace. Build 877 prep. threecee-claude
Loop: slice-tag comment prefix strip across WatchHandoff stack, WatchApp Extension, and WatchAlgorithmKit (~57 line-comment prefixes; doc comments preserved). HandoffPolicyEngine tick 1Hz to 5s (efficiency #1, lockstep across iOS+watch pair). ExtendedRuntimeCoordinator gated on HandoffOrchestrator.handoffState == .watchDriver via Combine subscription (efficiency LoopKit#6); 3 new gating tests + 3 existing tests updated. OmniBLE: slice-tag comment prefix strip in Common/ (state machine, state persistence, handoff payload, ownership coordinator). No HandoffPolicyEngine copy on OmniBLE side. algorithm-fidelity-audit NOT required (no algorithm-adjacent files touched). LoopTests: 303 pass, 1 skipped. WatchApp ExtensionTests: 124 pass. Tagged b9-complete on all three repos. Build 878 prep. threecee-claude
Mechanical strip of B.x.y / Phase n / Tn slice-tag prefixes from /// doc-comments. ~50-70 lines across both submodules. No behavioral change. Tagged b10.1-complete on all three repos. Build 879 prep. threecee-claude
Lifts 5 iOS<->watch duplicated handoff-stack files into OmniBLE/Common parameterized by HandoffRole: ShadowStateScheduler, PhoneWatchTransport, PhoneWatchSessionCoordinator + HeartbeatScheduler, HandoffPolicyEngine, HandoffOrchestrator. Adds HandoffStack.assemble(role:) factory that deduplicates LoopAppManager + ExtensionDelegate bootstrap chains. ~2,675 LOC reduction in Loop submodule (dedup); +1,500 LOC in OmniBLE/Common (single source of truth, with role-conditional behavior folded into the lifted types). Tests deduplicated: 76 -> 51 paired methods, all green. Phase 8 adds 3 HandoffStack factory tests (phone/watch/reentrancy). Drive-by re-add of WatchApp ExtensionTests/PhoneWatchTransportMock.swift (deleted in Phase 6 but still referenced by watch-only WatchPumpManagerSettingsTests - pre-existing watch-test-target build regression that B.10 closes). algorithm-fidelity-audit PASS (Class A pure structural). Closes /simplify quality #1 + LoopKit#5. Report: /tmp/algorithm-fidelity-audit-2026-05-04-b10.md. Tagged b10-complete on Loop, OmniBLE, and LoopWorkspace. Build 880 prep. Recommend HV-1 within 2 weeks of ship to verify no subtle handoff regressions in real-hardware paths. threecee-claude
Untracked .claude/ dir was causing release.sh to abort on the working-tree-clean precondition. Mirrors the loop-and-learn repo's gitignore narrowing pattern. threecee-claude
Advances Loop and OmniBLE submodules to b11.0-complete. B.11.0 adds: - aps-environment entitlement to WatchApp Extension target - WKExtension.shared().registerForRemoteNotifications() on launch - new PhoneWatchMessage.apnsTokenPublish case (proto v6 -> v7) - APNsTokenStore App-Group persistence + symmetric exchange - PhoneWatchTransportQueueing protocol seam NO Nightscout integration (B.11.1/B.11.2). Class A additive transport. algorithm-fidelity-audit PASS (zero scope files modified). Phase 0 (Apple Developer portal APNs enablement + provisioning regen) remains a Carl-action gate; required before next TestFlight upload registers for live APNs. Build target: 884. threecee-claude
B.11.1 RemoteCareUploader role parameterization. New OmniBLE-side protocol abstracts the Nightscout upload trigger surface; iOS uses LoopRemoteCareUploader wrapping RDSM; watchOS reuses the same wrapper (Branch A confirmed in Phase 1 — NightscoutServiceKit links into watchOS via the static-link path). HandoffOrchestrator owns the driver-only-writes invariant and quiesce-on-handoff semantics. 13 iOS callsites + 1 watchOS bootstrap migrated to proxyUpload(for:). Per-iteration dedup verified by RemoteCareDedupE2ETests across a simulated phone->watch handoff window. algorithm-fidelity-audit verdict: PASS / Class B (additive plumbing). Special-check 5.5 (LoopDataManager refactor) PASS — proxy is a pre-RDSM filter, not an interceptor; dosing decision payload bit- identical when device is current driver. NightscoutService not touched (Branch A). Tagged b11.1-complete on all three submodules + LoopWorkspace. Build 885 prep. threecee-claude
DriverTokenRendezvous Codable type with HMAC-SHA256 signing (CryptoKit). HandoffOrchestrator gains buildSignedRendezvous() method that constructs + signs the rendezvous from the App Group APNs token store (B.11.0), guarded by the existing isCurrentDriver gate (B.11.1 driver-only-writes invariant). Empty Nightscout API secret degrades to empty signature with debug-log warning (spec Risks LoopKit#4). End-to-end token-rotation propagation tests in Loop exercise the B.11.0 -> B.11.2 producer chain. Production splice into Nightscout devicestatus (loop.testingDetails.driverToken — riding LoopStatus.testingDetails since LoopStatus is in external NightscoutKit and adding a sibling key would require forking it) is deferred to B.11.2.1 since the slice scope per Carl's instruction was Loop + OmniBLE submodules only. Producer side (orchestrator-side build + sign) fully implemented and tested. Algorithm-fidelity-audit: PASS (Class A/B — additive metadata). No algorithm-output path touched. Tagged b11.2-complete on all three repos. Build 886 prep. threecee-claude
…splice NightscoutService: first time touched in B.11. Adds the consumer-side splice for B.11.2's DriverTokenRendezvous producer — caretaker apps now find the signed rendezvous at JSON path `loop.testingDetails.driverToken` in devicestatus uploads. NightscoutService b11.2.1-complete: b39af55. Loop: wires `driverTokenProvider` on active NightscoutService instances at boot, plus passes `nightscoutAPISecretProvider` into the handoff stack so the orchestrator can sign rendezvous payloads with the active Nightscout API secret. Loop b11.2.1-complete: 7d7fe81. OmniBLE: UNTOUCHED in this slice. Tag-only no-op (`b11.2.1-start` and `b11.2.1-complete` both at `b11.2-complete`/1e1b677). Algorithm-fidelity audit: PASS Class A/B (additive metadata only). Report: /tmp/algorithm-fidelity-audit-2026-05-04-b11.2.1.md known-deviations.md update: /tmp/known-deviations-b11.2.1-entry.md (permission on the source of truth was denied during the slice; manual merge required). Tests: - NightscoutServiceKitTests (Shared scheme, iOS sim): 16/16 pass (4 new + 12 existing). - LoopTests (iPhone 17 sim): 379 pass, 1 skip, 6 pre-existing locale flakes (BolusEntryViewModelTests / SimpleBolusViewModelTests decimal-comma — unrelated to slice). All 3 B.11.2.1 splice tests pass; all 3 B.11.2 propagation tests still pass. - WatchApp ExtensionTests build: TEST BUILD SUCCEEDED. Carl-action gated: release.sh Build 887. threecee-claude
B.11.3 ties B.11.0 (APNs entitlements + token publication) + B.11.1 (RemoteCareUploader role-gating) + B.11.2 (DriverTokenRendezvous + currentDriver field) into a coherent pending-driver handshake. Sequence: 1. Outgoing driver writes a devicestatus with currentDriver: incoming (the rendezvous) BEFORE the BLE role flips. Pre-flip publish is fire-and-forget — Option D (Carl-decided) — because B.11.1 landed RemoteCareUploader as fire-and-forget without a Result-returning surface. 2. Caretaker apps polling Nightscout pick up the new currentDriver within their poll window (LoopCaregiver: 1-5 min, LoopFollow: configurable). 3. BLE bond moves; HandoffStateMachine flips. 4. New driver's first iteration uploads devicestatus(currentDriver: self), confirming the rendezvous and re-stamping currentDriver idempotently. THIS IS THE LOAD-BEARING SAFETY PROPERTY: a transient pre-flip upload failure is recovered automatically within ~5 min. If outgoing driver crashes between steps 1 and 4, the incoming driver's first iteration after BLE detection corrects state idempotently. The plan's test_role_flip_aborted_on_step1_upload_failure is INTENTIONALLY DROPPED under Option D — aborting role flip on a transient network failure would block legitimate handoff when the user walks out of BLE range, worse than the brief caretaker-visibility gap. Idempotency on first-iteration is the safety net. commandsAllowedCheck (B.8.x) continues to short-circuit pump commands during the rendezvous window. B.11.3 makes no algorithm-scope changes — algorithm-fidelity-audit verdict: PASS / Class B (additive structural). threecee-claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Sub-project B.1 of a longer program to build a standalone watchOS Loop variant (watch talks BLE directly to DASH + G7 with the phone absent). This PR is a fork-internal review view on
threecee/LoopWorkspacefrompersonal→main. It is not intended for upstream submission yet — upstream PRs on each of the ported packages are scheduled to publish only after sub-project B.2 demonstrates real BLE on watchOS.Full spec:
/Users/carl/Projects/loop-and-learn/docs/superpowers/specs/2026-04-20-b1-watchos-port-design.mdFull plan:
/Users/carl/Projects/loop-and-learn/docs/superpowers/plans/2026-04-20-b1-watchos-port.mdScope — what B.1 does
Platform-compile work only. No new behaviour, no new algorithm, no BLE changes. Each ported Swift package builds cleanly for watchOS 10+ with iOS behaviour unchanged.
Three packages ported (the original spec said four;
LoopAlgorithmturned out to live insideLoopKitatLoopKit/LoopKit/LoopAlgorithm/as part of the LoopKit target — see§10 Amendmentin the spec):LoopKit(+ embedded LoopAlgorithm)threecee/LoopKit@watchos-supportOmniBLEthreecee/OmniBLE@watchos-supportG7SensorKitthreecee/G7SensorKit@watchos-supportCI failures on the first two runs were debugged to configuration issues (hardcoded Xcode version, transient submodule pin) rather than port defects; re-runs are in flight at PR-creation time. All three packages build locally for both iOS and watchOS via
LoopWorkspace.xcworkspace.What's in this PR
11 commits on
personal, all submodule-pointer changes inLoopWorkspace:.gitmodulesre-pointsLoopKit,OmniBLE,G7SensorKitat thethreeceeforks.watchos-supporttip of each fork..gitignoreexcludes scratch audit artefacts (.b1-*.txt).Real code changes live in each package fork's
watchos-supportbranch — not in this LoopWorkspace repo.Methodology (same per package)
LoopKit/<Package>→threecee/<Package>.watchos-supportfrom canonical default (devfor LoopKit/OmniBLE,mainfor G7SensorKit).UIKit,BackgroundTasks,UserNotifications-iOS-specifics,LocalAuthentication), API call sites (BGTaskScheduler,UIApplication, view controllers, biometrics, keep-alive audio).SUPPORTED_PLATFORMS += "watchos watchsimulator",TARGETED_DEVICE_FAMILY = "1,2,4",WATCHOS_DEPLOYMENT_TARGET = 10.0,SDKROOT = auto. Where UI sources must be excluded on watchOS, useEXCLUDED_SOURCE_FILE_NAMES[sdk=watchos*]. Where iOS-only frameworks are linked, useplatformFilter = ios.#if os(iOS)(matching upstream's existing gate style). Every gated call site that must exist on both platforms gets aTODO(B.3):stub pointing at the watchOS-equivalent API for B.3 to implement.Findings worth a reviewer's eye
LoopKit-watchOStarget from 2019 withWATCHOS_DEPLOYMENT_TARGET = 6.1and 43 missing sources. Extending the existing target (rather than creating a new one) preserves maintainer-established structure. No iOS-only code in LoopKit's core module needed gating — surprisingly clean.AVAudioSession),UIApplicationlifecycle observers, background-task scheduling, Face ID. Handled with a combination ofEXCLUDED_SOURCE_FILE_NAMES(for whole UI subtree) and per-call-site#if os(iOS)gates withTODO(B.3):stubs. One type (ReservoirLevelHighlightState) lives insidePumpManagerUI— gated out of watchOS; upstream may prefer promotion into the core framework at PR-time.import LoopKitUIstripped, no gates needed.Deferred to B.3 (follow-up sub-project)
Every gated-out iOS path carries a
TODO(B.3):marker:OmniBLEPumpManager.swift— lifecycle observers, pod keep-alive, background tasks,ReservoirLevelHighlightStatepromotion.How to review
If you want to see the actual Swift changes, review each fork's
watchos-supportbranch directly. The commits here are purely submodule-pointer advances.Status
threecee-claude