Skip to content

fix: sync-freshness banner, burst validation, and UI/UX polish - #39

Merged
abdulsaheel merged 1 commit into
feat/edge-revampfrom
fix/sync-freshness-and-ux-polish
Jul 5, 2026
Merged

fix: sync-freshness banner, burst validation, and UI/UX polish#39
abdulsaheel merged 1 commit into
feat/edge-revampfrom
fix/sync-freshness-and-ux-polish

Conversation

@abdulsaheel

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes a persistent "last data is more than an hour behind" regression: the freshness signal (AppState.lastRecordAt) only ever read decoded_onehz's max timestamp (misses R10-lite historical records, which land in samples instead) and only ever refreshed once at app boot. Now reads the rec_ts_hw sync cursor (the same frontier RecordGate/backfill policies already trust) and refreshes on every successful persist via the existing onDataStored hook — covers foreground bursts, background/headless drains, and live-triggered stores uniformly, with no race window against the async commit.
  • Makes HISTORY_END packet-count validation advisory instead of a hard gate. A mismatch used to discard the entire buffered chunk and re-request the same block forever — since every buffered record already passed frame-level CRC32 and the RecordGate plausibility check before reaching this point, a count mismatch (whose exact semantics on real hardware traffic aren't fully confirmed) was never real evidence of bad data. Added burstPacketCountMatches as a small pure/testable function plus regression tests covering the real failure shape observed in the field.
  • Profile screen: real back button (migrated to AppScaffold), dropped ~6 decorative/meaningless icons from personal-info rows (Sex→heart, Height→activity, etc.) and the "Storage" section (static notice, no content).
  • Today screen: fixed a list-reflow glitch where the freshness banner toggling on/off shifted every sibling's position in an unkeyed list, causing Flutter to remount and replay every entrance animation at once — added stable Keys. Weekly steps card relabeled with context ("Steps goal (week)").
  • MetricRow (shared "label left, value right" row powering Sleep/Heart/Respiratory/Skin-temp trend lists): dropped the per-row leading icon (repetitive across a whole screen of rows) and fixed numeric clipping — value now shrinks via FittedBox instead of silently ellipsizing.
  • Journey + Timeline screens merged into one multi-vital "lookback" (heart rate, HRV, resp, skin temp on one scrubbable chart, values hidden until touched).
  • Workout-type icons (cardio/run/cycling/walk/swim/yoga/hiit/workout-other, from the newly-merged icons package) wired through a new shared lib/ui/workouts/workout_types.dart seam, replacing three divergent per-screen icon tables.
  • Regenerated pubspec.lock against real git-ref dependencies (was accidentally committed with local path overrides baked in — CI has never actually tested this state since the regression landed).

Test plan

  • flutter analyze clean (only the pre-existing glassBoxReadiness deprecation baseline)
  • flutter test --concurrency=1 → 376/376 pass, including 4 new regression tests for the burst-validation fix
  • Verified against real main-branch git-ref dependencies (not local path overrides) — matches what CI resolves
  • On-device verification of the freshness fix pending (root-caused via a temporary in-app diagnostics screen, since removed, using real device sync-ledger dumps)

Fixes a persistent "last data > 1hr behind" regression: the freshness
signal only ever read decoded_onehz's max timestamp (misses R10-lite
records) and only ever refreshed at app boot; now reads the rec_ts_hw
sync cursor and refreshes on every successful persist (onDataStored),
foreground or background. Also makes HISTORY_END packet-count
validation advisory instead of a hard gate — a mismatch used to
discard an entire buffered chunk and re-request it forever, since the
band's reported count and our tally don't have fully-confirmed
semantics; every buffered record already passed CRC32 + plausibility
checks, so a count mismatch was never real evidence of bad data.

UI/UX: Profile screen gets a working back button (AppScaffold) and
drops decorative icons + the Storage section; Today screen fixes a
list-reflow glitch behind the freshness banner (stable keys) and adds
context to the weekly steps card; MetricRow drops its per-row icon
(icon spam) and fixes numeric clipping (FittedBox, never ellipsis);
Journey/Timeline merge into one multi-vital lookback; workout-type
icons wired through a new shared workout_types.dart seam.
@abdulsaheel
abdulsaheel merged commit bc8780d into feat/edge-revamp Jul 5, 2026
@abdulsaheel
abdulsaheel deleted the fix/sync-freshness-and-ux-polish branch July 12, 2026 05:53
abdulsaheel added a commit that referenced this pull request Aug 7, 2026
Five conflicted files, all reconciled to keep BOTH sides' intent rather than
picking a winner.

kAlgoVersion / changelog. Both branches wrote a v55 and a v56 with different
content: main's are the nap + strain work (#204), this branch's are the step
and movement-minute rebuild. Main's stay as history; this branch's three
entries are renumbered 55/56/57 -> 57/58/59 and the constant becomes 59.
Strict monotonicity is load-bearing, not cosmetic -- the derive gate matches
algo_version EXACTLY while the read seam serves MAX(algo_version), so a version
that goes backwards writes rows nobody reads and re-derives forever.

Note: git had silently DROPPED this branch's v57 entry (the absent-steps
tier/labelling follow-up) when resolving the overlapping tail toward main. It
is restored here as v59; the code it documents was never lost, only its
changelog.

Analytics pin. Neither side was right: HEAD had #35's PR-BRANCH head (38a8636,
never on main) and main had the older #38 merge (c3a30be). Now 1fb34dce =
analytics main @ #35 merge, which carries all three hops this release needs --
#38 (nap detector), #39 (the awake-HR baseline P0 that shipped inside #38), and
#35 (the sibling half of THIS branch). Verified against the SHA: absent
`dailyStepEstimate`, present `dailyActiveMinutes`.

Health export delete list. The two intents compose rather than conflict, so
`_rewriteTypes` is now `healthDeleteTypes(isApplePlatform:)` MINUS STEPS:
  * main's helper drops the sleep types and HEART_RATE on Android, where the
    native SleepSessionRecord writer and the minute-HR batch own their cleanup;
  * STEPS comes off on top, because nothing writes steps any more -- deleting a
    type we never write would run on every re-export of the not-yet-finalized
    tail forever, and a false `delete()` flips `success`, so it could stall a
    day's export cursor permanently. The historical fabricated samples are
    handled once by `_purgeLegacyStepsIfNeeded`, outside success accounting.
Also kept main's Android sleep-session write alongside this branch's one-shot
purge, and kept this branch's removal of the steps EXPORT (writing our derived
copy back would double-count against the phone's own HealthKit pedometer rows,
which we now read).

healthSyncNow. Composed: the export runs through main's single-flight guard,
and the phone-steps sync stays gated on `phoneStepsEnabled`.

flutter analyze clean; full suite 1324 passing, 0 failing.
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