fix: show the blocking rule's shield when it overlaps an open-limit rule - #58
Merged
Merged
Conversation
When an app was covered by both an open-limit rule (not blocking) and another rule that was actively blocking (schedule window open or time-limit budget spent), the shield extensions still rendered the open-limit shield with an "Open" button. Pressing it spent one of the day's opens and cleared only the open-limit rule's own store, so the other rule's shield kept the app blocked - the shield dismissed into a still-blocked app (perceived freeze) and the open was wasted. ShieldLookup now arbitrates: the Open offer is withheld whenever another covering rule's activation(...) is blocking (unless that rule is inside its granted open session, when its store is cleared). The token wrappers filter covering rules mode-aware - an Allow-Only schedule covers every app outside its selection, and all categories - and the configuration(shielding:in:) overload feeds the presented category into the arbitration so category-based blockers are seen too. The action extension re-checks the same arbitration before granting, so a press on a stale shield can no longer waste an open that would not lift the block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DLt6aLtGJexUnUcwPUQKKH
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.
Summary
When an app was covered by both an open-limit rule (not currently blocking) and another rule that was actively blocking (schedule window open, or time-limit budget spent), the shield extensions still rendered the open-limit shield with an "Open" button. Pressing it spent one of the day's opens and cleared only the open-limit rule's own
ManagedSettingsStore— the blocking rule's store kept the app shielded (shields union across stores, strictest wins), so the shield dismissed into a still-blocked app (perceived freeze) and the open was wasted.Root cause
ShieldLookup.openLimitSnapshotselected the shield purely on "an enabled open-limit rule's selection contains this token" — it never consulted whether any other covering rule was actively blocking.Fix
Shared/Enforcement/ShieldLookup.swift— new token-free arbitration core: the Open candidate is withheld whenever another covering rule'sactivation(...)(the existing shared source of temporal truth) is blocking right now, unless that rule is inside its granted open session (its store is cleared then). The candidate's own exhausted budget never suppresses it, so the "no opens left" shield keeps its counts. Token wrappers filter covering rules mode-aware: an Allow-Only schedule covers every app outside its selection and all categories (.all(except:)semantics).OpenAppLockShieldConfig/ShieldConfigurationExtension.swift— renders the plain blocked shield when arbitration withholds the candidate, and theconfiguration(shielding:in:)overload now feeds the presented category token into the arbitration so rules blocking by category membership are seen (they can never match the opaque app token).OpenAppLockShieldAction/ShieldActionExtension.swift— re-runs the same arbitration before granting, so a press on a stale shield cannot waste an open that would not actually lift the block.AGENTS.mdfeature map row updated;ShieldLookupdoc comments carry the arbitration spec.Test plan
ShieldLookupTestssuite (14 tests, written red-first): active schedule window suppresses the Open offer; spent time-limit suppresses; paused/disabled/out-of-window/not-scheduled-today blockers don't; other spent open-limit suppresses unless in its granted session; exhausted candidate still returned; Block/Allow-Only coverage semantics.🤖 Generated with Claude Code
https://claude.ai/code/session_01DLt6aLtGJexUnUcwPUQKKH