Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Docs/Architecture/RealAppTestingPlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,22 @@ Sandbox and helper direction:
- Apple identifies assistive Accessibility API use as restricted under App Sandbox, so real app text insertion and window automation should not be forced through the sandboxed main app.
- Apple recommends diagnosing sandbox issues from the concrete sandbox violation log before adding capabilities or temporary exceptions.
- Keep the main app sandboxed for ordinary app behavior. Put assistive automation behind a separate helper boundary so the permissioned process is narrow and operator-visible.
- `SiriousInheritedSandboxHelper` is the article-shaped embedded command-line helper probe from Apple's sandboxed helper recipe. XcodeGen builds it as a sandboxed tool with `com.apple.security.inherit`, embeds it in `Contents/MacOS`, and validates it through the app's `--inherited-helper-status` diagnostic. This proves the recipe maps cleanly into the project, but it also confirms that this helper category inherits the app sandbox and is not the unsandboxed Accessibility helper path.
- Prefer a bundled LaunchAgent registered with `SMAppService.agent(plistName:)` before considering a LaunchDaemon, because these automation flows run in the logged-in user session and need user-facing Accessibility context. The first checked-in helper is `SiriousAutomationHelper`, a hardened-runtime command-line tool copied into the app bundle with its LaunchAgent plist at `Contents/Library/LaunchAgents/com.galewilliams.Sirious.AutomationHelper.plist`.
- `SiriousAutomationHelper` now embeds generated Info.plist metadata into the executable with `CREATE_INFOPLIST_SECTION_IN_BINARY` and `GENERATE_INFOPLIST_FILE`, keeping the helper identity visible to codesigning and Service Management.
- The helper LaunchAgent plist declares `BundleProgram` as `Contents/Resources/SiriousAutomationHelper` and advertises `MachServices` for `com.galewilliams.Sirious.AutomationHelper`.
- Use the XPC command channel as the normal app-to-helper path. The app sends command arguments to the helper's Mach service, and the helper returns termination status plus output/error strings over a small shared protocol. The helper still accepts direct CLI commands such as `--status` for diagnostics.
- The helper LaunchAgent plist declares `BundleProgram` as `Contents/Library/HelperTools/SiriousAutomationHelper`, targets the logged-in GUI session with `LimitLoadToSessionType` set to `Aqua`, keeps an explicit `ProgramArguments` entry for launchd-style argv shape, and advertises `MachServices` for `com.galewilliams.Sirious.AutomationHelper`.
- Use the XPC command channel as the normal app-to-helper path. The app sends command arguments to the helper's Mach service, and the helper returns termination status plus output/error strings over a small shared protocol. The helper still accepts direct CLI commands such as `--status` for diagnostics. External user LaunchAgent validation installs `SiriousAutomationHelper` under `~/Library/Application Support/Sirious/AutomationHelper`, writes `~/Library/LaunchAgents/com.galewilliams.Sirious.AutomationHelper.plist`, bootstraps it into the `gui/$UID` domain, and requires the app's narrow `com.apple.security.temporary-exception.mach-lookup.global-name` entitlement for `com.galewilliams.Sirious.AutomationHelper`.
- Gale's local Apple signing team identifier is `BC73766F69`. Some certificate common names include `AMRC3N39SQ`, but the certificate subject's organizational unit is the Team ID Xcode uses for `DEVELOPMENT_TEAM`. Keep `DEVELOPMENT_TEAM` aligned with `BC73766F69` in `project.yml` so macOS TCC can associate prompts with a stable development identity during real-app testing.

Installed-app validation note:

`scripts/validate-installed-automation-helper.sh` is the local installed-app probe. It builds Sirious, copies the app to a stable temporary path, verifies the app and helper signatures, confirms the LaunchAgent plist exists, runs the helper direct CLI, and asks the installed app for `SMAppService.agent(plistName:)` status. With `--register`, it can also request registration, check the XPC `--status` path when the agent is enabled, and unregister afterward.
`scripts/validate-installed-automation-helper.sh` is the local installed-app probe. It builds Sirious, installs the app into a validation location, verifies the app and helper signatures, confirms the LaunchAgent plist exists, runs the helper direct CLI, and asks the installed app for `SMAppService.agent(plistName:)` status. By default it copies the app directly. With `--package-style`, it stages the app under `Library/Application Support/SiriousInstalledAppValidation`, builds a local macOS Installer package with bundle relocation disabled, installs that package into `CurrentUserHomeDirectory`, and then runs the same validation checks. Running either path with `--keep-installed` keeps the validation app so later runs update it in place. Running it with `--uninstall` removes the retained validation copy. With `--register`, it can also request registration, check the XPC `--status` path when the agent is enabled, and unregister afterward.

Current local result: the copied app reports the expected app bundle path and bundle identifier, the signatures verify, the helper direct CLI answers `--status`, and the LaunchAgent plist is present at `Contents/Library/LaunchAgents`, but `SMAppService.agent(plistName:)` still reports `.notFound` from both the repo-local `.build` copy and a `~/Applications/SiriousInstalledAppValidation/Sirious.app` copy. The next helper slice should follow Apple's package-style sample more closely by installing the controller app under an Application Support location through a package or equivalent install step, then rerun the same status/register/XPC checks from that installed path.
Current local result: the copied app reports the expected app bundle path and bundle identifier, the signatures verify, the helper direct CLI answers `--status`, the helper executable is present at `Contents/Library/HelperTools`, and the LaunchAgent plist is present at `Contents/Library/LaunchAgents`, but `SMAppService.agent(plistName:)` still reports `.notFound` from the repo-local `.build` copy and a package-installed `~/Library/Application Support/SiriousInstalledAppValidation/Sirious.app` copy. Adding the launchd bridge keys `LimitLoadToSessionType=Aqua` and `ProgramArguments=["SiriousAutomationHelper"]` did not move either installed-app probe out of `.notFound`. The copied-app and package-style probes treat this as successful bundle-shape validation unless registration or `--require-service-found` is requested.

External LaunchAgent result: `scripts/manage-external-automation-helper.sh install --check-xpc` successfully builds the non-sandboxed helper, installs it into Gale's user Application Support directory, bootstraps the user LaunchAgent, and verifies the sandboxed app can reach the helper over `NSXPCConnection`. Before adding the Mach lookup temporary exception, the concrete sandbox log was `deny(1) mach-lookup com.galewilliams.Sirious.AutomationHelper`; after adding the entitlement, the app diagnostic returned `SiriousAutomationHelper is available.` While the external same-label LaunchAgent is installed, `SMAppService.agent(plistName:)` status from a validation app may report `enabled`; treat that as the external agent being active, not proof that the bundled LaunchAgent path is recognized.

Localhost socket fallback plan: keep this as a backup only if App Store review, entitlement policy, or a future distribution lane makes the Mach lookup exception unacceptable. In that design, the external user LaunchAgent would bind to `127.0.0.1` on an ephemeral port, write a user-only endpoint file containing the port plus a short-lived authentication token under `~/Library/Application Support/Sirious/AutomationHelper`, and accept requests only from clients that present the token and pass the existing code-signing audit checks where available. The app would use `com.apple.security.network.client` for the outbound localhost connection, not the Mach lookup exception. The helper should rotate the token whenever it starts, reject stale protocol versions, keep the protocol request/response-shaped like `AutomationHelperCommandResult`, and treat Accessibility commands as explicitly permissioned operations with the same human-readable failures as the XPC path. This fallback costs more implementation surface than Mach XPC: port discovery, token storage, request authentication, stale server recovery, protocol framing, and local firewall or endpoint-collision diagnostics all become product responsibilities.

## Initial Scenario Matrix

Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,29 @@ Run the installed-app helper validation probe with:
scripts/validate-installed-automation-helper.sh --install-app "$HOME/Applications/SiriousInstalledAppValidation/Sirious.app" --keep-installed
```

The script builds Sirious, installs a temporary app copy, verifies the app and helper signatures, checks the bundled LaunchAgent plist, runs the helper directly, and asks the installed app for `SMAppService.agent(plistName:)` status. Add `--register` only when that status is no longer `notFound`; registration can add a Login Items entry and may require local macOS approval.
The script builds Sirious, installs or updates a validation app copy, verifies the app and helper signatures, checks the bundled LaunchAgent plist, runs the helper directly, and asks the installed app for `SMAppService.agent(plistName:)` status. A copied validation app can still report `notFound`; that means the bundle-shape checks passed, but Service Management registration remains blocked.

Run the package-style helper validation probe with:

```sh
scripts/validate-installed-automation-helper.sh --package-style --keep-installed
```

The package-style probe installs the validation app through a local macOS Installer package into the current user's Application Support directory before checking the same helper shape and Service Management status. If either probe still reports `notFound`, do not add `--register`; registration can add a Login Items entry and may require local macOS approval. Add `--register` only when status moves to `notRegistered`, `requiresApproval`, or `enabled`.

For the currently working local external-helper lane, use:

```sh
scripts/manage-external-automation-helper.sh install --check-xpc
```

That script installs the non-sandboxed helper as a user LaunchAgent, verifies the helper is loaded, and asks the sandboxed app to reach it over XPC. The app needs the narrow Mach lookup temporary exception for `com.galewilliams.Sirious.AutomationHelper`; `network.client` is only relevant if a future fallback replaces Mach XPC with a localhost socket transport.

Remove a retained validation copy with:

```sh
scripts/validate-installed-automation-helper.sh --install-app "$HOME/Applications/SiriousInstalledAppValidation/Sirious.app" --uninstall
```

Install the local SwiftFormat pre-commit hook:

Expand Down
19 changes: 17 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ In Progress
- [ ] Decide whether a safer or richer now-playing/media-control surface can support exact play, exact pause, stop, and app-specific media behavior.
- [ ] Add app-specific default text field focus strategies for predictable compose or search targets.
- [x] Add a bundled `SiriousAutomationHelper` LaunchAgent with embedded Info.plist metadata, a launchd `MachServices` entry, and an XPC command channel for Accessibility-owned text insertion.
- [ ] Validate `SiriousAutomationHelper` registration from an installed app bundle outside DerivedData, then document the local install/update/uninstall workflow for helper testing.
- [x] Document the copied-app install/update/uninstall workflow for local `SiriousAutomationHelper` validation.
- [x] Validate `SiriousAutomationHelper` status from a package-style installed app outside DerivedData.
- [x] Align the helper LaunchAgent plist with GUI-session launchd guidance by adding `LimitLoadToSessionType=Aqua` and explicit `ProgramArguments`, and capture that Service Management still reports `.notFound`.
- [x] Add an article-shaped inherited sandbox helper probe and verify it works when spawned by the app, confirming that recipe is sandbox-inheriting rather than the unsandboxed Accessibility path.
- [x] Add an external user LaunchAgent install path for the non-sandboxed automation helper and verify the sandboxed app can reach it over XPC with a narrow Mach lookup exception.
- [x] Capture a localhost socket fallback plan in repo docs in case the Mach lookup entitlement becomes unacceptable for a later distribution lane.
- [ ] Decide whether the external user LaunchAgent helper becomes the local power-user lane, and what distribution/update/install UI should own it.

### Exit Criteria

Expand Down Expand Up @@ -184,7 +190,11 @@ Planned
- [ ] Add supervised routed-audio scenarios that play generated command audio through a virtual microphone into Sirious.
- [ ] Add Computer Use setup, observation, and recovery notes for scenarios where Accessibility or app automation leaves a real gap.
- [x] Add an installed-app helper validation slice that copies Sirious into a stable local app location before checking bundle shape, signatures, direct helper status, and `SMAppService.agent(plistName:)` status.
- [ ] Add a package-style installed-helper probe to determine whether Apple's Application Support install shape moves Service Management out of `.notFound` before registration, XPC connection, and helper Accessibility prompting.
- [x] Add a package-style installed-helper probe to determine whether Apple's Application Support install shape moves Service Management out of `.notFound` before registration, XPC connection, and helper Accessibility prompting.
- [x] Add launchd GUI-agent compatibility keys to the bundled helper plist and verify that copied-app and package-style installed probes still report `.notFound`.
- [x] Investigate why Service Management still reports `.notFound` after package-style Application Support installation verifies the app signature, HelperTools helper signature, LaunchAgent plist, and direct helper status; external LaunchAgent install works, while the bundled ServiceManagement path remains blocked.
- [x] Document the localhost socket backup transport and its extra authentication, endpoint discovery, and recovery costs.
- [ ] Design the external helper install, update, uninstall, and version check surface for local distribution.
- [ ] Decide which scenarios belong in a local `.xctestplan`, which should be manifest-gated, and which should remain manual supervised checks.

### Exit Criteria
Expand Down Expand Up @@ -222,3 +232,8 @@ Planned
- Added the local-only real-app scenario model for gated setup, expectations, cleanup, and artifact reporting.
- Added a bundled automation helper LaunchAgent with embedded helper Info.plist metadata, a `MachServices` plist entry, and an XPC command channel for helper-owned Accessibility commands.
- Added an installed-app helper validation script and captured that copied app installs still report `.notFound` from Service Management despite valid bundle shape and signatures.
- Added a package-style validation path through a local Installer package and captured that user Application Support package installs still report `.notFound` from Service Management despite valid bundle shape and signatures.
- Added launchd GUI-agent compatibility keys to the bundled helper plist and captured that the copied-app and package-style installed probes still report `.notFound`.
- Added a sandbox-inherited embedded helper probe for Apple's command-line helper recipe and captured that it works only when spawned by the sandboxed app.
- Added an external user LaunchAgent install path for the non-sandboxed automation helper and captured successful sandboxed-app XPC after adding the narrow Mach lookup exception.
- Added a localhost socket fallback plan as a backup if the Mach lookup entitlement becomes unacceptable for a future distribution lane.
Loading
Loading