# \[Bug\]: Storage cleanup deletes git worktrees nested inside a thread worktree, including uncommitted work · Issue \#23866 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/issues/23866)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: backlog

Priority: 0

## Description

[Discussion and activity](https://git.cafe/pingdotgg/t3code/issues/23866)

---

### Before submitting

- [x] I searched existing issues and did not find a duplicate.
- [x] I included enough detail to reproduce or investigate the problem.

### Area

apps/server

### Steps to reproduce

1. Use a project whose root is a git repo that gitignores a folder where another repo lives, for example `/backend/` holding a separate `backend` clone. This is a common "workspace repo" layout: agent instructions and skills sit at the root, and the product repos are gitignored inside it.
2. Start a thread in a new worktree.
3. Inside that thread worktree, have the agent create a worktree of the *other* repo at the ignored path (`git -C <root>/backend worktree add <thread-worktree>/backend -b fix/x`), and leave an uncommitted file in it.
4. Leave the thread idle, with storage cleanup set to `worktreeUnchanged: true` (or delete the thread with `worktreeOnDelete`), and run storage cleanup.

A git-only repro of the underlying removal:

```sh
git init -q umb && printf '/backend/\n' > umb/.gitignore && git -C umb add . && git -C umb commit -qm base
git init -q be && touch be/f && git -C be add . && git -C be commit -qm base
git -C umb worktree add -q ../task -b task/x
git -C be worktree add -q ../task/backend -b fix/y
echo wip > task/backend/wip.txt
git -C task status --porcelain          # empty: backend/ is ignored
git -C umb worktree remove ../task      # succeeds; task/backend/wip.txt is gone
git -C be worktree list                 # task/backend is now "prunable"
```

### Expected behavior

Cleanup keeps a worktree that contains another git worktree or repository, the same way it already keeps one that contains a project checkout.

### Actual behavior

The nested worktree is deleted along with its uncommitted files, and the other repo is left with a stale (prunable) worktree entry. Its commits survive, because they're stored in the other repo.

### Impact

Uncommitted work is lost silently. An umbrella worktree never gets commits of its own, so with `worktreeUnchanged` on it is cleaned up as soon as the thread is idle and its session has ended.

Cause (in `apps/server/src/storageCleanup.ts`):

- `localChangesReason` relies on `git status`, which doesn't report ignored paths.
- `containsProjectRoot` only protects checkouts of registered T3 projects.
- `removeLinkedWorktree` then runs `git worktree remove`, which deletes ignored nested repos recursively.
- With `worktreeKeepWhen: "tracked-changes"`, the `git clean -ffdx` before removal also deletes nested repos (the double `-f`).

`worktreeKeepWhen: "any-local-files"` avoids it, but it isn't the default.

Suggested fix: before removing a worktree, refuse if it contains any `.git` file or folder other than its own at the root, or any path registered in another repo's `git worktree list`.

### Version or commit

0.1.2-nightly.20261022.3541 (source read at `2c8bdf61`)

### Environment

Linux, git 2.53.0, Node 22.23.2, Claude Code 2.1.308

### Logs or stack traces

None; the git-only repro above shows the removal.

### Workaround

Keep other repos' worktrees outside T3's worktrees folder, or set `worktreeKeepWhen: "any-local-files"` for the project.

