Skip to content

Set up OneSecStanfordStudy package#1

Open
PSchmiedmayer wants to merge 7 commits into
mainfrom
setup/monorepo-package
Open

Set up OneSecStanfordStudy package#1
PSchmiedmayer wants to merge 7 commits into
mainfrom
setup/monorepo-package

Conversation

@PSchmiedmayer

Copy link
Copy Markdown
Contributor

Set up OneSecStanfordStudy package

♻️ Current situation & Problem

This PR creates the standalone OneSecStanfordStudy Swift package from the consolidated OneSec study integration setup prepared during the Spezi monorepo work.

The previous two-package setup used separate interface and implementation packages to work around deployment target constraints. This package depends on the Spezi monorepo setup directly and uses the Spezi feature branch for now.

⚙️ Release Notes

  • Adds the OneSecStanfordStudy Swift package with iOS 15 package compatibility and active integration on iOS 17+.
  • Adds unit tests and a consolidated iOS UI test app.
  • Adds compact CI through the SchmiedmayerLab reusable Swift package CI workflow.
  • Temporarily points the Spezi dependency at SchmiedmayerLab/Spezi branch oldiOSVersion until the monorepo changes are merged and tagged.

📚 Documentation

The README documents installation, the temporary Spezi branch dependency, the later 0.x tagged release dependency, basic usage, testing, contributing, license, and contributor provenance.

✅ Testing

Local preflight before opening the PR:

  • swiftlint lint --strict --quiet --no-cache
  • reuse lint
  • swift package dump-package

CI is expected to run the SchmiedmayerLab reusable Swift package setup, test, static-analysis, and coverage workflows.

Code of Conduct & Contributing Guidelines

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

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PSchmiedmayer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b7cc5248-4492-45f0-92e7-633c4f5b9c32

📥 Commits

Reviewing files that changed from the base of the PR and between e031aba and ce8521d.

📒 Files selected for processing (2)
  • Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift
  • Tests/UITests/TestAppUITests/TestAppUITests.swift
📝 Walkthrough

Walkthrough

This PR adds the OneSecStanfordStudy Swift package, including its public module API, async sequence wrappers, HealthKit export pipeline, WebView-driven survey flow, unit tests, and a UI test app. It also adds the package manifest, CI workflow, repository metadata, licensing files, and Xcode project assets and schemes for UI testing.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant StudyButton
  participant StudySurveySheet
  participant WebView
  participant OneSecStanfordStudy
  participant BulkHealthExporter

  User->>StudyButton: tap Initiate Flow
  StudyButton->>StudySurveySheet: present sheet
  StudySurveySheet->>WebView: load surveyUrl
  WebView-->>StudySurveySheet: didNavigate
  StudySurveySheet->>OneSecStanfordStudy: updateState(.active)
  StudySurveySheet->>OneSecStanfordStudy: triggerHealthExport(forceSessionReset: true)
  OneSecStanfordStudy->>BulkHealthExporter: start export session
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: setting up the OneSecStanfordStudy package.
Description check ✅ Passed The description accurately matches the package setup, CI, tests, and documentation changes in the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@PSchmiedmayer PSchmiedmayer self-assigned this Jul 8, 2026
@PSchmiedmayer PSchmiedmayer added the enhancement New feature or request label Jul 8, 2026
@PSchmiedmayer PSchmiedmayer marked this pull request as ready for review July 8, 2026 06:18
@PSchmiedmayer

Copy link
Copy Markdown
Contributor Author

@LambdaDigamma & @lukaskollmer This should simplify our setup by a lot; would be great to get your input here 🚀

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.

Pull request overview

This PR establishes OneSecStanfordStudy as a standalone Swift package (iOS 15 package compatibility with active integration on iOS 17+), including a consolidated UI test app, unit tests, and a reusable-workflow-based CI setup.

Changes:

  • Adds the OneSecStanfordStudy Swift package target, public integration entry points, and core implementation (survey sheet + WebView integration + HealthKit bulk export plumbing).
  • Adds unit tests plus a consolidated Xcode-based UI test app (scheme, test plan, resources).
  • Adds repo scaffolding (README, licensing/REUSE metadata, SwiftLint config) and CI via SchmiedmayerLab reusable workflows.

Reviewed changes

Copilot reviewed 32 out of 34 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme Adds shared scheme for running the UI test app and UI tests.
Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Xcode workspace check metadata for the UI tests project.
Tests/UITests/UITests.xcodeproj/project.xcworkspace/contents.xcworkspacedata Workspace definition for the UI tests project.
Tests/UITests/UITests.xcodeproj/project.pbxproj UI test app + UI test target project configuration.
Tests/UITests/TestAppUITests/TestAppUITests.swift UI tests validating wrapper behavior + WebView alert/confirm hooks.
Tests/UITests/TestApp/WebViewTests/WebViewAlertAndConfirmTestButton.swift UI surface to open the sheet with a local HTML page for alert/confirm testing.
Tests/UITests/TestApp/TestAppDelegate.swift Initializes the package in an app-delegate context with HealthKit export config.
Tests/UITests/TestApp/TestApp.swift SwiftUI app entrypoint applying the .oneSecStanfordStudy() modifier.
Tests/UITests/TestApp/TestApp.entitlements Enables HealthKit entitlement for the UI test app.
Tests/UITests/TestApp/StudyButton.swift UI entry to start the survey flow in the UI test app.
Tests/UITests/TestApp/Resources/alert-confirm-test.html Local HTML resource used by UI tests to trigger alert() / confirm().
Tests/UITests/TestApp/ContentView.swift Root UI for iOS 17+ integration testing in the UI test app.
Tests/UITests/TestApp/Assets.xcassets/Contents.json Asset catalog metadata for the UI test app.
Tests/UITests/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json App icon asset metadata for the UI test app.
Tests/UITests/TestApp/Assets.xcassets/AccentColor.colorset/Contents.json Accent color asset metadata for the UI test app.
Tests/UITests/TestApp.xctestplan Xcode test plan (including coverage configuration) for the UI test setup.
Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift Unit tests for HealthExportConfiguration and AnyAsyncSequence behavior.
Sources/OneSecStanfordStudy/WebView.swift SwiftUI WKWebView wrapper with navigation hooks and JS alert/confirm bridging.
Sources/OneSecStanfordStudy/StudySurveySheet.swift Survey sheet UI, navigation handling, and export initiation trigger points.
Sources/OneSecStanfordStudy/OneSecStanfordStudyModule.swift Public module interface/base class + HealthExportConfiguration type.
Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift Concrete module implementation integrating Spezi runtime and bulk export logic.
Sources/OneSecStanfordStudy/Integration.swift Public initialization function and root SwiftUI modifier for apps.
Sources/OneSecStanfordStudy/HKSampleToFHIRProcessor.swift Batch processor converting HealthKit samples to FHIR and writing compressed output.
Sources/OneSecStanfordStudy/AnyAsyncSequence.swift Type erasure for AsyncSequence/Iterator (including pre-iOS18 “never throws” wrapper).
REUSE.toml REUSE licensing annotations for generated/config assets and Xcode project files.
README.md Installation/usage/testing documentation for the package.
Package.swift SwiftPM package definition, platforms, targets, and Spezi dependency pinning.
LICENSES/MIT.txt MIT license text under LICENSES directory.
LICENSE.md Root MIT license file for the repository/package.
CONTRIBUTORS.md Contributors list and attribution to upstream repositories.
.swiftlint.yml SwiftLint configuration and exclusions.
.spi.yml Swift Package Index documentation build configuration.
.gitignore Git ignore rules for SwiftPM/Xcode artifacts.
.github/workflows/ci.yml CI workflow using SchmiedmayerLab reusable Swift package workflows.
Files not reviewed (1)
  • Tests/UITests/UITests.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/OneSecStanfordStudy/WebView.swift Outdated
Comment thread Tests/UITests/TestApp/Resources/alert-confirm-test.html Outdated
Comment thread Tests/UITests/TestAppUITests/TestAppUITests.swift Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment on lines +110 to +111
let session = try await healthExportSession()
let stream = try session.start(retryFailedBatches: true)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lukaskollmer & @LambdaDigamma as far as I can see doesn't have to be written as probably managed outside of this package? If so, probably good to document and/or move this write into the package.

Comment on lines +140 to +142
if isCompleted {
healthExportConfig.didEndExport()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same here as above in the other comment @lukaskollmer & @LambdaDigamma.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 10

🧹 Nitpick comments (6)
Sources/OneSecStanfordStudy/AnyAsyncSequence.swift (1)

86-89: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider as! over as? for the element cast in next().

The as? Element cast returns nil on failure, which would silently terminate iteration (appearing as end-of-sequence). Given the initializer constraints guarantee S.Element == Element / I.Element == Element, the cast should never fail. Using as! would surface a programmer error immediately rather than silently truncating the sequence.

Optional: fail fast on cast failure
     `@inlinable`
     public mutating func next() async throws -> Element? {
-        try await base.next() as? Element
+        try await base.next() as! Element?
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/OneSecStanfordStudy/AnyAsyncSequence.swift` around lines 86 - 89, The
`AnyAsyncSequence.next()` implementation is using a conditional cast that can
silently return nil and end iteration early; update the cast in `next()` to a
force cast since the generic constraints on `AnyAsyncSequence`/its initializer
already guarantee `S.Element == Element` and `I.Element == Element`. Keep the
change localized to `next()` so any unexpected mismatch fails fast instead of
being treated as end-of-sequence.
Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift (1)

14-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding tests for the throwing and iOS 18 next(isolation:) paths.

The current tests only cover the Failure == Never (non-throwing) path. The throwing path (Failure == any Error) and the iOS 18 next(isolation:) method are untested. Adding a test that wraps a throwing AsyncSequence and verifies error propagation would improve confidence in the type-erasure correctness.

🧪 Suggested test for the throwing path
`@Test`
func anyAsyncSequencePropagatesErrors() async throws {
    struct TestError: Error, Equatable {}
    let stream = AsyncThrowingStream<Int, Error> { continuation in
        continuation.yield(1)
        continuation.finish(throwing: TestError())
    }
    let sequence = AnyAsyncSequence<Int, any Error>(stream)

    var values: [Int] = []
    var caughtError: Error?
    do {
        for try await value in sequence {
            values.append(value)
        }
    } catch {
        caughtError = error
    }

    `#expect`(values == [1])
    `#expect`(caughtError is TestError)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift` around lines
14 - 52, Add tests that cover the missing AnyAsyncSequence paths: the throwing
Failure == any Error case and the iOS 18 next(isolation:) behavior. Extend
OneSecStanfordStudyTests with a test that wraps a throwing AsyncSequence using
AnyAsyncSequence<Int, any Error>, verifies yielded values are preserved, and
asserts the thrown error propagates correctly. Also add a test that exercises
iteration through the newer isolation-aware next(isolation:) path so the type
erasure is validated beyond the current unsafelyAssumingDoesntThrow initializer.
.github/workflows/ci.yml (1)

22-27: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider scoping down workflow-level write permissions and secrets: inherit.

Static analysis flags issues: write and pull-requests: write as overly broad at the workflow level, and secrets: inherit unconditionally passes all secrets to the reusable coverage workflow. If the reusable workflows don't require these permissions or all secrets, consider moving write permissions to only the jobs that need them and passing specific secrets explicitly (e.g., secrets: CODECOV_TOKEN) instead of inheriting everything. If the reusable SchmiedmayerLab/.github workflows do require these, this is acceptable as-is.

Also applies to: 86-86

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 22 - 27, The workflow-level
permissions and secret forwarding are broader than necessary; review the CI
workflow’s permissions block and the reusable workflow calls to scope them down.
Move `issues: write` and `pull-requests: write` to only the jobs that actually
need them, and replace `secrets: inherit` with explicit secret passing (for
example, only `CODECOV_TOKEN`) unless the `SchmiedmayerLab/.github` reusable
workflows genuinely require full inheritance.

Source: Linters/SAST tools

Package.swift (1)

35-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

traits: [] is redundant and ExistentialAny is duplicated across targets.

traits: [] is the default for .package(url:branch:traits:) and can be omitted. The swiftSettings block is identical between the target and testTarget — consider whether a shared array would reduce duplication, though for two targets this is minor.

📝 Proposed cleanup
         .package(url: "https://github.com/SchmiedmayerLab/Spezi.git", branch: "oldiOSVersion")

Also applies to: 45-48

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Package.swift` around lines 35 - 38, Remove the redundant empty traits
argument from the package dependency declaration and simplify the duplicated
Swift settings setup. In Package.swift, keep the
ExistentialAny/InternalImportsByDefault settings in a shared reusable array (or
equivalent shared constant) and reference it from both the target and testTarget
declarations so the duplicated swiftSettings blocks are centralized; use the
package/dependency declaration symbols and the target definitions as the place
to update.
CONTRIBUTORS.md (1)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix heading style inconsistency flagged by markdownlint.

The title uses setext style (====) while ## Attributions uses atx style. markdownlint (MD003) expects consistent setext headings throughout.

📝 Proposed fix
-## Attributions
+Attributions
+-----------
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CONTRIBUTORS.md` at line 19, The heading style is inconsistent in the
Attributions section, which triggers markdownlint MD003. Update the existing
Attributions heading to match the document’s setext heading style used
elsewhere, and keep the heading formatting consistent throughout
CONTRIBUTORS.md.

Source: Linters/SAST tools

Tests/UITests/UITests.xcodeproj/project.pbxproj (1)

15-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Template leftover names: remoteInfo and productName still say "Example".

The PBXContainerItemProxy at line 20 has remoteInfo = "Example" and the native targets at lines 141 and 162 have productName = "Example" / "ExampleUITests". Since PRODUCT_NAME is set to $(TARGET_NAME) in build settings, the product name override is cosmetic, but remoteInfo is used by Xcode for dependency resolution display and should match the actual target name TestApp.

♻️ Proposed cleanup
 		2F6D13AD28F5F386007C25D6 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 2F6D138A28F5F384007C25D6 /* Project object */;
 			proxyType = 1;
 			remoteGlobalIDString = 2F6D139128F5F384007C25D6;
-			remoteInfo = Example;
+			remoteInfo = TestApp;
 		};
 			name = TestApp;
 			packageProductDependencies = (
 				809757DB2E61D2AD009B747D /* OneSecStanfordStudy */,
 			);
-			productName = Example;
+			productName = TestApp;
 			productReference = 2F6D139228F5F384007C25D6 /* TestApp.app */;
 			name = TestAppUITests;
-			productName = ExampleUITests;
+			productName = TestAppUITests;
 			productReference = 2F6D13AC28F5F386007C25D6 /* TestAppUITests.xctest */;

Also applies to: 141-141, 162-162

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tests/UITests/UITests.xcodeproj/project.pbxproj` around lines 15 - 21, The
project file still contains template placeholder names, so update the
PBXContainerItemProxy entry’s remoteInfo to match the real target name TestApp
and clean up the native target productName overrides in the same project file so
they are consistent with the actual UITests target naming. Use the
PBXContainerItemProxy section plus the target entries for productName to locate
the stale "Example" and "ExampleUITests" values and replace them with the
correct names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@LICENSE.md`:
- Line 3: The copyright year in LICENSE.md is inconsistent with LICENSES/MIT.txt
and the repo’s SPDX headers. Update the LICENSE.md notice to match the same year
used elsewhere (or, if 2022 is intended, add a brief note clarifying that it
preserves the original upstream copyright) so the LICENSE.md header is
consistent with the project’s license files.

In `@Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift`:
- Line 72: The OneSecStanfordStudy state load is silently falling back to
.available when localStorage.load(.oneSecStanfordStudyState) throws, which can
overwrite a completed or unavailable study state. Update the initialization path
in OneSecStanfordStudy to avoid defaulting to .available on load failure, and
instead preserve the existing .unavailable-safe behavior or surface/log the
error before deciding the state. Use the updateState call and the
localStorage.load(.oneSecStanfordStudyState) access as the key locations to
adjust.
- Around line 71-87: The resume path in configure() is dead because
didInitiateBulkExport is only read and never written. Update the bulk export
initiation flow in OneSecStanfordStudy/BulkHealthExporter-related code so that
when the export is first started (before or during
triggerHealthExport(forceSessionReset:)), localStorage.store(true, for:
.didInitiateBulkExport) is saved, and keep the existing configure() check that
reloads state and resumes via healthExportSession() and
triggerHealthExport(forceSessionReset: false).
- Around line 132-143: _guard against duplicate completion handling in
_trackCompletion(of:)_: the current withObservationTracking/onChange recursion
can re-enter after session.state leaves and returns to .completed, causing
healthExportConfig.didEndExport() to fire multiple times. Add a one-time
completion guard in OneSecStanfordStudy (for example, a private boolean or
similar state) and check it before calling didEndExport(), then set it when the
export completion is first observed so subsequent _trackCompletion recursions
become no-ops.

In `@Sources/OneSecStanfordStudy/StudySurveySheet.swift`:
- Around line 142-149: The `initiateHealthExport()` error path in
`StudySurveySheet` only logs failures and leaves the participant in `.active`
without any visible feedback. Update the `catch` block to surface a user-facing
failure state in the survey flow, such as presenting an alert or setting an
error state that the UI can react to, using the existing
`oneSecStanfordStudy`/`logger` flow to locate the handling point.
- Around line 154-164: The helper methods pageContainsField(named:) and
pageContainsElement(withId:) currently interpolate variableName and id directly
into JavaScript, which is unsafe and fragile. Update these checks to pass the
values as arguments instead of embedding them in the JS string, ideally by using
WKWebView.callAsyncJavaScript(_:arguments:) through WebViewProxy.wkWebView. If
access control blocks that path, adjust the wkWebView visibility so the safe
parameterized call can be used from the page-check helpers.

In `@Sources/OneSecStanfordStudy/WebView.swift`:
- Around line 47-58: The onAlert/onConfirm handlers in WebView are overwriting
the existing `@State` alert/confirmation while a previous withCheckedContinuation
is still pending, which can orphan the first continuation and hang the JS
bridge. Update the alert/confirmation handling in WebView to either queue
pending requests or add a runtime guard before assigning a new
alert/confirmation that resumes or rejects the existing continuation first; do
not rely on the assert alone since it is debug-only.
- Around line 79-88: The confirmation alert binding in WebView’s alert setup is
clearing the wrong state variable in its `set` closure. Update the closure to
clear `self.confirmation` (not `self.alert`) when `isPresented` becomes false,
so the confirmation flow is dismissed correctly and its continuation can be
resumed from the confirmation handling path.

In `@Tests/UITests/TestApp/ContentView.swift`:
- Around line 23-27: The Task launched from the “Trigger Health Export” Button
in ContentView currently lets triggerHealthExport(forceSessionReset:) throw
without handling, which can cause an unhandled task warning. Update that Task
body to use explicit do-catch error handling, and mirror the existing
TestAppDelegate.handleHealthExportDidStart pattern by catching failures and
routing them to a deliberate test-time failure path (for example, fatalError or
equivalent) so errors are surfaced consistently during testing.

In `@Tests/UITests/TestApp/Resources/alert-confirm-test.html`:
- Line 73: Fix the malformed table header markup in the alert-confirm test HTML
by replacing the invalid closing tag in the “Confirm” row so the `<th
scope="row">` element is properly closed. Update the `alert-confirm-test.html`
content around the table row used by the UI tests to use the correct `</th>`
closing tag, ensuring the table structure remains valid for `WKWebView` parsing
and element lookup.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 22-27: The workflow-level permissions and secret forwarding are
broader than necessary; review the CI workflow’s permissions block and the
reusable workflow calls to scope them down. Move `issues: write` and
`pull-requests: write` to only the jobs that actually need them, and replace
`secrets: inherit` with explicit secret passing (for example, only
`CODECOV_TOKEN`) unless the `SchmiedmayerLab/.github` reusable workflows
genuinely require full inheritance.

In `@CONTRIBUTORS.md`:
- Line 19: The heading style is inconsistent in the Attributions section, which
triggers markdownlint MD003. Update the existing Attributions heading to match
the document’s setext heading style used elsewhere, and keep the heading
formatting consistent throughout CONTRIBUTORS.md.

In `@Package.swift`:
- Around line 35-38: Remove the redundant empty traits argument from the package
dependency declaration and simplify the duplicated Swift settings setup. In
Package.swift, keep the ExistentialAny/InternalImportsByDefault settings in a
shared reusable array (or equivalent shared constant) and reference it from both
the target and testTarget declarations so the duplicated swiftSettings blocks
are centralized; use the package/dependency declaration symbols and the target
definitions as the place to update.

In `@Sources/OneSecStanfordStudy/AnyAsyncSequence.swift`:
- Around line 86-89: The `AnyAsyncSequence.next()` implementation is using a
conditional cast that can silently return nil and end iteration early; update
the cast in `next()` to a force cast since the generic constraints on
`AnyAsyncSequence`/its initializer already guarantee `S.Element == Element` and
`I.Element == Element`. Keep the change localized to `next()` so any unexpected
mismatch fails fast instead of being treated as end-of-sequence.

In `@Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift`:
- Around line 14-52: Add tests that cover the missing AnyAsyncSequence paths:
the throwing Failure == any Error case and the iOS 18 next(isolation:) behavior.
Extend OneSecStanfordStudyTests with a test that wraps a throwing AsyncSequence
using AnyAsyncSequence<Int, any Error>, verifies yielded values are preserved,
and asserts the thrown error propagates correctly. Also add a test that
exercises iteration through the newer isolation-aware next(isolation:) path so
the type erasure is validated beyond the current unsafelyAssumingDoesntThrow
initializer.

In `@Tests/UITests/UITests.xcodeproj/project.pbxproj`:
- Around line 15-21: The project file still contains template placeholder names,
so update the PBXContainerItemProxy entry’s remoteInfo to match the real target
name TestApp and clean up the native target productName overrides in the same
project file so they are consistent with the actual UITests target naming. Use
the PBXContainerItemProxy section plus the target entries for productName to
locate the stale "Example" and "ExampleUITests" values and replace them with the
correct names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7065fbf6-3654-4e9e-ab36-12e389b07692

📥 Commits

Reviewing files that changed from the base of the PR and between ceb03d8 and 6fd2fc9.

📒 Files selected for processing (34)
  • .github/workflows/ci.yml
  • .gitignore
  • .spi.yml
  • .swiftlint.yml
  • CONTRIBUTORS.md
  • LICENSE.md
  • LICENSES/MIT.txt
  • Package.swift
  • README.md
  • REUSE.toml
  • Sources/OneSecStanfordStudy/AnyAsyncSequence.swift
  • Sources/OneSecStanfordStudy/HKSampleToFHIRProcessor.swift
  • Sources/OneSecStanfordStudy/Integration.swift
  • Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift
  • Sources/OneSecStanfordStudy/OneSecStanfordStudyModule.swift
  • Sources/OneSecStanfordStudy/StudySurveySheet.swift
  • Sources/OneSecStanfordStudy/WebView.swift
  • Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift
  • Tests/UITests/TestApp.xctestplan
  • Tests/UITests/TestApp/Assets.xcassets/AccentColor.colorset/Contents.json
  • Tests/UITests/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json
  • Tests/UITests/TestApp/Assets.xcassets/Contents.json
  • Tests/UITests/TestApp/ContentView.swift
  • Tests/UITests/TestApp/Resources/alert-confirm-test.html
  • Tests/UITests/TestApp/StudyButton.swift
  • Tests/UITests/TestApp/TestApp.entitlements
  • Tests/UITests/TestApp/TestApp.swift
  • Tests/UITests/TestApp/TestAppDelegate.swift
  • Tests/UITests/TestApp/WebViewTests/WebViewAlertAndConfirmTestButton.swift
  • Tests/UITests/TestAppUITests/TestAppUITests.swift
  • Tests/UITests/UITests.xcodeproj/project.pbxproj
  • Tests/UITests/UITests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme

Comment thread LICENSE.md Outdated
Comment thread Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift
}

func configure() {
updateState((try? localStorage.load(.oneSecStanfordStudyState)) ?? .available)

@coderabbitai coderabbitai Bot Jul 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Defaulting to .available on storage error could reset a completed study state.

If localStorage.load(.oneSecStanfordStudyState) throws, the state silently resets to .available, which could re-enroll a participant who already completed or was marked unavailable. Consider logging the error and preserving the current .unavailable default, or surfacing the failure.

💡 Suggested improvement
- updateState((try? localStorage.load(.oneSecStanfordStudyState)) ?? .available)
+ do {
+     let restored = try localStorage.load(.oneSecStanfordStudyState)
+     updateState(restored)
+ } catch {
+     logger.error("Failed to load study state: \(error). Defaulting to .unavailable.")
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updateState((try? localStorage.load(.oneSecStanfordStudyState)) ?? .available)
do {
let restored = try localStorage.load(.oneSecStanfordStudyState)
updateState(restored)
} catch {
logger.error("Failed to load study state: \(error). Defaulting to .unavailable.")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift` at line 72, The
OneSecStanfordStudy state load is silently falling back to .available when
localStorage.load(.oneSecStanfordStudyState) throws, which can overwrite a
completed or unavailable study state. Update the initialization path in
OneSecStanfordStudy to avoid defaulting to .available on load failure, and
instead preserve the existing .unavailable-safe behavior or surface/log the
error before deciding the state. Use the updateState call and the
localStorage.load(.oneSecStanfordStudyState) access as the key locations to
adjust.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lukaskollmer & @LambdaDigamma Probably intentional as you want this to be explicit written and set to .available?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@PSchmiedmayer That's a reasonable possibility — if .available is meant to be the explicit, intentional default whenever no prior state can be loaded (e.g., first launch, or storage cleared), then this behavior would be by design rather than a bug. The main risk I flagged is specifically the case where a decode/load failure (as opposed to "no value has ever been stored") is treated the same as "no prior state," which could mask a genuine storage/corruption issue and silently reset an already-.completed or .unavailable participant back to .available.

I'll wait for confirmation from @lukaskollmer and @LambdaDigamma on whether that distinction matters here, or whether treating all load failures as .available is indeed the intended behavior for this study.

Comment thread Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift
Comment thread Sources/OneSecStanfordStudy/StudySurveySheet.swift
Comment thread Sources/OneSecStanfordStudy/StudySurveySheet.swift
Comment thread Sources/OneSecStanfordStudy/WebView.swift
Comment thread Sources/OneSecStanfordStudy/WebView.swift
Comment thread Tests/UITests/TestApp/ContentView.swift
Comment thread Tests/UITests/TestApp/Resources/alert-confirm-test.html Outdated

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
.github/workflows/ci.yml (2)

31-31: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider pinning the reusable workflow to a commit SHA.

Pinning to @v0.2 is better than tracking @main, but tags are mutable and can be moved. For supply-chain hardening, pin to the immutable commit SHA that the v0.2 tag currently points to. This is a common convention for third-party GitHub Actions and reusable workflows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 31, The reusable workflow reference in the
CI workflow is still using a mutable tag, so update the workflow call in the
`uses` entry to point at the immutable commit SHA for the current `v0.2`
revision instead of the tag. Keep the existing reusable workflow target
(`SchmiedmayerLab/.github/.github/workflows/swift-package-ci.yml`) and replace
the version selector with the pinned SHA so the workflow stays reproducible and
supply-chain hardened.

32-32: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Avoid unconditionally inheriting all secrets.

secrets: inherit passes every repository secret to the called workflow. If the reusable workflow is ever compromised or develops a vulnerability, all secrets are exposed. Prefer passing only the specific secrets the called workflow needs.

If the reusable workflow doesn't require any secrets, remove the secrets: inherit line entirely. Otherwise, replace it with explicit secret mappings (e.g., secrets: { SOME_SECRET: ${{ secrets.SOME_SECRET }} }).

zizmor also flagged this as a secrets-inherit warning.

🔒 Suggested change (if no secrets are needed)
   uses: SchmiedmayerLab/.github/.github/workflows/swift-package-ci.yml@v0.2
-  secrets: inherit
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 32, The reusable workflow call is
inheriting every repository secret via secrets: inherit, which is too broad. In
the workflow dispatch section in ci.yml, update the call site to either remove
the secrets entry entirely if the called workflow needs no secrets, or replace
it with explicit secret mappings for only the required names. Use the workflow
call configuration around the reusable workflow invocation to keep the secret
scope minimal.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 31: The reusable workflow reference in the CI workflow is still using a
mutable tag, so update the workflow call in the `uses` entry to point at the
immutable commit SHA for the current `v0.2` revision instead of the tag. Keep
the existing reusable workflow target
(`SchmiedmayerLab/.github/.github/workflows/swift-package-ci.yml`) and replace
the version selector with the pinned SHA so the workflow stays reproducible and
supply-chain hardened.
- Line 32: The reusable workflow call is inheriting every repository secret via
secrets: inherit, which is too broad. In the workflow dispatch section in
ci.yml, update the call site to either remove the secrets entry entirely if the
called workflow needs no secrets, or replace it with explicit secret mappings
for only the required names. Use the workflow call configuration around the
reusable workflow invocation to keep the secret scope minimal.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d81196e5-ea7a-42f0-8530-69350a147c9a

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd2fc9 and e031aba.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • CONTRIBUTORS.md
  • LICENSE.md
  • Package.swift
  • README.md
  • Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift
  • Sources/OneSecStanfordStudy/StudySurveySheet.swift
  • Sources/OneSecStanfordStudy/WebView.swift
  • Tests/OneSecStanfordStudyTests/OneSecStanfordStudyTests.swift
  • Tests/UITests/TestApp/ContentView.swift
  • Tests/UITests/TestApp/Resources/alert-confirm-test.html
  • Tests/UITests/TestAppUITests/TestAppUITests.swift
  • Tests/UITests/UITests.xcodeproj/project.pbxproj
💤 Files with no reviewable changes (1)
  • Tests/UITests/TestAppUITests/TestAppUITests.swift
✅ Files skipped from review due to trivial changes (4)
  • CONTRIBUTORS.md
  • LICENSE.md
  • README.md
  • Tests/UITests/UITests.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (5)
  • Package.swift
  • Tests/UITests/TestApp/ContentView.swift
  • Sources/OneSecStanfordStudy/WebView.swift
  • Sources/OneSecStanfordStudy/OneSecStanfordStudy.swift
  • Sources/OneSecStanfordStudy/StudySurveySheet.swift

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants