Repository navigation
fix(server): worktree launches no longer fail when the first git call is slow - #174
Conversation
… is slow Repository detection capped `git rev-parse --is-inside-work-tree` at 5 s while every git call after it gets the 30 s default. The first git process to touch a folder absorbs any first-contact stall: macOS holds it until the user answers the "access files in your Documents folder" prompt, and a cold disk or a burst of launches can do the same. Detection is shared, so one stalled call failed every concurrent launch in that repo. Detection now uses the default timeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
jayleaton
left a comment
There was a problem hiding this comment.
MERGE — reviewed e5f74ae against original base a74ab4f; the reviewed files match the already-merged 4883e15 exactly.
No new correctness, security, performance, or concurrency findings. Removing the 5-second override inherits the existing 30-second VcsProcess limit. Process concurrency, scoped cancellation, detection-cache deduplication, and retry after failed detection remain intact. A genuinely stalled probe can wait 25 seconds longer, as documented.
Read AGENTS.md, CLAUDE.md, CONTRIBUTING.md, package scripts and Vite/TypeScript configuration. Local checks through heavy.sh:
- Repository scripts/check-changed.ts: all pass (format, lint, both Knip passes, GraphQL, three-package typecheck, 57 adjacent tests).
- GitVcsDriver/GitVcsDriverRegistry/VcsProcess: 88 tests pass.
- Existing concurrent/parallel worktree tests: 2 pass.
- New regression: restoring the old timeout fails with VcsProcessTimeoutError after 5000ms; restoring this head passes.
- git diff --check passes; the checkout is restored and clean.
The default check runner exceeded the 7 GiB scope and initially hit a harness temporary-path socket limit. The successful run used Node 26.12.1, a shorter temporary path, and serialized unchanged vp child commands; no checks were removed or source files changed. No macOS privacy prompt was exercised locally.
Summary
Launching several threads into worktrees at once, or launching the first thread on a new machine, could fail with
Failed to resolve the VCS driver. The launch then had no worktree.The cause was not a race. Repository detection capped its first git call at 5 s, while every git call after it gets the 30 s default. The first git process to touch a folder is the one that absorbs any first-contact stall:
Because detection is cached per repository, one stalled call failed every concurrent launch in that repository together. That is why this looked like a concurrency bug.
The cap dates from the original VCS driver code (pingdotgg#2435), with no stated reason. If the prompt or the stall lasts longer than 30 s, the launch still fails.
Root cause from the field (studio-01, nightly 0.1.3-…432001, a fresh install)
git rev-parse --is-inside-work-treein~/Documents/projects/agent-smoke: T3's first git call under~/Documentssandboxdsends a TCC request for git (responsible:com.jayleaton.t3agents);kTCCServiceSystemPolicyDocumentsFoldershows the consent promptprovision-worktreekTCCServiceSystemPolicyDocumentsFolder granted by TCC for gitThe two launches never overlapped. The parallel-worktree
.git/configlock race was already fixed by pingdotgg#25700, which is in that build.GitVcsDriverCore.test.ts› "prepares concurrent worktrees without losing their branch config" (12 concurrent worktrees on one repository) passes on this branch.Evidence
The new test is
GitVcsDriver.test.ts› "detects a repository while git waits on a privacy prompt". A fake spawner holds--is-inside-work-treeuntil the test releases it, andTestClockadvances 10 s. It exercises the realProcessRunnertimeout and uses no wall-clock waits.vp test run src/vcs/GitVcsDriver.test.ts src/vcs/VcsDriverRegistry.test.ts: 60 passed.GitVcsDriverCore.test.ts -t "concurrent worktrees": 1 passed.vp fmt --checkandvp linton the changed files: clean.check:changedcould not run in the box, which has no git history. Typecheck results are in the thread notes; CI runs the full typecheck.Merge Danger
Door: two-way. This is a single timeout value; reverting it restores the old behavior.
Blast Radius: server
Detection now waits up to 30 s instead of 5 s before reporting a timeout, and only when git really hangs (a dead network mount, an unanswered prompt). Callers that hit an uncached detection, such as status streams and launches, wait longer in that case. Normal detection takes about 10 ms and is unchanged.
Follow-up, not in this PR
After a failed worktree setup, the thread keeps
worktreePath: null. A follow-up message sent to start right away (for examplet3_thread_send) runs in the project checkout. Queued messages are already held in this case; a direct start is not. That is how Doug's retry ended up committing in the root checkout.Model: Claude Opus 5.5 via Claude Code in T3 Code.
🤖 Generated with Claude Code