Skip to content

[Bug]: Automatic worktree cleanup never removes worktrees set up by t3.json's Setup Worktree action #13836

Description

@leomontigatti

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

  1. In pingdotgg/t3code, import the t3.json "Setup Worktree" action and start a thread in a new worktree. scripts/setup-worktree.ts symlinks $T3CODE_PROJECT_ROOT/.env into the worktree.
  2. Settle or delete the thread, leaving the worktree clean (no working-tree changes).
  3. Enable Settings → Storage → automatic worktree cleanup (for example "Delete worktrees with deleted threads", or an inactivity rule).
  4. Wait for the next cleanup pass.

Expected behavior

The worktree is removed: it has no local changes, and everything in it can be recreated by running the setup action again.

Actual behavior

The worktree is kept. Before removing a worktree, storageCleanup.ts lists its gitignored files, and any entry other than node_modules/ blocks removal (L226–L239, repeated at L331–L342):

git ls-files --others --ignored --exclude-standard --directory

The .env symlink created by the setup action is gitignored, so it always appears in that list. In practice, every worktree the recommended setup action has run in is ineligible. Other repos hit the same thing with ordinary generated output; in ours, setup also leaves .husky/_/ (written by husky's prepare script during install) and .react-router/ (route type generation).

The guard exists for a good reason, since ignored files can hold secrets or local data. But its only exception is a hardcoded, Node-specific node_modules/. Two changes that could each land independently:

  1. Treat symlinks pointing outside the worktree as safe to remove. Removing the worktree deletes the link, not its target. This is small and fixes this repo's own .env and infra/relay/.env links.
  2. Replace the node_modules/ special case with a general rule for reproducible paths, so it works for any language (.venv/, target/, __pycache__/, .next/, vendor/, generated types, git hook shims). Two possible shapes:
    • Baseline after setup (preferred). T3 already runs runOnWorktreeCreate, so it can record the ignored-path listing right after setup succeeds. Anything in that baseline can be recreated by running setup again and shouldn't block removal. It needs no configuration, and paths created later still block: .t3/ dev userdata only appears once a dev server runs, so it stays protected. Worktrees created before this change have no baseline and keep today's behavior.
    • Declared in t3.json. For example cleanup.reproduciblePaths: ["node_modules/", ...], defaulting to today's ["node_modules/"]. More explicit and reviewable, but each repo has to maintain the list. This follows feat(settings): resolve t3.json inside the project settings resolver #12954's pattern of letting t3.json provide settings.

Impact

Minor bug or occasional failure

Version or commit

Read from source at 6530de0339d2 (v0.0.43-nightly.20260926.2282), not yet run on a nightly. The ignored-files listing was checked locally: a symlinked .env appears in it.

Environment

Linux, T3 Code desktop 0.0.42 (AppImage).

Workaround

Delete threads by hand and confirm "Delete the worktree too?", or run git worktree remove yourself.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions