Giving my coding agents their own Mac A developer set up coding agents Claude Code and Codex to run and test Mac apps inside a macOS virtual machine via Tart, and iPhone and iPad apps on dedicated Simulator devices, so agents can build, launch, screenshot and inspect apps without taking over the host Mac's desktop or keyboard. In the first successful run, Codex captured the card table of the developer's game Olsen in a Golden Gate VM, read its accessibility tree and opened the About window through a menu command; the next test is handing the same setup to Claude Code. The setup keeps Xcode and source code on the host, copies only the built Debug app bundle into the guest, and uses Tart's exec command, which requires the guest agent included in Cirrus Labs' non-vanilla images. Giving my coding agents their own Mac Yesterday I asked my agents to prepare a place where they can run my apps, inspect the result and try the controls while I keep working on my Mac. A build can pass while a button disappears behind a sheet. I want the agent to see that too. I would also like to keep the keyboard. The setup puts Mac apps in a macOS virtual machine and iPhone and iPad apps in dedicated Simulator devices. The agent builds on my Mac, launches the app in its test environment, reads an image and uses accessibility information to find controls. Claude Code and Codex get the same scripts and instructions. The first checks now run with Olsen, my card game, in a Golden Gate VM. Codex has captured its card table, read the accessibility tree and opened the About window through a menu command. My next test is to hand the same setup to Claude Code. You can build a similar loop with your own agent. A desktop for the agent Browser automation has a useful separation. Playwright can drive a page without borrowing the browser window I’m using to answer email. Native Mac apps need somewhere for their windows and menus to go too. Tart https://tart.run/quick-start/ runs macOS virtual machines on Apple Silicon. It can run without displaying a VM window. The app still has a desktop inside the guest, where opening a menu leaves my desktop alone. My host Mac already has Xcode and the source code. It builds the Debug app, then copies the finished bundle to the guest. The guest only needs to run it. That keeps a second Xcode installation and its build caches out of the VM. A shell wrapper can send an app through tar without a shared source folder: tar -cf - -C "$BUILD PRODUCTS" MyApp.app | tart exec -i "$MAC VM" tar -xf - -C /Users/admin/my-app The destination must already exist. Tart’s exec command https://github.com/cirruslabs/tart/blob/main/Sources/tart/Commands/Exec.swift requires its guest agent, which the non-vanilla Cirrus Labs images include. Keeping the source tree on the host also makes the boundary easier to understand. The guest receives the app and its test helpers. The first successful run Ask the agent to check for Tart, existing VMs and free disk space before installing anything. The image is a large download, and the VM needs room to grow. Let it ask before downloading an image or changing guest settings. For a compatible app, the initial setup looks like this: brew install cirruslabs/cli/tart tart clone ghcr.io/cirruslabs/macos-golden-gate-base:latest mac-test tart set mac-test --memory 4096 --display 1920x1200 I chose Golden Gate because some of my apps require macOS 27. Check your app’s minimum OS and the available image templates https://github.com/cirruslabs/macos-image-templates before cloning. The guest also needs the libraries your app expects. Have the wrapper start tart run --no-graphics mac-test as a managed background job. A launchd job survives the terminal command that started it. Wait for both the guest agent and the guest’s Dock before launching the app. Record which process the wrapper owns so cleanup can stop the right one. The first goal is small: launch one known scene, capture one window and read one accessibility tree. Get that working before adding a command for every control in the app. Seeing and using the app The image answers whether the screen looks right. The accessibility tree answers which element the agent can operate. On macOS, a small helper can read the application’s accessibility tree by process ID. A useful entry contains the role, identifier, title, value and frame. Actions can use a stable accessibilityIdentifier , with text matching as a fallback. If several controls match, the helper should report the ambiguity. The guest needs Accessibility permission for the process macOS attributes these commands to. Arrange that once with the user, inside the VM. For screenshots, the app can render its own window content to an image. That avoids asking for permission to record the whole desktop. Capture sheets separately when needed. Native AppKit views and Mac Catalyst need different capture implementations, so this belongs in the app-specific part of the setup. System file dialogs can live in another process. Read their accessibility information and cancel them after the check. A menu command should open the menu, choose its item and close any remaining menu in one operation. Olsen has another wrinkle. Its card table is drawn with SpriteKit. A card visible in a screenshot does not automatically become an accessibility button. The agent needs accessible game controls or a small semantic command interface before it can play a turn reliably. Repeatable scenes and separate data A scene argument lets the agent jump to the screen it needs to check. Olsen already has screenshot fixtures, including soloBasicRule , that give it a known starting position. Your app might need a seeded empty library, a long list or an error state. Keep that fixture data separate from real data. Use a distinct agent bundle identifier and a directory such as .build/agent/