macOS test package¶
Source builds need Xcode and CocoaPods 1.17.0 (gem install cocoapods --version
1.17.0 --no-document) in addition to the toolchains in Contributing.
Use scripts/package-unix.sh macos: it applies the macOS 12 deployment target
to Swift packages and CocoaPods dependencies as well as the app. The committed
Podfile, lockfile and Xcode integration include plugins that still require CocoaPods.
The Flutter app builds for the host CPU, matching the bundled Rust and .NET helpers.
CI builds Apple Silicon (arm64, macos-15) by default. Dispatch CI with
macos_arch: x64 for Intel (macos-15-intel). A dispatch with macos_only: true
and macos_draft_release: true provides the ZIP and probe evidence through a
private draft release, independent of Actions artifact quota. Alternatively use
upload_packages: true for seven-day Actions artifacts. Download the matching
architecture's ZIP, then check it with
shasum -a 256 -c focalet-macos-<arch>.zip.sha256 before extracting it.
The ZIP includes the app, release identity and checksums. Move Focalet.app to
Applications before granting permissions so the app keeps a stable location.
The default package is ad-hoc signed and is not notarized. For a trusted test
build, use Finder's Open action and, if macOS blocks it, System Settings >
Privacy & Security > Open Anyway. Developer ID signing can be supplied through
FOCALET_MACOS_SIGNING_IDENTITY; it does not itself notarize the app. For repeated
local builds, select an existing Apple Development or Developer ID certificate
from security find-identity -v -p codesigning, then save its name or fingerprint:
git config --local focalet.macosSigningIdentity '<chosen signing identity>'
bash scripts/package-unix.sh macos
The environment variable overrides this checkout-local setting. The package
manifest records the actual signing authority and team. Keep the same identity
and /Applications/Focalet.app path across updates. Ad-hoc signatures identify the
binary by its hash, so every rebuild can invalidate Screen Recording and
Accessibility grants even while System Settings still displays Focalet as enabled.
Switching from an ad-hoc build to a certificate-signed build needs a one-time
grant for the new identity. If the old entry remains enabled but access is
denied after reopening, remove that stale Focalet entry and add the installed app
again using +. Subsequent builds must keep the saved signing identity.
First launch¶
The default window is 900×760 points; the large setting is 1100×860. Both fit to the display's available work area. A fresh install opens a small welcome flow:
- Detect local agent runtimes and select the one to use.
- Sign in through that runtime's terminal command if needed. Scan again after installing an agent, or use Configure runtime to choose its executable.
- Connect and continue, or explicitly choose Set up later. Existing settings skip the welcome flow. A failed connection or settings save leaves it open.
Finder launches also search Homebrew and the common user-local executable folders, passing that PATH to both the broker and its runtime children. Unusual locations can be added through Configure runtime.
Focalet keeps authentication, models and history owned by the selected runtime. The welcome flow also checks Accessibility (application context and AX element capture) and Screen Recording (images). If screenshot access is missing, Select and Option+A show an explanation before capture. Click Open System Settings, then turn on Focalet under Privacy & Security > Screen & System Audio Recording (called Screen Recording on older macOS versions). If Focalet is missing, use + to add it from Applications. Choose Quit & Reopen if macOS asks. The dialog rechecks permission when you return, also offers Check again, and starts the picker only when you click Start selecting. Not now cancels without a permission request or capture. The Screen Recording Allow button in setup and App settings opens the same guide. Browser URLs may additionally trigger macOS Automation consent for System Events and the selected browser. Enable denied permissions under Privacy & Security, then restart Focalet when macOS requires it. Permission checks and capture failures never open Settings themselves. Only the guide's explicit button requests Screen Recording, at most once per launch; later clicks can reopen the pane without repeating the system request.
Native build and automated evidence¶
The Mac runner uses Flutter's Skia Metal backend. Flutter 3.47's default wide-gamut Impeller path produced invalid pixel-format blits and a black window on the native Apple Silicon runner. GPU rendering remains enabled. Acceptance also checks the restored app's visible pixels and retains its screenshot.
The macOS CI job compiles Flutter and Rust on the selected Mac architecture, assembles and verifies the signed app/checksums, and runs the Rust core's actual initialize/shutdown exchange. It then launches the packaged Focalet to probe its native permission channel, window size, external TextEdit context and screen pixels when permission permits. It also verifies the app survives hiding its window for capture, then restores it. Evidence includes JSON, startup logs and a desktop screenshot if the runner allows one. Every run also prints the probe and startup log; manual dispatch can retain the evidence in a draft release or Actions artifact.
A successful startup probe with captureVerified: false is not capture
acceptance. Hosted runners may have no TCC grants. From the extracted package
folder on your Mac, close Focalet, enable its permissions, then run:
python3 docs/accept-macos.py . --output macos-acceptance --require-capture
The script opens a disposable TextEdit fixture and uses LaunchServices to start
the app with its own macOS permission identity. Directly executing the binary
can inherit the terminal or automation host's TCC identity instead. To test the
copy already granted permission in Applications, add --app /Applications/Focalet.app.
The report identifies the process and executable. It requires the fixture's
actual text in a region attachment, aligned Accessibility elements and captured
pixels. This backend check does not inject mouse gestures. It closes only the Focalet process it started;
it never changes permissions automatically. Temporary captured pixels are
removed; the output desktop screenshot remains in your local evidence folder.
For a separately launched local Chromium fixture, pass --window-title,
--expected-text, --browser-endpoint and --require-dom. This exercises the
installed app's native AX viewport and the packaged DOM helper together. A
normal browser without an authorized CDP connection can provide AX context but
does not count as a DOM pass. The probe never enables CDP in the user's browser.
Native window binding ignores the omitted cursor and uses the Dock's actual AX list bounds instead of macOS 26's full-display transparent backing window. Recording indicators outside the crop no longer erase unrelated window data; changed or overlapping windows still block that region. Kernel process start times identify CLI-launched browsers, and Chromium's lazy AX tree is requested for the selected app, with a bounded wait for its first web subtree. Repeated AX observations compare nested values rather than dictionary insertion order; changed text, bounds or element order still discard semantic metadata.
Interactive capture acceptance¶
The interactive probe drives the shared capture editor with CGEvents, verifies
the PNG matches its region mapping, and cancels a second selection with Escape. This
requires input permission for the driver as well as Focalet capture permissions.
Run the same path with --interactive on an authorized test desktop. Use
--manual-interactive to drive the two gestures yourself or through Computer
Use without launching the CGEvent driver. Otherwise manually
verify these gestures:
- With TextEdit or Safari in front, press Option+A or click Select, then drag a rectangle. Continue dragging to add up to eight boxes, with no modifier key required. Press Enter or click Attach to add the batch in selection order. Delete removes the selected box; Escape or Cancel discards the batch. Select a display from the menu to add regions from another monitor. Choose pen, arrow, shape or highlighter; Cmd+Z undoes and Cmd+Shift+Z redoes drawings independently for each region.
- Check each image preview against the selected pixels. The overlay freezes the displays before selection, and crops the original pixels without its dimming, borders, labels or toolbar. Screen coordinates and Retina image dimensions are retained. Stable accessible apps include AX text, roles, states and image-coordinate bounds. Change the source while selecting: the image and drawings stay, and stale AX/DOM context must be discarded.
- Repeat and press Escape: cancellation must add no image.
- Deny Screen Recording and retry: Focalet should explain how to enable it. Grant it, restart if requested, and repeat selection successfully.
- Quit/reopen: the welcome flow should stay completed, and Focalet should discover and reconnect to the runtime normally.
Record the commit from release-manifest.json, macOS version, architecture and
these results when reporting an issue. A mock test or Linux build cannot replace
this native evidence.