Replace the wide BlockingRule/RuleDraft "god struct" — where every option
existed for every kind — with a RuleConfiguration sum type that carries only
the options each kind actually has:
.schedule(ScheduleConfig: window + selectionMode + blockAdultContent)
.timeLimit(TimeLimitConfig: dailyLimitMinutes)
.openLimit(OpenLimitConfig: maxOpens)
Name, days, Hard Mode, app list, and pause stay common to all kinds. This
makes illegal states unrepresentable: Block / Allow Only and Block Adult
Content are now structurally Schedule-only.
User-visible behavior change: the Time Limit and Open Limit editors no longer
offer a Block Adult Content toggle, and their detail sheets drop the "Adult
websites" row — those never made sense for a usage budget. Limit rules are
always Block and never engage the web-content filter.
BlockingRule keeps flat columns as raw persistence behind a computed
`configuration` bridge (lowest SwiftData risk; the cross-process RuleSnapshot
wire format and the Screen Time extensions are untouched). Logic, the editors,
and the detail sheet all switch on the sum type.
Spec (§1, §3.5/§3.6, §5.2) updated first; tests reworked to the new API with
new structural-guarantee unit tests and a UI test asserting the Time Limit
editor/detail omit adult content. Full suite green (180 tests).
Co-Authored-By: Claude <noreply@anthropic.com>
- RuleUsage + UsageLedger: per-rule, per-day minutes/opens in app-group
defaults (monotonic minutes, incrementing opens, midnight rollover by
day-keying); MockUsageLedger for tests and seeded UI scenarios
- usage-aware status: a limit rule whose daily budget is spent on an
enabled day is active (blocking) until next midnight; Hard Mode gating
and app-list locking honor it; soft unblock pauses until midnight
- RuleEnforcer shields spent limit rules (always Block mode) while the
app runs
- new Usage section under Blocked Apps: '18m of 45m used today · 27m
left', '2 of 5 opens today · 3 opens left', 'Blocked until tomorrow'
- new 'limits' seed scenario + UI tests
Co-Authored-By: Claude <noreply@anthropic.com>