8000 fix(cbio): judge the live surface by receipt time, not a sensor clock by shroominic · Pull Request #175 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content

fix(cbio): judge the live surface by receipt time, not a sensor clock - #175

Merged
shroominic merged 1 commit into
feature/cbio-gs1-app-livefrom
fix/cbio-live-freshness
Sep 18, 2026
Merged

shroominic merged 1 commit into
feature/cbio-gs1-app-livefrom
fix/cbio-live-freshness

Conversation

@shroominic
Copy link
Copy Markdown
Owner

Closes #174.

The defect

Timestamps locate two different things in this codebase, and they were being
collapsed into one:

Question Honest source CBio/GS1
When did the sensor sample this? CgmReading.recordedAt does not exist (minute counter, #146)
When did this phone receive it? CgmHistorySyncState.lastSyncAt exists and advances per ingest

buildLiveActivityPayload asked only the first question. For cbio that answer is
permanently null, so isStale was permanently true — and the raw/provisional
branch above it hard-coded isStale: true as well, so every cbio surface took a
constant path regardless of link health.

RED

cd openhealth && flutter test test/cbio_live_freshness_test.dart

On 17db5b4 (the base this branch forks):

exit 1
Expected: false
  Actual: <true>
  a batch accepted 30 s ago is a fresh surface
test/cbio_live_freshness_test.dart 60:5
00:00 +3 -1: Some tests failed.

A batch ingested 30 s ago and a session that ingested nothing were
indistinguishable.

GREEN

Same command on this branch:

exit 0
00:00 +17: All tests passed!

The fix

  • liveSurfaceFreshnessAt({snapshot, reading, now}) and liveSurfaceIsStale(...)
    in session_presentation.dart become the single place that answers "how old is
    this surface". A timestamped driver is still judged by its reading time; cbio
    is judged by historySync.lastSyncAt. No recordedAt is fabricated, and the
    receipt time is clamped so a future value cannot read as fresh.
  • live_activity_payload.dart uses that clock for both the provisional branch
    and the reading branch, replacing isStale: true and the inline 10-minute
    comparison. The window is unchanged at 10 minutes, now
    liveSurfaceStaleAfter.
  • recordedAtIso8601 is untouched: it still carries only a sensor timestamp, so
    cbio still publishes null there. Receipt freshness never crosses the platform
    boundary as a sensor time.

Tests added

  • cbio freshness is the receipt clock, not a missing sensor clock — 30 s and
    9 min are fresh, 25 min is stale.
  • a cbio session that never ingested anything is stale.
  • receipt freshness never becomes a published sensor timestamp — the flag can
    flip while recordedAtIso8601, lastReadingText and valueText stay redacted.
  • a timestamped driver still judges freshness from its own reading — a stale
    lastSyncAt does not override a fresh sensor reading for another driver.

Verification

  • cd openhealth && flutter test — exit 0, +484: All tests passed!
  • flutter test test/cbio_live_freshness_test.dart test/live_activity_payload_test.dart — exit 0, +17
  • flutter analyze — No issues found!
  • dart format --output=none --set-exit-if-changed lib/src/session_presentation.dart lib/src/live_activity_payload.dart test/cbio_live_freshness_test.dart — clean

Scope note

No lock-screen behaviour changes here. cbio still does not publish to the Live
Activity: shouldPublishLiveActivity rejects a raw or provisional reading, and
every cbio reading is both. This lane closes the latent staleness contract so
that whichever lane makes cbio publishable does not inherit a flag that is always
true. shouldPublishLiveActivity is deliberately not touched.

Fixture values are synthetic (AA:BB:CC:DD:EE:FF, CGMTEST-style records); no
credential material and no real device address appear in this change.

@shroominic
Copy link
Copy Markdown
Owner Author

Rebase reminder (comment only, no edits to this branch).

The lane head is now 05785b0 (#192 merged). This PR is still based on 17db5b4; the merge-base is fine, but expect a conflict in
packages/cgm_cbio/lib/src/cbio_glucose_session.dart around credential resolution. Resolve it by keeping the injected path:

  • the session resolves CbioCredentials once per session into _resolved, before the transport is touched, and every mask/unmask/builder call receives _streamKey/the material explicitly;
  • CbioCompiledAuthMaterialProvider and the compiled constants are gone; a build without material fails closed with cbio.auth.material;
  • the lane also reads _clockReferenceEpochSeconds (set in _writeClock) when it derives the record-index anchor, so keep that field and the _historyReadingsFor(anchor) / metadata spread when you resolve.

Rebase onto feature/cbio-gs1-app-live before review so CI runs against the lane head.

@shroominic
shroominic force-pushed the fix/cbio-live-freshness branch from 96a0569 to fe7cc0c Compare September 18, 2026 09:27
@shroominic
Copy link
Copy Markdown
Owner Author

Rebased onto the lane tip 9de5544 (05785b0 + #193 + #197) and force-pushed; the PR is one commit again, now fe7cc0c.

One amendment, because the lane moved under this PR: #197 gives a GS1 record a real timestamp whenever the session holds a clock anchor, so CgmReading.recordedAt is no longer "null by design" for this driver. liveSurfaceFreshnessAt now judges an anchored record by that time and only falls back to the receipt time (historySync.lastSyncAt) for a record the anchor does not cover. The receipt time is still staleness-only and is never published as a sensor time; the doc comment says so.

Verification on the rebased head: openhealth flutter test 492 passed, flutter analyze clean, dart format clean on both touched files.

@shroominic
shroominic merged commit c1aaa39 into feature/cbio-gs1-app-live Sep 18, 2026
5 of 10 checks passed
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

0