Anthropic publishes a page called "Choose a sandbox environment". Its stated purpose is to help you choose the right isolation for your threat model, and it names five ways of doing that: the sandboxed Bash tool, the sandbox runtime, a dev container, a custom container, a virtual machine.

Git worktrees are not on that list.

I have been running agents in worktrees for months. If you had stopped me in the street and asked what the worktree was for, I would have said it was the edge of the damage.

That is not what the documentation says, and it is not what the feature was built for. It is what I had quietly promoted it to, some time during the months of letting background agents run unattended, and I never once noticed myself doing it.

What the page actually says a worktree is for

The worktrees page is not vague. A worktree is "a separate working directory with its own files and branch, sharing the same repository history and remote as your main checkout".

Running each session in one, it says, "means edits in one session never touch files in another, so one session can build a feature while a second fixes a bug".

That is a collision story, start to finish. Two sessions, one repository, nobody stepping on anybody. The page adds that worktrees "isolate file edits, while subagents and agent teams coordinate the work itself".

The subagent documentation is in the same register. Set isolation: worktree and the subagent gets "an isolated copy of the repository".

An isolated copy of the repository. Read that as somebody who has just handed an autonomous process a shell, and tell me you do not hear a promise in it.

The word is doing work the sentence never asked it to

Nobody wrote anything false. The trouble is that "isolation" is not a neutral word. It arrives carrying the idea that something is being kept away from something else.

Once a feature has that word attached, the reader supplies the rest for free. So the worktree stops being a way to avoid merge conflicts and becomes, in my head and I suspect in yours, the wall. The agent is in there. My real branch is out here. Whatever happens in the box stays in the box.

Six fixes in twenty-eight days

Then I went and read the Claude Code changelog properly, which is a thing I recommend and almost never do.

On 7 July, version 2.1.203: "Fixed worktree-isolated subagents sometimes running shell commands in the parent checkout instead of their own worktree."

On 8 July, 2.1.205, the Windows removal path deleting files outside the worktree when an NTFS junction or directory symlink sat inside it.

On 14 July, 2.1.210: "Fixed isolation: 'worktree' subagents being able to run git-mutating commands against the main repo checkout instead of their own isolated worktree."

On 17 July, 2.1.212, worktree creation following a repository-committed symlink at .claude/worktrees, "which could create files outside the repository".

That one is worth sitting with. The symlink is committed to the repository, and the repository is the thing you cloned. The input is attacker-controlled by construction.

On 20 July, 2.1.216, subagents redirecting git into the shared checkout via git -C, --git-dir, or the GIT_DIR and GIT_WORK_TREE environment variables.

And yesterday, 4 August, version 2.1.222: "Fixed worktree-isolated sessions and their subagents being able to run destructive git commands against the main checkout; isolation now applies to file edits and Bash in every session type."

Six fixes, all in one direction, inside four weeks.

Read the last one backwards

"Isolation now applies to file edits and Bash in every session type."

Now means not before. Every session type means that for some session types, previously, no. That single clause is the most honest description of the feature I have found anywhere in the product, and it sits in a bug fix rather than on the page where somebody choosing an isolation strategy would have to read it.

None of this is me catching Anthropic out. Shipping six fixes in a month for the same class of escape is what taking something seriously looks like, and that is the point.

You do not spend four weeks of engineering defending a boundary you consider decorative. The behaviour of the code says worktree isolation is a boundary. The documentation still files it under parallel work.

The vendors are not the ones being vague

The part that surprised me is that the disclaimers are already written, and they are good. The sandboxing documentation is blunt: "Sandboxing reduces risk but is not a complete isolation boundary. Review the limitations below before relying on it as a hard security control."

The environments page repeats it. Anthropic even names the specific foot-gun, that allowing /var/run/docker.sock "effectively grants access to the host system through the Docker socket".

Cursor is blunter still, in its own enterprise documentation: "There is no security boundary between agents and your user account. If your account can delete files, agents can delete files."

Zed says a sandbox "is not a substitute for good security practices. It is one layer in a defense-in-depth strategy", then lists what its own sandbox has no effect on, including language servers and the built-in git client.

So the disclaimers exist, they are clear, and every one of them lives on a page about sandboxes. My mistake was never reading a sandbox page, because I was not using a sandbox. I was using a directory with a reassuring name.

Somebody outside the box always reads the files

Pillar Security published a writeup on 20 July covering escapes across Cursor, Codex, Gemini CLI and Antigravity. Their summary of the whole set is the sentence I would put on a poster.

"In almost every case, the agent did not need to break the sandbox directly. It only had to write something that a trusted component outside the sandbox would later run, load, scan, or treat as safe."

A virtualenv interpreter the Python extension invokes. Git metadata the git extension walks. A .vscode task file the host runs on its own.

One of them became CVE-2026-48124, scored 8.5, where Cursor executed hook commands out of a workspace file without asking. Worth saying plainly, because the file is called .claude/settings.local.json and the name has confused people: that is a Cursor bug, in Cursor's own reading of that file. The writeup never mentions Claude Code at all.

Zed makes the same point about itself, unprompted, with an example I have not stopped thinking about. An agent can add a malicious Rust procedural macro to your codebase, and rust-analyzer will execute it outside the sandbox, because executing procedural macros is what rust-analyzer is for.

Every one of these is the same shape. The container held. The consequences did not, because containment was applied to the process while the damage was done by a well-behaved tool doing its job on a file the process was allowed to write.

What I changed

I stopped treating the worktree as a safety feature and went back to using it as what it is, which is a very good way to keep four agents from fighting over the same files. When I want a blast radius, I use something from the list. A container, a VM. Those are on the page for a reason.

I also went looking for the places where my own instructions leak. An absolute path in a prompt does not care which worktree you are in. Neither does git -C.

I have watched an agent inside an isolated worktree write straight into the main checkout because a path I had written months earlier still carried the primary repository's prefix, and the agent was following my instructions exactly.

And I read the changelog now. Not thoroughly, and not every day. But a tool I hand a shell to five days a week publishes a list of the things it has recently stopped being able to do, and I had been ignoring that list for months while forming confident opinions about what it could not do.

The list is the useful part

There is a page that lists the isolation options. It is short, it is specific, and the thing I was relying on is not on it.

That gap is not a documentation bug, and I do not think a footnote fixes it. It is what happens when a convenience feature works well enough for long enough that people start resting weight on it.

Nobody in the chain, me included, ever writes down that the weight is now there. The changelog is the only place that fact lives, one bug fix at a time, in the past tense.

Go and look at whatever you are currently treating as your boundary. Then find the page where the vendor lists its boundaries, and check whether yours is on it. Mine was not, and I had been trusting it for months.