fix: stop time-limit rules re-blocking unused apps after midnight#13
Merged
Merged
Conversation
A time-limit rule that legitimately spent its budget one day could re-shield its apps the next morning with zero usage that day, often right after an unrelated schedule rule's window opened. Root cause: Screen Time batches DeviceActivity threshold callbacks and fires them late, when it next wakes the monitor extension (e.g. as a schedule window opens). A leftover `minutes-k` checkpoint from yesterday's spent budget was delivered after midnight and recorded by handleUsageMinutes as *today's* usage, tripping the limit and shielding apps the user never opened. - handleUsageMinutes now drops any checkpoint whose minute count exceeds the minutes elapsed since local midnight, since that usage cannot belong to today (it is a stale cross-midnight delivery). - RuleScheduler's restart fingerprint hashed the app selection with Data.hashValue, which is seeded randomly per process, so every limit activity restarted on each launch and reset its threshold accounting (and re-armed the per-minute event chain, widening the stale-callback window). Switched to a deterministic SHA-256 fingerprint. On-device verification of the background cross-midnight path is still pending (the simulator delivers no DeviceActivity callbacks); covered by unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
A time-limit rule that legitimately spent its budget one day could re-shield its apps the next morning with zero usage that day — often right after an unrelated schedule rule's window opened.
Root cause
Screen Time batches
DeviceActivityEventthreshold callbacks and fires them late, when it next wakes the monitor extension (e.g. as a schedule window opens — there is one extension for every activity). A leftoverminutes-kcheckpoint from yesterday's spent budget was delivered after midnight and recorded byhandleUsageMinutesas today's usage, tripping the limit and shielding apps the user never opened. That timing is why it appeared right after the morning schedule block — the schedule callback was the wake that flushed the stale time-limit callback.A contributing factor:
RuleScheduler's restart fingerprint hashed the app selection withData.hashValue, which Swift seeds randomly per process, so every limit activity restarted on each launch — resetting threshold accounting and re-arming the per-minute event chain, widening the window for stale/duplicate callbacks.Changes
Shared/LimitEnforcement.swift—handleUsageMinutesdrops any checkpoint whose minute count exceeds the minutes elapsed since local midnight, since that usage cannot belong to today (it is a stale cross-midnight delivery). Primary fix.OpenAppLock/Services/RuleScheduler.swift— replaced the per-process-randomData.hashValuefingerprint with a deterministic SHA-256 (selectionFingerprint), so an unchanged selection no longer restarts its activity every launch. Also addresses the Issue-2 "usage stalls at 14/15m" display lag.Docs/AGENT_RULES_FEATURE_SPEC.md— documented both the stale-checkpoint guard and the process-stable fingerprint requirement.Test plan
OpenAppLockTests/SchedulingTests.swift(stale checkpoint ignored; in-window checkpoint still honoured at the 00:45 boundary; fingerprint asserted against a fixed SHA-256 constant).🤖 Generated with Claude Code