isolation: "worktree" agents can't produce persistent side-by-side previews
Problem
When using isolation: "worktree" on the Agent tool to spawn multiple parallel agents (e.g., 5 design variations of the same page), there's no way to get persistent dev servers running for side-by-side browser comparison.
What we tried
Goal
Spawn 5 agents in parallel, each implementing a different UI layout variation of the same page, each with its own dev server so the user can open 5 browser tabs and compare designs visually.
Attempt 1: isolation: "worktree" (the correct approach per docs)
Agent(
subagent_type="animatix-ui-agent",
description="Option A: Layout variation",
prompt="...",
isolation="worktree",
run_in_background=true,
mode="bypassPermissions"
)
// × 5 agents in parallel
What happened:
- WorktreeCreate hook fired correctly (after fixing a bug in the hook command —
$WORKTREE_PATHwasn't resolving in the$(git -C $WORKTREE_PATH ...)subshell) - Hook started dev server via
pnpm dev -- -p $PORT & - Agents worked correctly and committed their changes
- But: when each agent finished, the temporary worktree was cleaned up, killing the dev server
- All 5 commits stacked linearly on
ivan-developinstead of staying on separate branches - No persistent preview URLs for the user to compare
The fundamental issue: isolation: "worktree" is designed for work-then-merge, not for persistent parallel previews. The worktree lifecycle is tied to the agent lifecycle.
Attempt 2: Manual git worktree add + various process persistence methods
Tried keeping dev servers alive across Bash tool invocations:
| Approach | Result |
|----------|--------|
| nohup pnpm dev & | Process killed when Bash tool exits |
| tmux new-session -d | Session created but killed/inaccessible |
| setsid pnpm dev & | Process killed |
| bash -c "... &" & disown | Subshell killed |
| run_in_background Bash param | Process killed when task completes |
| osascript Terminal.app | Works but is a hacky escape hatch |
The only method that persists is osascript launching Terminal.app windows, which defeats the purpose of Claude Code's built-in worktree support.
Expected behavior
Ideally, one of these would work:
Option A: Persistent worktree mode on Agent tool
A parameter like isolation: "worktree-persistent" or keep_worktree: true that:
- Creates the worktree (fires WorktreeCreate hook → dev server starts)
- Agent does its work and commits
- Worktree stays alive after agent finishes (dev server keeps running)
- Returns the worktree path + port to the orchestrator
- User reviews in browser, then orchestrator cleans up explicitly
Option B: Dev servers survive agent completion
If the WorktreeCreate hook starts a background process (dev server), that process should survive the agent's completion and only be killed when WorktreeRemove fires.
Option C: Separate branches per worktree agent
When 5 agents all use isolation: "worktree" from the same base, their commits should land on 5 separate branches (e.g., worktree-abc123, worktree-def456), not stack on the parent branch. This would at least allow the orchestrator to create persistent worktrees from those branches afterward.
Use case
This is critical for design exploration workflows where you want to:
- Spawn N agents with different design specs
- Each modifies the same file(s) with a different approach
- All run in parallel with live dev servers
- User opens N browser tabs to compare visually
- User picks the winning design
- Orchestrator merges the winner and cleans up the rest
Environment
- macOS (Darwin 24.6.0)
- Claude Code CLI
- Next.js + Convex project
- WorktreeCreate/WorktreeRemove hooks configured via
.claude/settings.json
Workaround
Currently the only working approach is:
- Use
isolation: "worktree"for code changes (agents commit successfully) - Manually separate the stacked commits into branches (
git branch design-x <commit>) - Manually create persistent worktrees (
git worktree add) - Launch dev servers via
osascriptTerminal.app (only method that persists)
This is fragile and defeats the purpose of the built-in worktree support.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗