One of the products I'm building is a Windows desktop app, a from-scratch C# and WPF rewrite of a Mac tool I already ship. I do all my work on a Mac. The problem writes itself: WPF depends on the Windows Desktop targeting pack, which does not exist for macOS, so the project simply cannot build on the machine I actually use. For a while I assumed that meant I had to move my whole workflow into Windows, agent included, and give up the Mac environment I've spent years tuning.
That assumption was wrong, and unlearning it is the whole point of this piece. An AI agent does not have to live on the machine that runs the code. The edit surface and the execution surface can be two different computers, and once you accept that, the setup gets simple in a way that surprised me.
Two planes, not one machine
The mental model that made this click is to stop thinking about "where the project lives" and start thinking about two separate planes.
There's an edit plane: reading files, searching the tree, applying diffs, moving things around. This is high-frequency, latency-sensitive work. Every keystroke of an agent's editing loop touches it. I want that plane to be as close to native as I can make it.
Then there's an execution plane: compiling, running the test suite, invoking git, publishing a build. This is lower-frequency but it has a hard requirement the edit plane doesn't. It has to happen on Windows, because that's the only place the code will build.
Almost all the friction I used to feel came from assuming both planes had to sit on the same box. They don't. So I put the edit plane on the Mac and the execution plane on Windows, and I bridge them with two boring, decades-old technologies: a mounted network share and an SSH session.
The edit plane: a mounted C: drive
I run the Windows 11 build inside a Parallels VM on the Mac. Parallels exposes the VM's C: drive back to the host as a mounted volume, so the entire repository shows up as an ordinary directory on my Mac at a path like /Volumes/[C] ck-win-01/.... From the agent's point of view there is nothing special about it. It's a folder. It reads files with the same tool it uses on any local project, greps the tree the same way, applies edits the same way. No remote-editing protocol, no syncing step, no "push to the VM" dance. The bytes it writes land directly on the VM's disk because that is literally where the folder is.
This is the part I'd tell anyone to copy first. Editing is where an agent spends most of its calls, and making editing feel local removes an entire category of overhead before you've solved anything else. The share does one job, does it transparently, and gets out of the way.
The execution plane: SSH into the VM
For anything that has to actually run on Windows, I keep an SSH session open into the VM. The VM has an SSH server; I authenticate with a key; the agent runs dotnet build, dotnet test, and git commands over that connection and reads the output back. The command executes on Windows, against the same files the Mac just edited, and the result streams home.
That's the entire bridge. The share carries edits one direction, SSH carries commands the other, and both point at the same files on the same disk. When the agent finishes writing a change, it doesn't need to transfer anything before it can test; the file it edited over the mount is already the file the build compiles over SSH. The two planes share one source of truth, which is the only reason the split is safe instead of a synchronization nightmare.
I lean on SSH here for the same reason I lean on giving an agent real tools instead of asking it to describe what it would do, which I wrote about in give your AI tools, not just chat. A build command that runs and returns a real compiler error is worth more than any amount of the agent reasoning about whether the code probably compiles. SSH is what turns "I think this builds" into "it built, here's the output."
The gotchas of straddling two operating systems
None of this is free, and the cost lands exactly where you'd predict: at the seam between the two operating systems. These are the specific rough edges that setup created, and they're the kind of thing you only learn by hitting them.
The PATH is not the PATH you think it is. My .NET SDK is a user-local install at C:\Users\coreyklass\.dotnet\dotnet.exe, and a non-interactive SSH session doesn't load the profile that puts it on the PATH. So a plain dotnet build over SSH fails with "not recognized," even though the exact same command works fine in an interactive Windows terminal. The fix is to call the full path to the executable in the commands the agent runs remotely. Obvious in hindsight, mystifying the first time, and precisely the sort of thing that belongs in a written note so it's solved once instead of every session.
The mount is flaky, and you have to plan for it dropping. The Parallels share is convenient but not bulletproof; it can disappear mid-session. So the workflow can't depend on it being up. When it drops, the fallback is to edit and run git directly on the VM over SSH instead, treating the mount as a nicety rather than a load-bearing assumption. Designing the two planes so either one can carry more weight when the other fails is what keeps a dropped volume from becoming a stopped afternoon.
macOS litters the Windows build with hidden files. When the Mac writes to that mounted volume, it sprinkles AppleDouble sidecar files, the ._ prefixed ones, next to real files to hold metadata the Windows filesystem doesn't natively carry. Harmless, until the C# build's source glob happily picks up ._Something.cs alongside Something.cs and hands the compiler garbage. The rule that came out of that is to delete the ._* files before building whenever a shell tool on the Mac side has been writing to the mount. It's a small thing that produces a baffling error if you don't know it's the cause.
Every one of these is a cross-OS impedance mismatch, and every one of them lives in the project's instructions file now. This is exactly what a gotchas file is for, the running log of hard-won specifics that I described in a gotchas file for your AI. The agent reads "call dotnet by its full path over SSH, clean the AppleDouble files before you build, and fall back to SSH editing if the mount drops" and it just handles the seam correctly instead of rediscovering the same three surprises every time. The instructions are what turn a fragile two-machine setup into a routine one.
Why bother, instead of just working in Windows
I could move everything into the VM and be done. I don't, and the reason is that the environment I'm productive in, my Mac, my tools, my terminal, my muscle memory, is worth preserving even when the artifact I'm producing targets another platform. The two-plane split lets me keep the whole authoring experience on the machine I like while the code gets built and tested where it has to be. The agent doesn't care which machine is which; it edits over here and executes over there, and I get to stay home.
The general lesson is bigger than one Windows app. When a task forces you onto a platform you don't want to live on, ask whether the agent actually needs to relocate or whether you just need to split editing from execution and bridge the gap. A mounted share and an SSH connection are unglamorous, forty-year-old tools, and between them they let an AI agent develop, on the machine I prefer, software for a machine it isn't running on. The context and the gotchas file, covered in give your AI the context it needs, are what make the seam invisible. The seam is still there. The agent just knows how to cross it.