Skip to content

fix(oura): stop HR-only sleep from double-counting a ring-bounded night - #3

Merged
llmassisted merged 1 commit into
mainfrom
fix/oura-sleep-double-count
Sep 27, 2026
Merged

llmassisted merged 1 commit into
mainfrom
fix/oura-sleep-double-count

Conversation

@llmassisted

Copy link
Copy Markdown
Owner

What this PR does

Stops Oura nights from being double-counted and a real night from being deleted.

On an Oura wake-day the ring's native-resting sleep window and the upstream HR-only fallback both produced sessions. Field log (build 536, 2026-09-26):

  • hr-only spine day=2026-09-24 … longestMin=934 built a block from Wed 10:44 to Thu 02:18, across a whole daytime, beside the ring's resting-window night.
  • sleep day=2026-09-24 totalSleepMin=1379 … matched=2: both were scored (~23 h).
  • Dedup(#1284): dropped [1790147107 -> 1790177240] min=502 … kept [1790174640 -> 1790230739] min=934, repeated on every pass: the Publish the command result code on Apple, as Android already does (#894) ryanbr/noop#899 overlap heal (≥ 30 min overlap, longest wins) deleted the real Tue→Wed night (03:05–11:27) because the bogus block overlapped it by 43 min.

Fix: in IntelligenceEngine.resolveSleepEvidence, the HR-only spine is a fallback only. For an Oura owner it is skipped when the ring supplies at least one native-resting sleep window for that wake day (new gate reason oura-resting-sleep). Manual edits alone, days with no ring evidence, and WHOOP keep the existing behaviour. Once nothing re-banks the stale block, the heal's fresh-witness rule drops it in favour of the re-scored night.

Android-only (the ring's resting-window hints exist only in this fork). versionCode 539.

Type of change

  • Bug fix

How it was tested

  • New OuraSleepEvidencePrecedenceTest (5): ring resting sleep suppresses the HR-only fallback (failed before the fix, passes after); the fallback still runs with no ring evidence, with only a manual edit, and on WHOOP; the field-timestamp heal keeps the night and drops the stale block.
  • Existing OuraSleepRestorationTest, SleepStagerHrOnly* and IntelligenceEngineJacocoBudgetTest pass.
  • Full local Android suite: 6,473 / 6,475 pass; the two failures are the known macOS/JBR RecoveryDriversTest float assertions.
  • Doc-comment lint and parity ratchet (errors=0) pass.
  • Hardware: not yet validated overnight; the next morning log should show hr-only gate … reason=oura-resting-sleep on ring nights and no 900+ min sessions.

Related issues

Refs #2

🤖 Generated with Claude Code

On an Oura wake-day the ring's native-resting sleep window and the upstream
HR-only fallback both produced sessions. In the field (2026-09-24) the HR-only
spine built a 934-min block across a whole daytime beside the ring's night, the
day scored 1,379 min (matched=2), and the ryanbr#899 overlap heal (>= 30 min overlap,
longest wins) then deleted the adjacent real 09-23 night on every pass.

The HR-only spine is now a fallback only: for an Oura owner it is skipped when
the ring supplies at least one native-resting sleep window for that wake day
(gate reason `oura-resting-sleep`). Manual edits alone, days without ring
evidence, and WHOOP keep the existing behaviour. With nothing re-banking the
stale block, the heal's fresh-witness rule drops it in favour of the re-scored
night (pinned with the field timestamps).

versionCode 539.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@llmassisted
llmassisted merged commit fe7ae9a into main Sep 27, 2026
3 checks passed
@github-actions
github-actions Bot deleted the fix/oura-sleep-double-count branch October 5, 2026 11:34
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