Repository navigation
fix(cbio): read the raw payload word, not the empty packed field (G1) - #210
shroominic wants to merge 2 commits into
Conversation
The evidence artifact reported the packed ten-bit field of an `08` record as
the raw glucose, and on this firmware that field is `0x0000` in every record of
every captured session. The artifact therefore showed `rawGlucoseMinimum` and
`rawGlucoseMaximum` of zero while the app's live path rendered a moving value
from the same frames: two decoders, two fields, one reported as the other.
The same bytes carry both fields, so the disagreement was never an offset or a
width. It is field identity:
* `08` record offsets 0, 2, 4, 6 are `temperature, dump, payload, processed`.
* The payload word (offset 4, LE16) is what the app's live path renders and the
only field with content on this firmware; `parseCbioRawDataFrame` now exposes
it as `rawPayload`.
* The processed word (offset 6) keeps the recovered `0A` packed layout and is
exposed as `processed`, with the record's `rawWord` alongside the ten bits so
the span is visible.
* `CbioHistoryArchive` no longer repeats the record layout: it decodes through
`parseCbioRawDataFrame`, so the two paths cannot drift again.
The evidence artifact moves to `cbio.session-evidence/2` and reports both series
separately, each as `{count, minimum, maximum, nonZero}`. `nonZero` is what
separates "this field is empty" from "this measured zero", and a payload count
that disagrees with the raw record count fails validation. The authenticated
harness now decodes every notification through the app path and the frame path
in one session, emits the comparison, and fails the run when the payload field
is empty everywhere or the two paths disagree.
`tool/replay_gs1_decode.dart`, reachable as `make cbio-gs1-decode-comparison`,
turns a captured log into a durable per-record comparison without a radio.
|
CI position, for the record: |
|
Absorbed into #209 (head
The absorption keeps this line's unit-free accessor policy: the record exposes Carrying PR: #209. |
Fixes #207. Gate G1: the decoded-value question is a wrong field, not a
missing one.
The arithmetic
One real captured
08record (session 2026-09-18 13:02, first record after thebatch header) is
3a 01 6a 0b 40 00 00 00:3a 01rawTemperature6a 0brawDump40 00rawPayload/10→ 6.4 (scale unverified)00 00processed(b >> 6) | (b1 << 2)→ 0The evidence path read offset 6, the firmware's processed word in the
0Apacked layout. That word is zero in every record of every captured session, so
"every raw glucose field reads 0" was the decoder reading the empty field, not
a sensor that reports nothing.
Proof over one window
make cbio-gs1-decode-comparisonreplays one captured log through two decodersover the same bytes and writes
evidence/gs1-decode-comparison-20260918T102533.json.For index 1..1520 (25.3 h):
payload: 51..95, non-zero in 1520/1520 records; hourly median 5.3 at 05:00Zto 8.6 at 12:00Z — dips overnight, rises through the day.
processed: 0..0, non-zero in 0/1520 records.CbioHistoryArchive, what the installed app renders) vs frame path:compared1520,agreeing1520,missing0,disagreeing0./10is stillunverified: nothing here is a reference measurement, and novalue is presented as a physical glucose reading.
Changes
parseCbioRawDataFrameis the single owner of the eight-byte08layout;record fields are
rawTemperature,rawDump,rawPayload,processed.CbioHistoryArchivedecodes through it instead of duplicating offsets.cbio_decode_comparison.dart: per-record agreement between the app's livepath and the harness path, with a byte-arithmetic block.
cbio_log_replay.dart+tool/replay_gs1_decode.dart+make cbio-gs1-decode-comparisonto reproduce the comparison from a captured log.cbio.session-evidence/2:rawPayloadandprocessedGlucoseare separate{count, minimum, maximum, nonZero}blocks,and payload
nonZero == 0or a payload/record count disagreement failsvalidation.
run that reports an empty payload field fails loudly instead of passing.
Verification
dart format --set-exit-if-changedclean,flutter analyze --fatal-infoscleanon every workspace project,
cgm_cbiopackages green (81 tests) and the appsuite green at 472/473 — the one failure is the pre-existing
protocol_capture_status_testevent-loop-budget flake, already owned byfix/protocol-capture-status-flake(#203) and untouched here.Not in this PR
phone (repeated
aborted_no_targetscans), so the hardware form of this proofis queued behind sensor availability.
a known index; it stays an owner decision.