Files
amethyst/commons
Claude 1e4c076cf0 fix(fitness): stop the partial Health Connect pass from downgrading what is already on screen
Audit of the progressive-read commit. All three findings share one root cause:
a DetectedWorkout with no metrics is now visible to the UI, which was
impossible before — previously nothing left HealthConnectManager until every
per-session aggregate had come back.

1. Sharing during the pending window published a lossy event.
   `toNewWorkoutRoute` collapses the null metrics to `0` (the composer route
   carries primitives), and `NewWorkoutViewModel.applyPrefill` reads `0` as
   absent and clears the field. So a tap on Share before stage 2 landed opened
   the composer with duration only, and publishing it marked the workout as
   shared — the real numbers never got their turn. The Share button now waits
   for the metrics. Everything still offering one in that window is a Health
   Connect workout, since a published one is already published, so the gate is
   blanket rather than per-workout.

2. Every resume stripped an already-complete dashboard.
   `refresh` runs on `LifecycleResumeEffect`, and the collect body overwrote
   the contribution with stage 1's skeletons — removing the distance, calories,
   heart-rate, steps and elevation cells and most of Best Efforts for as long
   as stage 2 took. The comment claiming a resume "re-reads in place" was only
   true for the few ms before stage 1 arrived. A partial stage now reaches the
   screen only when there is nothing better on it already; otherwise the
   previous numbers stay up, unannotated, and stage 2 swaps them atomically.
   The rule is a pure fold on the new top-level `HealthConnectContribution`,
   so it is testable without a device.

3. Stage 1 evicted published workouts that still had their metrics.
   `TrainingLog.merge` prefers the Health Connect copy because it "carries the
   metrics the published event may have dropped" — a rationale that inverts
   when that copy has not read its metrics yet. A user with published workouts
   watched their distance and calorie totals fall to zero and climb back. A
   copy carrying no metric at all now yields to a matching one that does. This
   also fixes the non-progressive case where aggregation legitimately returns
   nothing and the published event holds the only numbers.

The dedupe rewrite preserves the existing shape exactly: the rule changes
which copy survives a tie, never how many. One session matching several
published events still collapses to one entry.

Tests: 5 more in PartialMetricsReportTest (published totals held through the
partial pass, the loaded copy still winning, both-metric-less and
several-matches edges), and HealthConnectContributionTest covering the fold —
first load shows stage 1, a resume does not, an empty completed read still
clears the gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VgfgpDWnu635p2qc2K2yMX
2026-09-19 01:03:41 +00:00
..