Repository navigation
Description
Finding
The public Oura decoders disagree on malformed text bytes:
decodeState/1: Swift uses strict UTF-8 decoding, retaining the state code but returningtext=nil; Kotlin usesString(bytes, UTF_8), substitutes U+FFFD, and returns replacement text.decodeDebugText/1: Swift returnsnilfor malformed UTF-8; Kotlin substitutes U+FFFD and emits a debug-text event.
Representative exact outputs:
- State payload
08 63 68 67 2e 20 64 65 74 65 63 74 65 64 ff(code 8 +chg. detected+FF): SwiftOuraState(code:8,text:nil)and no charger transition; KotlinOuraState(code=8,text="chg. detected\uFFFD"), which matches the charger-start predicate. - Debug payload
41 ff 42: Swiftnil/ no event; Kotlin"A\uFFFDB"/DebugTextEvent. - Other representative malformed sequences follow the same split:
c3 28=> Swiftnil, Kotlin"\uFFFD("; truncatede2 82=> Swiftnil, Kotlin"\uFFFD";f0 28 8c 28=> Swiftnil, Kotlin"\uFFFD(\uFFFD(".
Reachability and severity
These payloads are reachable from parsed raw BLE records: framing validates record shape/length but does not validate text encoding before OuraDriver dispatch. State events feed the live charger/wear tracker, so Kotlin can transiently change the charging badge where Swift does not. DebugText is emitted by the protocol driver but app ingest drops it; it remains visible to diagnostic consumers/CLI. No durable metric or scoring row is affected.
The raw protocol contract distinguishes valid from malformed data: State carries an optional trailing UTF-8 string; DebugText is specified as ASCII. Therefore conforming ring frames are unaffected. This is a P2 robustness/parity defect for corrupt or misaligned frames, not a valid-wire semantic disagreement.
Existing coverage and duplicate search
Current mirrored tests cover valid ASCII, short State, and valid/short binary decoder shapes, but no malformed State/DebugText bytes. Exhaustive title/body search in both bhelm/noop and ryanbr/noop found no existing malformed-UTF issue or PR. Related upstream PRs #88, #94, and ryanbr#143 are closed/unmerged debug logging/framing work; merged ryanbr#710 (FeatureStatus) and ryanbr#1109 (ASCII diagnostics docs) do not address this behavior.
Acceptance
- Add mirrored public decoder tests with invalid UTF-8 sequences and valid ASCII/UTF-8 controls.
- Choose and document one strict cross-platform malformed-input contract without changing valid text behavior.
- Make Swift and Kotlin State/DebugText outputs and driver emission agree.
- Verify charger/wear behavior cannot be triggered by replacement-decoded malformed bytes.
- Preserve public/product callsite behavior for conforming frames.
- Close only after the fix and regression tests are accepted upstream in
ryanbr/noop; a fork-only fix is not acceptance.
No production fix is part of the RED-test handoff.
Activity
bhelm commentedon Aug 21, 2026
Resolved upstream by ryanbr/noop#1478, merge commit eb4fe181, which is contained in current upstream
main. The merged fix includes regression coverage and passed upstream CI. Closing as completed.