From 38598d0173323c9dfe6e62158f2874133f626f8e Mon Sep 17 00:00:00 2001 From: ryanbr Date: Mon, 27 Jul 2026 20:43:12 -0700 Subject: [PATCH] Don't let the body-location probe's detail line contradict its own verdict (#690) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare-stub report said two things at once: Verdict: opcode 84 answered with a bare stub — ambiguous ... No payload beyond the command byte (bare stub) — no body-location data on this firmware The verdict is right and the detail line settles what the verdict just called unsettled. A reply carrying no payload is one reply: it does not distinguish a firmware without body-location data from one that needs the strap worn, or in a determinate position, or asked with an argument this probe does not send. This file's own enum has UNKNOWN(0) and NOT_CONCLUSIVE(128), so the firmware plainly models "cannot tell right now" — which is exactly the reading the detail line ruled out. Same class as #913 and #914: a probe asserting a conclusion the run's inputs could not support, in the sentence a reader quotes. Found while sweeping the probes after #913 rather than reported. Both platforms carried the identical string — this is a shared defect, not a divergence — so both are reworded to say what was observed and what it does not establish, and both tests now assert the over-claim is ABSENT rather than only that "bare stub" appears. The old assertions passed under either wording, which is why the contradiction survived. The golden parity lock is unaffected: it pins a payload-bearing frame, so the bare-stub branch was never covered by it. --- .../Sources/WhoopProtocol/BodyLocationProbe.swift | 2 +- .../Tests/WhoopProtocolTests/BodyLocationProbeTests.swift | 6 ++++++ android/app/src/main/java/com/noop/ble/WhoopBleClient.kt | 2 +- .../test/java/com/noop/ble/BodyLocationProbeFormatTest.kt | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Packages/WhoopProtocol/Sources/WhoopProtocol/BodyLocationProbe.swift b/Packages/WhoopProtocol/Sources/WhoopProtocol/BodyLocationProbe.swift index 21a85f83d1..e4a7d17ee3 100644 --- a/Packages/WhoopProtocol/Sources/WhoopProtocol/BodyLocationProbe.swift +++ b/Packages/WhoopProtocol/Sources/WhoopProtocol/BodyLocationProbe.swift @@ -99,7 +99,7 @@ public enum BodyLocationProbe { sb += "Δ vs previous capture: first capture — probe again in another position to diff" } } else { - sb += "\nNo payload beyond the command byte (bare stub) — no body-location data on this firmware" + sb += "\nNo payload beyond the command byte (bare stub) — this reply carried no body-location data, which is not the same as the firmware having none (see the Verdict above)" } return (sb, payloadHex) } diff --git a/Packages/WhoopProtocol/Tests/WhoopProtocolTests/BodyLocationProbeTests.swift b/Packages/WhoopProtocol/Tests/WhoopProtocolTests/BodyLocationProbeTests.swift index 0e8dfc4b85..4c4ecfd0d2 100644 --- a/Packages/WhoopProtocol/Tests/WhoopProtocolTests/BodyLocationProbeTests.swift +++ b/Packages/WhoopProtocol/Tests/WhoopProtocolTests/BodyLocationProbeTests.swift @@ -64,6 +64,12 @@ final class BodyLocationProbeTests: XCTestCase { let (text, payHex) = BodyLocationProbe.format(frame: hexToBytes("aa0700fa24005446758858"), cmdOff: 6, isWhoop5: false, prevPayloadHex: nil) XCTAssertTrue(text.contains("bare stub")) XCTAssertNil(payHex) + // A bare stub is one reply, not a firmware capability. The Verdict line already calls it + // "ambiguous"; the detail line used to contradict it with "no body-location data on this + // firmware", which is the conclusion a reader would quote. Neither line may say it. (#914 class) + XCTAssertTrue(text.contains("ambiguous"), text) + XCTAssertFalse(text.contains("no body-location data on this firmware"), text) + XCTAssertTrue(text.contains("not the same as the firmware having none"), text) } /// Golden FULL-output lock: pins the exact byte-for-byte report so the Swift and Kotlin twins can't diff --git a/android/app/src/main/java/com/noop/ble/WhoopBleClient.kt b/android/app/src/main/java/com/noop/ble/WhoopBleClient.kt index 53d3c0dfd5..cca5742ce4 100644 --- a/android/app/src/main/java/com/noop/ble/WhoopBleClient.kt +++ b/android/app/src/main/java/com/noop/ble/WhoopBleClient.kt @@ -1298,7 +1298,7 @@ class WhoopBleClient( sb.append("Δ vs previous capture: first capture — probe again in another position to diff") } } else { - sb.append("\nNo payload beyond the command byte (bare stub) — no body-location data on this firmware") + sb.append("\nNo payload beyond the command byte (bare stub) — this reply carried no body-location data, which is not the same as the firmware having none (see the Verdict above)") } return sb.toString() to payloadHex } diff --git a/android/app/src/test/java/com/noop/ble/BodyLocationProbeFormatTest.kt b/android/app/src/test/java/com/noop/ble/BodyLocationProbeFormatTest.kt index 11f993337c..1a4b565244 100644 --- a/android/app/src/test/java/com/noop/ble/BodyLocationProbeFormatTest.kt +++ b/android/app/src/test/java/com/noop/ble/BodyLocationProbeFormatTest.kt @@ -2,6 +2,7 @@ package com.noop.ble import org.junit.Assert.assertEquals import org.junit.Assert.assertNull +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -61,6 +62,10 @@ class BodyLocationProbeFormatTest { @Test fun bareStubIsCalledOut() { val (text, payHex) = WhoopBleClient.formatBodyLocationProbe(hexToBytes("aa0700fa24005446758858"), 6, false, null) assertTrue(text.contains("bare stub")) + // Twin of the Swift assertion: a bare stub is one reply, not a firmware capability. + assertTrue(text.contains("ambiguous")) + assertFalse(text.contains("no body-location data on this firmware")) + assertTrue(text.contains("not the same as the firmware having none")) assertNull(payHex) }