Real work rarely consists of one task at a time. While the agent fixes one bug, a second arrives, and a small refactoring you were asked to do along the way waits next to it. The temptation is to run all of it in the same working tree, right where you stand: the window is open, the branch is the same, nothing to switch. That very closeness is what creates the problem you later untangle by hand.
The naive move is clear: since the agent already works with the project files, let it edit right in the active workspace. It seems faster - nothing to prepare, the result is right there, the diff shows in the same window. As long as the task is single and short, this really works and costs nothing extra.
It breaks the moment there is more than one task, or one of them is long. The agent edits the same files as you, on top of your uncommitted changes; two parallel streams merge into one diff, and you can no longer tell whose edit is whose. Rolling back one task without touching the other becomes a separate job, and the active branch stops being a clean point to return to.
The professional mechanism removes exactly this mixing. When you create a session, Devin Local lets you choose where it will run: in the current workspace, in a new worktree, or in an existing one. For an independent task a new worktree is preferable - an isolated git working tree in which the agent edits its own diff without touching your active branch. When it is done, the Merge button carries the result back into the main workspace. An existing worktree is chosen when you return to a shelved branch; the choice is made per session, not once for the project.
Why a git worktree rather than a separate copy of the directory. A worktree shares one object store and history with the main repository, so it is cheap to create and stays a full branch, not a snapshot of files. Several worktrees live side by side, each with its own working directory and its own HEAD, and an agent in one of them physically cannot see the neighbor's edits. Isolation here is a property of the filesystem and git, not a promise in the prompt.
This isolation has a cost, and it lies in the environment. A worktree receives only tracked files - what is under git's control. Local .env, build artifacts, installed dependencies and anything else that lives outside the index may be absent in the new tree, and a task that needs them will honestly hit their absence. The fix is a safe setup hook that rebuilds the environment in the worktree from scratch: installs dependencies, prepares configs, but does not copy production secrets from your main tree. The first run in a fresh tree is therefore slower than usual - that is the price of a clean start, not a fault.
A worktree is not always justified. For a one-line edit with an obvious diff it is overkill - Command or a session right in the workspace is enough. Its zone is a multi-step independent task you would rather not run in the active branch: a long refactoring, a parallel bug fix, an experiment that may not even be kept. The rule is simple: the more a task can touch, the earlier it belongs in a separate tree. A doubt about a task's independence is itself an argument for a separate tree.
You must verify the result before the merge, not after. First read the diff in the worktree itself and run the relevant suite there - tests, linter, build. Merge does not replace review: the button moves the changes but does not decide whether they are correct. Hence the order - check in the worktree, then merge, resolve conflicts and re-run the needed set in the target branch, because the merge may have brought together pieces that were green in isolation. A practical habit is to treat a worktree like a small pull request: you do not merge it until the diff is read and the checks in it have passed.
The engineering conclusion is one: isolation is cheap up front and expensive after the fact. Creating a worktree is seconds and one button; untangling a mixed diff from two tasks is manual work with the risk of losing someone's edit. A tree for a task is worth creating when you are not yet sure the task is independent, not when you already realize it has tangled everything.
The typical failures are two-sided. The first is taking Merge for review: the changes are moved without reading the diff, and what nobody looked at in the worktree rides into the target branch. The second is building the environment with a setup hook that copies production secrets into it, turning a temporary tree into a place of leakage. The third is assuming a green run in the worktree guarantees green after merge, and skipping the re-check of the target branch. The sign of all three is the same: trusting the transfer instead of checking what exactly was transferred.