Observation
On a 5/MG install, a full 21-night re-score produces HRV and Charge for only the most recent eleven nights, while sleep and resting HR reach the whole window. The census line is stable across consecutive passes:
re-score census: 21 night(s) 2026-09-01..2026-09-21 charge=11 effort=14 sleep=20 hrv=11 rhr=20
The eleven nights carrying HRV are contiguous and are the newest eleven. Every night older than that boundary has no HRV, and therefore no Charge.
What the evidence says
R-R is ABSENT before the boundary, not present-and-rejected. The clearest single line, from the same pass:
sleep-detect day=2026-09-07 NO-NIGHT hr=134089 rr=0 resp=0 grav=0 skin=0 steps=0
Abundant heart rate, zero R-R, on a day that sits before the boundary. On the eleven nights after it, R-R beat counts per night run from roughly 3,700 to 23,000, so where R-R exists there is plenty of it.
So HRV and Charge are following R-R availability. That part is not in question.
What is ruled out
Two plausible explanations were checked and do not hold:
- Not a quality gate.
HRVAnalyzer only gates on the two OVER-COUNT verdicts; underCovered and unmeasurable stay trusted (HRVAnalyzer.swift:422, :436). Most of the nights that DO produce HRV read underCovered, so that verdict plainly is not what removes a night.
- Not age-based retention. The only R-R prune is
pruneRrByTs, called from WhoopRepository with a plausibility window of MIN_PLAUSIBLE_UNIX through now + futureMargin. It removes implausible timestamps, not old rows.
What is NOT established
Why R-R stops at that boundary while heart rate continues through it. The install has two registered straps, a 5/MG active and a 4.0 last seen a fortnight ago, so a handover between sources is one candidate, and a capture path that only began storing R-R later is another. Nothing in the log distinguishes them, and this issue deliberately does not pick one.
What would discriminate
A single store query settles the first fork: do rr rows exist for the days before the boundary under EITHER device id?
- No rows at all means R-R was never captured for those days, and the question becomes which source was writing heart rate for them and why it carried no R-R.
- Rows present means they are being captured but not reaching the analyzer, and the question moves to the read path and its device-id scoping.
Worth running that before anyone changes a threshold or a window.
Why it matters
Charge depends on HRV, so a user with more history than the R-R boundary sees roughly half their recovery scores silently absent, with the census line reporting it as "the pass produced nothing for" rather than as a missing input. That reads like a scoring fault when the inputs are the thing that is missing.
Filed from a maintainer device log rather than a user report. No biometric values are quoted here, only row counts and dates.
Observation
On a 5/MG install, a full 21-night re-score produces HRV and Charge for only the most recent eleven nights, while sleep and resting HR reach the whole window. The census line is stable across consecutive passes:
The eleven nights carrying HRV are contiguous and are the newest eleven. Every night older than that boundary has no HRV, and therefore no Charge.
What the evidence says
R-R is ABSENT before the boundary, not present-and-rejected. The clearest single line, from the same pass:
Abundant heart rate, zero R-R, on a day that sits before the boundary. On the eleven nights after it, R-R beat counts per night run from roughly 3,700 to 23,000, so where R-R exists there is plenty of it.
So HRV and Charge are following R-R availability. That part is not in question.
What is ruled out
Two plausible explanations were checked and do not hold:
HRVAnalyzeronly gates on the two OVER-COUNT verdicts;underCoveredandunmeasurablestay trusted (HRVAnalyzer.swift:422,:436). Most of the nights that DO produce HRV readunderCovered, so that verdict plainly is not what removes a night.pruneRrByTs, called fromWhoopRepositorywith a plausibility window ofMIN_PLAUSIBLE_UNIXthroughnow + futureMargin. It removes implausible timestamps, not old rows.What is NOT established
Why R-R stops at that boundary while heart rate continues through it. The install has two registered straps, a 5/MG active and a 4.0 last seen a fortnight ago, so a handover between sources is one candidate, and a capture path that only began storing R-R later is another. Nothing in the log distinguishes them, and this issue deliberately does not pick one.
What would discriminate
A single store query settles the first fork: do
rrrows exist for the days before the boundary under EITHER device id?Worth running that before anyone changes a threshold or a window.
Why it matters
Charge depends on HRV, so a user with more history than the R-R boundary sees roughly half their recovery scores silently absent, with the census line reporting it as "the pass produced nothing for" rather than as a missing input. That reads like a scoring fault when the inputs are the thing that is missing.
Filed from a maintainer device log rather than a user report. No biometric values are quoted here, only row counts and dates.