Skip to content

[codex] Follow up WHOOP history retry handling - #8

Merged
BarMal merged 1 commit into
masterfrom
codex/whoop-history-sleep-gates
Jun 20, 2026
Merged

BarMal merged 1 commit into
masterfrom
codex/whoop-history-sleep-gates

Conversation

@BarMal

@BarMal BarMal commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #7 after retesting issue #6.

The first PR fixed persistence and dashboard repair, but retesting still showed historical sync reporting connection failure on the first attempt and disconnected/no-op behavior on later attempts. This PR addresses the remaining session lifecycle and UI visibility issues.

What changed

  • Explicitly cleans up failed WHOOP service sessions instead of waiting for Android service destruction, so a failed historical sync cannot leave currentSessionId open and swallow the next backfill request.
  • Treats strap disconnect after historical packets have arrived as a completed history transfer, since the strap can end the backfill by closing BLE without the metadata completion marker.
  • Tracks historical frame and decoded-record counts in the live debug trace to make this easier to verify on-device.
  • Adds a selected-range WHOOP heart-rate trend card so last-week HR samples appear in the dashboard instead of only today's intraday chart.

Validation

  • ./gradlew.bat :app:assembleDebug
  • ./gradlew.bat testDebugUnitTest

@BarMal
BarMal marked this pull request as ready for review June 20, 2026 08:42
@BarMal
BarMal merged commit 3d62715 into master Jun 20, 2026
1 check passed

BarMal commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Follow-up after checking the local Whoopsie and Goose reference repos:

  • Whoopsie’s Gen4 protocol documents the startup as a strict five-packet init sequence ending with SEND_HISTORICAL_DATA; Sploot was inserting SET_CLOCK before that final history command. This branch now sends the five init packets uninterrupted.
  • Whoopsie also documents historical batch ACKs as using the full 8-byte frame[17:25] batch id. Sploot was ACKing only the first 4 bytes and padding the rest with zeroes, which could stall or fail the historical transfer at the first batch marker. This branch now uses all 8 bytes and adds a focused unit test for that frame shape.
  • Goose is mostly V5/fd4b-oriented, but it reinforces the same idea that history transfer should be treated as an explicit command/response flow with ACK/completion handling rather than mixing unrelated commands into the transfer window.

Validated locally with:

  • ./gradlew.bat :whoop-ble:testDebugUnitTest
  • ./gradlew.bat :app:assembleDebug
  • ./gradlew.bat testDebugUnitTest

BarMal commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Follow-up for the remaining Connection failed during historical backfill:

The backfill path still treated history as failed unless one of a few terminal signals arrived within 180s. That is too brittle for a large strap backlog, especially if historical frames are flowing but the strap does not emit the exact completion marker we expected.

This update mirrors the Goose-style completion behavior more closely:

  • after historical data frames arrive, schedule completion after a 12s idle period;
  • keep the existing explicit completion paths for metadata, realtime transition, and strap disconnect;
  • extend the hard historical-sync timeout to 30 minutes so a week-size backlog does not fail while frames are still arriving;
  • ensure strap-disconnect completion does not incorrectly publish the connection state as READY.

Validated locally with:

  • ./gradlew.bat :whoop-ble:testDebugUnitTest
  • ./gradlew.bat :app:assembleDebug
  • ./gradlew.bat testDebugUnitTest

BarMal commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Follow-up for historical data not appearing in the dashboard:

I found a persistence-side issue separate from the BLE handshake. WhoopRecordingService.shouldPersist(...) assumed incoming timestamps are monotonic increasing. Historical backfill can arrive newest-to-oldest or otherwise out of order; after the first/newest sample set lastPersistedHrSecond/IMU/PPG, older historical samples failed (ts - last) >= interval and were silently dropped before reaching the database/dashboard.

This update keeps live-session downsampling unchanged, but makes historical sync order-agnostic: if a historical record passes the existing exact timestamp/type duplicate guard, it is persisted regardless of arrival order.

Validated locally with:

  • ./gradlew.bat :app:assembleDebug
  • ./gradlew.bat testDebugUnitTest

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.

1 participant