Extract SustainedConditionTracker: FastDrain / SlowCharge share one sustained-condition engine (#163)#190
Merged
Conversation
…ine (#163) FastDrainDetector (#109) and SlowChargeDetector (#123) were structural twins: persisted streak state, the observation-gap lapse rule, sleep-on-unmeasurable, re-arm-on-clear hysteresis, the persist-only-on-change churn guard, and the load/save/clear boilerplate were duplicated near line-for-line — ~150 lines that had to be kept in lock-step by hand. New SustainedConditionTracker owns that machinery once: - generic Streak / Outcome records + a pure decide() core (streak-start, lapse, sustained-window, fire-once) - pluggable condition (caller passes "active": drain >= limit vs power < floor) - pluggable RepeatPolicy: fireOnce() (slow charge) vs withReminders() (fast drain reminds while the screen is off/locked) - StreakStore for persistence, keyed by each detector's own preference keys Each detector keeps a thin, rate/power-oriented decide() wrapper (its behaviour contract), delegating the mechanics to the shared core. Prefs keys unchanged, so no in-progress episode is lost on upgrade. The existing decision tests port over with only type/accessor renames — behaviour identical — and a new SustainedConditionTrackerTest covers the engine + StreakStore (incl. loading state written under the legacy keys). #132 can now add its trigger on the shared core instead of copying the pattern a third time. Closes #163 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…local (#163) Per review feedback: decide()/StreakStore signatures put each parameter on its own line (4+ params), method parameters drop `final` (kept on locals), and the inline condition expression is a named local (conditionActive = ratePph >= limit / milliwatts < floorMw) so its meaning is clear at the call site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What
FastDrainDetector(#109) andSlowChargeDetector(#123) were structural twins — the persisted streak, the observation-gap lapse rule, sleep on an unmeasurable reading, re-arm hysteresis, the persist-only-on-change churn guard, and theload/save/clearboilerplate were duplicated near line-for-line (~150 lines that had to be kept in lock-step by hand).New
SustainedConditionTrackerowns that machinery once:Streak/Outcomerecords + a puredecide()core (streak-start, lapse, sustained-window)ratePph >= limitvsmilliwatts < floor)fireOnce()(slow charge: one warning per session) vswithReminders(activelyUsed, gap)(fast drain: first alert always, then remind while the screen is off/locked)StreakStorefor persistence, constructed with each detector's own preference keysEach detector keeps a thin, rate/power-oriented
decide()wrapper — its behaviour contract — that just picks the condition + policy and delegates.evaluate()uses the sharedStreakStore.Safety / acceptance
_fast_drain_*keys.FastDrainDetectorTest(14) andSlowChargeDetectorTest(11) port over with only type/accessor renames (FastDrainState/SlowChargeState→ sharedStreak;.streakStart()→.start(),.lastSeenAbove()/.lastSeenBelow()→.lastSeen()) — every scenario and assertion is unchanged.SustainedConditionTrackerTest(9) covers the generic engine (sleep / re-arm / lapse / both policies) andStreakStore(round-trip, reminder-less variant, churn-guarded clear, legacy-key load).Pure refactor, no user-facing change (no new strings).
testDebugUnitTest(429) +lintDebuggreen; debug build installed on the Mate 10 Pro.Closes #163
Related: #109, #123, #132.
🤖 Generated with Claude Code