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

Held-checkout archiving holds the claim-ownership lock and starves ownership checks for minutes

Closed
#12,682 0 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@atomantic is already working on this.

Since Oct 21, 2026.

  • #12751 by @atomantic — merged

Assessment

This issue has not been assessed yet.

Description

bug effort:high in-progress model:heavy planner:opus-5-5

Problem

retireHeldCheckout() in server/services/heldCheckoutRecovery.js runs archiveCheckout() — a full byte-preserving archive of the checkout (tracked, untracked, ignored files and Git administration) — inside withClaimOwnershipLock. That lock is the single process-wide queue behind every claim-ownership check/reserve/bind/release (ownershipTransaction in server/services/cosClaimOwnership.js), whose callers give up after CLAIM_OWNERSHIP_QUEUE_TIMEOUT_MS (15 s) with ownership-queue-timeout.

When the deterministic held-checkout recovery retires several checkouts back to back, the queue is starved for many minutes. Every ownership check during that window is refused.

Evidence (redacted, one incident on 2026-10-20)

  • The last 🔒 Claim ownership … status=settled line came at about 18:14 UTC. After that, every reserve logged status=queue-timeout phase=ownershipQueue ownershipQueueMs≈15000, with no pending/started operation, until the first admission at 18:32 UTC: ~18 minutes.
  • In the same window: 🧹 held-checkout recovery: retired …claim-…-issue-12569 (18:26), …issue-12573 (18:28).
  • Effect: three concurrent branch-reconcile sub-agents each got admitted:false for 10+ minutes on PRs that were green and CLEAN. One of them gave up and merged without a successful recheck (a protocol breach the gate is meant to prevent). The other was held until the queue cleared.

Expected vs actual

  • Expected: an ownership check queues behind at most one short critical section (fresh owner read, compare, persist), as the 15 s budget assumes.
  • Actual: it queues behind arbitrarily long archive I/O for N checkouts in sequence.

Chosen fix

Do the archive outside the claim-ownership lock and keep only the fences inside it:

  1. Under the lock: matchRelease plus owner/inventory/review checks. Then take a per-checkout retirement reservation (the existing withCheckoutPublication/CHECKOUT_RETIREMENT_RESERVED admission in server/lib/checkoutRetirementAdmission.js) so no claim can bind that checkout while it is being archived. Release the global lock.
  2. Without the global lock: archiveCheckout().
  3. Re-take the lock for the existing second matchRelease → verifyRemoval (administration and content fingerprints) → removal. The fingerprint comparison already detects any change made during the archive, so moving the archive out does not weaken preservation.

Keep all current refusal reasons, the fingerprint-last ordering, and started-mutation settlement. Apply the same pattern to branchReconcile.js removal (withClaimOwnershipLock around line 1569) if it also archives under the lock.

Acceptance criteria

  • archiveCheckout no longer runs while withClaimOwnershipLock is held. A deterministic test with a deferred archive shows a concurrent checkClaimOwnership settling while the archive is pending.
  • A claim bind for the checkout being retired is refused (checkout-retirement-reserved) for the whole archive, and is never admitted halfway through.
  • A change to the checkout content or administration during the archive still yields checkout-content-changed / checkout-administration-changed, and the checkout is kept.
  • Existing heldCheckoutRecovery and claim-ownership suites pass unchanged in intent.

model:heavy because this reorders lock and fence boundaries around destructive removal. effort:high because the preservation invariants must be re-proven at each boundary.

Dominant language
JavaScript
Stars
38
Forks
32
Avg merge
35m
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.