fix(health,stats,wall): decode HC sleep stages, surface HR + active calories, period-aware heatmap, muscle-balance toggle, un-pin wall shell#35
Merged
Conversation
…active calories, period-aware heatmap, muscle-balance toggle, un-pin wall shell
Health data fixes (root-caused against the live store, which only ingests
steps/heart_rate/sleep/active_calories — no resting_heart_rate, no exercise):
- Deep sleep: Health Connect serialises sleep stages as numeric Stage codes
("4"=LIGHT, "5"=DEEP, "6"=REM, 1/3/7=awake), but addSleepStages only matched
string labels, so every stage bucket stayed 0. Decode both numeric codes and
labels.
- Heart rate: no resting_heart_rate is ingested, so the Resting HR tile/line
were always blank. Synthesize a resting proxy from each day's minimum
heart_rate when no real resting record exists; a real record still wins.
- Cardio / active calories: no exercise-session records exist (only daily
active_calories). Surface active_calories through the parser + aggregation as
a new ActiveCalories series, with a dashboard tile and a daily chart on the
Cardio tab. True per-session cardio still needs the HC bridge to export
ExerciseSession records (documented in the empty-state note).
Stats:
- Muscle Balance: add a Sets/Volume toggle (defaults to Sets — number of sets).
Radar chart, breakdown panel, titles and tooltips all follow the selection.
- Consistency heatmap: was hard-pinned to a fixed 53-week year. Scale the grid
to the selected period (week/month/year/all time); days before the period
start render muted.
Wall (AI page):
- The standard navbar has shipped since v0.0.88, but wall-sw.js served the
/wall/ HTML shell cache-first and never bumped CACHE_NAME, so a long-lived
kiosk kept painting the pre-navbar shell forever. Serve the shell
network-first (cache fallback for offline boot) and bump to pump-wall-v4 to
evict the stale cache.
Adds healthstats_test.go covering stage decode, the resting-HR proxy
(including real-record precedence), and active-calorie summing. No schema
change (ActiveCalories is aggregation-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses four reported issues, root-caused against the live store (which currently ingests only
steps,heart_rate,sleep,active_calories— noresting_heart_rate, noexercise).1. Health — deep sleep, heart rate, cardio
Stagecodes ("4"=LIGHT,"5"=DEEP,"6"=REM,1/3/7=awake).addSleepStagesonly matched string labels, so Deep/Light/REM stayed0. Now decodes both numeric codes and labels.resting_heart_rateis ingested, so the Resting HR tile/line were always blank. Synthesize a resting proxy from each day's minimumheart_ratewhen no real resting record exists; a real record still wins.active_calories). Surfacedactive_caloriesthrough parser → aggregation as a newActiveCaloriesseries, with a dashboard tile and a daily chart on the Cardio tab. True per-session cardio still needs the HC bridge to exportExerciseSessionrecords (see Follow-up).2. Muscle Balance — Sets/Volume toggle
New toggle on the tab, defaulting to Sets (number of sets). Radar chart, breakdown panel, titles, and tooltips all follow the selection.
3. Consistency heatmap — period-aware
Was hard-pinned to a fixed 53-week year. Now scales to the selected period (week/month/year/all time); leading days before the period start render muted.
4. AI page header (wall)
The standard navbar has shipped since
v0.0.88, butwall-sw.jsserved the/wall/HTML shell cache-first and never bumpedCACHE_NAME, so a long-lived kiosk kept painting the pre-navbar shell forever. Now serves the shell network-first (cache fallback for offline boot) and bumps topump-wall-v4to evict the stale cache.Tests
internal/web/healthstats_test.gocovers numeric stage decode, the resting-HR proxy (incl. real-record precedence), and active-calorie summing.go test ./...green.Schema
No schema change —
ActiveCaloriesis aggregation-only over existinghealth_recordrows. No migration / no schema version bump.Follow-up (bridge, out of this repo)
Per-session cardio needs the Android Health Connect → Webhook bridge to export
ExerciseSessionrecords under anexercisekey. Until then the Cardio tab shows active calories + an explanatory empty state.🤖 Generated with Claude Code