docs(oura): reconcile protocol spec with decode fixes, wear/charge signal, observed tags#678
Conversation
ryanbr
left a comment
There was a problem hiding this comment.
Reviewed against the code — this is solid, valuable docs work, and a clean single-commit supersede of the entangled #488. Confirmed docs-only, no content lost: both - lines are in-place updates (the roadmap Oura row and one §6.4 clause); everything else is additive. Spot-checked the tags it asserts against the decoders — temp 0x46, HRV 0x5D, activityInfo 0x50, IBI 0x80/0x60, the 0x50 two-slope MET formula, OuraDecoders.decodeActivityInfo → OuraEvent.activityInfo, and the sibling-anchoring claim — all match.
Three small accuracy nits, none blocking, but worth fixing since a spec doc's value is precision.
1. 0x12/0x13 mislabeled as the UTC anchor (contradicts §6.11)
The new roadmap line reads "SyncTime (0x12/0x13) UTC anchoring." But 0x12/0x13 is the app→ring time-SET command/response (Commands.syncTime, 12 09 …). NOOP's actual UTC anchor is the ring-emitted 0x42 time-sync event (+ 0x85 RTC fallback) — which this same document already states in §6.11 ("the primary UTC anchor") and §5.5, and which the driver keys on (OuraDriver .timeSync → unixSeconds(forRingTimestamp:)). As written the roadmap contradicts its own §6.11.
Suggest: SyncTime (0x12/0x13) handshake + 0x42 time-sync event as the UTC anchor.
2. §6.4 caveat is past-tense for an unmerged fix
The §6.4 text — "…the datastore's rrInterval was not until .ibi was anchored like its sibling banked streams" — describes #677 as already done, but #677 isn't merged yet (I've left a change request on it). Merging these docs first documents behaviour main doesn't have.
Cleanest is to land #677 first, then this — or soften to "pairs with #677 (pending)." (The PR body's phrasing is already honest about the pairing.)
3. Optional — §9 appendix RE-usability
The observed-tags table gives one header-stripped example payload per tag. We got bitten once (noop #123) by a truncated prefix that hid tail fields, so for the higher-rate tags (0x61, 0x72, 0x6d) the full type/len/rt record + 2–3 samples each would be more useful than a single stripped example. You already do exactly this for 0x76 and note the full hex is in the capture — just extend that to the frequent tags. Enhancement, not a blocker.
Happy to sequence this right after #677 lands so both Oura changes go in together — and I can push the nit-1/nit-2 wording fixes myself if you'd prefer.
…tense, §9 full records) - Roadmap: correct "SyncTime (0x12/0x13) UTC anchoring" — 0x12/0x13 is the app→ring time-SET handshake; the actual UTC anchor is the ring-emitted 0x42 time-sync event (§6.11). Fixes the self-contradiction the reviewer flagged. - §6.4: reword the rrInterval caveat to present tense + "PR ryanbr#677 (pending merge)"; the docs no longer describe an unmerged fix as already landed on main. - §9: reproduce full type/len/rt records + 2–3 samples for the high-rate tags 0x61/0x72/0x6d (like 0x76) so future RE sees framing and cross-sample variation directly; note 0x61's payload length is not fixed (len byte moves 0x10/0x11/0x12). Docs-only. Full records extracted verbatim from the NOOP Gen-3 raw capture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016DN6cU2ERWZUS9tWYCjPvi
|
Thanks — all three addressed in dc4f7ca (docs-only):
Left it as a follow-up commit for review visibility; glad to squash into the single supersede commit on merge. Fine to land right after #677. |
|
Linked to #710 What I added to docs/OURA_PROTOCOL.md
|
…e signal, and observed tags Brings docs/OURA_PROTOCOL.md and docs/DEVICE_SUPPORT_ROADMAP.md up to date with the Oura findings, as a clean single commit off main (supersedes the entangled draft ryanbr#488): - §6.4: reconcile the 0x60/0x80 IBI decode with the corrected byte layout + `quality==1` gate (matches open_oura's parse_api_green_ibi_quality_event), and the IBI->HR research finding (HR from the 0x80/0x60 records, not 0x50; ~94% overnight coverage tracking the hypnogram, sparser clean daytime HR). Still Tier-B, never scored. - §6.4 caveats: "usable overnight HR" holds only on WORN nights (a charger night banks a hypnogram + temp but no IBIs), and a banked IBI must be persisted at its own anchored ring-time, not the drain-arrival wall-clock, or overnight beats misfile to the daytime sync moment. - §6.15: the NOOP wear/charge signal from 0x45/0x53 (trailing "chg. detected"/"stopped" strings + live-HR presence + a live-HR silence watchdog), and the 0x86 aohr note: open_health documents it "appears when worn" but it never appears in a NOOP capture. - §8 + §9: 0x68/0x86 are not emitted (0 of ~131k records); new "Observed-but-undecoded tags" appendix with verbatim example payloads from the Gen-3 corpus (0x61/0x4a/0x72/ 0x6a/0x6d/0x6c/0x5b/0x79/0x76/0x5c/0x56) + shape hints - flags that 0x61 (highest-rate tag) is NOT battery despite the open_oura-adjacent label. - DEVICE_SUPPORT_ROADMAP.md: MET validation + HR-from-0x80 status. Docs only. No decoder or behaviour change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016DN6cU2ERWZUS9tWYCjPvi
…tense, §9 full records) - Roadmap: correct "SyncTime (0x12/0x13) UTC anchoring" — 0x12/0x13 is the app→ring time-SET handshake; the actual UTC anchor is the ring-emitted 0x42 time-sync event (§6.11). Fixes the self-contradiction the reviewer flagged. - §6.4: reword the rrInterval caveat to present tense + "PR ryanbr#677 (pending merge)"; the docs no longer describe an unmerged fix as already landed on main. - §9: reproduce full type/len/rt records + 2–3 samples for the high-rate tags 0x61/0x72/0x6d (like 0x76) so future RE sees framing and cross-sample variation directly; note 0x61's payload length is not fixed (len byte moves 0x10/0x11/0x12). Docs-only. Full records extracted verbatim from the NOOP Gen-3 raw capture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016DN6cU2ERWZUS9tWYCjPvi
…server-gated OFF The read-only feature-status probe (spo2_status / realsteps_status) NOOP ships had both SpO2 (0x04) and real_steps (0x0b) read back all-zero (mode/status/state/subscription = 0) on a real Gen 3 ring (2026-07-20). Add a new §7.4 documenting the probe (READ verb only, never set-mode), the all-zero = server-gated signature vs the active daytime-HR (mode=1 status=0x11 state=2), and tie the §7.1 SpO2 + real_steps rows to that hardware capture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5dd56f5 to
a6bc062
Compare
Summary
Brings
docs/OURA_PROTOCOL.mdanddocs/DEVICE_SUPPORT_ROADMAP.mdup to date with the Oura findings, as a clean single docs commit offmain. Supersedes the entangled draft #488 (which carriedstale #570 cherry-picks
mainhas since surpassed via #572). Docs only — no decoder or behaviour change.Changes
0x60/0x80byte layout +quality==1gate (matches open_oura'sparse_api_green_ibi_quality_event); the IBI→HR finding (HR comes from the0x80/0x60records, not0x50; ~94% overnight coverage tracking the hypnogram; sparser-but-clean daytime HR). Still Tier‑B, never scored.the drain-arrival wall-clock — otherwise overnight beats misfile to the daytime sync moment (pairs with the
.ibianchor fix, Oura: stamp banked IBIs at their real ring-time, not the drain-arrival time #677).0x45/0x53(the trailing"chg. detected"/"chg. stopped"strings + live‑HR presence + a live‑HR silence watchdog), and addsthe
0x86aohr note: open_health documents it "appears when worn" but it never appears in a NOOP capture (code-confirmed only).0x68/0x86are not emitted (0 of ~131k records). New "Observed-but-undecoded tags" appendix with verbatim example payloads from the Gen‑3 corpus(
0x61/0x4a/0x72/0x6a/0x6d/0x6c/0x5b/0x79/0x76/0x5c/0x56) + shape hints — flags that0x61(the highest-rate tag) is not battery despite the open_oura-adjacent label.DEVICE_SUPPORT_ROADMAP.md— MET validation + HR-from-0x80status.Verification
Docs only; renders as Markdown. Confirmed the diff vs
mainis a pure superset (the two-lines are in-place updates of the Oura roadmap row and one §6.4 clause — no content lost).Supersedes #488