You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
P1: Per-day reuse cache serves HRV-less night scans permanently when R-R lands after HR; force refresh does not bypass it #29
Found by an independent review of the integrated fork state (2026-08-18). The code arrived with
the upstream range d4b7cc8..f2271cd (ryanbr#1005 / ryanbr#1395 / ryanbr#1396 per-day reuse cache); affects
upstream equally. Filed here first — forwarding upstream is a separate decision.
Problem
The per-day reuse cache in Strand/Data/IntelligenceEngine.swift:780 (same design in the
Kotlin twin, IntelligenceEngine.kt:617-643) keys a day's scan on owner + HR fingerprint
(count, maxTs) + skin anchor + config signature only. R-R / respiratory / SpO2 rows arriving
with no HR-row delta produce the identical key, so a stale cached DayScan is served.
Force-refresh does not bypass the day cache — force=true only bypasses the whole-pass
watermark gate.
Failure scenario
WHOOP history offload delivers channels independently, and offloaded HR rows duplicating live
rows are dropped by ON CONFLICT DO NOTHING. A banked night whose R-R packets land after its
HR is scored once with rmssd=nil and cached; every subsequent pass — including user-initiated
force refreshes — recomputes the same key and re-serves the HRV-less scan. Night HRV /
respiratory rate stay blank until an unrelated HR row, a config change, or an app restart
changes the key.
Possible directions
Fold the other stream fingerprints (R-R / resp / SpO2 counts + maxTs) into the day key, or let force=true bypass the day cache as users already expect it to.
changed the title [-]Per-day reuse cache serves HRV-less night scans permanently when R-R lands after HR; force refresh does not bypass it[/-][+]P1: Per-day reuse cache serves HRV-less night scans permanently when R-R lands after HR; force refresh does not bypass it[/+]on Aug 18, 2026
Largely fixed upstream by ryanbr#1907 (merged), which folds a device- and window-scoped
count/max witness for every other consumed stream — band sleep state included — into the day-cache
key, so a later, richer scan no longer gets served the earlier HRV-less result.
One part was deliberately not taken upstream: force=true still does not bypass the day
cache, on the grounds that doing so "would effectively disable normal reuse". The cache-key fix
addresses the correctness half of this report; the force-refresh expectation is a UX question. If
you still want an explicit user-triggered refresh that ignores the cache, please reopen (or file a
fresh issue) framed as a UX request, so it can be decided on its own merits. Closing for now.
Activity
[-]Per-day reuse cache serves HRV-less night scans permanently when R-R lands after HR; force refresh does not bypass it[/-][+]P1: Per-day reuse cache serves HRV-less night scans permanently when R-R lands after HR; force refresh does not bypass it[/+]bhelm commentedon Sep 7, 2026
Largely fixed upstream by ryanbr#1907 (merged), which folds a device- and window-scoped
count/max witness for every other consumed stream — band sleep state included — into the day-cache
key, so a later, richer scan no longer gets served the earlier HRV-less result.
One part was deliberately not taken upstream:
force=truestill does not bypass the daycache, on the grounds that doing so "would effectively disable normal reuse". The cache-key fix
addresses the correctness half of this report; the force-refresh expectation is a UX question. If
you still want an explicit user-triggered refresh that ignores the cache, please reopen (or file a
fresh issue) framed as a UX request, so it can be decided on its own merits. Closing for now.