Skip to content

Fix battery estimate anchoring on a stale, multi-cycle-old peak (#99)#109

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:fix-99-battery-estimate
Jul 8, 2026
Merged

Fix battery estimate anchoring on a stale, multi-cycle-old peak (#99)#109
ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:fix-99-battery-estimate

Conversation

@digitalerdude

Copy link
Copy Markdown

Summary

  • Fixes Battery time remainder estimate #99: a WHOOP MG that tops up short of full every day never trips the near-full anchor (rule 1), so the discharge fit fell back to scanning the SoC buffer's global max — which, across many partial-top-up cycles, can sit several days back. The endpoints-only fit then nets across every undetected intermediate top-up in between, diluting the actual recent drain rate into something far flatter than reality (reported "9% = ~3 days" when the true recent drain was much faster).
  • Bounds the max search in dischargeFitWindow to at most the last two charge-step cycles, so it stays anchored to how the strap is being used right now. Buffers with 0 or 1 charge-steps search from the start exactly as before, so CI: release workflow bumps version (patch/minor/major) in source before releasing #8 and #919 are unaffected.
  • Ported the same fix to the Kotlin twin (android/.../BatteryEstimator.kt) to keep both platforms behaviour-identical, per the file's existing convention.

Test plan

  • swift test in Packages/StrandAnalytics — all 954 tests pass, including the new testOldPeakDoesNotFlattenARecentFastDrain regression case and all pre-existing fixtures (CI: release workflow bumps version (patch/minor/major) in source before releasing #8, #919, #713).
  • xcodebuild build -scheme Strand -destination 'platform=macOS' succeeds with no new warnings/errors.
  • Android: the Kotlin change mirrors the Swift one line-for-line and adds the matching JVM test, but I couldn't run ./gradlew testDebugUnitTest in this environment (no JDK installed) — worth a CI check before merge.

🤖 Generated with Claude Code

…br#99)

A WHOOP MG that tops up short of full every day never trips the near-full
anchor (rule 1), so the discharge fit fell back to scanning the SoC
buffer's global max — which, across many partial-top-up cycles, can sit
several days back. The endpoints-only fit then nets across every
undetected intermediate top-up in between, diluting the actual recent
drain rate into something far flatter than reality (reported "9% = ~3
days" when the true recent drain was closer to 6h left).

Bound the max search to at most the last two charge-step cycles so it
stays anchored to how the strap is being used right now. Buffers with 0
or 1 charge-steps search from the start exactly as before, so ryanbr#8 and
#919 are unaffected — verified against all existing fixtures plus a new
multi-cycle regression test. Ported the same fix to the Kotlin twin to
keep both platforms behaviour-identical.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ryanbr ryanbr merged commit 00a6ca8 into ryanbr:main Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Battery time remainder estimate

2 participants