You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the drift-control plan in #17 (plan step 2); split out because it needs a scope decision, not just a fix.
The claim vs. the tree
HRVAnalyzer.swift:304 documents sdnnIndex with "Kotlin twin: HrvAnalyzer.sdnnIndex" — that function does not exist. sdnnIndex / avgSdnn have zero matches under android/.
This is not a dead helper on the Swift side. The value is production surface:
computed per day in AnalyticsEngine (avgSdnn: at AnalyticsEngine.swift:822);
persisted through WhoopStore (Database.swift, MetricsCache.swift);
consumed by the macOS app layer (Strand/Data/Repository.swift, Strand/Data/IntelligenceEngine.swift) and the iOS HealthKit bridge (StrandiOS/Health/HealthKitBridge.swift).
So Android users simply don't have the metric, and the stored-data surface of the two platforms differs — the exact class of drift docs/CROSS_PLATFORM.md promises can't happen.
The decision needed
Two honest ways to close this, and the choice is a maintainer call because it touches the Android schema:
Port it: implement sdnnIndex/avgSdnn in Kotlin, including the Room storage for the persisted daily value — i.e. a versioned Room migration, pinned by a migration test, with column order matching the entity (CLAUDE.md rules).
Declare it platform-specific: keep it Swift-only, record it as an explicit non-twin entry in the parity twin map (drift/01-twin-ledger) so the ledger stops flagging it — visible and deliberate instead of silently divergent.
Until decided, drift/02b-avgsdnn only corrects the false twin claim in the Swift doc comment so the tree stops asserting a twin that isn't there. The twin ledger (#17, plan step 1) catches this class mechanically from now on (a declared twin reference must resolve on the other side).
Decision (2026-08-22): port sdnnIndex / persisted avgSdnn to Android rather than declaring the metric permanently Swift-only.
The implementation must be a complete product port, not a ledger waiver:
mirror the Swift algorithm, units, nullability, cleaning rules, and segment-boundary behavior in Kotlin;
compute the value in the Android daily analytics path;
persist it through the Room daily-metric surface with a versioned, lossless migration;
cover algorithm vectors, migration, DAO/cache round-trips, and the production call path;
prove Swift/Kotlin agreement on boundary cases;
remove the temporary governance exemption once the Android declaration exists.
The change will be independently reviewed and fixed until no P0/P1 findings remain before an upstream PR is opened. This issue stays open until the complete port is merged and verified.
Portierung ist nach zwei unabhängigen Abschlussreviews mit P0=0/P1=0 als Upstream-PR veröffentlicht: ryanbr#1535. Das Issue bleibt bis zum Merge offen.
Integration verification (2026-08-22): tested end to end on Android staging after merging the reviewed port into bhelm/integration (74807ae9). A newly exported .noopbak reports Android schema version 32; the embedded Room database passes PRAGMA quick_check and integrity_check, contains trailing nullable dailyMetric.avgSdnn, and has three recent nights with non-null, distinct avgHrv (RMSSD) and avgSdnn values. This verifies the production calculation, persistence, and backup/export path on real app data. Algorithmic correctness remains covered by the mirrored vectors, full test suites, and the two P0/P1=0 reviews on ryanbr#1535. The issue should remain open until the upstream PR is merged.
Fixed upstream by ryanbr#1535 (merged), which took option 1 and ported the SDNN index to
Android: HrvAnalyzer.sdnnIndex, wired into AnalyticsEngine, with the avgSdnn Room column,
its migration and mirrored tests. The comment now describes a twin that exists. Closing.
Activity
bhelm commentedon Aug 21, 2026
Decision (2026-08-22): port
sdnnIndex/ persistedavgSdnnto Android rather than declaring the metric permanently Swift-only.The implementation must be a complete product port, not a ledger waiver:
The change will be independently reviewed and fixed until no P0/P1 findings remain before an upstream PR is opened. This issue stays open until the complete port is merged and verified.
bhelm commentedon Aug 21, 2026
Portierung ist nach zwei unabhängigen Abschlussreviews mit P0=0/P1=0 als Upstream-PR veröffentlicht: ryanbr#1535. Das Issue bleibt bis zum Merge offen.
bhelm commentedon Aug 22, 2026
Integration verification (2026-08-22): tested end to end on Android staging after merging the reviewed port into
bhelm/integration(74807ae9). A newly exported.noopbakreports Android schema version 32; the embedded Room database passesPRAGMA quick_checkandintegrity_check, contains trailing nullabledailyMetric.avgSdnn, and has three recent nights with non-null, distinctavgHrv(RMSSD) andavgSdnnvalues. This verifies the production calculation, persistence, and backup/export path on real app data. Algorithmic correctness remains covered by the mirrored vectors, full test suites, and the two P0/P1=0 reviews on ryanbr#1535. The issue should remain open until the upstream PR is merged.bhelm commentedon Sep 7, 2026
Fixed upstream by ryanbr#1535 (merged), which took option 1 and ported the SDNN index to
Android:
HrvAnalyzer.sdnnIndex, wired intoAnalyticsEngine, with theavgSdnnRoom column,its migration and mirrored tests. The comment now describes a twin that exists. Closing.