- 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
Screen 1 is a plain List (name field, the apps the list contains via
FamilyControls token Labels, and an Edit Apps button); screen 2 pushes
Apple's FamilyActivityPicker whose Save applies the selection back.
Documents that the picker silently drops selections without real
FamilyControls authorization (mocked in -ui-testing launches).
- AppList @Model with launch migration from legacy inline selections
(rules with identical selections share one list; idempotent)
- rules point at one app list; Block/Allow Only belongs to the rule and
is offered only in the Schedule editor (limit kinds sanitize to Block)
- app-list picker sheet (select / create / edit / delete with in-use
protection) replaces the per-rule selection sheet
- Hard Mode locks app-list editing while any hard rule is blocking
- SwiftData stability: relationships are only wired between managed
models, and unit tests share one in-memory container (fresh context +
data wipe per test) — per-test container creation trapped
intermittently inside SwiftData
Full rename ahead of App Store submission: project, targets, bundle
identifiers (dev.bchen.Severed -> dev.bchen.OpenAppLock), entitlements
file, app entry point, user-facing onboarding strings, test helpers,
and docs. Verified: all 62 unit tests pass.