Skip to content

Today: HRV and Resting HR cards read "No data" while the tile above shows a value #1842

Closed

Description

@ryanbr

Reported

On one screen, the Key Metrics tiles show HRV 35 ms and REST HR 58 bpm, while the cards below in
YOUR CARDS read No data for the same two metrics. Tapping either card opens a detail screen with a
full graph.

Cause: two carry policies on one screen

The tile carries the last scored day; the card does not.

// Key Metrics tile
val v = d?.restingHr ?: carriedDay?.restingHr

// Dashboard card, same screen
withUnit((day?.restingHr ?: vitalsDay?.restingHr)?.toString() ?: NO_DATA)

carriedDay (lastScoredRecoveryDay) is passed into the card resolver, which even computes
val vd = carriedDay ?: day — the HRV and Resting HR branches simply do not use it. Before today's row is
scored, the tile shows last night's value and the card, falling back to vitalsDay instead, has nothing.
The detail screen reads history directly, which is why tapping through shows a graph.

The Blood Oxygen branch a few lines down already states this precedence as deliberate:

Candidate LAST, after the carries — the exact precedence the dashboard Blood Oxygen card uses two
sections down this same screen

So the intent was established; HRV and Resting HR were missed. Same family as the Fitness Age divergence
(#140 / #139 / #134), where the engine held a value the card gated away.

Also: Skin Temp shows "-0.0 Δ°C"

Different bug, same card list. numberString formats deviations with %+, which prints a sign
unconditionally — so any deviation in (-0.05, 0) renders as -0.0. A deviation that rounds to zero
means "no change from your baseline", and "-0.0" reads as a fault instead.

Present on both platforms: SkinTempDisplay is a twin.

Fix

  • Put carriedDay in the HRV and Resting HR card chains, matching the tile.
  • Drop the sign when a deviation rounds to zero, keeping it for a real -0.1. Both platforms, with twin
    tests.

Activity

  1. ryanbr commented on Sep 4, 2026

    @ryanbr
    OwnerAuthor

    Done. PR #1843 landed the per-field carries and they are wired on main, not merely defined:

    • lastHrvRow / lastRestingHrRow in LogicalDay.kt
    • both called from TodayScreen.kt at the card sites (guarded to selectedDayOffset == 0)
    • unit-tested in LastHrvRowTest.kt
    • Swift twins lastHrvDay / lastRestingHrDay in MetricsCache.swift, so the two platforms resolve the same way

    The cause was an OR-predicate: the shared "last vitals row" carried a day forward if it had ANY vital, so a row with a value for one metric and nothing for the other made the tile and the card disagree. Resolving each field independently removes that.

    Closing on a stronger basis than "it merged": this is deterministic data resolution with no firmware or timing dependency, so the tests and the wiring are the whole story. If a card still reads "No data" while the tile above shows a value, that is a different bug and worth a fresh report with the day in question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions