fix(pool): prune stale worktree registrations before add in Acquire#31
Open
e-jung wants to merge 2 commits into
Open
fix(pool): prune stale worktree registrations before add in Acquire#31e-jung wants to merge 2 commits into
e-jung wants to merge 2 commits into
Conversation
A crashed or forcibly removed worktree leaves prunable bookkeeping in .git/worktrees/. The next "treehouse get" then fails hard with "missing but already registered worktree" because git refuses the add, wedging every subsequent spawn until a human runs "git worktree prune" by hand. Run "git worktree prune" immediately before "git worktree add" in Acquire's create-new-worktree branch. Prune is safe by design: it only removes registrations whose target directories are already gone, so it cannot destroy live worktrees or data. Adds TestGetRecoversFromStaleWorktreeRegistration covering the recovery. Refs kunchenguid#30
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.
Intent
treehouse getfails hard when git has a stale/prunable worktree registration at the pool path it tries to create the new worktree in. It does not rungit worktree prune, does not pass--force, and does not catch the "already registered" error to recover. A manualgit worktree pruneis required beforegetworks again.This blocks every spawn until a human prunes by hand. Reported in the wild on v1.4.0 (the fleet hit it); reproduced deterministically on current
main(v1.7.0,7e3fd54). See #30 for the issue with the full repro.#28(v1.7.0) hardened theprunesubcommand to classify orphaned worktrees. Thegetacquisition path was untouched and still has no defense against a stale registration, so the bug is live onmain.Refs #30.
Approach
Run
git worktree pruneimmediately beforegit worktree addinpool.Acquire's create-new-worktree branch.git worktree pruneis safe by design — it removes only bookkeeping entries whose target directories are already gone, so it cannot destroy live worktrees or data. Running it unconditionally on every acquire is therefore correct and cannot cause data loss.Rejected alternative: catching the specific "already registered"/"prunable" error and retrying after prune. That requires string-matching git's error output across versions, which is brittle. The unconditional prune is simpler and idempotent.
The prune is placed only in the create-new-worktree branch (not in the reusable-worktree loop) because
healStatealready filters missing-dir entries from treehouse's own state file, so the create-new path is the sole exposure.Scope
Three files, +72 / -0 (functional):
internal/git/git.go— newPruneWorktrees(repoRoot) errorwrapper next toAddWorktree, documented as safe-by-design.internal/pool/pool.go—Acquirecallsgit.PruneWorktrees(repoRoot)beforegit.AddWorktree(...)in the create-new-worktree branch; fails closed withfmt.Errorf("failed to prune stale worktrees: %w", err).cmd/e2e_test.go—TestGetRecoversFromStaleWorktreeRegistration: plants a prunable worktree at the slot pathgetwill target, asserts git lists it asprunable, then assertstreehouse getsucceeds and recreates the worktree directory.AGENTS.mdandREADME.mdupdated to document the self-healing behavior (added by theno-mistakesdocument step).Risk
Low. The change adds one safe, idempotent
git worktree prunecall before the onlygit worktree addsite inAcquire. Prune cannot delete live worktrees or data — git only removes registrations for directories that are already missing. The lock placement is unchanged (the prune runs inside the existingWithStateLockcritical section, same as the surroundingAddWorktree). Error wrapping matches the existingAddWorktreeerror handling.Verification
TestGetRecoversFromStaleWorktreeRegistrationpasses.go test -race ./...(race detector clean acrosscmd,internal/config,internal/git,internal/hooks,internal/pool,internal/process,internal/updater).gofmt -lclean;go vet ./...clean./tmpgit repos (no contact with any real pool):treehouse get→ exit 1,failed to create worktree: ... fatal: '...' is a missing but already registered worktree.treehouse get→ exit 0,🌳 Entered worktree at ...— worktree directory recreated.Reproduction log (base vs fixed)
Regression test transcript
Validated through the
no-mistakespipeline: intent, rebase, review, test, document, lint, push all passed with zero findings.AI disclosure
Human-reviewed. The investigation (code-path analysis, deterministic reproduction), the fix design (Option A: prune-before-add, with the rejected Option B documented), and the regression test were produced autonomously and then reviewed by a human operator before opening. The fix is small and the safety argument (prune is safe-by-design) is stated explicitly above for review.