[Bug]: A seeded dev server can remove the developer's real worktrees when copied settings enable cleanup#33901

opened4d agobyLLenoridev

Before submitting

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

Area

apps/server

Steps to reproduce

This needs two settings that are off by default, so it does not happen on a dev server with no settings.json. I reproduced it with throwaway Git repositories only.

  1. Have a live install that uses a custom worktrees folder (worktreesDirectory, or an older one in previousWorktreesDirectories), with a stopped thread whose worktree is in that folder.
  2. In a linked Git worktree of T3 Code, seed the dev database with vp run migrate-dev-db or the first vp run dev. The seeded thread keeps its worktreePath, and its project keeps workspace_root.
  3. Copy settings.json into <worktree>/.t3/userdata/, with at least one worktree cleanup rule on, such as storageCleanup.worktreeAfterDays. AGENTS.md allows this when the flow under test needs it. Setting the same values in the dev server's Settings would have the same effect, but I did not exercise that route.
  4. Let StorageCleanup run. It runs hourly, or from Settings.

Expected behavior

A dev server seeded from the developer's data leaves the developer's real worktrees alone. It can still clean worktrees it created under its own T3 home.

Actual behavior

StorageCleanup runs git worktree remove against the developer's repository for the seeded thread. Every other guard passes, because the worktree is inside a folder the copied settings name as managed.

The guards protect less on a copied database:

  • Open terminals and live provider sessions are only checked in the dev server's own process, so a live install using that checkout does not count.
  • Thread activity stops at the time of the seed, so "inactive for N days" grows even while the live install keeps working there.
  • In the default uncommitted-changes mode, ignored files such as .env are removed with the checkout. In tracked-changes mode, git clean -ffdx also removes untracked files first.
  • Committed, unpushed work stays on its branch. Only the checkout goes away.

When settings.json adds no custom folder, main is already safe: the worktree is kept as "Outside the managed worktree folder", because the only managed folder is <worktree>/.t3/worktrees. The dev runner and worktree setup do not write settings.json. Per-project cleanup overrides live in settings.json, not in the database, so the seed does not bring them.

This is the same class of problem as #15513 and #32736.

Impact

Minor bug or occasional failure

Version or commit

main @ 41443e5706

Environment

Linux, Node 24.22.0, Git 2.47.3

Logs or stack traces

shell
1$ node scripts/migrate-dev-db.ts --base-dir <tmp>/dev-checkout/.t3 --source <tmp>/source/userdata/statev2.sqlite
2Dev database ready: <tmp>/dev-checkout/.t3/userdata/statev2.sqlite (860 KB)
3 Synthetic: 1 threads
4seeded thread: worktreePath=<tmp>/real/worktrees/real-feature workspace_root=<tmp>/real/repo
5cleanup report: removed | Inactive for 101 days | <tmp>/real/worktrees/real-feature
6after: worktree exists=false ignored .env exists=false
7branch real-feature still at: unpushed work

Workaround

Do not copy a settings.json that has both a cleanup rule and a custom worktrees folder into a seeded dev home.

Investigated with Claude Opus 5.5 in Claude Code.