Skip to content

Convert Health Data through Grove Instead of the App's FHIR Layer - #204

Open
PSchmiedmayer wants to merge 33 commits into
lukas/uk-study-variant-fixesfrom
lukas/grove-fhir-migration
Open

PSchmiedmayer wants to merge 33 commits into
lukas/uk-study-variant-fixesfrom
lukas/grove-fhir-migration

Conversation

@PSchmiedmayer

@PSchmiedmayer PSchmiedmayer commented Aug 26, 2026 •

Copy link
Copy Markdown
Collaborator

♻️ Current situation & Problem

My Heart Counts previously owned parts of the FHIR exchange contract itself: it converted HealthKit and SensorKit records, assembled upload payloads, projected questionnaire answers into HealthKit, and maintained legacy questionnaire rendering alongside Grove's native implementation.

That duplicated the Grove FHIR Implementation Guides and allowed the app, Grove, and the published contract to evolve independently. It also left successful uploads and source-query acknowledgements insufficiently coupled: an interrupted upload or account transition could make it difficult to prove which source records had been published and which anchor could safely advance.

This migration makes Grove the FHIR implementation boundary while keeping the app responsible for collection, durable staging, account ownership, and transport. Clinical records remain lossless for both DSTU2 and R4.

It follows #202, #213, and #216: #213 carries the Grove 0.3.0-beta.8 UI adoption and #216 adds the UK consent-selection fixes. This PR carries the FHIR port. It adopts Grove SchmiedmayerLab/Grove#67, aligned with the producer contract in SchmiedmayerLab/grove-fhir#42.

⚙️ Release Notes

  • Convert HealthKit and SensorKit inputs through Grove's typed FHIR exchange APIs and validate the resulting exchange graph before upload.
  • Preserve exact source identity and native metadata in the governed Grove fields, including full SensorKit Device Usage payloads rather than an app-defined summary.
  • Stage SensorKit source evidence, the FHIR Bundle, and the acknowledgement state as one durable publication batch; advance the source anchor only after the matching upload succeeds.
  • Fence staged exchanges to the signed-in account generation and captured Firestore destination so sign-out or account replacement cannot retarget an in-flight upload.
  • Use deterministic identifiers and retry digests so retries reproduce the same exchange instead of creating duplicate clinical resources.
  • Project questionnaire quantities and blood-pressure groups through the native Grove Questionnaire model, including UCUM validation and one HealthKit correlation for the two blood-pressure components.
  • Replace the legacy Grove Questionnaire and ResearchKit-on-FHIR rendering path with native Grove Questionnaire views and composition.
  • Keep DSTU2 and R4 clinical-record bytes and their declared FHIR release intact through the Grove transport document.
  • Remove app-local FHIR extensions and conversion code that are now authoritative in Grove.
  • Derive every identifier system from https://myheartcounts.stanford.edu/fhir, and bundle a known enrollment as ResearchStudy, PlanDefinition (…/PlanDefinition/<study uuid>|<study revision>) and ResearchSubject, with the study UUID in lower case.
  • Store every HealthKit graph and retraction under its sample's UUID, so Firebase ingestion keys them as before.
  • Retract deleted samples through Grove's HealthKitConverter.retraction(for:context:retractedAt:), and keep each deletion's lower bound (deletedAfter, the previous query's start) for Grove Proof-of-concept: on-device LLM generated nudges #94's retraction period.
  • Log refusals with their registry code, and conversion warnings per exchange graph.
  • Render questionnaires from the merged multilingual resource in the participant's language, and export responses with language through renderedIn:; questionnaire time functions run on an explicit clock.

📚 Documentation

The implementation follows the Grove FHIR Implementation Guides and keeps contract reasoning next to the conversion and staging boundaries. The branch pins the exact development revisions used for verification: Grove 66f0a88e, Study Definitions 490ffab0 (SchmiedmayerLab/MyHeartCounts-StudyDefinitions#47) and Firebase 12f2992c (SchmiedmayerLab/MyHeartCounts-Firebase#217).

✅ Testing

  • At e151f6ea: SwiftLint strict passes, the app unit tests pass (95 tests in 25 suites), MyHeartCountsShared passes on macOS (13) and on Linux with Swift 6.4 (5), and the HealthDashboard, TaskHandling and survey-extraction UI tests pass against the Firebase emulators (12 run, 2 skipped by the tests themselves).
  • Earlier: the complete MyHeartCounts Unit Tests plan passed locally with 51 tests across 15 suites.
  • The HealthSampleProcessing suite also passes across five repeated executions of its persistence tests.
  • swift test --package-path MyHeartCountsShared passes locally: 14 tests across 4 suites.
  • The Study Definitions package passes locally: 2 tests, including questionnaire-catalog conformance.
  • Repository-wide swiftlint lint --strict and git diff --check pass.
  • The Grove GroveHealthKitFHIR target passes locally: 123 tests across 16 suites.

Targets #216 in the stack. Merge conflicts will be resolved separately, including reconciling the FHIR StudyDefinitions dependency with the UK consent placeholder from SchmiedmayerLab/MyHeartCounts-StudyDefinitions#48. No branch merge or rebase was performed when inserting #216. This pull request remains a draft.

Code of Conduct & Contributing Guidelines

By creating and submitting this pull request, you agree to follow our Code of Conduct and Contributing Guidelines:

Copilot AI lite review requested due to automatic review settings August 26, 2026 16:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch 2 times, most recently from f064bc5 to 2fbc1fa Compare August 26, 2026 16:51
@PSchmiedmayer PSchmiedmayer changed the title Convert Every SensorKit and HealthKit Upload Through Grove Convert Health Data through Grove Instead of the App's FHIR Layer Aug 28, 2026
@SchmiedmayerLab SchmiedmayerLab deleted a comment from coderabbitai Bot Aug 28, 2026
@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch from 96c2689 to e814da7 Compare August 28, 2026 05:11
@coderabbitai

coderabbitai Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 14280b28-f97b-4ad2-a54f-0c2efa21b2f4


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch from e814da7 to 6a55550 Compare August 28, 2026 06:08
@PSchmiedmayer
PSchmiedmayer marked this pull request as draft August 29, 2026 16:16
@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch 3 times, most recently from 2fb5895 to 5fdcfae Compare August 30, 2026 18:29
PSchmiedmayer added a commit to SchmiedmayerLab/.github that referenced this pull request Sep 23, 2026
### ♻️ Current situation & Problem

`swift-test.yml` builds with a plain `swift build` before `swift test`,
and its only passthrough, `test_flags`, reaches `swift test` alone.
A caller therefore cannot pass options both commands must share, such as
a build system or compiler flags.

This is needed now because Swift 6.4.0's default build system compiles
targets that are only reachable through an inactive platform condition
([swiftlang/swift-package-manager#10538](swiftlang/swift-package-manager#10538)).
On Linux, consumers of Grove fail in `GroveKeychainStorage` (`no such
module 'Security'`) until the fix, already backported in
[#10556](swiftlang/swift-package-manager#10556),
ships in a 6.4.x release.
With `build_flags: '--build-system native'` they can build until then.

### ⚙️ Release Notes

- Add a `build_flags` input to `swift-test.yml`, passed to both `swift
build` and `swift test`; it defaults to empty, so existing callers are
unchanged.
- Clarify that `test_flags` is passed to `swift test` only.

### 📚 Documentation

The input descriptions and the Run Swift Tests section of the README
explain which flags go where.

### ✅ Testing

`actionlint` passes, and the flag expansion was checked for an empty, a
single and a multi-flag value.
The first callers are SchmiedmayerLab/MyHeartCounts-StudyDefinitions#47
and SchmiedmayerLab/MyHeartCounts-iOS#204 once this is released.

### Code of Conduct & Contributing Guidelines

- [x] I agree to follow the [Code of
Conduct](https://github.com/SchmiedmayerLab/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/SchmiedmayerLab/.github/blob/main/CONTRIBUTING.md).
@PSchmiedmayer PSchmiedmayer mentioned this pull request Sep 24, 2026
1 task done
@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch from 0a073cb to e151f6e Compare September 24, 2026 04:03
@PSchmiedmayer
PSchmiedmayer changed the base branch from lukas/grove to feature/track-grove-0.3.0-beta.8 September 24, 2026 04:03
@PSchmiedmayer
PSchmiedmayer added this pull request to stack #214 September 24, 2026 04:03
PSchmiedmayer added a commit to SchmiedmayerLab/.github that referenced this pull request Sep 24, 2026
### ♻️ Current situation & Problem

Swift 6.4's default build system writes its products to
`.build/out/Products/<Config>-<platform>`, with one `<Target>Tests.so`
per test target instead of a `.xctest`.
The coverage step of `swift-test.yml` only searched the native layout
(`.build/<triple>/<config>/codecov` and `*.xctest`), found nothing, and
`llvm-cov export` failed with "exactly one of -instr-profile and
-empty-profile must be specified".
Every caller that sets `artifact_name` fails on Swift 6.4 even though
its tests pass, for example SchmiedmayerLab/MyHeartCounts-iOS#204.

### ⚙️ Release Notes

- Find the coverage profile and the test binaries in either build
system's layout, and export coverage from every test binary, passing
additional ones as `-object`.

### 📚 Documentation

A two-line comment in the step names both layouts.

### ✅ Testing

`actionlint` passes.
The exact step was run in Swift 6.4 on Linux against
`MyHeartCountsShared`: with the default build system it exports 402 LCOV
records from the test `.so`, and with `--build-system native` 342
records from the `.xctest`.

### Code of Conduct & Contributing Guidelines

- [x] I agree to follow the [Code of
Conduct](https://github.com/SchmiedmayerLab/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/SchmiedmayerLab/.github/blob/main/CONTRIBUTING.md).
@PSchmiedmayer
PSchmiedmayer force-pushed the lukas/grove-fhir-migration branch from 222355e to 22bbdb0 Compare September 24, 2026 21:42
@lukaskollmer
lukaskollmer self-requested a review September 25, 2026 06:59
This was referenced Sep 26, 2026
@lukaskollmer
lukaskollmer removed this pull request from stack #214 September 26, 2026 07:30
@lukaskollmer
lukaskollmer changed the base branch from feature/track-grove-0.3.0-beta.8 to lukas/uk-study-variant-fixes September 26, 2026 07:30
@lukaskollmer
lukaskollmer added this pull request to stack #217 September 26, 2026 07:30
…ration

Combine study variants with the FHIR migration using the published
StudyDefinitions integration and Grove's restored standard deployment targets.
Update the dependency pins and apply the required Grove API compatibility
changes while preserving existing export-state and retraction behavior.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants