(changes): run a local status() one git call at a time (#421) - #430
Merged
Merged
Conversation
status(), git diff --numstat and git diff --cached --numstat ran in parallel. Each may refresh the index and rename a new .git/index over the old one; on Windows a concurrent git then fails with "index file open failed: Permission denied" and the panel showed an error. The remote transport keeps its parallel calls. Closes #421
devsuitup
commented
Oct 3, 2026
devsuitup
left a comment
Owner
Author
There was a problem hiding this comment.
Review at d9bc7cd (merged with main): 0 blocking. The root cause is a production race, not a test bug. status() ran git status and both git diff --numstat calls in parallel. Each can refresh a stale index and rename .git/index over the old one, and on Windows a concurrent open then fails with "index file open failed: Permission denied". This was captured on the old code and reproduced at git level, 4 rounds in 400. The fix runs the three calls sequentially for local runners only; remote keeps them parallel. A new test asserts that two git calls are never in flight at once, and it is red on the Promise.all version. GIT_OPTIONAL_LOCKS=0 was measured and rejected.
Merged
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.
Closes #421
Cause
status()ingit-changes-runner.jsrangit status,git diff --numstatandgit diff --cached --numstatat the same time. Each may refresh a stale index and rename a new.git/indexover the old one. On Windows a git that opens.git/indexwhile another renames it fails withfatal: .git/index: index file open failed: Permission denied, andstatus()returned{ ok: false, error }. Which test oftest/git-changes-runner-real-git.test.jsfailed depended on which one hit the race (captured: that exact message in "a leaf symlink to a DIRECTORY ...";Cannot read properties of undefined (reading 'some')in the..test, fromstatus.filesbeing undefined).This is a production bug: the Changes panel could show that error on a busy machine.
Change
A local
status()now runs its three git calls one after the other. The remote transport keeps them parallel (the host is not Windows). Rationale:.ai/contexts/changes-view.md, "Local reads run one at a time". OneFixedline under Unreleased inCHANGELOG.md.GIT_OPTIONAL_LOCKS=0was tried and rejected: measured,git diffstill rewrites a stale index with it set.Evidence
local runner .status(): never two git calls in flight at oncefails on thePromise.allversion (peak 3) and passes now.task -d <wt> check: exit 0. The two git-changes-runner test files under Node 22 + c8: 168 pass, 0 fail, 0 cancelled.Not verified
The race is probabilistic: 12 green runs are consistent with the fix but do not prove it alone. The proof is the mechanism plus the message captured on the old code. The 400-round git-level loop was not repeated against the fixed
status().