fix(cleanup): date worktree cleanup from git activity, not Pebble metadata edits - #125
Merged
Conversation
…adata edits Worktree.LastActivityAt is only ever written by applyWorktreeMetadataUpdate, i.e. when the user renames a worktree or edits its comment. It is never stamped on creation, on session activity, or on any git operation, so it stays 0 for practically every worktree. workspaceCleanupReasons computes idle = scannedAt - LastActivityAt against a 30-day threshold. With LastActivityAt at 0 that difference is ~58 years, so every clean non-main worktree is classified "idle-clean" and offered up as a cleanup candidate no matter how recently it was used. Read git's own record instead: the timestamp inside the newest reflog entry of the worktree's git directory, falling back to HEAD / COMMIT_EDITMSG / ORIG_HEAD mtimes when the reflog has been expired away. The reflog file's own mtime is deliberately not used, and neither are `index` or `gitdir` — git maintenance and a bare `git status` restamp all three, which is the false activity signal upstream #12131 removed. Applied to local projects only: an SSH worktree's path does not exist on this machine, so probing it here would stat an unrelated local directory. The cleanup fixture now runs git with GIT_COMMITTER_DATE backdated, because a worktree standing in for an abandoned one has to look abandoned to git too.
TsekaLuk
force-pushed
the
port/worktree-git-activity
branch
from
August 10, 2026 01:11
f99ef68 to
b2ff97e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes part of #81 (third acceptance criterion: cleanup should use reflog
timestamps rather than a signal
git maintenancecan forge).Problem
Worktree.LastActivityAthas exactly two writers, both inapplyWorktreeMetadataUpdate: renaming a worktree, and editing its comment.Nothing stamps it on creation, on session activity, or on any git operation —
so for practically every worktree it is
0.workspaceCleanupReasonsthen does:scannedAt - 0is about 58 years. Every clean, non-main, non-folder worktreeclears the 30-day threshold, so the cleanup scan offers up worktrees the user
was working in an hour ago.
cleanupEligibleWorktreeshas no other gate — thegit evidence read that follows only adds blockers for dirty trees, so a clean
worktree in daily use is presented as an idle deletion candidate.
Change
Take the activity signal from git rather than from Pebble's own metadata edits.
worktreeGitActivityAtreads the timestamp recorded inside the newestreflog entry of the worktree's git directory — resolving the
gitdir:pointerthat
git worktree addleaves in place of a.gitdirectory — and raisesLastActivityAtto it when it is newer than the stored value.Deliberately not used as signals:
git maintenanceand a baregit statusrewrite
logs/HEADin linked worktrees, which reports an untouched worktreeas freshly active. This is the specific false signal upstream #12131 removed;
reading the entry contents is immune to it.
indexandgitdirmtimes, restamped by the same background work.When the reflog has been expired to an empty file there is no entry to read, so
HEAD,COMMIT_EDITMSGandORIG_HEADmtimes stand in — metadata git onlywrites when something real happened.
The probe runs for local projects only. An SSH worktree's path does not
exist on this machine; stat-ing it here would read an unrelated local directory
or nothing at all. Remote projects keep going through
disconnectedWorkspaceCleanupCandidateunchanged.It reads files directly rather than shelling out to
git, so a scan acrossmany worktrees costs no subprocesses, and only a bounded 8 KB tail of the
reflog is read.
Fixture change
createCleanupGitFixturenow runs git withGIT_AUTHOR_DATE/GIT_COMMITTER_DATEbackdated.TestWorkspaceCleanupEmitsIncrementalProgressdeclares
LastActivityAt: -45 daysbut built its worktree withgit worktree addmoments earlier, so git rightly reported it as seconds oldand it stopped being a candidate — the test blocked forever on its progress
channel. A fixture standing in for an abandoned worktree has to look abandoned
to git too. That hang is itself the proof the new signal reaches the classifier.
Tests
git_worktree_activity_test.gopins the reflog contract: newest entry wins; arestamped
logs/HEADmtime does not leak in;index/gitdirare notactivity; an expired reflog degrades to commit metadata; the bounded tail read
still finds the newest entry in a 600-entry reflog; linked worktrees and plain
repositories both resolve; malformed and dangling layouts report no signal; and
committer names containing spaces (or a
>) still parse.Two classifier tests cover the actual behaviour change: a worktree git says is
active is no longer offered for cleanup, a worktree git says is stale still is,
and a stored stamp newer than the git signal is never lowered.
Five mutations were checked and each fails at least one test: trusting the
reflog mtime, counting
indexas activity, taking the oldest entry in thetail, letting the git signal lower a newer stored stamp, and dropping linked
worktree resolution.
Verification
go test ./internal/runtimecore/ -count=1green (17s).go test ./internal/runtimecore/ -race -count=1green (20s).go vetclean;gofmtclean on all touched files.node config/scripts/verify-tauri-mainline.mjspasses, with two new rulespinning the excluded-file list and the local-only guard.
verifydoes not run Go tests (.github/workflows/pr.ymlhas nosetup-goand no
go test), so all of the above was run locally.Not in scope
The other three acceptance criteria on #81 — Windows setup shell, non-blocking
delete, and avoiding a redundant fetch on delete — are untouched here and the
issue stays open for them.