SomnoTrace v3.0.3
SomnoTrace v3.0.3 is a hotfix release for an oximetry upload bug: once a card held more than 64 O2 recordings, the newest ones were uploaded to SleepHQ on every scheduler pass — forever.
🐛 Fixed: O2 recordings re-uploading every pass
- The oximetry upload-state table had a fixed capacity of 64 entries. When it filled, the newest recording could never be marked as uploaded — the mark was silently dropped — so it stayed "pending" and re-uploaded on every scheduler pass (observed as identical SleepHQ imports a few minutes apart, while the log insisted
0 unit(s) pending). - Upload state now uses the same model as the CPAP day index: one small JSON file per recording under
upload_state/ox/, with no capacity limit, backends keyed by name (so a change in backend registration order can't renumber existing state), atomic writes, and marks persisted the moment an upload succeeds. - Existing state is migrated automatically on first boot (
oximetry.json→upload_state/ox/), keeping every recording's already-uploaded marks. State for recordings deleted from the card is now cleaned up instead of counting as pending forever. - The scheduler's pending line now reports oximetry work explicitly, so "0 units pending" can no longer hide an O2 upload in progress.
🧪 Testing & Internal Quality
- New
upload_ox_testhost test simulates real reboots across three phases: >64 recordings, upload marks surviving a restart, fingerprint changes resetting just one recording, deleted-recording eviction, and legacy-state migration — all validated against the real state file and recording data from an affected device. - Three new mutation tests lock the fix in place, including one that reproduces this incident's exact failure shape (a created state never becoming findable).
- Internal hardening found during on-device testing: state init and migration now run on the upload scheduler's larger stack instead of the app main task.
- Host suite now runs 16 tests, all green.