Skip to content

diag: capture the 4.0 GET_BATTERY_LEVEL frame in the strap log (#900) - #1400

Merged
ryanbr merged 1 commit into
mainfrom
diag-battery-provenance-900
Aug 17, 2026
Merged

diag: capture the 4.0 GET_BATTERY_LEVEL frame in the strap log (#900)#1400
ryanbr merged 1 commit into
mainfrom
diag-battery-provenance-900

Conversation

@ryanbr

@ryanbr ryanbr commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Why

#900 has been blocked for three weeks on one thing: a real WHOOP 4.0 GET_BATTERY_LEVEL COMMAND_RESPONSE of known provenance, to settle whether its zeroed [seq][result] prefix means prefix-absence (the value lives at a fixed offset, the two leading bytes are padding) or a genuine failure. Between zero and one confirmed-real captures exist in-tree; the rest are declared-synthetic fixtures.

The blocker is that a pasted strap log can't currently resolve it: today the log carries only the decoder's verdict (FAILURE(0) … carried a value, the #923 annotation) — which is the very interpretation in dispute — not the raw bytes.

What

On any non-SUCCESS COMMAND_RESPONSE, dump the full raw frame (0xAA..crc32) to the strap log, once per command per connection:

This doesn't decide what the result byte means — it makes a normal strap-log sync self-capture the artefact so the issue can finally be resolved (confirm prefix-absence → per-reply-shape fix; or refute → close #900, relabel fixtures).

Parity

Swift FrameRouter had no twin of the #923 annotation at all — a standing parity gap. It gains both the annotation and the dump, byte-identical to the Kotlin WhoopBleClient text (enumName/CommandNames.label produce the same NAME(raw) label; same result table). Both platforms: log-only, no behaviour/decode change.

Verification

Closes nothing on its own; unblocks #900.

#900 is blocked on one thing: a real WHOOP 4.0 GET_BATTERY_LEVEL COMMAND_RESPONSE
of known provenance, to settle whether its zeroed [seq][result] prefix is
prefix-absence or a genuine failure. Today the strap log only carries the decoder's
verdict (the #923 annotation) — which is the interpretation in dispute — not the raw
bytes, so a pasted log can't resolve it.

Make the log self-capture the artefact: on any non-SUCCESS COMMAND_RESPONSE, dump the
FULL raw frame (0xAA..crc32) once per command per connection. Full frame, not the
post-prefix payload helper, because the disputed bytes ARE the prefix; contiguous-hex
format matches the GET_DATA_RANGE dump (#451) and the aa0f00.. form the #900 fixtures
are quoted in. Rate-limited (a 4.0 hits this branch every battery poll) and re-armed
each connection. The same line also captures a genuinely-failing command in the same
session — the comparison frame the issue asks for. Default log: a battery reply carries
no serial/key (unlike the TestCentre-gated GET_HELLO dump #1303).

Parity: Swift FrameRouter had no twin of the #923 annotation at all — it gains both the
annotation and the dump, byte-identical to the Kotlin WhoopBleClient text. Log-only, no
behaviour/decode change. StrandTests pins the annotation, the once-per-session dump, and
the per-connection re-arm; Android compiled locally.
@ryanbr
ryanbr merged commit 9da37e3 into main Aug 17, 2026
5 checks passed
@ryanbr
ryanbr deleted the diag-battery-provenance-900 branch August 17, 2026 06:03
ryanbr added a commit that referenced this pull request Aug 17, 2026
… build 221)

Stay on 10.1.1 (marketing/versionName unchanged); bump only the build numbers so the
fresh testing build installs over the previous one (versionCode 339 > 338 staging
high-water, iOS build 221 > 220). Bundles this session's merged fixes: #1400 (#900
battery-frame capture), #1401 (baseline-relative daytime stress), #1402 (analyzeRecent
cache backfill fix), #1403 (computed -noop spine + day-span diag), #1404 (VO2max trend).
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.

Need one real WHOOP 4.0 GET_BATTERY_LEVEL capture: every in-tree fixture may be synthetic

1 participant