Repository navigation
Fix review findings in the Grove FHIR migration - #220
Open
lukaskollmer wants to merge 6 commits into
Open
lukaskollmer wants to merge 6 commits into
lukaskollmer wants to merge 6 commits into
Conversation
#216 temporarily defaulted `--studyBundle` to the bundle shipped with the app. That default is not DEBUG-gated, so every TestFlight and App Store launch exported the compiled-in study definition and never fetched the hosted one: new study revisions, consent versions and sample-type lists could not reach installed apps. The default is `.firebase(.stanford)` again. The variant is a placeholder that StudyBundleLoader replaces with the active study variant, and the Firebase path still falls back to the bundled definition when the hosted bundle is missing or can't be decoded, so the Imperial variant keeps working until its bundle is published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every HealthKit sample and retracted deletion reserved its event with its own LocalStorage.modify of the whole encrypted ledger, plus another full load for its identifier and two keychain reads, while the batch's earlier reservations stayed in the ledger. A delivery or historical-export batch therefore cost O(n^2) ECIES work and could not finish in the background. FHIRExchangeStateStore.events(forKeys:) now reserves a whole batch in one transaction: existing keys come back unchanged, new keys get consecutive sequences in input order, and a batch whose keys all exist only reads the ledger. HealthKitConversionBatch reserves live-delivery, direct-upload and historical-export batches up front and builds each sample's context from the returned producer instance and a once-read event scope; retraction drain chunks do the same via healthKitRetractions(of:). Reservations are still durable before any upload uses their identity, and stale account generations are still rejected on both the read and write paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A record whose Grove preparation or conversion throws is now logged (without content), its exchange reservation is released, and the batch is still acknowledged. Account-fence, retry-ledger, staging and Firestore errors still abort the batch unacknowledged. This applies to all five upload strategies, including the single-record CSV and PPG batches. When a fetch fails with pendingBatchMismatch or retryContentChanged, the sensor's pending batches are discarded via Grove's discardPendingBatches(for:) and their retry-only state is dropped, so the next run refetches the range under fresh coordinates. lastSensorKitFetch is only bumped once every sensor succeeded. resetAllQueryAnchors no longer swallows errors: failures of authorized sensors make clearPendingAccountData keep pendingAccountDataCleanupRequired set. Requires the Grove revision that adds SensorKit.discardPendingBatches(for:). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
event(key:) went through the batch API, whose read-only probe adds a full decrypt of the ledger before the write transaction whenever a key is new. SensorKit reserves records one at a time and most of them are new, so that made its per-record path slower. Single-key reservations now run one transaction again; batches keep the probe, which spares an exact retry any rewrite. Both share one reservation helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Grove moves to 611d0cd8 (SchmiedmayerLab/Grove#117), which adds SensorKit.discardPendingBatches(for:) used by the SensorKit recovery above and fixes the State of Mind, partial-extraction, breathing-disturbance, ECG timing and DST issues. StudyDefinitions moves to d1cbe285 (SchmiedmayerLab/MyHeartCounts-StudyDefinitions#52), which only re-pins Grove, because SwiftPM rejects two different revision requirements for Grove. Pinned in the project, MyHeartCountsShared, Package.resolved and the submodule. A half-answered blood-pressure panel still refuses, but Grove now reports it as componentIncomplete instead of answerMissing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 task done
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PaulGoldschmidt
approved these changes
Sep 28, 2026
lukaskollmer
added this pull request to stack #217
September 28, 2026 15:59
Xcode 26.6, which CI uses, could not type-check the `Date + Double * 60 + 30` arithmetic inside the untyped `map` closure in reasonable time and failed the test build. Explicit closure types and addingTimeInterval keep the same samples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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 free
to 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.
♻️ Current situation & Problem
A review of the Grove FHIR migration stack (#202 → ...) found some problems, which this PR fixes on the app side.
for … tryloop, so a single record that Grove refuses aborted the batch. The batch was then never acknowledged and was delivered again on every run. Separately, when SensorKit did not replay a pending batch exactly (pendingBatchMismatch), neither the fetcher nor logout cleanup could recover, becauseresetAllQueryAnchorsswallowed the error withtry?.⚙️ Release Notes
--studyBundledefaults to.firebase(.stanford)again, instead of.bundledWithApp(.stanford). The loader still substitutes the active study variant and still falls back to the bundled definition when the hosted one fails to load.for ... tryloop, which caused the batch to never get acknowledged/finalized, causing it to get re-run on every launch)pendingBatchMismatchorretryContentChanged, the fetcher callsSensorKit.discardPendingBatches(for:)and drops that sensor's retry bookkeeping, and the next run fetches the range again under fresh identities.lastSensorKitFetchis only updated when the fetch succeeded.resetAllQueryAnchorsnow propagates failures, andclearPendingAccountDatacounts the anchor reset toward success.611d0cd8and StudyDefinitions tod1cbe285, in the project, MyHeartCountsShared,Package.resolvedand the submodule.Behavior changes to be aware of:
lastSensorKitFetchstale, so foreground launches retry it.Out of scope, per the review triage:
loinc.orgas the quantity system;healthDeletions/;statuspatch has no effect on Bundle documents.📚 Documentation
Doc comments cover the study-bundle default, the refusal rule, and the batch reservation's durability and retry guarantees.
✅ Testing
MyHeartCounts Unit Testsplan (build-for-testing, generic iOS Simulator, Xcode 27), with packages resolved against the new pins.New tests:
StudyBundleLaunchOptionsTests.defaultsToFirebaseDeviceUsageFHIRExchangeTests.refusedRecordsDoNotFailTheirBatchandaccountFenceAbortsBatchInsteadOfRefusingRecordFHIRExchangeStateTests: three batch-reservation tests (input order, existing keys consume no sequence, stale account generation),healthKitBatchReservesEverySampleUpFront, andabandoningSensorBatchesDropsOnlyThatSourcesRetryStateQuestionnaireGroveProjectionTests.bloodPressureRefusesAnIncompleteComponentSetnow expects Grove'scomponentIncomplete.Not covered by tests:
Code of Conduct & Contributing Guidelines
By creating and submitting this pull request, you agree to follow our Code of Conduct and Contributing Guidelines:
🤖 Generated with Claude Code