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
22 changes: 22 additions & 0 deletions .codex/environments/xcode-project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copy to .codex/environments/xcode-project.toml and replace SCHEME_NAME.
# Keep DerivedData inside the worktree so Codex GUI worktree validation is isolated.
version = 1
name = "xcode-project"

[setup]
script = "xcodebuild -scheme Sirious -derivedDataPath ./DerivedData -resolvePackageDependencies"

[[actions]]
name = "resolve packages"
icon = "tool"
command = "xcodebuild -scheme Sirious -derivedDataPath ./DerivedData -resolvePackageDependencies"

[[actions]]
name = "build"
icon = "tool"
command = "xcodebuild -scheme Sirious -derivedDataPath ./DerivedData build"

[[actions]]
name = "test"
icon = "tool"
command = "xcodebuild -scheme Sirious -derivedDataPath ./DerivedData test"
1 change: 1 addition & 0 deletions .github/workflows/validate-repo-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
name: validate
runs-on: macos-26
steps:
# This is a validated floor, not a ceiling; update to newer stable official versions when validated.
- uses: actions/checkout@v6.0.2
- name: Report selected Xcode
run: xcode-select --print-path
Expand Down
1 change: 0 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

--rules andOperator,anyObjectProtocol,applicationMain,assertionFailures,blankLineAfterImports,blankLinesAfterGuardStatements,blankLinesAroundMark,blankLinesAtEndOfScope,blankLinesAtStartOfScope,blankLinesBetweenChainedFunctions,blankLinesBetweenImports,blankLinesBetweenScopes,braces,conditionalAssignment,consecutiveBlankLines,consecutiveSpaces,consistentSwitchCaseSpacing,docComments,docCommentsBeforeModifiers,duplicateImports,elseOnSameLine,emptyBraces,emptyExtensions,enumNamespaces,environmentEntry,extensionAccessControl,fileMacro,genericExtensions,headerFileName,hoistAwait,hoistPatternLet,hoistTry,indent,initCoderUnavailable,isEmpty,leadingDelimiters,linebreakAtEndOfFile,linebreaks,modifierOrder,noForceTryInTests,noForceUnwrapInTests,noGuardInTests,numberFormatting,opaqueGenericParameters,organizeDeclarations,preferFinalClasses,privateStateVariables,redundantAsync,redundantBackticks,redundantBreak,redundantClosure,redundantEquatable,redundantExtensionACL,redundantFileprivate,redundantGet,redundantInit,redundantInternal,redundantLet,redundantLetError,redundantMemberwiseInit,redundantNilInit,redundantObjc,redundantOptionalBinding,redundantParens,redundantPattern,redundantPublic,redundantRawValues,redundantReturn,redundantSelf,redundantSendable,redundantStaticSelf,redundantSwiftTestingSuite,redundantThrows,redundantType,redundantTypedThrows,redundantVariable,redundantViewBuilder,semicolons,simplifyGenericConstraints,sortDeclarations,sortImports,sortTypealiases,spaceAroundBraces,spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundOperators,spaceAroundParens,spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,strongOutlets,strongifiedSelf,swiftTestingTestCaseNames,todos,trailingClosures,trailingCommas,trailingSpace,typeSugar,validateTestCases,void,wrap,wrapArguments,wrapAttributes,wrapLoopBodies,wrapMultilineFunctionChains,wrapSingleLineComments,yodaConditions

--swift-version 6.0
--acronyms ID,URL,UUID
--allow-partial-wrapping true
--anonymous-for-each convert
Expand Down
13 changes: 11 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# AGENTS.md

## Apple / Xcode Project Workflow

- Use `xcode-build-run-workflow` for normal Xcode build, run, diagnostics, preview, file-membership, and guarded mutation work inside this existing project.
- Use `xcode-testing-workflow` when the task is primarily about Swift Testing, XCTest, XCUITest, `.xctestplan`, flaky tests, retries, or test diagnosis.
- Use `apple-ui-accessibility-workflow` when the task is primarily about SwiftUI accessibility semantics, Apple UI accessibility review, accessibility tree shaping, or UIKit/AppKit accessibility bridge behavior.
- Use `sync-xcode-project-guidance` when this repo's local workflow guidance drifts and should be refreshed or merged forward.
- Use `sync-xcode-project-guidance` when the repo guidance for this project drifts and needs to be refreshed or merged forward.
- Re-run `sync-xcode-project-guidance` after substantial Xcode-workflow or plugin updates so local guidance stays aligned.
- Use `scripts/repo-maintenance/validate-all.sh` for local maintainer validation and `scripts/repo-maintenance/sync-shared.sh` for repo-local sync steps.
- Use `scripts/repo-maintenance/release.sh --mode standard --version vX.Y.Z` from a feature branch or worktree only when the task is actually a protected-main release, publish, merge, tag, or release-PR preparation.
- Do not run the standard release workflow from `main`; when a protected-main release is explicitly requested, let it validate, bump versions, tag, push the branch and tag, open the release PR, watch CI, address valid PR comments or record out-of-scope concerns in `ROADMAP.md`, merge to protected `main`, fast-forward local `main`, and clean up stale branches.
- Treat `scripts/repo-maintenance/config/profile.env` as the installed `maintain-project-repo` profile marker, and keep it on the `xcode-app` profile for native Apple app repos.
- Read relevant Apple documentation before proposing or making Xcode, SwiftUI, lifecycle, or architecture changes.
- Read relevant Apple documentation before proposing or making Xcode, SwiftUI, lifecycle, architecture, or build-configuration changes.
- Prefer Dash or local Apple docs first, then official Apple docs when local docs are insufficient.
- Prefer the simplest correct Swift that is easiest to read and reason about.
- Prefer synthesized and framework-provided behavior over extra wrappers and boilerplate.
Expand All @@ -19,8 +21,15 @@
- Keep data flow straight and dependency direction unidirectional.
- Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for app integration, schemes, and build settings.
- Prefer Xcode-aware tooling or `xcodebuild` over ad hoc filesystem assumptions when project structure or target membership is involved.
- For new Xcode app, framework, and workspace repositories, prefer XcodeGen plus checked-in `.xcconfig` files by default unless there is a concrete reason to avoid that generator dependency.
- If this repo is XcodeGen-backed, treat `project.yml`, `project.yaml`, and any included XcodeGen specs as the source of truth for generated targets, schemes, build settings, build configurations, packages, and file membership.
- For XcodeGen-backed projects, edit the spec set and rerun `xcodegen generate` instead of hand-editing generated `.pbxproj` files.
- Prefer external `.xcconfig` files for nontrivial build settings, wire them from the XcodeGen spec, keep secrets out of committed configs, and review config diffs with the spec and generated project diff.
- After regenerating an XcodeGen project, review the spec diff, `.xcconfig` diff, and generated `.xcodeproj` diff, then validate the affected scheme with explicit `xcodebuild` commands.
- Prefer Swift Testing for modern unit-style tests, keep XCTest where Apple tooling or dependencies still require it, and use XCUITest with explicit element wait APIs instead of fixed sleeps.
- Keep `.xctestplan` files versioned when the project depends on repeatable test-plan configurations, and inspect or run them explicitly with `xcodebuild -showTestPlans` and `xcodebuild -testPlan ...`.
- Prefer normal Xcode and XCTest parallel execution for ordinary Swift Testing, XCTest, and XCUITest runs when the project, scheme, destination, and test plan support it. Do not serialize regular tests just because they use Swift, XCTest, async tests, UI automation, or `.xctestplan` matrices.
- Treat tests that load large local AI or ML models, especially models over 500 million parameters, as heavy system-resource tests. Run those tests sequentially, one at a time, and call `unload_models` on Gale's live TTS service before the heavy run and `reload_models` after it ends, even when the run fails or is interrupted.
- Prefer a checked-in repo-root `.swiftformat` file as the Swift formatting source of truth.
- Prefer a pre-commit hook such as `scripts/repo-maintenance/hooks/pre-commit.sample` that formats staged Swift sources and then verifies them with `swiftformat --lint` before commit.
- Treat SwiftLint as an optional complementary signal layer for clarity, safety, and maintainability after SwiftFormat owns formatting shape.
Expand Down
10 changes: 7 additions & 3 deletions Docs/Architecture/RealAppTestingPlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ This plan keeps those two tracks separate. Normal Xcode tests should remain repe
- [XCUITest](https://developer.apple.com/documentation/xctest) remains the default UI automation surface for Sirious-owned app windows and ordinary app-launch assertions.
- [XCUIElement.waitForExistence(timeout:)](https://developer.apple.com/documentation/xctest/xcuielement/2879412-waitforexistence) is the preferred wait primitive for UI automation. Tests should avoid fixed sleeps except where a real external app or audio route has no observable readiness signal.
- [Accessibility AXUIElement](https://developer.apple.com/documentation/applicationservices/axuielement_h?language=objc) remains the primary real-app inspection and editing surface for focused controls.
- [Apple's Service Management package-installer sample](https://developer.apple.com/documentation/ServiceManagement/updating-your-app-package-installer-to-use-the-new-service-management-api) is the next reference shape for package-style LaunchAgent validation when copied `.app` probes still report `.notFound`.
- [Audio Hijack scripting](https://rogueamoeba.com/support/manuals/audiohijack/?page=scripting) is the first documented automation candidate for supervised audio-session setup.
- [Loopback](https://rogueamoeba.com/support/manuals/loopback/?print=true) is the first documented virtual-device candidate for routing generated audio into Sirious as microphone-like input.
- [SoundSource](https://www.rogueamoeba.com/support/manuals/soundsource/) is a documented per-app audio control candidate for supervised local routing checks.
Expand Down Expand Up @@ -164,13 +165,15 @@ Sandbox and helper direction:
- 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.
- 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/MacOS/SiriousAutomationHelper` and advertises `MachServices` for `com.galewilliams.Sirious.AutomationHelper`.
- 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.
- 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:

The next helper slice should install or copy the built app into a stable local app location before treating Service Management status as authoritative. The validated bundle checks so far are: the LaunchAgent plist is copied to `Contents/Library/LaunchAgents`, the helper direct CLI answers `--status`, and the helper binary contains an embedded `__TEXT,__info_plist` section. The remaining check is whether `SMAppService.agent(plistName:)` moves from `.notFound` to `.notRegistered`, `.requiresApproval`, or `.enabled` after running from that stable installed location.
`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.

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.

## Initial Scenario Matrix

Expand Down Expand Up @@ -217,6 +220,7 @@ Cleanup failures should be reported as first-class test diagnostics. They should
11. Add supervised routed-audio scenarios that play generated command audio through the virtual microphone path.
12. Add Computer Use notes and recovery hooks only for scenarios where app automation or audio tooling leaves a real gap.
13. Add a local installed-app helper validation script or manual runbook that copies the built app into a stable test location, checks `SMAppService.agent(plistName:)` status, registers the LaunchAgent, verifies the XPC `--status` equivalent path, prompts for helper Accessibility trust, and unregisters or removes the test install cleanly.
14. Add a package-style installed-helper probe when copied app validation still reports `.notFound`, matching Apple's sample install shape before treating Service Management registration as blocked by code structure.

## Non-Goals

Expand All @@ -233,4 +237,4 @@ Cleanup failures should be reported as first-class test diagnostics. They should
- Which app versions and target apps should be treated as required for the first real-app validation pass?
- How much audio-route setup should Sirious automate versus only detect and document?
- Should routed-audio runs produce a local report artifact separate from `.xcresult` so route, app, and permission state can be inspected outside Xcode?
- Should the first installed-app helper validation use a plain copied `.app` under a local build-products or Applications-style directory, or should it use a `.pkg` path matching Apple's Service Management sample before broader real-app automation depends on it?
- Does a package-style install under Application Support move `SMAppService.agent(plistName:)` from `.notFound` to `.notRegistered`, `.requiresApproval`, or `.enabled` for the current helper layout?
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ Real-app and routed-audio testing is planned in [Real App Testing Plan](./Docs/A

Automation-helper testing is part of that local-only path. Sirious bundles `SiriousAutomationHelper` as a LaunchAgent-backed helper with an XPC command channel for Accessibility-owned text insertion. The current code validates the built bundle shape, but Service Management registration should be checked from a stable local app install rather than only the DerivedData build product.

Run the installed-app helper validation probe with:

```sh
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.

Install the local SwiftFormat pre-commit hook:

```sh
Expand Down
4 changes: 3 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ Planned
- [ ] Add Loopback and Audio Hijack route detection before attempting any automatic audio setup.
- [ ] 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.
- [ ] Add an installed-app helper validation slice that copies or packages Sirious into a stable local app location before checking `SMAppService.agent(plistName:)` status, registration, XPC connection, and helper Accessibility prompting.
- [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.
- [ ] 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 @@ -220,3 +221,4 @@ Planned
- Added a repo-local SpeakSwiftlyServer fixture generation command and refreshed the paired MP3 corpus through the live service.
- 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.
Loading
Loading