Skip to content

fix: stop time-limit rules re-blocking unused apps after midnight#13

Merged
brendan-ch merged 1 commit into
mainfrom
chore/fix-time-limit-immediate-trigger
Jun 16, 2026
Merged

fix: stop time-limit rules re-blocking unused apps after midnight#13
brendan-ch merged 1 commit into
mainfrom
chore/fix-time-limit-immediate-trigger

Conversation

@brendan-ch

Copy link
Copy Markdown
Owner

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 DeviceActivityEvent threshold 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 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. 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 with Data.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.swifthandleUsageMinutes 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). Primary fix.
  • OpenAppLock/Services/RuleScheduler.swift — replaced the per-process-random Data.hashValue fingerprint 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

  • 3 new unit tests in OpenAppLockTests/SchedulingTests.swift (stale checkpoint ignored; in-window checkpoint still honoured at the 00:45 boundary; fingerprint asserted against a fixed SHA-256 constant).
  • Full suite green: 222 passed, 0 failed (Xcode, iOS simulator).
  • On-device (pending): spend a time-limit budget one day; next morning trigger a different rule's window and confirm the time-limit apps stay open. The simulator delivers no DeviceActivity callbacks, so the background cross-midnight path can only be confirmed on hardware.

🤖 Generated with Claude Code

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>
@brendan-ch brendan-ch merged commit b6e31a5 into main Jun 16, 2026
1 check failed
@brendan-ch brendan-ch deleted the chore/fix-time-limit-immediate-trigger branch June 16, 2026 22:08
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.

1 participant