Skip to content

Require continuous effort for heart-rate recovery eligibility - #1901

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
bhelm:feature/noop-hrr-continuity
Sep 6, 2026
Merged

ryanbr merged 1 commit into
ryanbr:mainfrom
bhelm:feature/noop-hrr-continuity

Conversation

@bhelm

@bhelm bhelm commented Sep 5, 2026 •

Copy link
Copy Markdown

What this PR does

Changes heart-rate recovery eligibility on Swift and Android to require one continuous qualifying effort of at least two minutes. Previously, separate high-heart-rate fragments could be added together and incorrectly qualify a workout.

The calculation keeps the existing threshold, left-sample interval attribution, 10-second gap cap, duplicate-timestamp handling, recovery windows, and score outputs.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / tooling

How it was tested

  • Matching Swift and Kotlin regressions failed before the calculation change for disconnected runs, a gap over the cap, and a below-threshold reset.
  • Focused Swift tests pass 12/12 using the actual analytics source, tests, and WhoopProtocol dependency in a minimal Linux package.
  • Focused Android HeartRateRecoveryTest passes through testFullDebugUnitTest.
  • Boundary coverage includes a gap exactly at the 10-second cap, a gap over the cap, exactly 120 continuous qualifying seconds, left-sample attribution, and duplicate timestamps.
  • A focused differential check of 22 shared HRR cases produced byte-identical Swift/Kotlin output, including the corrected null result for disconnected effort. This used an isolated adapted fork harness, not the full fork governance/coverage guard: an unrelated obsolete runner dispatch was excluded with an explicit error, and the pre-fix known-bug oracle was retired without changing the input cases. A missing-result negative control failed as expected.

This is a pure analytics change and does not touch BLE behavior. No physical strap was tested. Local focused verification ran on Linux; the complete Swift package suite subsequently passed on macOS in Swift Packages CI, and the complete Android unit suite passed in Android CI.

Checklist

  • Swift package tests pass for any package I touched (swift test in Packages/<name>) — complete StrandAnalytics package tests passed in macOS CI.
  • Android unit tests pass if I touched android/ (./gradlew testFullDebugUnitTest) — complete unit suite passed in Android CI.
  • No new build warnings introduced
  • UI changes use only StrandDesign tokens — no UI changes
  • No hardcoded hex frame bytes; protocol facts live in the schema / decoders — no protocol changes
  • Follows the conventions in docs/CONTRIBUTING.md
  • I did not commit generated output (Strand.xcodeproj/) or any secrets/keystores

Related issues

Fixes bhelm#55.

@ryanbr
ryanbr merged commit 2b46dd0 into ryanbr:main Sep 6, 2026
17 checks passed
@bhelm
bhelm deleted the feature/noop-hrr-continuity branch September 15, 2026 22:35
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.

HeartRateRecovery accepts accumulated disconnected high-intensity segments

2 participants