Before submitting
Area
apps/server
Steps to reproduce
- Enable any worktree rule under Settings → Storage (for example Delete unchanged worktrees).
- Start a thread in a new worktree, in a repository without ignored files other than
node_modules/.
- In that thread, let the agent start a subagent: Claude's Agent tool, a Codex or Cursor native subagent, or T3's
delegate_task.
- Let the turn finish, and leave the checkout clean with no open terminal.
- Wait for a cleanup sweep (startup, a settings change, or the hourly sweep).
Expected behavior
The subagent works inside its parent's checkout, so it should count as part of the parent thread. The checkout should be removed under the same rules as a thread without subagents, as long as the parent and all its subagents are idle.
Actual behavior
The worktree is never considered. Every subagent thread is stored with its parent's worktreePath. cleanWorktrees in apps/server/src/storageCleanup.ts (around lines 215-222) only keeps a worktree path that exactly one thread owns:
const groups = Map.groupBy(threads.filter((t) => t.worktreePath !== null), (t) => path.resolve(t.worktreePath!));
const candidates = [...groups.values()].flatMap((group) => (group.length === 1 ? [group[0]!] : []));
So a thread that ever used a subagent keeps its worktree forever, whatever its age, merge state or status. The re-check before removal (latest.length !== 1) has the same rule.
The single-owner rule came from V1 (#11598), where subagents were not app threads. In V2 they are app threads with lineage.relationshipToParent: "subagent", so the rule now also catches a thread's own subagents. The user guide says "shared worktrees ... prevent removal", which reads as sharing between separate threads, not a thread and its own subagents.
Note on delegated children: delegate_task children (creationSource: "mcp") can take messages later. When a turn starts on a thread whose checkout is missing, ProviderTurnStartService recreates it from that thread's own branch. Some delegated children on my machine have branch: null, so a fix should only count a child that cannot take messages (provider-native) or that has the parent's branch.
Impact
Major degradation or frequent failure
Version or commit
main @ 9bd1d80; T3 Code Nightly 0.0.46-nightly.20261005.2667
Environment
Linux, desktop AppImage. Claude and Codex providers.
Logs or stack traces
# One private project, 28 linked worktrees, read-only from statev2.sqlite.
# Every subagent thread with a worktree has the same worktreePath as its parent (133 of 133).
# Top-level thread status: 23 completed, 3 idle, 2 running.
# Of the 23 completed: 17 share the worktree with 1-12 subagent threads; 6 do not.
# Subagent kinds sharing a parent's worktree: provider-native and delegate_task (mcp).
On this machine, #15146 (the completed status gate) also blocks these 17 worktrees, so they are hidden twice. Fixing #15146 alone still leaves them, because they never pass the single-owner grouping. This issue is separate from #15146 and from #14742 (squash merges).
Workaround
Remove the worktree by hand. The branch stays, and the next turn on the thread recreates the checkout.
Investigated and written by Claude Opus 5.5 through T3 Code's Claude Code harness, on behalf of the reporter.
Before submitting
Area
apps/server
Steps to reproduce
node_modules/.delegate_task.Expected behavior
The subagent works inside its parent's checkout, so it should count as part of the parent thread. The checkout should be removed under the same rules as a thread without subagents, as long as the parent and all its subagents are idle.
Actual behavior
The worktree is never considered. Every subagent thread is stored with its parent's
worktreePath.cleanWorktreesinapps/server/src/storageCleanup.ts(around lines 215-222) only keeps a worktree path that exactly one thread owns:So a thread that ever used a subagent keeps its worktree forever, whatever its age, merge state or status. The re-check before removal (
latest.length !== 1) has the same rule.The single-owner rule came from V1 (#11598), where subagents were not app threads. In V2 they are app threads with
lineage.relationshipToParent: "subagent", so the rule now also catches a thread's own subagents. The user guide says "shared worktrees ... prevent removal", which reads as sharing between separate threads, not a thread and its own subagents.Note on delegated children:
delegate_taskchildren (creationSource: "mcp") can take messages later. When a turn starts on a thread whose checkout is missing,ProviderTurnStartServicerecreates it from that thread's ownbranch. Some delegated children on my machine havebranch: null, so a fix should only count a child that cannot take messages (provider-native) or that has the parent's branch.Impact
Major degradation or frequent failure
Version or commit
main @ 9bd1d80; T3 Code Nightly 0.0.46-nightly.20261005.2667
Environment
Linux, desktop AppImage. Claude and Codex providers.
Logs or stack traces
On this machine, #15146 (the
completedstatus gate) also blocks these 17 worktrees, so they are hidden twice. Fixing #15146 alone still leaves them, because they never pass the single-owner grouping. This issue is separate from #15146 and from #14742 (squash merges).Workaround
Remove the worktree by hand. The branch stays, and the next turn on the thread recreates the checkout.
Investigated and written by Claude Opus 5.5 through T3 Code's Claude Code harness, on behalf of the reporter.