Repository navigation
fix(worktree): a create never waits for a spare checkout that isn't ready - #29115
Closed
brennanb2025 wants to merge 3 commits into
Closed
brennanb2025 wants to merge 3 commits into
brennanb2025 wants to merge 3 commits into
Conversation
…eady A create reserved a prepared spare checkout that could still be building and then awaited it with no deadline; under disk contention one create waited 209 of 268 s. Only a spare whose latest build or tip refresh has finished is now claimable; otherwise the create takes the plain add at once and records the miss as not_ready. The skipped spare keeps building for the next create.
A tip refresh made a built spare unclaimable exactly when a create that awaits the same fetch arrived, so the create did a full cold add; and a skipped spare kept checking out beside the cold add. A spare is now built once (finalize already resets it onto the create's base), readiness comes from its build alone, and a create that finds only an unfinished match cancels it under a claim and cleans it up in the background.
Cancelling an unfinished spare is now the common path, and on WSL an abort killed only wsl.exe while the guest reset kept writing the tree. Opt the materializing reset into the termination barrier so cleanup starts after the guest git exits.
Closed
7 of 9 tasks
Contributor
Author
|
Closing. Real create-timing data (14 days, ~582k creates) shows that when a create finds a spare checkout still building, finishing it is usually as fast as or faster than a fresh checkout (larger repos: median 19 s remaining vs 39 s fresh), and cancelling adds deletion work plus a second checkout while the disk is busy. The one independent correctness fix found here (stopping git inside WSL when a spare's reset is cancelled) is moving to its own small PR. |
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.
ELI5
Creating a worktree could hang for minutes.
Orca builds a "spare" checkout in the background so the next create is fast.
If you clicked Create while that spare was still being built, the create sat and waited for it, with no time limit.
Now the create only uses a spare that is already finished. If the matching spare is still being built, Orca stops it and makes the worktree the normal way right away.
What Changed
Before: a create could pick a spare that was still being built and wait for it to finish, however long that took. On 2026-09-28 one create took 268 s, 209 s of it waiting on an unfinished spare while Orca's own background Git work competed for the disk.
After: a create never waits on a spare. A finished spare is used as before. If the only matching spare is still being built, the create stops it and immediately runs the same
git worktree addit already uses when there is no spare. The stopped spare is cleaned up in the background, and the create does not wait for that either.A spare is now built once and not touched again. Previously, each time the new-worktree dialog reopened after a fetch, Orca moved the existing spare to the newest commit in the background. That refresh made the spare unusable at exactly the moment a create (which waits for the same fetch) arrived. Now the create claims the spare right away, and the existing finishing step moves it onto the requested commit, as it already did whenever the base had moved.
Both create entry points get this through their shared path: the new-worktree dialog and the runtime API used by the CLI, agents, the phone app and paired clients.
Telemetry: the existing prepared-checkout outcome gains one miss reason,
not_ready.prepare_failedand theprepared_checkout_waitphase are no longer produced; they stay in the event vocabulary so the event shape does not change. Expect more hits to reportbase_moved, because the move to the newest commit now happens at claim time.Why
The structural problem was that a user action depended on speculative background work: it either waited for it with no deadline or ran its own checkout beside it. A timeout on the wait would still make the create sit behind disk work for the length of the timeout.
Now readiness is one fact, owned by the spare's build: finished or not. A create takes only a finished spare. An unfinished match is taken out of the pool and stopped, so the create's own checkout is not sharing the disk with a second full checkout of the same tree. Prefetches for the same base wait until the create finishes, through the claim mechanism that already exists.
Removing the background refresh removes the only way a finished spare could become unfinished again, so readiness never flips back. The finishing step already resolved the requested commit and reset the spare onto it, so nothing it did is lost.
On WSL, stopping a build used to kill only
wsl.exewhile Git inside the distro kept writing. The spare's checkout now uses the existing process-group termination that other long WSL Git operations use, so cleanup starts only after it has really stopped.The common pattern is that a create runs its own fetch and
git worktree addand never depends on speculative work. This change makes Orca's spare purely opportunistic.Differences from the common pattern:
Trade-offs, stated plainly:
Unchanged: ownership checks, the fetch barrier a spare's first build waits on, local base refresh, recovery, host routing (local and WSL), and the create timing telemetry.
Scope: this does not remove spares, change when they are re-armed, add cooldowns, defer conflict checks, or gate creates.
Linked Issue
Old context: #23699 (parked; its broader scheduling changes are not part of this PR).
Visual Proof
N/A: no UI change. The difference is how long Create takes while a spare is building; see Testing.
Testing
Tests for the preparation pool, claim, timing, cancellation, contention, fetch barrier, WSL root, discard retry, runtime create, and the real-Git preparation suites pass.
New/updated tests pin: a create while the spare is still building gets
not_ready, stops the build, and returns while its cleanup is still running; a prefetch for the same base during that create waits until the create finishes; a finished spare is still used, including after the dialog reopens and fetches again, with no second build; selection prefers a finished spare over a building one and names a building match.Removed: tests for the background tip refresh (the mechanism is gone) and tests for a create waiting on or joining an unfinished build (that state can no longer happen).
Real-Git probe (not committed): stopping a build while it waits on the fetch and 30 ms into its reset of a 20,000-file repo, with a cold
git worktree addrunning at the same time; the stop returns in under 50 ms, the cold add succeeds clean, and the partial spare and its Git registration are gone.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
Cross-platform: no path logic changed. A stopped build's Git now runs under the existing termination barrier: on WSL it is terminated inside the distro before cleanup; on macOS, Linux and Windows the background cleanup of a stopped spare starts after Git has fully exited (about 2 s later than before). The user's create never waits on either.
SSH: SSH repos never get spares (the remote Orca server runs its own create path), so nothing changes there.
Folder workspaces: never get spares; unchanged.
Mixed versions: telemetry only gains a value in this build's own closed list; nothing crosses the client/host wire.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)