Repository navigation
fix(garmin): fold a watch night over its own sleep window - #48
Merged
Merged
Conversation
A Forerunner 165 writes a SLEEP_LEVEL row only when the stage changes, and each row closes the span before it. The fold split nights at any gap over an hour (one night became 3 sessions: a short main sleep plus a fake nap) and started each night at its first row, dropping the onset span. - Decode EVENT 74 (sleep start/stop) and fold each night over that window; the first row closes the span from onset. Files without markers fall back to a 4 h gap rule. - A stored Garmin night replaces the overlapping nights of the same watch, so re-folds and re-syncs stay one session (user-edited nights are kept). - Once per watch, forget stored SLEEP files in the ledger so the next sync re-downloads and re-folds them (read-only; files within the sync lookback). - Label a Garmin night "Garmin" / "Garmin watch stages" instead of "On-device" / "stages approximate (on-device)". Swift and Kotlin twins, oracle outputs pinned on both sides. Refs #47
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.
Refs #47
Root cause (real data, Forerunner 165 fw 29.05)
Last night's files were pulled read-only from the watch with a scratch harness built on the app's own
GarminSource. It sends no archive, flag or delete messages. All 48 FIT files were listed and downloaded, and nothing was changed on the watch. Times below are local (+07:00).IntelligenceEnginenever stages sleep from heart rate (watch-stages-its-own-sleep). Stale computed copies are dropped by the existing Publish the command result code on Apple, as Android already does (#894) ryanbr/noop#899 overlap heal after the post-sync rescore.No FIT files or logs are committed.
Changes (Swift + Kotlin twins)
Parser (
GarminWellnessParser):sleepMarks.sleepSessionsfolds each night over the watch window. The first row closes the span from onset.Fold (
GarminWatchIngest.stagesJSON): the stage timeline includes the onset span.Store (
ingestGarminFile/GarminSyncStore.ingest):forgetGarminSyncedFiles/forgetFiles.Repair of stored nights:
noop.garmin.sleepRefold.v1.<id>), the SLEEP-file ledger rows are forgotten, so the next sync re-downloads and re-folds them. The watch is not touched.Labels:
garmin-<id>night.Tests
sleepWindow, shaped like this night (relative times only):GarminWellnessParserTest(s)andGarminWatchIngestTest(s).SleepProvenanceLabelTest(Android) covers the Garmin and Oura captions and badges.Local verification (Actions is disabled on this fork)
swift testStrandImport, WhoopStorexcodebuild -scheme Strand(macOS)xcodebuild -scheme NOOPiOS(iOS Simulator; watch embed removed locally, not committed)testFullDebugUnitTest(JDK 17,--no-build-cache --rerun-tasks)RecoveryDriversTesthalf-tie rounding (-0.5vs-0.4999999999999929). This PR changes no analytics code. Not checked onmain.parity_ratchet.py --base origin/main(after the guarded--refresh-derived)doc_comment_lint.pyi18n_audit.py --ci mainToolsunittesttest_check_source_referencesfailures, which also fail on cleanmainNot yet confirmed in the app on the phone: after this build, the first Garmin sync should log
re-folding stored nights; N sleep files will download againand show the night as one 01:36–09:50 session.