Skip to content

[P2] Charge driver skin-temperature text is locale-dependent across platforms #52

Closed

Description

@bhelm

Summary

The Swift and Kotlin chargeDrivers twins render the same negative skin-temperature deviation differently. A native seeded case with skinTempDev = -0.35 produced -0.3 C vs baseline on Swift and -0.4 C vs baseline on Kotlin.

The Recovery charge-drivers contract requires stable en_US_POSIX/US formatting for exact user-facing rows. The headline Charge score is unchanged.

Found by the Recovery charge-drivers differential rollout at base fd48e88872f70be232353429a63543febfa52052.

Reproduction

The native seeded case used:

{
  "hrv": 46.0,
  "rhr": 58.0,
  "resp": 14.0,
  "hrvBaseline": {
    "baseline": 51.0,
    "spread": 5.0,
    "nValid": 16,
    "nightsSinceUpdate": 1,
    "status": "trusted"
  },
  "rhrBaseline": {
    "baseline": 58.0,
    "spread": 4.0,
    "nValid": 12,
    "nightsSinceUpdate": 1,
    "status": "trusted"
  },
  "respBaseline": {
    "baseline": 15.0,
    "spread": 1.5,
    "nValid": 12,
    "nightsSinceUpdate": 1,
    "status": "trusted"
  },
  "sleepPerf": 0.8999999999999999,
  "skinTempDev": -0.35
}

Native observed behavior

Platform Skin-temperature valueText
Swift -0.3 C vs baseline
Swift en_US_POSIX reference -0.4 C vs baseline
Kotlin (Locale.US) -0.4 C vs baseline

The Swift native rollout stopped with actual="-0.3 C vs baseline" expected="-0.4 C vs baseline"; the Kotlin native output completed with -0.4 C vs baseline.

Cause

Swift's production skinTempDevText uses String(format: "%.1f", dev) without an explicit locale. Kotlin uses String.format(Locale.US, "%+.1f C vs baseline", skinTempDev). The same finite input therefore crosses different locale/formatting paths.

Duplicate search

This is related to the rounding family in #38 and #47, but neither issue covers the user-facing chargeDrivers skin-temperature formatter or its one-decimal locale contract. No exact open or closed duplicate was found.

Impact and severity

P2. The score is unchanged, but a user-visible driver value is byte-different across platforms and depends on the Swift runtime locale/formatter path.

Acceptance criteria

  • Make the production Swift and Kotlin skin-temperature formatter use one explicit POSIX/US locale contract.
  • Emit identical text for -0.35, +0.35, ordinary non-ties, and signed values around zero.
  • Preserve the explicit sign, one-decimal precision, unit, and vs baseline suffix.
  • Confirm respiration formatting remains byte-identical under the same locale contract.
  • Add mirrored unit tests and exact native differential fixtures.

No production fix is included in the rollout that discovered this issue.

Activity

  1. bhelm commented on Aug 19, 2026

    @bhelm
    OwnerAuthor

    Resolved and differentially verified.

    • The focused production fix is merged in the fork.
    • ChargeDrivers rollout PR Parity: roll out Recovery chargeDrivers/8 #60 adds exact ordered Swift/Kotlin fixtures for this issue.
    • The complete ChargeDrivers slice matched in 14/14 cases; the full pilot matched in 291/291 cases before integration and remained hash-stable after rebasing.
    • Fresh Swift/Kotlin coverage passed, stale output was rejected, and delta/order mutants were detected on both platforms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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