From 2ca365e3f44c05c80b08dae586780b5a81ebb630 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Fri, 12 Jun 2026 15:22:12 -0400 Subject: [PATCH] docs: add agent guide (AGENTS.md) and remove reference recording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AGENTS.md: project context for agents — architecture, domain facts (schedule math, hard mode policy, shields), build/test via Xcode MCP, TDD workflow expectations, UI-test harness (launch args, seed scenarios, accessibility identifiers, XCUI gotchas), known gaps - CLAUDE.md points to AGENTS.md - Delete the Opal reference screen recording (spec already captures it) and drop the video patterns from .gitignore Co-Authored-By: Claude --- .gitignore | 4 -- AGENTS.md | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 5 +++ 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 11d8786..0d11cdc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,3 @@ xcuserdata/ # Build artifacts DerivedData/ build/ - -# Screen recordings / reference material -*.MP4 -*.mp4 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2031051 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,120 @@ +# Severed — Agent Guide + +Severed is an iOS Screen Time app: recurring **rules** that block selected +apps (Schedule windows, Time Limits, Open Limits), with a **Hard Mode** that +makes an active block impossible to lift, edit, or delete until it ends. The +feature set is a clone of Opal's "Rules"; the presentation is bare native iOS +(List/Form/NavigationStack, default color scheme). + +## Repo layout + +``` +Severed/ App target (iOS 26, SwiftUI + SwiftData) + Models/ BlockingRule (@Model), RuleDraft, RuleKind, + Weekday, RulePreset + Logic/ Pure, heavily unit-tested: + RuleSchedule (window math, incl. overnight), + RuleStatus (derived status + labels), + RulePolicy (Hard Mode gating, unblock/pause) + Services/ ScreenTimeAuthorization (FamilyControls behind a + protocol + mock), ShieldController (ManagedSettings + shields + adult-content web filter + mock), + RuleEnforcer (active rules → shields), + LaunchConfiguration + SampleRules (UI-test harness) + Views/ Native SwiftUI screens (see docs spec §6) +SeveredTests/ Swift Testing unit suites (@MainActor — the app + target defaults to MainActor isolation) +SeveredUITests/ XCUITest flows (see harness below) +docs/RULES_FEATURE_SPEC.md Feature spec derived from the Opal reference + recording; §6 maps it to the native presentation. + Review/update this BEFORE behavior changes. +``` + +## Domain facts worth knowing + +- Times are stored as **minutes from midnight**; `end <= start` means the + window crosses midnight (e.g. 22:00→06:00) and belongs to the day it + *starts* on. `start == end` = 24h window. +- Status is always **derived** (`rule.status(at:calendar:)`), never stored: + `disabled / dormant / active(until:) / paused(until:) / upcoming(startsAt:)`. + Labels match the reference app ("6h left" rounds hours **up**). +- **Hard Mode**: `RulePolicy` is the single gate — while a hard-mode rule is + actively blocking, canEdit/canDisable/canDelete/canUnblock are all false. + Soft rules can be "unblocked", which sets `pausedUntil` = window end (the + rule re-arms at its next window). +- Shields: one `ManagedSettingsStore` per rule (`rule-`), tracked in + UserDefaults for stray cleanup. `blockAdultContent` engages + `webContent.blockedByFilter = .auto()` alongside the shield. +- `RuleEnforcer.refresh` is the only place shields change; the home view runs + it on rule changes and a 30s loop while visible. + +## Build & test + +- Open `Severed.xcodeproj` in Xcode; build/test through the **Xcode MCP** + tools (`BuildProject`, `RunAllTests`, `RunSomeTests` — get the tab id from + `XcodeListWindows`). Make sure the scheme destination is an iOS + **simulator**; a physical-device destination makes test runs hang or get + cancelled. +- The project uses Xcode file-system-synchronized groups: adding/removing + `.swift` files on disk is enough, no pbxproj editing. +- Family Controls entitlement is configured (`Severed/Severed.entitlements`). + FamilyControls/ManagedSettings compile and run on the simulator, but real + blocking behavior is only observable on a device. + +## Workflow expectations (user preference) + +- **Red-green TDD**: update `docs/RULES_FEATURE_SPEC.md` first for behavior + changes, write the failing test, run it (compile failure counts as red), + implement, re-run focused tests, then the full suite. Run tests often and + fail fast. +- Conventional commits (`feat:`, `fix:`, `refactor:` …), no AI attribution + footers. Commit only when the user asks. + +## UI-test harness + +`SeveredApp` reads launch arguments (parsed by `LaunchConfiguration`): + +| Argument | Effect | +|---|---| +| `-ui-testing` | In-memory SwiftData store, mock authorization, mock shields | +| `-onboarding-completed` / `-onboarding-required` | Force the onboarding flag | +| `-seed-scenario=standard` | Active soft rule "Work Time" + upcoming "Sleep" | +| `-seed-scenario=hard-mode-active` | Active Hard Mode rule "Locked In" + upcoming "Sleep" | + +Use `XCUIApplication.launchSevered(...)` (UITestSupport.swift), which also +provides `app.element(_:)` for identifier lookup across element types and +`waitToAppear()`. + +Key accessibility identifiers (keep stable — tests and future work rely on +them): `newRuleButton`, `ruleCard-`, `ruleStatus-`, +`blockedTile-`, `nothingBlockedLabel`, `emptyRulesCard`, +`closeNewRuleButton`, `ruleKind-`, `preset-`, `ruleEditorTitle`, +`fromTimePicker`/`toTimePicker`, `dayToggle-1…7`, `selectedAppsRow`, +`hardModeToggle`, `adultContentToggle`, `dailyLimitStepper(+Value)`, +`maxOpensStepper(+Value)`, `commitRuleButton`, `doneButton`, +`toggleEnabledButton`, `deleteRuleButton`, `closeDetailButton`, +`detailRuleName`, `detailStatusLabel`, `detailRow-