Skip to content

ConnectionTrace session duration rounds half-ties differently on Swift and Android #87

Description

@bhelm

Finding

ConnectionTrace.sessionHeldSuffix/1 is a declared Swift/Kotlin byte contract, but native formatting disagrees at millisecond values that land on a tenth-second half-tie.

Machine-readable native reproduction:

millis Swift Kotlin
50 after 0.0s after 0.1s
150 after 0.2s after 0.2s
250 after 0.2s after 0.3s

The fresh differential pilot therefore fails session_held_half_tie at 50 ms. This is not harness normalization noise: Swift calls Foundation String(format: "%.1f", ...); Android calls Java/Kotlin "%.1f".format(...), and the native runtimes select different half-tie results.

Reachability and impact

The helper is public on both platforms and is used by both live disconnect emitters:

  • Swift BLEManager appends it to connect down (uptime ends...) while Connection Test Mode is active.
  • Android WhoopBleClient appends it to the corresponding live log line.

This changes diagnostic text by 0.1 seconds only; it does not affect reconnect behavior, storage, or user health data. Severity is bounded P2 parity contract / P3-low operational. Exact half-ties are possible because both callsites derive integer milliseconds, although typical sessions last much longer.

RED / acceptance

Add mirrored public tests for at least 50, 150, and 250 ms and require byte-identical output under an explicit canonical rounding policy. Preserve existing contracts:

  • negative millis => empty suffix (unknown session start)
  • zero => after 0.0s
  • ordinary 432/6800/120000 ms outputs unchanged
  • locale remains fixed to a decimal point

The parity harness must then pass the complete curated Readout corpus without an exemption or output normalization.

Provenance / routing

The helper originated in upstream PR ryanbr#1023 for issue ryanbr#1020. That PR tested ordinary tenth-second values but not half-ties. Duplicate searches across open/closed issues and PRs in both ryanbr/noop and bhelm/noop found no existing rounding report or fix.

Fix routing: upstream-only production PR against ryanbr/noop; do not patch production only in the fork.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions