Skip to content

fix(apple): the Charge breakdown reports skin temperature once - #2899

Open
UtkuDenizAltiok wants to merge 1 commit into
ryanbr:mainfrom
UtkuDenizAltiok:fix/charge-sheet-skin-temp-once
Open

UtkuDenizAltiok wants to merge 1 commit into
ryanbr:mainfrom
UtkuDenizAltiok:fix/charge-sheet-skin-temp-once

Conversation

@UtkuDenizAltiok

Copy link
Copy Markdown

What was wrong

ChargeBreakdownSection (classic Today's Charge sheet, Coupled, Intelligence) draws the scorer's driver rows and then, whenever the night carries a skin-temperature deviation, a separate SkinTempDeviationRow. RecoveryScorer.chargeDrivers already lists a "Skin temperature" driver for every night with a deviation, read from the same skinTempDevC every caller passes as skinTempRel. So one reading was printed twice, a few lines apart:

  • driver row: "Skin temperature · +0.3 C vs baseline · warmer than baseline, limiting recovery"
  • extra row: "Skin temperature · +0.3 C vs your normal · Warmer than your baseline"

That is the "two readouts of one fact" shape AGENTS.md rules out. Android's sheet (RecoveryDriversSection → DriverRow) draws the driver row only, so this is also an Apple-only divergence.

The fix

The separate row is drawn only when no driver row reports skin temperature (ChargeBreakdownSection.showsSeparateSkinTempRow). That keeps it for any caller that passes a relative marker without a matching driver, and removes the duplicate everywhere else. The driver label is pinned against the real scorer's output, so renaming it fails a test instead of silently bringing the second row back. No analytics change; Android is already correct.

Verification

  • ChargeBreakdownSkinTempOnceTests (2 new, running the real RecoveryScorer.chargeDrivers for +0.4, −0.6 and 0.0 °C and for no deviation) plus the existing ChargeBreakdownFormatTests and ChargeBreakdownWiringTests: 20 tests pass on the Strand macOS host.
  • NOOPiOS simulator build.
  • doc_comment_lint.py clean, i18n_audit.py --ci upstream/main passes, parity-governance acceptance passes.

🤖 Generated with Claude Code

`ChargeBreakdownSection` drew the scorer's driver rows and then, whenever
the night carried a skin-temperature deviation, a separate
`SkinTempDeviationRow`. `RecoveryScorer.chargeDrivers` already lists a
"Skin temperature" driver for every night with a deviation, read from the
same `skinTempDevC` every caller passes as `skinTempRel`, so the classic
Today Charge sheet, Coupled and Intelligence printed the one reading twice
("+0.3 C vs baseline" and "+0.3 C vs your normal" a few lines apart):
the "two readouts of one fact" shape AGENTS.md rules out. Android's sheet
draws the driver row only.

The separate row is now drawn only when no driver row reports skin
temperature, which keeps it for any caller that passes a relative marker
without a matching driver. The driver label is pinned against the real
scorer's output, so renaming it fails a test instead of bringing the
second row back.

Verification: ChargeBreakdownSkinTempOnceTests (2 new) plus the existing
ChargeBreakdownFormatTests and ChargeBreakdownWiringTests pass on the
Strand macOS host; NOOPiOS simulator build; doc_comment_lint clean;
i18n_audit --ci passes; parity-governance acceptance passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@UtkuDenizAltiok

Copy link
Copy Markdown
Author

The NOOPiOS leg of App build shows as failed, but it never ran: the job was cancelled after waiting about 15 minutes with no runner assigned (no steps executed). The macOS Strand leg of the same run passed, and this branch builds locally for the iOS Simulator (NOOPiOS, Xcode 27.2). A re-run of that leg should clear it.

UtkuDenizAltiok added a commit to UtkuDenizAltiok/noop that referenced this pull request Nov 5, 2026
Tree is exactly 4a6a22c: upstream/main + every open app PR + Simple mode
(Today, More, Sleep, Trends) + ryanbr#2897 ryanbr#2898 ryanbr#2899 ryanbr#2900 ryanbr#2901 (the ryanbr#2896
swipe fix is already inside Simple mode) + the parity refresh. Recorded
as a merge over the previous stack so the branch moves forward without
rewriting history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DX23876 pushed a commit to DX23876/noop that referenced this pull request Nov 8, 2026
Adopted from ryanbr#2899 and ryanbr#2904 (by @UtkuDenizAltiok), merged by
hand because both edit the same block, plus the same fix for the Forge
design.

- The classic Charge breakdown (Today, Intelligence, Coupled, macOS)
  drew the scorer's "Skin temperature" driver row and then a separate
  SkinTempDeviationRow for the same deviation. The extra row now shows
  only when no driver row already reports it (ryanbr#2899).
- Both rows printed the engine's Celsius figure without a degree sign
  to everyone, Fahrenheit readers included. They now show the reader's
  unit ("+0.4 °C" / "+0.8 °F vs baseline"); a difference converts by
  9/5 with no +32 offset (ryanbr#2904). The engine text stays unchanged.
- Forge's Charge page and Today driver rows (ForgeToday.driverValue)
  always printed "°C vs usual". They now use the same figure in the
  reader's unit; the key "%@ °C vs usual" becomes "%@ vs usual" with the
  existing translations minus the hard-coded unit.
- Polish "%@ względem bazy" reads "%@ względem wartości bazowej".

The Android (Kotlin) hunks are left out and can follow separately; this
change covers the Apple apps only.

Not compiled here (no Swift toolchain in this environment).

Analysis migration required: no

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AWc14WNvMmSF4RHP6ww4d
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.

1 participant