Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Agent worktree cleanup and external-repo reaping still archive under the claim-ownership lock

Open
#12,748 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
43/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript, node.js
Domain
backend, devtools

Research direction

Read server/services/worktreeManager.js, server/services/branchReconcile.js, and the retirement helper in server/services/cosClaimOwnershipState.js; first review the existing unlocked archive pattern from #12682. Split the listed cleanup and reaping paths so archive or backup work runs outside the ownership lock while preserving the fence and removal checks. Add deterministic tests that hold the archive open and verify the lock settles and the checkout is fenced, then run the worktreeManager, worktreeReap, and branchReconcile suites.

Written by the indexing model from the issue text.

Description

bug effort:high model:heavy plan

Discovered while working issue #12682.

Problem

#12682 moved checkout archiving out of the shared claim-ownership lock (withClaimOwnershipLock) for operator held-checkout retirement, branch-reconcile merged retirement and detached-tree reaping, and reapMergedWorktrees. It added a per-checkout retirement fence (reserveCheckoutRetirementFence / checkoutRetirementFenceReason in server/services/cosClaimOwnershipState.js) that refuses binds and reconcile admissions for the checkout being archived.

Three paths still run slow archive or backup I/O while holding the lock. Every claim-ownership bind/release/reserve queues behind that lock with a 15 s budget (CLAIM_OWNERSHIP_QUEUE_TIMEOUT_MS), so each can still cause ownership-queue-timeout refusals:

  1. removeWorktree() in server/services/worktreeManager.js: agent cleanup runs removeWorktreeNow inside withClaimOwnershipLock, and its preserveOrHold() calls preserveMergedCheckout at three points (the missing-directory prune, the merged/clean path and the discard path). This runs on every agent completion that owns a worktree.
  2. cleanupExternalRepoWorktrees() in the same file: the removeIfStillOrphaned callback calls preserveMergedCheckout under the lock.
  3. Superseded reap in server/services/branchReconcile.js: retireBranch runs the prepare hook (backupSupersededBranch, which copies dirty files) inside the first critical section. It is opt-in (reapSuperseded).

Chosen approach

Use the shared helper retireWithUnlockedArchive({ admit, archive, finish, busy }) from server/services/cosClaimOwnershipState.js, added by #12682. It implements this three-phase pattern:

  1. Under the lock, run the owner, policy and status checks, then take the fence with reserveCheckoutRetirementFence({ repoPath, path, branch }). If the fence is refused, hold with checkout-retirement-reserved.
  2. Release the lock and run the archive or backup.
  3. Re-take the lock for verifyRemoval (owners plus preservation.verify fingerprints), removal and branch deletion. Release the fence inside that critical section, and again in an outer finally.

For removeWorktreeNow, split the function at each preserveOrHold() call rather than wrapping it whole. Keep the merge-back and discard semantics exactly as they are now.

Acceptance criteria

  • No preserveMergedCheckout or backupSupersededBranch call runs while withClaimOwnershipLock is held. rg preserveMergedCheckout server/services shows each remaining call between two lock sections.
  • For each path, a deterministic test holds the archive open and shows that withClaimOwnershipLock(() => …) settles and the checkout is fenced while the archive is pending. See the expectArchiveOutsideOwnershipLock helper in server/services/branchReconcile.test.js and the worktreeReap.test.js case added by #12682.
  • Existing worktreeManager, worktreeReap and branchReconcile suites still pass.

model:heavy because this reorders lock and fence boundaries around destructive removal. effort:high because the merge-back and discard paths in removeWorktreeNow each need their preservation invariants re-proven.

Depends on #12682

Dominant language
JavaScript
Stars
38
Forks
32
Avg merge
34m
Merged PRs (30d)
993

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from atomantic/PortOS

All issues in atomantic/PortOS

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.