Repository navigation
diag: say when an alarm readback has no stored frame - #2545
Merged
Merged
Conversation
The readback frame line was emitted only when the frame existed, so a readback carrying an epoch and no frame printed nothing at all, which reads as "the frame was checked and was fine". The two causes are indistinguishable from outside: #1707 began banking the frame on 2026-08-28, so any readback taken before that has an epoch and no frame, and a write that failed looks identical. A 2045 readback in a 2026-09-28 capture cost a trip through git history to establish that the record simply predated the capture. The line now always appears and names both possibilities, the way the strap-clock verdict above it already says why it abstains rather than printing no line. Since the writer stores at least "empty" for any frame it decodes, this branch is reachable in practice only for records banked before that capture existed, which is the intent. Also aligns the Apple twin's blank handling with Kotlin's isNotBlank. A whitespace-only value printed a frame line carrying no bytes there; it counts as not stored on both platforms now. Four Robolectric tests. Mutation-checked: removing the branch turns exactly the two new-behaviour tests red and leaves both controls green.
The branch landed on both platforms but only Android got tests, and the Apple half is where the behaviour actually changed twice: the new else branch, and blank handling moving from isEmpty to a trimmed check so a whitespace-only value stops printing a frame line carrying no bytes. `alarmLines` is a static func reading UserDefaults.standard, so it is seedable directly, the way StrapProvidesLineTests and OrphanedSamplesLineTests already cover sibling diagnostic lines. Keys are cleared in both setUp and tearDown so no case can inherit another's readback. The absent-frame assertion pins the Kotlin text verbatim, not merely a substring. These lines exist so an Android report and an Apple one compare directly, and the two strings are byte-identical today, so asserting each side against only itself would let them drift apart silently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Found reviewing a 2026-09-28 strap log. Its alarm block showed
Strap reports: 2045-06-10 14:03and noReadback frame:line at all, and establishing why cost a trip through git history.The frame line was emitted only when the frame existed. A readback carrying an epoch and no frame therefore printed nothing, which reads as "the frame was checked and was fine" rather than "there is nothing to check". Worse, the two causes are indistinguishable from outside the app:
The readback in that log was taken 2026-08-26, two days before the capture existed. That is knowable only from the repo, not from the export, which is the wrong way round for a diagnostic whose whole job is to separate a genuinely-stored stale alarm from a misdecode of a fixed response field.
What changes
The line now always appears. When there is no frame it names both possibilities instead of staying silent, the way the strap-clock verdict a few lines above it already says why it abstains rather than printing nothing:
Worth noting for whoever reads this next: the writer stores at least
"empty"for any frame it decodes, so this branch is reachable in practice only for records banked before the capture existed. It will stop firing for a given install once its next WHOOP 4.0 readback lands. That is the intent, not a gap.Parity
Both platforms carry it. The Apple twin also had a blank-handling divergence in the same expression: Kotlin used
isNotBlank()while Swift used!raw.isEmpty, so a whitespace-only value printed a frame line carrying no bytes on Apple and counted as absent on Android. Swift trims first now, so blank means not stored on both.Verification
./gradlew :app:testFullDebugUnitTest --no-build-cache --rerun-tasks: 763 suites, 6497 tests, 0 failures, result XML checked fresh against the clock../gradlew :app:lintVitalFullRelease -PstagingRelease: clean.alarmLinesrather than asserting on source text. The Apple half is where the behaviour changed twice (the new branch, and blank handling), so it needed its own coverage;alarmLinesthere is a static func overUserDefaults.standard, seedable the wayStrapProvidesLineTestsalready covers sibling lines.python3 Tools/doc_comment_lint.py: clean.Two things re-review caught, recorded because each would otherwise have shipped. The mutation run had the Kotlin branch deleted on disk when I re-read the diff, so the working tree looked like the fix was missing; judging a tree mid-mutation is as unsafe as editing one. And the Apple half had no tests at all, on the side where the behaviour changed twice.