Repository navigation
Last sync belongs to a strap, not to the install - #1745
Merged
Merged
Conversation
Reported three times before it was believed, which is the part worth keeping. The number was plausible, so the reading was treated as mistaken rather than the label. `noop.lastSyncAtSec` is ONE key, stamped on any strap's HISTORY_COMPLETE and read back for whichever strap is active. On a single-strap install those are the same strap, which is why it survived this long. With two paired it reports the other one's: the capture shows "Last sync: 4d ago" beside "Days: whoop-MGB...=0" and a 4.0 last seen three days earlier. The 5/MG had never banked a row. The timestamp on its screen was the 4.0's. Wrong in the reassuring direction, which is what made it expensive. A strap that has never synced read as recently synced, so a pull-to-refresh that could not possibly change the number looked broken instead of inapplicable - and it sent this whole investigation after a sync regression that never existed. This is the same defect resolveFirmware fixed for firmware strings, so it takes the same shape: stamped per BLE address, read per strap, and the legacy global honoured ONLY when exactly one strap is paired - the one case where it cannot be ambiguous, which keeps single-strap installs reading correctly across the upgrade. Deliberately NOT migrated by writing the global onto the active strap: that is the same guess in a different place, and on a multi-strap install it would bake the wrong attribution in permanently instead of self-correcting at the next real sync. The #57 write-health pair had it too, one line below in the same capture: "Data write: rows last landed 4d ago" against a strap whose own row count was zero. Two lines that both read as evidence the 5/MG had been syncing, neither about the 5/MG. Both halves are scoped, not just the one that was noticed - they are read as a pair ("stalled more recently than ok" is the alarm), so scoping one would compare a strap's own stall against another strap's success. For the strap in the capture the answer is now "never (this strap)". Not a degraded answer - the correct one, and the one nobody had been given. No Swift twin needed: the Apple side has no persisted global equivalent. Staging 392 / iOS 273. 4907 tests, lintVital, doc lint and i18n clean.
The bug this PR removes, surviving inside the fix for it. Caught by a question about what the screen would actually show. The constructor seeded LiveState.lastSyncAt from NoopPrefs.lastDevice, which records the last strap to BOND - and `bonded` is set for a 5/MG by the live-HR path, so on a two-strap install it is whichever strap was worn most recently, not the one the screens are scoped to. Wear the 4.0 for an evening and the next launch would put its sync time on the 5/MG's Today screen: the same misattribution, one layer down, and it would have shipped inside the change whose entire purpose is that it must not happen. Seeded from the registry's ACTIVE row now, resolved exactly as the strap-log header resolves firmware and last-sync. That read is suspend, so the constructor seeds NOTHING and the value arrives a moment later. A blank moment is honest; another strap's timestamp is not, and there is no cheap synchronous guess that is not the bug in miniature - which is why there is no fallback here at all. The async fill never overwrites a value the session earned: a HISTORY_COMPLETE landing while the registry read is in flight is newer than anything persisted and must win. 4907 tests, lintVital, doc lint and i18n clean.
ryanbr
added a commit
that referenced
this pull request
Aug 31, 2026
Build numbers only - 10.6.1 stays the unreleased line, and 395 clears the staging high-water so the APK installs over 394. Carries the sparse-bridge change: a night interrupted by one short trip out of bed is no longer ended at the interruption. Anyone whose nights were being truncated that way should see fuller totals; the honest caveat is that the absorbed minutes land in sleep rather than wake, because both stagers call wake off HR and the bridge only absorbs spans whose HR was already in the sleep band. Also the #1746 PENDING-ack change, so one "offsets may have moved" line per sync stops appearing on healthy 4.0 hardware, and the per-strap Last sync / Data write attribution from #1745.
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.
Reported three times before it was believed — and that is the part worth keeping. The number was plausible, so the reading was treated as mistaken rather than the label.
The defect
noop.lastSyncAtSecis one key, stamped on any strap'sHISTORY_COMPLETEand read back for whichever strap is active. On a single-strap install those are the same strap, which is why it survived this long.With two paired, it reports the other one's. From the 30 Aug capture:
The 5/MG had never banked a row. The timestamp on its screen was the 4.0's.
Why it was expensive
It was wrong in the reassuring direction. A strap that has never synced read as recently synced, so a pull-to-refresh that could not possibly change the number looked broken rather than inapplicable — and it sent an entire investigation after a sync regression that never existed.
The shape of the fix
This is the same defect
resolveFirmwarealready fixed for firmware strings, so it takes the same shape:HISTORY_COMPLETEarrives; resolving to a registry id there would repeat the mis-mapping "Last seen" on the Devices card shows time-since-ADDED, never time-since-seen (both platforms) #1527 fixed forlastSeen)Deliberately not migrated by writing the global onto the active strap. That is the same guess in a different place, and on a multi-strap install it would bake the wrong attribution in permanently instead of letting it self-correct at the next real sync.
The sibling line had it too
One line below, in the same capture:
Data write: rows last landed 4d ago— against a strap whose own row count was zero. Two lines that both read as evidence the 5/MG had been syncing, and neither was about the 5/MG.Both halves of the #57 write-health pair are scoped, not just the one that was noticed. They are read as a pair — "stalled more recently than ok" is the alarm — so scoping one would compare a strap's own stall against another strap's success.
What it says now
For the strap in the capture: "never (this strap)". Not a degraded answer — the correct one, and the one nobody had been given.
Notes
LiveState.lastSyncAtresolves the single-strap fallback just after construction rather than in the constructor, because the paired count needs the registry and the registry issuspend. Seeding from the global first and correcting later would have shown the other strap's timestamp for as long as that took — the bug, briefly, rather than not at all.Verification
4907tests, 0 failureslintVitalFullRelease, doc-comment lint, i18n--cicleanRelated to #1635.