BuildNot yet confirmed elsewhere1 publisher3 min readPublished
Parallel coding agents on Windows break at the home directory, not the launcher
One practitioner's teardown notes: shared authenticated sessions, half-redirected homes, hardlinks that deny being links, and a delete that blames permissions for something else.
The Engineer · Build desk
What happened
- Two agents run under the same Windows account share ~/.claude, ~/.codex and ~/.gemini, which means the same config and the same authenticated session.
- Removing a git worktree while a process still has its working directory inside returns "failed to delete ... Permission denied".
- The list of concerns came from a reader comment by Alex Shev, naming isolation, logs, file ownership and cleanup after failed runs as the things that decide whether a setup survives.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure A detach control built on symlink checks leaves hardlinked accounts writing into the shared global config while reporting themselves separated, so one agent's edit reaches every other pane.
- constraint Nothing in the redirect path raises an error, so the earliest available signal of a leaked identity is commits already attributed to the wrong account.
- decision Anyone building a launcher has to keep its own storage path and the child's home in separate variables from the start, because retrofitting that split means moving where existing agents keep their...
- precedent Because the same teardown succeeds on Linux, maintainers on other platforms will keep closing these as unreproducible, and the cost stays with the Windows users who filed them.
Each of these failures either says nothing or points somewhere else. Redirecting `HOME` alone, a POSIX convention Windows itself does not honor [4], moves a CLI's config to the new directory while git carries on writing `.gitconfig` to the old one [5], and the first visible symptom, according to the post, is two panes sharing a git identity [6]. The set that actually works is `HOME`, `USERPROFILE`, `HOMEDRIVE` and `HOMEPATH` per process [7], plus `GEMINI_CLI_HOME` pointed at its own subdirectory for Gemini CLI [8]. Five variables to relocate one idea of home [20], and nothing errors if you set three of them.
The linking layer has the same property. Full isolation is not the goal, since `CLAUDE.md`, `settings.json` and skills are meant to be identical in every pane, so they get linked back to the global copy instead of duplicated [9]. On Windows, `mklink` needs elevation or Developer Mode for a file symlink while junctions need neither, so directories are easy and files fall back to hardlinks [10]. A hardlink returns false from `lstat().isSymbolicLink()` [11]. Build a "detach this account from shared config" action as "replace symlinks with real copies" and the hardlinked files are skipped: the account goes on editing the global config while the interface reports it detached [12]. The author's detection compares device and inode numbers with `bigint: true`, because a plain `ino` comes back as `0` on some Windows configurations, which makes every pair of files look like the same file [13]. A comparison tested without that flag would have passed.
Teardown is where the error message actively misdirects. `git worktree remove --force` returns `failed to delete ... Permission denied` [14] when the actual cause is that Windows will not delete a directory that is some process's current directory, and git renders that refusal as a permissions error [15]. Testing it is worse than reading it. PowerShell's `Set-Location` does not reproduce the failure, because the PowerShell location is a provider concept layered over a process whose real working directory never moved [16], so a harness built on `Set-Location` will tell you the bug is imaginary. Killing the shell is not sufficient either: in the author's run the `cmd.exe` was dead and the delete still failed, because the holder was `timeout.exe`, a child that had inherited the directory [17]. Any teardown that kills the pane process and then removes the worktree will fail on the first agent that leaves a grandchild running.
For people writing the launcher rather than using one, Node's `os.homedir()` does not reliably reflect a `USERPROFILE` injected at spawn time on Windows [22]. The author's project ended up with a dedicated variable for its own storage and a separate function for where a new shell should start, because collapsing the two meant every new terminal opened inside the agent's config directory [18].
This is one account, from someone who discloses that he works on a product in this space and marks which decisions are his own [19]. It is also the class of detail that only gets written down by someone who has already lost a weekend to it.
What to watch
- Whether the CLI vendors document a supported per-instance config directory on Windows, which would retire the four-variable dance entirely.
- Whether git changes its message for a worktree directory held open by a running process, instead of reporting it as a permissions failure.
- Independent write-ups from other Windows launcher authors, since the hardlink and cwd findings here rest on one vendor's account of its own bugs.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence52
- Adoption
- Insufficient
- Hype gap+8
- Incentives55
- Confidence48
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Alex Shev commented that parallel agents are only practical when workspace boundaries are boring and explicit, and that on Windows he would care less about the launch trick and more about isolation, logs, file ownership and cleanup after failed runs.
- [2]
Two agents running under the same Windows account share ~/.claude, ~/.codex and ~/.gemini.
- [3]
Sharing those directories means sharing the same config and, more importantly, the same authenticated session; two Claude accounts side by side require separate home directories.
- [4]
On Windows, HOME is not the variable that redirects a home directory; it is a POSIX convention that some tools honor and Windows itself does not.
- [5]
Setting only HOME produces a half-redirected agent: the CLI writes its config to the new location while git writes .gitconfig to the old one.
- [6]
The half-redirected state goes unnoticed until two panes start sharing a git identity.
- [7]
What is actually needed per process is HOME, USERPROFILE, HOMEDRIVE=C: and HOMEPATH all pointed at the account directory.
- [8]
Gemini CLI additionally wants GEMINI_CLI_HOME pointing at its own subdirectory.
- [9]
Full isolation is not wanted: CLAUDE.md, settings.json and skills should be the same in every pane, so they are linked back to the global copy instead of duplicated.
- [10]
On Windows, mklink for a file symlink needs elevation or Developer Mode, junctions do not, so directories are fine and the fallback for files is a hardlink.
- [11]
A hardlink is not a symlink, so lstat().isSymbolicLink() returns false for it.
- [12]
If a detach-from-shared-config feature is implemented as replacing symlinks with real copies, hardlinked files are skipped and the account silently goes on editing the global config while the UI says it is detached.
- [13]
Detecting hardlinks means comparing file ids via lstatSync with bigint: true and checking dev, ino equality, because the regular ino comes back as 0 on some Windows configurations, which makes every pair of files look identical.
- [14]
Removing a git worktree that holds a process's working directory fails with: error: failed to delete 'C:/dev/feat': Permission denied.
- [15]
Permissions are not the cause: Windows will not delete a directory that is some process's current directory, and git reports the refusal as a permissions error.
- [16]
PowerShell's Set-Location does not reproduce the failure, because the PowerShell location is a provider concept layered on the process and the underlying working directory stays where the process started; -WorkingDirectory or cmd sets the actual cwd.
- [17]
Killing the shell is not enough: the author killed cmd.exe and the delete still failed because the holder was timeout.exe, a child that had inherited the working directory.
- [18]
The author's team used a dedicated environment variable for storage and a separate function for where a new shell should start, because collapsing them meant every new terminal opened inside the agent's config directory.
- [19]
The author states he works on NestMux and has a stake in the subject, says most of the material is plain Windows and plain git, and flags which decisions are his project's and where they fall short.
- [20]
Relocating one agent's home on Windows takes five environment variables in total for a Gemini-capable setup.
- [21]
On Linux the same worktree removal succeeds, which is why the problem tends to reach Windows users first as a bug report nobody else can reproduce.
- [22]
Node's os.homedir() does not reliably reflect a USERPROFILE injected at spawn time on Windows, so an app reading homedir() for its own storage will disagree with the USERPROFILE it redirects for child processes.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toIsolation, file ownership and cleanup: the boring half of running coding agents in parallel on Windows
1 article · August 23, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.