fix(analytics): wire primary-session resting HR to fix nap floor dist… - #2358
Conversation
|
Reviewed at f697002. Thanks for picking this up. The parity half is careful and I have no complaint with it: Swift My hesitation is not in the diff. It is in the helper this wires up.
This PR performs the switch that paragraph defers. Merged as it stands, the repo would carry a doc asserting that nothing consumes the helper, sitting directly above a helper that now sets the headline resting HR, which in turn feeds recovery, strain, workout detection and energy. Two readouts of one fact, able to disagree, in documentation form. Three concrete things behind that:
What I would rather merge: keep this wiring exactly as you have written it, but put it behind a default-off Test Centre toggle in the shape #2361 uses for the Oura notification mask. Then both definitions can be compared over real nights before either becomes the scored one. That turns a re-baselining into an A/B, which is the bar the helper's own doc set, and it makes the eventual flip evidence-backed rather than argued. I am happy to write the toggle if you would rather not. One housekeeping item whichever way this goes: the last line of the description pairs a closing verb with the issue number. Could you switch it to |
|
Follow-up, and it is about sequencing rather than anything new in your diff. #2284 changes what a sleep session's That lands one layer below this PR and largely on the same complaint. This PR replaces The practical consequence for your numbers: the MAE improvements in your description (6.0 to 2.0 dev, 7.5 to 0.8 holdout) were measured against the floor statistic. Afterwards the comparison baseline is a different statistic, so those figures would need re-deriving before they mean what they currently say. It may turn out the remaining gap is small enough that the wiring is not worth a re-baselining. It may equally turn out the primary-session half still matters, because the other PR does nothing about the part of the issue where a short low-HR nap can replace the main overnight session through So none of this is a reason to withdraw anything. It is a reason not to merge the two independently. My earlier suggestion stands: land the wiring behind a default-off Test Centre toggle so both definitions can be compared on real nights, and if the session-level change goes in first, compare against that. Nothing here changes the earlier review. The last line of your description still pairs a closing verb with an issue number and wants switching to |
|
Sequencing decided, and it affects this PR more than it affects #2284: that one goes in first. Why that way round. #2284's argument is internal to the codebase, that the recovery baseline mixes the imported export's resting HR with computed lowest-bin values roughly 8 bpm lower, so a wearer's own nights read as unusually low and inflate Charge. That is demonstrable without any external reference. This PR's argument IS the comparison, which is a harder thing to carry on one participant, and it sits one layer above the statistic #2284 changes. What that means for your numbers. #2284 replaces a sleep session's I want to be clear this is not a polite way of turning the PR down. The half that is genuinely yours survives #2284 untouched: nothing in it addresses So the practical path: wait for #2284, re-run your dev and holdout split against the new statistic, and bring the numbers back. My earlier suggestion still stands as the way to land it either way, which is to put the wiring behind a default-off Test Centre toggle so both definitions can be compared on real nights rather than argued. Two things from the first review are unchanged and independent of all this: the last line of the description still pairs a closing verb with an issue number and wants switching to |
|
Thanks for the thorough review and clear context on sequencing with #2284, @ryanbr I agree with this path:
Regarding the toggle implementation: if you'd like to push the Test Centre toggle directly to this branch, you are more than welcome to. Otherwise, I can implement the toggle and matching tests once #2284 merges. Let me know your preference! |
…ortion (ryanbr#1169) Wires primarySessionRestingHR into restingHRDaily on both Swift and Kotlin AnalyticsEngine engines. When valid samples exist in the primary sleep session, calculates resting HR as the sample mean of the longest/primary sleep window (resolving issue ryanbr#1169 and nap-induced floor distortion). Cleanly falls back to the previous session minimum if primary session coverage is sparse. Validated via Tools/parity_ledger.py and Tools/parity_ratchet.py --base origin/main --offline (0 errors, decreases test-only-callsite debt).
… in providedSleep (ryanbr#804)
The helper's doc block was headed "Deliberately PURE and UNWIRED" and stated that nothing consumes it, that switching the consumers is a re-baselining of core scores, and that the issue asks for a larger multi-participant holdout first. The commit under this one performs that switch: restingHRDaily now prefers a device-provided primary-session value, then this mean, and falls back to the old floor only when coverage is sparse. Leaving the block in place would put a doc asserting nothing consumes the helper directly above a helper that sets the headline resting HR, and with it recovery, strain, workout detection and energy. Rewritten on both platforms to say what is true: that it is wired, that the switch was a maintainer call made ahead of the holdout the issue asks for, that the MAE evidence is one participant over five nights, and that ryanbr#2284 changes the statistic the fallback rests on, so the baseline those figures were measured against will not survive unchanged.
f697002 to
cb07932
Compare
What this PR does
Wires
primarySessionRestingHRintorestingHRDailyon both Swift and KotlinAnalyticsEngineimplementations.Previously,
restingHRDailytook the minimum (.min()) resting HR across all sleep sessions of the day. A daytime nap or brief artifact would drag down the entire day's resting HR by 4–8 bpm, artificially lowering daily cardiovascular strain and distorting the recovery baseline.This change calculates resting HR as the sample mean of the primary (longest) overnight sleep session, using the existing
PrimarySessionRestingHRlogic that was previously run only as shadow instrumentation. If primary session data is sparse (< 30 valid samples), it cleanly falls back to the previous session-minimum behavior.Type of change
How it was tested
python3 Tools/parity_ledger.py: Passed cleanly with zero new findings; decreasedtest-only-callsitedebt on bothPackages/StrandAnalyticsandandroid/analytics.python3 Tools/parity_ratchet.py --base origin/main --offline: Passed witherrors=0.PrimarySessionRestingHRTests.swiftandPrimarySessionRestingHRTest.kt(multi-session nap vs. main night fixtures)..min().Checklist
swift testinPackages/<name>)android/(./gradlew testFullDebugUnitTest)StrandDesigntokens — no hardcoded colors, fonts, or spacingdocs/CONTRIBUTING.mdStrand.xcodeproj/) or any secrets/keystoresRelated issues
Refs #1169