Repository navigation
Read WHOOP physiology across all registered straps - #1730
Conversation
|
@bhelm — the read contract is right, and the architecture test that blocks the old device-scoped names from returning is a good touch. One stream worries me, and I think it needs treating differently from the others. R-R interleaving changes the number, not the volumeThe boundary is stated as benign:
For HR buckets, gravity, sleep sessions and habitual midsleep that reasoning holds — averaging and interval data tolerate extra rows. R-R does not, because of where it lands:
RMSSD is the root mean square of SUCCESSIVE DIFFERENCES. Interleaving two straps' beats does not duplicate it — the successive differences start alternating between two independent measurements of the same heart, so they carry the inter-device offset instead of real beat-to-beat variability. The result is not a doubled number that a reader can discount; it is a wrong number that looks normal. This is reachable today, not theoretical: I have a period of genuine simultaneous 4.0 + 5.0 wear with both straps registered, so both feed the union across that window. It also stacks on an open defect. #1008 / #1118 have 4.0 R-R already over-counting roughly 2x via What I would suggestKeep the union for every other stream, and hold R-R to the active strap (or exclude it) until there is a wear-interval / device-era ownership signal. That keeps the whole benefit of the change — Stress, Sleep, Coach and timeline stop losing history at a strap swap — without letting one stream's semantics change silently for anyone who has ever worn two straps at once. Alternating straps, which is the case the PR is really aimed at, is unaffected either way: disjoint periods never interleave. Two smaller things
|
|
Dispatched That covers the gap you flagged. The R-R question above is the only thing still open from my side. |
Why
Raw physiology is currently stored under the device ID that produced or imported it. Some readers treated that storage provenance as the boundary of the user's timeline and queried only the currently active device or the legacy
my-whoopsource.That breaks as soon as a user replaces, removes, archives, or alternates WHOOP straps: data remains in the database but disappears from Stress, Sleep, AI Coach, and timeline views. It also makes correctness depend on every caller remembering which device namespace happens to contain a given period.
Device IDs should remain precise write provenance, but they should not fragment reads of the user's own WHOOP physiology. The read contract therefore resolves one deterministic source set everywhere:
my-whooplast,Archived devices are deliberately included: archiving stops future connection attempts; it must not orphan retained history. Non-WHOOP devices are not implicitly folded into this WHOOP timeline.
What changes
*ForDeviceAPIs, while personal timeline consumers use only union APIs; an architecture test prevents the old names from returning.(timestamp, rrMs, sequence)and aligns deterministic ordering across Kotlin and Swift.my-whoopbehavior.Parity guard
A shared JSON oracle defines source ordering, legacy collapse, non-WHOOP handling, and R-R identity/order. Native Android and Swift tests consume the same contract, while source-wiring guards prevent consumers from regressing to direct current-device reads.
The clean PR branch passed all Android parity/wiring suites (22 tests, 0 failures). Source-level Apple parity review found no divergence. Apple tests cannot execute on the Linux build host because Xcode/Swift is unavailable.
Verification
assembleFullDebug: successful.Known boundary
Alternating straps is supported: each strap's disjoint retained periods form one timeline. If two straps are genuinely worn at the same time, distinct same-time R-R beats can coexist because the current schema has no wear-interval/device-era ownership signal. Exact duplicates are removed deterministically; selecting a single owner for simultaneous wear is intentionally outside this change.
Related issues and PRs
my-whoopread assumptions; this PR closes the raw-physiology subset, not every canonical-source use.WhoopRepository,Reads,IntelligenceEngine,TodayView), feat(imu): unlock WHOOP 5/MG 100 Hz capture and labeled recording sessions #1709 (WhoopRepository,Reads, BLE/export/Test Centre), and feat: AI Coach — streaming, voice, brief, history, widgets, Siri (K1-K15) #1547 (AI Coach). None implements this complete raw-read contract or makes this PR redundant.