Skip to content

fix(oura): make the nameless-ring label rule one tested twin, not two - #1797

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
pipiche38:fix/oura-nameless-ring-label
Sep 1, 2026
Merged

ryanbr merged 1 commit into
ryanbr:mainfrom
pipiche38:fix/oura-nameless-ring-label

Conversation

@pipiche38

Copy link
Copy Markdown

Diff shape

file change
Strand/BLE/OuraLiveSource.swift new top-level ouraAdvertisedLabel; both call sites routed through it; .isEmpty → whitespace-aware
android/…/ble/OuraLiveSource.kt Kotlin twin ouraAdvertisedLabel; both call sites routed through it
android/…/ui/AddDeviceWizard.kt subtitle fallback uses the existing l10n_…_oura_ring_e3431536 key
android/…/ble/OuraNamelessRingDiscoveryTest.kt +1 oracle test, 12 cases from the Swift twin's stdout

88 insertions, 5 deletions. No new strings, so nothing owed in any locale.

Verification actually run

  • Android testFullDebugUnitTest -Duser.language=en: 5040 tests, 4/4 in
    OuraNamelessRingDiscoveryTest. The 2 RecoveryDriversTest float-rounding failures
    (expected:<-0.5> but was:<-0.4999999999999929>) are pre-existing on upstream/main.
  • macOS xcodebuild -project Strand.xcodeproj -scheme Strand -destination platform=macOS CODE_SIGNING_ALLOWED=NO build → BUILD SUCCEEDED. Required: this touches app-target Swift,
    which swift-packages.yml does not compile and app-build.yml is disabled.
  • Tools/doc_comment_lint.py exit 0, Tools/i18n_audit.py --ci upstream/main exit 0.
  • No hardware. Not owed: this changes a label, not the BLE path. Discovery is untouched.

The oracle, for the PR body if asked

Swift twin compiled standalone (swiftc -O twin.swift main.swift) over the 12-case list; its stdout
is pasted verbatim as the Kotlin expectations. It immediately caught my own first Kotlin draft,
advertisedName.trim().ifEmpty { fallback } — returns the trimmed name where Swift returns the
original. " Oura Ring 4 " is the separating case. Note the oracle guards only the
Kotlin→Swift direction; the Swift side has no equivalent test, because Strand/BLE tests run only
under xcodebuild … test and app-build.yml is off by default.

Known boundary, stated not hidden

Kotlin isBlank (Java Character.isWhitespace) and Swift .whitespacesAndNewlines disagree on
exotic Unicode spaces — U+00A0 is whitespace to Swift, not to Java. Documented at the helper on both
sides rather than asserted, since no BLE local name carries one.

ryanbr#1783 reports that a ring advertising no local name lists with a blank label on
both platforms. It does not. Both platforms already substitute a fallback at the
single `DiscoveredRing` construction site, and have since the original Oura
commit (4897f26), so `ring.name` is never blank by the time the wizard renders
it:

  Strand/BLE/OuraLiveSource.swift   name: name.isEmpty ? "Oura" : name
  android/…/ble/OuraLiveSource.kt   name = name.ifBlank { "Oura" }

The three call sites the issue names (`AddDeviceWizard.kt:1339`,
`AddDeviceWizard.swift:1160`, and `:1646`, which the issue does not name but has
the same shape) render an already-guarded value. Adding a second guard there
would be dead code, and a test pinning it would pin nothing.

What IS there is a silent divergence between the two guards. `ifBlank` treats an
all-whitespace name as absent; `isEmpty` does not. A ring advertising `" "` would
have listed as "Oura" on Android and blank on Apple — the same class of
Swift/Kotlin drift the parity contract calls out, invisible in review because
both lines read correctly on their own.

So both guards become one named helper, `ouraAdvertisedLabel`, whitespace-aware
on both sides and returning the name UNTRIMMED when present, pinned by an oracle
test. The oracle earned its place immediately: the first Kotlin draft was
`advertisedName.trim().ifEmpty { fallback }`, which returns the trimmed name
where Swift returns the original. Reading the two side by side would not have
caught it; `" Oura Ring 4 "` does.

Also localises `AddDeviceWizard.kt`'s `?: "Oura ring"` subtitle fallback, which
was a hardcoded English literal while `l10n_add_device_wizard_oura_ring_e3431536`
already existed in all six locales. That fallback renders precisely when the
generation is undetected — which is exactly the nameless bonded-ring case from
ryanbr#1776 — so a French user hit untranslated copy on the one row this issue is
about. No new string on either platform.

Verification:
- Android: 5040 tests, 4/4 in OuraNamelessRingDiscoveryTest including the new
  oracle case. The 2 RecoveryDriversTest float-rounding failures are pre-existing
  on upstream/main, unrelated.
- Apple: `xcodebuild -scheme Strand -destination platform=macOS` BUILD SUCCEEDED
  (app-target Swift, which no default CI job compiles).
- Tools/doc_comment_lint.py and Tools/i18n_audit.py --ci both exit 0.
- No hardware validation: this changes a label, not the BLE path. Discovery
  behaviour itself is unchanged.

Refs ryanbr#1783, ryanbr#1776

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Xj4EcmHmVGeL6JrG4JBXK
@ryanbr
ryanbr merged commit 1413363 into ryanbr:main Sep 1, 2026
4 checks passed
ryanbr added a commit that referenced this pull request Sep 1, 2026
…abel

Both doc blocks claimed the Kotlin and Swift label rules part on U+00A0, citing that
Java's Character.isWhitespace excludes it while Swift's .whitespacesAndNewlines includes
it. The first half is true of Character.isWhitespace in isolation, but ifBlank never
calls it: Kotlin's Char.isWhitespace is

    Character.isWhitespace(c) || Character.isSpaceChar(c)

and isSpaceChar covers exactly the non-breaking spaces (U+00A0, U+2007, U+202F) that
isWhitespace drops. Verified on the JVM. U+00A0 is therefore blank on BOTH sides - the
one case the note picked out is the case they agree on.

The real divergence set is the opposite pair: U+0085, which .whitespacesAndNewlines
includes and Kotlin does not, and U+001C-U+001F, which Kotlin counts and Swift does not.
Neither reaches a BLE local name, so this is a comment correction with no behaviour
change and no test change.

Worth correcting rather than leaving: the parity ledger reads these doc blocks for twin
claims, the wrong claim was duplicated across both files, and it advertised itself as
"stated rather than silently overclaimed".

Follow-up to #1797 by @pipiche38.
@pipiche38
pipiche38 deleted the fix/oura-nameless-ring-label branch September 12, 2026 17:41
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