Skip to content

fix(server): worktree cleanup no longer deletes files hidden by showUntrackedFiles=no - #15834

Merged
juliusmarminge merged 2 commits into
pingdotgg:mainfrom
SunkenInTime:t3code/cleanup-keeps-hidden-untracked-files
Oct 7, 2026
Merged

juliusmarminge merged 2 commits into
pingdotgg:mainfrom
SunkenInTime:t3code/cleanup-keeps-hidden-untracked-files

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Automatic worktree cleanup deleted an uncommitted file because git status was configured not to show it. With this change Git refuses the removal and the file survives:

before (main at 2a45557)                        after
$ git status --porcelain       # looks clean    $ git status --porcelain       # looks clean
$ cat notes.txt                                 $ cat notes.txt
half-written migration plan                     half-written migration plan
  ... thread deleted, cleanup runs ...            ... thread deleted, cleanup runs ...
$ ls t3code-a7870ac9                            WARN GitVcsDriver.removeWorktree: git worktree
No such file or directory                         remove exited with code 128 for ...t3code-c5e48d49
                                                $ ls t3code-c5e48d49
                                                .git  .gitignore  README.md  notes.txt

Problem

git worktree remove without --force refuses a worktree that has untracked files. That check runs git status, so it honors status.showUntrackedFiles. People set that to no to speed up status in large repos. Then status reports new files as clean, Git's own check sees nothing, and git worktree remove exits 0 and deletes them. I checked this with Git 2.55 directly.

This hits automatic cleanup, which trusts git status before removing and calls git worktree remove without --force. The web client's own worktree removal already passes force: true, so it isn't affected either way.

Change

removeWorktree runs git -c status.showUntrackedFiles=normal worktree remove. The setting reaches Git's internal status check, so Git refuses the removal (exit 128) the same way it does for anyone else with untracked files. Doing it at removal time means a file created after cleanup's own checks is covered too. With --force Git skips the check, so forced removals behave as before.

Cleanup already handles a failed removal. It logs the refusal and keeps the worktree, and the warning names the worktree on each sweep.

Scope and approval

A bug fix, so no prior approval is needed: automatic cleanup deletes uncommitted files that status.showUntrackedFiles=no hides from git status. It changes only the arguments removeWorktree passes to git worktree remove. It was split out of #15434 so each PR fixes one problem.

Verification

Live run on a dev server (Windows 11, Git 2.55.0.windows.1), main at 2a45557 with and without only this change. A scratch repo with a remote, a thread in a new worktree, git config --worktree status.showUntrackedFiles no, and an uncommitted notes.txt. Settings → Storage → "Delete worktrees with deleted threads" on, then the thread deleted from the sidebar.

  • Before: cleanup logged storage cleanup removed worktree about 6 s later, and the worktree and notes.txt were gone.
  • After: removeWorktree logged git worktree remove exited with code 128 on each sweep, and the worktree and notes.txt stayed.

Tests in GitVcsDriverCore.test.ts:

  • New: "keeps a worktree whose untracked files status is configured to hide". It hides notes.txt from status, checks status reports clean, and asserts that removeWorktree fails and the file is intact. It fails without the -c flag and passes with it.
  • Two existing tests matched the exact arguments: one counted 3 arguments (now 5), one matched args[0] === "worktree" (now matches worktree remove anywhere in the arguments).
  • apps/server: vp test run src/vcs/GitVcsDriverCore.test.ts -t worktree, 18 passed. tsc --noEmit in apps/server: clean. vp lint on the changed files: one existing warning on a line this PR doesn't touch.

Not covered: the inactive, merged and unchanged rules currently skip any thread whose last turn completed (#15146, fixed by #15150), so the live run used the delete rule. The fix is in the removal itself, so it applies to every rule.

Split out of #15434, which keeps the change to which ignored files block cleanup.

Claude Opus 5.5 in Claude Code (via T3 Code).

🤖 Generated with Claude Code

…iles=no

Git refuses to remove a worktree that has untracked files unless forced,
but that check honors status.showUntrackedFiles. With it set to no, git
status reports new files as clean and `git worktree remove` deletes them,
which automatic cleanup does without --force. removeWorktree now runs Git
with status.showUntrackedFiles=normal so its own check sees them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 5, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at c20f53c

Macroscope's review found this PR approvable — This is a focused server-side bug fix that makes Git’s non-forced worktree safety check ignore hidden-untracked-file settings, preventing accidental file deletion. The accompanying integration test covers the affected scenario, and no product defaults or static-analysis suppressions are changed.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e98f1abc-537e-45c0-8e63-6f86e9d7ff4b
📥 Commits

Reviewing files that changed from the base of the PR and between f729e0e and c20f53c.

📒 Files selected for processing (2)
  • apps/server/src/vcs/GitVcsDriverCore.test.ts
  • apps/server/src/vcs/GitVcsDriverCore.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

removeWorktree now checks untracked files even when repository configuration sets status.showUntrackedFiles=no. Tests verify that removal fails and preserves an untracked file in that configuration.

Changes

Worktree Removal Check

Layer / File(s) Summary
Override hidden untracked-file setting
apps/server/src/vcs/GitVcsDriverCore.ts, apps/server/src/vcs/GitVcsDriverCore.test.ts
removeWorktree runs git worktree remove with status.showUntrackedFiles=normal. Tests check the updated command arguments and verify that removal fails while an untracked file remains when status.showUntrackedFiles=no.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to c20f5

Worktree cleanup now preserves untracked files hidden by repository configuration, while clean removal remains covered. No actionable merge risk remains.

Architecture Summary

Architecture risk: 🔵 Low · up to c20f5

The change affects 1 system.

Changed systems: apps/server

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — apps/server (service) was modified; 2 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in apps/server/src/vcs/GitVcsDriverCore.test.ts: The removal failure assertion now expects five command arguments rather than three.
  • observed — Modified behavior in apps/server/src/vcs/GitVcsDriverCore.test.ts: Adds a test that creates a worktree, hides untracked files via status.showUntrackedFiles=no, and verifies removal fails while the untracked notes.txt remains.
  • observed — Modified behavior in apps/server/src/vcs/GitVcsDriverCore.test.ts: The delayed-removal test now identifies the command by searching its joined arguments for "worktree remove" instead of separately matching worktree and remove as the first two arguments.
  • observed — Modified behavior in apps/server/src/vcs/GitVcsDriverCore.ts: removeWorktree adds a comment describing how status.showUntrackedFiles=no can hide untracked files from Git’s removal check, and adds -c status.showUntrackedFiles=normal to the removal command. The command now checks untracked files regardless of that setting.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the fix: worktree cleanup no longer deletes files hidden by the Git status setting.
Description check ✅ Passed The description covers the problem, change, scope and approval rationale, and verification results. It also identifies the untested cleanup rules and the agent used.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@juliusmarminge
juliusmarminge merged commit d9068d3 into pingdotgg:main Oct 7, 2026
30 checks passed
sandscooling pushed a commit to sandscooling/t3code that referenced this pull request Oct 7, 2026
Upstream sync (run on request ahead of a build): 13 commits to f570bd2,
including Claude session fixes (pingdotgg#16897, pingdotgg#16287), background subagent work
showing while the parent is idle (pingdotgg#16486), inline MCP apps (pingdotgg#16236) and
worktree cleanup changes (pingdotgg#14847, pingdotgg#15150, pingdotgg#15834, pingdotgg#14917). The one conflict,
ClaudeAdapterV2.ts, was additive: upstream's per-subagent toolCallsFor delete
is kept ahead of the fork's Claude task-tools block. The fork's Codex image
fixture gains pingdotgg#16236's MCP-app initialize extension. Attached worktrees stay
outside every new cleanup path.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 7, 2026
## What's Changed
* fix(web): replace Lineage timers with Stop on hover by @Bil0000 in pingdotgg/t3code#16791
* fix(clients): running subagent cards stay visible after their parent turn settles by @juliusmarminge in pingdotgg/t3code#16878
* feat: MCP apps render and run inline in threads by @juliusmarminge in pingdotgg/t3code#16236
* fix(server): a background Claude subagent's work shows while its parent is idle by @Vantrongs in pingdotgg/t3code#16486
* fix(mobile): hide threads from switched-off environments by @entity in pingdotgg/t3code#16886
* fix(server): a refused Claude turn no longer throws away its session by @SunkenInTime in pingdotgg/t3code#16287
* fix(web): onboarding Continue no longer locks on computers that won't connect by @juliusmarminge in pingdotgg/t3code#16887
* fix(server): worktree cleanup no longer deletes files hidden by showUntrackedFiles=no by @SunkenInTime in pingdotgg/t3code#15834
* fix(server): merged-worktree cleanup removes worktrees after squash merges by @tris203 in pingdotgg/t3code#14847
* fix(server): free worktrees for terminal thread statuses by @ANSHSINGH050404 in pingdotgg/t3code#15150
* fix(server): Windows worktrees with long paths no longer fail or strand by @That1Drifter in pingdotgg/t3code#14917
* fix(server): main typechecks again after a test used renamed helpers by @juliusmarminge in pingdotgg/t3code#16895
* fix(server): Claude prompts no longer hang on a uuid the session already holds by @juliusmarminge in pingdotgg/t3code#16897

## New Contributors
* @Vantrongs made their first contribution in pingdotgg/t3code#16486
* @entity made their first contribution in pingdotgg/t3code#16886
* @ANSHSINGH050404 made their first contribution in pingdotgg/t3code#15150
* @That1Drifter made their first contribution in pingdotgg/t3code#14917

**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261007.2774...v0.0.46-nightly.20261007.2787

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261007.2787
github-actions Bot added a commit to davidvanderklay/t3code-flake that referenced this pull request Oct 7, 2026
## What's Changed
* fix(web): replace Lineage timers with Stop on hover by @Bil0000 in pingdotgg/t3code#16791
* fix(clients): running subagent cards stay visible after their parent turn settles by @juliusmarminge in pingdotgg/t3code#16878
* feat: MCP apps render and run inline in threads by @juliusmarminge in pingdotgg/t3code#16236
* fix(server): a background Claude subagent's work shows while its parent is idle by @Vantrongs in pingdotgg/t3code#16486
* fix(mobile): hide threads from switched-off environments by @entity in pingdotgg/t3code#16886
* fix(server): a refused Claude turn no longer throws away its session by @SunkenInTime in pingdotgg/t3code#16287
* fix(web): onboarding Continue no longer locks on computers that won't connect by @juliusmarminge in pingdotgg/t3code#16887
* fix(server): worktree cleanup no longer deletes files hidden by showUntrackedFiles=no by @SunkenInTime in pingdotgg/t3code#15834
* fix(server): merged-worktree cleanup removes worktrees after squash merges by @tris203 in pingdotgg/t3code#14847
* fix(server): free worktrees for terminal thread statuses by @ANSHSINGH050404 in pingdotgg/t3code#15150
* fix(server): Windows worktrees with long paths no longer fail or strand by @That1Drifter in pingdotgg/t3code#14917
* fix(server): main typechecks again after a test used renamed helpers by @juliusmarminge in pingdotgg/t3code#16895
* fix(server): Claude prompts no longer hang on a uuid the session already holds by @juliusmarminge in pingdotgg/t3code#16897

## New Contributors
* @Vantrongs made their first contribution in pingdotgg/t3code#16486
* @entity made their first contribution in pingdotgg/t3code#16886
* @ANSHSINGH050404 made their first contribution in pingdotgg/t3code#15150
* @That1Drifter made their first contribution in pingdotgg/t3code#14917

**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261007.2774...v0.0.46-nightly.20261007.2787

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261007.2787
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants