{"slug": "giving-my-coding-agents-their-own-mac", "title": "Giving my coding agents their own Mac", "summary": "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.", "body_md": "# Giving my coding agents their own Mac\n\nYesterday 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.\n\nThe 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.\n\nThe 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.\n\n## A desktop for the agent\n\nBrowser 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.\n\n[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.\n\nMy 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.\n\nA shell wrapper can send an app through `tar` without a shared source folder:\n\n```\ntar -cf - -C \"$BUILD_PRODUCTS\" MyApp.app |\n  tart exec -i \"$MAC_VM\" tar -xf - -C /Users/admin/my-app\n```\n\nThe 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.\n\n## The first successful run\n\nAsk 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.\n\nFor a compatible app, the initial setup looks like this:\n\n```\nbrew install cirruslabs/cli/tart\ntart clone ghcr.io/cirruslabs/macos-golden-gate-base:latest mac-test\ntart set mac-test --memory 4096 --display 1920x1200\n```\n\nI 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.\n\nHave 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.\n\nThe 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.\n\n## Seeing and using the app\n\nThe image answers whether the screen looks right. The accessibility tree answers which element the agent can operate.\n\nOn 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.\n\nFor 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.\n\nSystem 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.\n\nOlsen 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.\n\n## Repeatable scenes and separate data\n\nA 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.\n\nKeep that fixture data separate from real data. Use a distinct agent bundle identifier and a directory such as `.build/agent/<session>/`. Each worktree or concurrent agent needs its own instance. Review App Groups, keychain access and explicit file paths as well as the default preferences store. A new bundle identifier alone does not redirect every storage API.\n\nAgent mode should also disable external effects such as analytics, ads and cloud sync. A disposable VM helps protect the host, but a test app can still make network requests.\n\nThe skill tells the agent to look for clipped text, overlapping controls, empty content and poor contrast. Then it tries the changed flow and checks the result again, including dark mode and a narrow window. The final report should say what it actually saw.\n\n## iPhone and iPad\n\nFor iOS and iPadOS, create a fresh Simulator device for the session and address it by UDID. `simctl` can boot and control it without opening the Simulator app. Build for Simulator, install on that device, capture its screen, then shut down and delete it when the check is over.\n\nApple’s Xcode MCP tools can provide the interaction layer. The Xcode 27.2 installed on my Mac exposes device sessions, screenshots, UI hierarchies, taps, swipes and typing. The agent reads the latest hierarchy to find a control, performs the action and checks the returned image. XCUITest still covers repeatable regression tests.\n\nThe [official Xcode plugin](https://github.com/anthropics/claude-plugins-official/pull/6338) packages this for Claude Code:\n\n```\n/plugin install xcode@claude-plugins-official\n```\n\n[Apple also documents](https://developer.apple.com/documentation/Xcode/giving-external-agents-access-to-xcode) the MCP connection for Claude Code and Codex:\n\n```\nclaude mcp add --scope user xcode -- xcrun mcpbridge\ncodex mcp add xcode -- xcrun mcpbridge\n```\n\nChoose one Claude connection method. On Xcode 27, the headless service can be enabled once with `sudo xcrun mcp-server enable`. Keep its normal agent and folder approvals. The tools available depend on the installed Xcode version, so have the agent inspect them first.\n\nMy scripts own the fresh simulator and its fixture data. The agent attaches Xcode’s interaction session to that exact UDID and verifies the returned device matches. It ends the MCP session before deleting the simulator. This avoids another dependency for everyday iPhone and iPad interaction.\n\nFor Mac apps, the host’s Xcode connection still acts on the host. Tart keeps the app’s windows and menus in the guest, where the accessibility helper operates. Keep the same scene arguments across the Mac and Simulator paths where they make sense.\n\nSeveral agents can then work on different apps without resetting a device someone else is using.\n\n## Moving to another Mac\n\nI keep the drivers and bootstrap in one skills repo. After installing Xcode and the agent clients, I can prepare another Apple Silicon Mac from that checkout:\n\n```\n./install.sh --repos --app-ui\n./install.sh --check --app-ui\n```\n\nThe setup asks before installing Tart, downloading Golden Gate and enabling Xcode’s headless service. It preserves existing VMs and MCP connections. The app repos carry their own wrappers, target settings and Debug adapters, so the setup does not depend on files left in a temporary folder on my current Mac.\n\n`./new-repo.sh my-app app` also creates the wrapper and a pending configuration. Once its Xcode target exists, the creating agent uses the skill to discover the real build settings and connect the Debug bridge with isolated data. The readiness check blocks launch until that is done. An empty repo cannot supply an app window quite yet.\n\n## The same skill in Claude and Codex\n\nThe scripts do the work. A short `SKILL.md` teaches the agent when and how to use them. [Claude Code](https://code.claude.com/docs/en/skills) discovers project skills in `.claude/skills/`, while [Codex](https://developers.openai.com/codex/skills/) uses `.agents/skills/`. One source can be linked into both locations.\n\nI call mine `se-appen`, Norwegian for “see the app”. It describes the verification loop and the commands, with longer platform details in supporting files. A short section in `AGENTS.md`, also read through my `CLAUDE.md` symlink, tells the agent where to start and to ask before running a Mac app on my desktop.\n\nFor Olsen, the wrapper exposes the loop like this:\n\n```\ntools/app-ui --platform mac start soloBasicRule\ntools/app-ui --platform mac bilde\ntools/app-ui --platform mac tre\ntools/app-ui --platform mac utseende dark\ntools/app-ui --platform mac str 900 700\ntools/app-ui --platform mac stopp\ntools/app-ui --platform mac av\n```\n\nThe agent reads the image returned by `bilde` before proceeding. `stopp` stops its app instance. `av` can shut down the VM once other sessions have finished.\n\nThis prompt gives your own agent enough to start:\n\n```\nPrepare isolated visual verification for this Apple app.\nRead the repo's instructions and existing screenshot fixtures first.\n\nFor macOS, build on the host and run in a headless Tart VM.\nCheck installed tools, existing VMs, disk space and OS compatibility.\nAsk before downloading a VM or changing guest system settings.\nNever launch the Mac app on my desktop without asking.\n\nStart with one deterministic scene, one app-owned window image\nand one accessibility tree. Use isolated data and bundle IDs.\nAdd stable identifiers where the changed flow needs them.\nFor iOS and iPadOS, use a new headless Simulator per session.\nUse Apple's Xcode MCP device tools when available, bound to its exact UDID.\nKeep Mac app interaction inside the VM.\n\nPut the commands in scripts and the workflow in one skill that\nClaude Code and Codex can both discover. Inspect each image.\nReport what worked, what remains unverified and how to clean up.\n```\n\nHardware and account-dependent flows still need separate checks. Treat audio input, Apple Intelligence and Apple-ID features as capabilities to verify in the chosen environment. Ask before moving a test to the host.\n\nStop the test app when finished. Shut down the VM once no other test session is using it. My next step is to hand Olsen to Claude Code and see whether I get to keep typing.", "url": "https://wpnews.pro/news/giving-my-coding-agents-their-own-mac", "canonical_source": "https://hjerpbakk.com/blog/2026/11/12/giving-agents-their-own-mac", "published_at": "2026-11-12 06:00:00+00:00", "updated_at": "2026-11-12 07:53:12.387572+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools"], "entities": ["Claude Code", "Codex", "Tart", "Cirrus Labs", "Olsen", "Xcode", "Apple Silicon"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/giving-my-coding-agents-their-own-mac", "markdown": "https://wpnews.pro/news/giving-my-coding-agents-their-own-mac.md", "text": "https://wpnews.pro/news/giving-my-coding-agents-their-own-mac.txt", "jsonld": "https://wpnews.pro/news/giving-my-coding-agents-their-own-mac.jsonld"}}