Repository navigation
Gate gen5 GET_DATA_RANGE decode on outer command status - #1
Merged
Merged
Conversation
A failed gen5 reply can keep a plausible body from an earlier successful read, including revision byte 1. The decoder trusted that body and emitted range_oldest/range_newest, trim_ts/current_read_ts and pages_behind (with raw_old_page/read_page) as current values. A stale read cursor makes the band's drain position look stalled or rewound in the band_backlog ledger. Port the behaviour of OpenStrap/protocol 11919c3 (OpenStrap#55) to the fork's decoder, covering the read-cursor fields this fork adds. Gen4 stays ungated because its status byte semantics are unconfirmed, matching the existing GET_CLOCK policy.
Owner
Author
|
Hosted CI on the exact head 4bab607: https://github.com/matsvarn/openband5-protocol/actions/runs/36137606205 passed on Dart 3.5.0 and stable. It was run via workflow_dispatch because the fork recorded no push or pull_request runs; see the note in the handoff. |
Owner
Author
|
Independent review (Grok 4.7, high reasoning): CLEAN, no findings. It reproduced the base failure ( |
matsvarn
marked this pull request as ready for review
September 25, 2026 13:10
matsvarn
added a commit
to matsvarn/openband5-app
that referenced
this pull request
Sep 25, 2026
Repin openstrap_protocol to 2c1bf3c5 (matsvarn/openband5-protocol#1 merge). Gen5 GET_DATA_RANGE now decodes only on outer status 1, so a failed reply's stale body can no longer reach band_backlog as a current range or read cursor during an interrupted sync. Gen4 is unchanged. No kAlgoVersion bump: the only protocol lib/ change is that command-reply gate. No record decode or metric input moves. kProtocolPin, pubspec.yaml and pubspec.lock agree.
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.
Why
A failed Gen5
GET_DATA_RANGEreply can keep a plausible body from an earlier successful read, including revision byte 1. The current decoder checks only the revision byte. It therefore reports stalerange_oldest/range_newest,trim_ts/current_read_ts, andpages_behind(includingraw_old_page/read_page) as current values.The app's single
GET_DATA_RANGEconsumer (ble_engine.dart) does not check the outer status before it adopts these fields as the backlog target and writes theband_backlogledger row. A stale read cursor makes the band's drain position look stalled or rewound. That would corrupt the cursor-movement evidence the interrupted-sync trial relies on.Change
1. Together they cover every field this opcode emits.GET_CLOCKpolicy.Verification
status=0 key=range_oldestwas emitteddart analyze --fatal-infos(Dart 3.11.4)dart test(full suite)dart format --set-exit-if-changedNo hardware verification yet. The synthetic replies cover the decoder only. The physical interrupted-sync trial runs after the app pins this commit.
Other upstream-only fixes reviewed (not included)
I checked these against the commands the current app sends. The app frames every Gen5 command through
buildCommand(..., session.band), and none of them is needed for the capture path:parseGen5HistoricalbeforedecodeRecord/realtimeRr. This is reachable on Gen5 only if a record isn't identified as Gen5, so it is a follow-up.