Repository navigation
Log why a day is skipped when HR samples are insufficient (#714) - #720
Merged
Merged
Conversation
When a day has fewer than 200 HR samples, the analytics pass silently skips it. The sleep line still emits totalSleepMin=nil matched=0 but nothing distinguishes "stager ran, found nothing" from "no data to stage." A one-line diagnostic on each platform now makes every "no sleep" report self-diagnosing from the strap log alone.
|
@ryanbr FYI I think this creates some build issues for the iOS app. ~/co/noop/Strand/Data/IntelligenceEngine.swift:544:21 Cannot find 'diag' in scope |
3 tasks done
ryanbr
pushed a commit
that referenced
this pull request
Jul 22, 2026
135c724 (PR #720, #714) added a `diag("sleep day=… SKIPPED …")` call to the Swift scan loop to mirror the Kotlin `diag` sink, but Swift has no such function — `diagnosticSink` is the equivalent, and it's MainActor- isolated while this call site runs inside `Task.detached`, off the main actor. The app target (NOOPiOS/Strand) doesn't build under default CI (app-build.yml is disabled), so this shipped without failing a check. Collect the skipped-day lines in the detached loop alongside the other per-day diagnostics (rhrLine, sleepTrace, hrvTrace, stepsTrace) and replay them through `diagnosticSink` back on the main actor, in the same always-on (untagged) fashion as the Kotlin `diag` sink. Verified: NOOPiOS and Strand (macOS) both build; Android compileFullDebugKotlin already passed (Kotlin's diag call was valid). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JromGj5eBnU4Lxx7UfNsY5
ryanbr
added a commit
that referenced
this pull request
Jul 22, 2026
Fix iOS build break introduced in #720 (missing diag call)
simoncad7
pushed a commit
to simoncad7/noop
that referenced
this pull request
Aug 17, 2026
Log why a day is skipped when HR samples are insufficient (ryanbr#714)
simoncad7
pushed a commit
to simoncad7/noop
that referenced
this pull request
Aug 17, 2026
135c724 (PR ryanbr#720, ryanbr#714) added a `diag("sleep day=… SKIPPED …")` call to the Swift scan loop to mirror the Kotlin `diag` sink, but Swift has no such function — `diagnosticSink` is the equivalent, and it's MainActor- isolated while this call site runs inside `Task.detached`, off the main actor. The app target (NOOPiOS/Strand) doesn't build under default CI (app-build.yml is disabled), so this shipped without failing a check. Collect the skipped-day lines in the detached loop alongside the other per-day diagnostics (rhrLine, sleepTrace, hrvTrace, stepsTrace) and replay them through `diagnosticSink` back on the main actor, in the same always-on (untagged) fashion as the Kotlin `diag` sink. Verified: NOOPiOS and Strand (macOS) both build; Android compileFullDebugKotlin already passed (Kotlin's diag call was valid). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JromGj5eBnU4Lxx7UfNsY5
simoncad7
pushed a commit
to simoncad7/noop
that referenced
this pull request
Aug 17, 2026
Fix iOS build break introduced in ryanbr#720 (missing diag call)
simoncad7
pushed a commit
to simoncad7/noop
that referenced
this pull request
Aug 17, 2026
Every diagnostic line ran LiveState.append(log:), which did an O(n) Array.removeFirst once the 5k ring was full AND synchronously mirrored up to 2000 lines to UserDefaults — on EVERY line. That per-line cost has grown release over release as more diagnostics (offload/backfill, ryanbr#700, ryanbr#714, ryanbr#720) funnel through this one sink, so a busy strap session gets laggier the longer it stays connected. Batch it: amortize the trim (let the ring overrun by a 256-line slack, then trim in one shot) and mirror the durable tail every 32 lines instead of every line, with an explicit flush on disconnect (clearBiometrics) so a finished session's tail is always persisted for the scheduled export. What gets stored/persisted is unchanged; only how often. Buffer stays hard- bounded (<= maxLogLines + slack). iOS-only: Android's log() already uses an O(1) ArrayDeque with no per-line disk write, so no Kotlin twin is needed.
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
When the analytics pass skips a day for having fewer than 200 HR samples, the strap log currently shows
sleep day=… totalSleepMin=nil matched=0with no indication of why — indistinguishable from "stager ran and found no sleep." This adds one diagnostic line per skipped day on both platforms:Makes every "no sleep detected" report (#714) self-diagnosing from the export alone — no back-and-forth needed to determine "no data" vs "data but no sleep found."
Changes
IntelligenceEngine.kt:diag()line before thecontinuein the HR-gate skipIntelligenceEngine.swift: matchingdiag()line in theguardelse branchTest plan
./gradlew compileFullDebugKotlin— compiles