Repository navigation
sync: skip the #689 backlog decode on the GET_DATA_RANGE PENDING ack - #1746
Merged
Merged
Conversation
…G ack GET_DATA_RANGE answers twice, a short PENDING(2) ack then the SUCCESS(1) payload, which Framing's result-code table already records. The ack carries no ring pointers, so the pagesBehind decode failed on it every time and logged a false 'offsets may have moved' once per sync on healthy hardware. A pure isPendingResponse predicate now gates the decode at both call sites; a SUCCESS frame that will not parse still logs loudly, and the unconditional ryanbr#451 raw-frame dump is untouched. Fixtures are the real PENDING/SUCCESS pair from one request.
ryanbr
added a commit
that referenced
this pull request
Aug 31, 2026
Build numbers only - 10.6.1 stays the unreleased line, and 395 clears the staging high-water so the APK installs over 394. Carries the sparse-bridge change: a night interrupted by one short trip out of bed is no longer ended at the interruption. Anyone whose nights were being truncated that way should see fuller totals; the honest caveat is that the absorbed minutes land in sleep rather than wake, because both stagers call wake off HR and the bridge only absorbs spans whose HR was already in the sleep band. Also the #1746 PENDING-ack change, so one "offsets may have moved" line per sync stops appearing on healthy 4.0 hardware, and the per-strap Last sync / Data write attribution from #1745.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
GET_DATA_RANGEanswers twice: a shortPENDING(2)acknowledgement, then the payload withSUCCESS(1). The #689 backlog decode runs on both. The ack carries no ring pointers, so it decodes to null and logs as a decode failure, once per sync, on healthy hardware:The split is structural rather than marginal. One session on my MG on 2026-08-28, 34 responses:
0x02PENDING0x01SUCCESSMy 2026-08-27 capture has the same shape (19 / 26), so it is not a one-off.
The tree already knows this happens.
Framing.kt's own result-code table says it:The decode site just had no way to act on it, because
pagesBehindonly sees the frame and a too-short frame is indistinguishable from a misaligned one.Why it is worth a change rather than ignoring
The message names a cause: "offsets may have moved", and points at the raw frame as the input to analyse. That was the right thing to write when the failure mode was unknown, and the comment above it explains why both branches log at all: until #818 the offsets really were wrong and this branch logged nothing, so a broken decode was invisible. That reasoning still holds for a SUCCESS frame that will not parse, and this PR leaves that case exactly as loud as it was.
What it stops is the ack. On current firmware that warning fires on a frame that is not a data-range frame by construction, so anyone following the hint goes looking for an alignment bug that is not there.
Shape of the change
A pure
DataRange.isPendingResponse(frame, cmdOff)on both platforms, reading the result byte atcmdOff + 2, used to gate the decode at the two call sites. A frame too short to hold a result byte returns false, so the existing too-short handling is untouched.The #451 raw-frame dump above it stays unconditional. It is cheap, and it is the input any future offset analysis would want.
Type of change
How it was tested
Not on the BLE path.
isPendingResponseandpagesBehindare pure and the change only gates a log line; no bytes move on the wire.Five new cases per platform in the files that already pin
pagesBehind, using the real PENDING/SUCCESS pair from one request on my MG (WS50_r00, fw50.39.1.0, 2026-08-28) rather than synthetic frames. One of them asserts the pair directly: the ack is exactly the framepagesBehindreturns null for, and the new predicate is what tells them apart../gradlew testFullDebugUnitTest --tests com.noop.ble.DataRangeScanTeston Windows 11, JDK 17:isPendingResponsestubbed toreturn false: 2 failed,the PENDING ack is exactly the frame pagesBehind cannot decodeandisPendingResponse true for the PENDING ack, bothjava.lang.AssertionError. The other 15, including all 12 pre-existing, still passedFull suite, since
DataRangeis shared surface:./gradlew testFullDebugUnitTest --rerun-tasks --no-build-cacheagainstmain@b2a4049— 4,903 tests across 600 classes, 0 failures, 0 errors (6 skipped, pre-existing).python Tools/doc_comment_lint.py:OK no NEW detached doc comments (1902 files, 24 baselined site(s) remaining).The gap I would rather state than have you find: the Swift half is not run locally, no Apple machine.
DataRangeTestslives inPackages/WhoopProtocol, whichswift-packages.ymlcovers, andBLEManager.swiftis compiled byapp-build.yml. The Swift change is line for line the twin of the Kotlin one that was run, but that is reasoning plus CI rather than a local run.Checklist
swift-packages.yml./gradlew testFullDebugUnitTest, full suite)StrandDesigntokens — n/a, no UIdocs/CONTRIBUTING.mdRelated issues
Refs #689 (the feature this guards), #818 (the offset fix whose comment explains why both branches log), #78 (where the PENDING-precedes-SUCCESS behaviour was confirmed).
Leaves #689 open on its own merits; that work landed in #694 and the decode itself is correct. This only stops it running on a frame that cannot carry its input. (Deliberately phrased with no closing verb adjacent to an issue number: GitHub matches those keywords literally and does not read negations, so a denial would have been actioned as a closing keyword on merge.)