Skip to content

Whoop5Config: add the 16th R22 enable flag (enable_sig12) from a real capture (#103) - #224

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:whoop5-config-enable-sig12
Jul 10, 2026
Merged

ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:whoop5-config-enable-sig12

Conversation

@digitalerdude

Copy link
Copy Markdown

What

The shipped Whoop5Config.enableR22Sequence carried the 15 feature flags transcribed from judes.club's frame-builder. A real on-strap iOS HCI capture of the official app (WHOOP 5.0, #103) reproduced those 15 byte-for-byte in the same order, and then wrote a 16th SET_FF_VALUE:

… enable_sig11_during_sleep=2  dorset_inhibit_wpt=2  enable_sig12=2   ← new

This appends enable_sig12 (ASCII '2') so NOOP's enable sequence matches what the official app actually writes on every connect.

Notes

  • Independent confirmation of the whole sequence: the capture is a third, on-hardware source that agrees with judes.club + Asherlc/dofek on flags 1–15 (names, order, and the two '1' values), which is itself worth having.
  • enable_sig12's effect is undocumented (same as enable_sig11_during_sleep). It uses the identical SET_CONFIG encoding, so there is no framing change — only one more entry in the table.
  • This intentionally diverges from the judes.club "byte-for-byte" note for a good reason: real hardware writes a flag judes.club's builder doesn't list. The source is now noted in the doc + code comments.

Cross-platform + tests

  • Swift Whoop5Config.swift + Android Whoop5Config.kt twin.
  • Both golden tests bumped 15 → 16 flags (seqs 1…16) with a byte-level guard on the new flag (name-NUL-padded body, value at offset 32, frame passes CRC).
  • Fixed the two "15 flags" references in docs/WHOOP5_DEEP_DATA.md.

Verification

cd Packages/WhoopProtocol && swift test → 253/253 green. Kotlin twin mirrored but not compiled (no Android SDK in my environment) — please run ./gradlew testFullDebugUnitTest.

Refs #103.

🤖 Generated with Claude Code

… capture (ryanbr#103)

The shipped enableR22Sequence carried the 15 flags transcribed from judes.club's
frame-builder. A real on-strap iOS HCI capture of the official app (WHOOP 5.0,
ryanbr#103) reproduced those 15 byte-for-byte in the same order and then wrote a 16th
SET_FF_VALUE: `enable_sig12` = ASCII '2'. Append it so NOOP's enable sequence
matches what the official app actually writes on every connect.

Its effect is undocumented (like enable_sig11_during_sleep); it uses the identical
SET_CONFIG encoding, so no framing change. Mirror in Whoop5Config.kt, bump both
golden tests (15 → 16 flags, seqs 1…16) with a byte-level guard on the new flag,
and fix the two "15 flags" references in docs/WHOOP5_DEEP_DATA.md.

Verification: swift test green (253/253). Kotlin twin mirrored but not compiled
(no Android SDK here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@digitalerdude

Copy link
Copy Markdown
Author

✅ Kotlin twin now verified (was flagged untested).

Method: the app module needs the Android SDK + full Gradle dependency graph (disk-constrained here), but com.noop.protocol is pure JVM Kotlin with zero android/androidx imports — so I compiled it standalone with kotlinc 2.4.0 + JUnit4, stubbing only the ~13 com.noop.data row types it imports (verbatim signatures; decode logic untouched). Caveat: standalone kotlinc, not your AGP toolchain, so ./gradlew testFullDebugUnitTest is still the canonical check.

Result: both WHOOP5 protocol test classes green — 16 tests OK, including sequenceIsSixteenFlagsWithExpectedValues (16 flags, enable_sig12 last with value 0x32).

@ryanbr
ryanbr merged commit e532929 into ryanbr:main Jul 10, 2026
6 checks passed
DX23876 pushed a commit to DX23876/noop that referenced this pull request Jul 22, 2026
… capture (ryanbr#103) (ryanbr#224)

The shipped enableR22Sequence carried the 15 flags transcribed from judes.club's
frame-builder. A real on-strap iOS HCI capture of the official app (WHOOP 5.0,
ryanbr#103) reproduced those 15 byte-for-byte in the same order and then wrote a 16th
SET_FF_VALUE: `enable_sig12` = ASCII '2'. Append it so NOOP's enable sequence
matches what the official app actually writes on every connect.

Its effect is undocumented (like enable_sig11_during_sleep); it uses the identical
SET_CONFIG encoding, so no framing change. Mirror in Whoop5Config.kt, bump both
golden tests (15 → 16 flags, seqs 1…16) with a byte-level guard on the new flag,
and fix the two "15 flags" references in docs/WHOOP5_DEEP_DATA.md.

Verification: swift test green (253/253). Kotlin twin mirrored but not compiled
(no Android SDK here).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
DX23876 added a commit to DX23876/noop that referenced this pull request Aug 4, 2026
…a row

A WHOOP export carries several wordings for the same habit — WHOOP re-worded its
journal questions over the years and the importer keeps question_text verbatim on
purpose. The question string is the key everywhere: the journal table's primary
key, byBehaviour in Insights, the coach's pattern analyser. So the same habit
reads as two, each with half its history and a correspondingly weak ranking. Only
whitespace and casing folded before this (ryanbr#224); a re-wording did not.

Merging is a reading, not an edit. JournalCatalogItem gains `aliases` — the
wordings folded onto it, verbatim, decoding as empty for every catalog written
before now — and Repository folds journal reads through it at the one door the
journal comes in by, so Insights, the coach and the logging card cannot disagree
about what counts as one behaviour. Not one row is rewritten, moved or deleted,
and separating restores the split exactly.

Where two folded wordings answer the same day, the winner is a real stored row,
picked by a rule a person can predict: native beats imported (mergeJournal's own
precedence, because a typed answer is the user's latest word), then the target's
own wording, then the earlier merge. Yes/no is never OR-ed and numbers are never
summed — "it said yes somewhere" would invent a day. journal has no modified-at
column, so "the newest wins" is not available and is not pretended.

Candidates are proposed, never applied: edit mode grows a "Possible duplicates"
review listing each wording with its stored answer count (one GROUP BY, not the
whole history in memory), proposing the wording with the most answers as the
survivor so the smaller history is the one that moves. Similarity reuses
CoachMemory.tokens — the tokeniser the coach's own duplicate check runs on, now
nonisolated since it is pure — over the question minus its opener, with a blunt
suffix trim so "use"/"used" meet. A declined pair is remembered.

Verified: Strand (macOS) and NOOPiOS build; 1778 tests, 0 failures (+12 pinning
the fold's precedence, that no day is lost, that an untouched catalog folds
byte-identically to mergeJournal, and the finder's hits and misses); WhoopStore
334 green; i18n audit clean.

Not verified on device, and worth knowing: the catalog (renames, groups, hidden
items — and now merges) is NOT in the .noopbak whitelist, so none of it survives a
backup restore today. That predates this change; flagged rather than widened here,
since the whitelist is a byte-identical cross-platform contract.
simoncad7 pushed a commit to simoncad7/noop that referenced this pull request Aug 17, 2026
… capture (ryanbr#103) (ryanbr#224)

The shipped enableR22Sequence carried the 15 flags transcribed from judes.club's
frame-builder. A real on-strap iOS HCI capture of the official app (WHOOP 5.0,
ryanbr#103) reproduced those 15 byte-for-byte in the same order and then wrote a 16th
SET_FF_VALUE: `enable_sig12` = ASCII '2'. Append it so NOOP's enable sequence
matches what the official app actually writes on every connect.

Its effect is undocumented (like enable_sig11_during_sleep); it uses the identical
SET_CONFIG encoding, so no framing change. Mirror in Whoop5Config.kt, bump both
golden tests (15 → 16 flags, seqs 1…16) with a byte-level guard on the new flag,
and fix the two "15 flags" references in docs/WHOOP5_DEEP_DATA.md.

Verification: swift test green (253/253). Kotlin twin mirrored but not compiled
(no Android SDK here).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants