agentHost: stabilize command auto-approver initialization - #330548
Merged
Paul (pwang347) merged 5 commits intoAug 13, 2026
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dcd23755-c332-4ebe-aa93-75679170b5be
Contributor
There was a problem hiding this comment.
Pull request overview
Stabilizes tree-sitter initialization for command auto-approval by sharing process-wide WASM resources.
Changes:
- Loads tree-sitter and shell grammars once through a shared promise.
- Keeps disposable parser instances scoped per approver.
- Adds concurrent-initialization regression coverage.
Show a summary per file
| File | Description |
|---|---|
commandAutoApprover.ts |
Shares tree-sitter initialization and grammar resources. |
commandAutoApprover.test.ts |
Tests concurrent approver initialization. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
roblourens
previously approved these changes
Aug 12, 2026
Paul (pwang347)
enabled auto-merge (squash)
August 12, 2026 22:56
…ilures The disposeSession retry test budgets exactly two registry write failures and relies on both being consumed by the unregistration. The provider backfill's markProviderBackfilled write is fire-and-forget, so when it lands after failRegistryWrites it steals part of that budget, letting _retryRegistryMutation succeed on its retry and producing 'Missing expected rejection'. Await listSessions() first, matching the sibling registration-retry test, so no background registry write can be pending. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dcd23755-c332-4ebe-aa93-75679170b5be
roblourens
approved these changes
Aug 13, 2026
Paul (pwang347)
deleted the
pwang347/fix-command-auto-approver-init-flake
branch
August 13, 2026 00:26
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.
Fixes the flaky Windows
node.jsunit-test failures reported formain(build 464074, build 464010), where 41 tests acrossagentSideEffects.test.js,commandAutoApprover.test.jsandsessionPermissions.test.jsfail together — every assertion returning'noMatch'/undefinedwhere'approved'or'denied'was expected.Root cause
@vscode/tree-sitter-wasm'sinitializeBindinghas no single-flight guard — itawaits before assigning the cached module:Any caller arriving before the first one resolves also passes the
!Module3check and instantiates a competing WASM module. This reproduces against the library alone, with no VS Code code involved — two overlappingParser.init()calls fail outright:The loser of the race observes a half-constructed module whose
wasmExportsare not yet bound.Neither degenerate case fails, which is what makes this intermittent rather than deterministic:
Module3is already cachedCommandAutoApproverfires_initTreeSitter()un-awaited from its constructor, and one approver is constructed perAgentSideEffects→SessionPermissionManager. InagentSideEffects.test.tsthat is one per test viasetup()(164 tests), plus ~30 more inside individual tests, plus thecommandAutoApproverandsessionPermissionssuites — hundreds of overlapping initializations in a single Node process.When an approver loses the race, the
catchin_initTreeSitterswallows the error and leaves_parser/_bashLanguage/_powershellLanguageundefined._extractSubCommandsthen returnsundefinedandevaluatefails closed tonoMatch. Because the degradation is per-approver and permanent, every test using that approver fails — hence the all-or-nothing block of 41 failures.Why it surfaced now
There is no single regression commit. The race has been latent since fbabc5c (tree-sitter adoption, Mar 31). Two gradual pressures pushed it past the threshold:
Language.load()to two.agentSideEffects.test.tsgrew from 148 to 164 tests since late July, each adding another overlapping initialization.On contended Windows CI agents this widened the overlap window enough to lose the race in roughly 30% of runs (6 of 20 iterations in build 464074).
Note
The automated investigation for build 464010 attributed this to c4d713e (#330424) and filed microsoft/vscode-engineering#3584 against that author. That attribution is incorrect — build 463994, at the earlier commit b741771, already showed the identical 41 failures, and later builds containing c4d713e passed. #3584 should be retargeted or closed against this PR.
Credit to Benjamin Christopher Simmonds (@benibenj), whose independent investigation earlier the same day reached the same conclusion and the same fix shape.
Change
Parser.init()and eachLanguage.load()happen exactly once and every approver observes a fully initialized parser.Parserinstance perCommandAutoApproverso it remains individually disposable; the sharedLanguage/Queryobjects are not deleted by instances.Second fix: a racy sibling test exposed by the timing change
CI surfaced a second, independent failure on the Electron jobs (macOS and Linux):
That test budgets exactly two registry write failures via
db.failRegistryWrites(2)and relies on both being consumed by the unregistration, because_retryRegistryMutationmakes exactly two attempts before giving up. The provider backfill'smarkProviderBackfilledwrite is fire-and-forget, so when it lands after the injection it steals part of that budget — the retry then succeeds anddisposeSessionresolves instead of rejecting.Instrumenting the fake database shows the intended ordering, with the backfill settling before the injection:
This is latent rather than newly introduced, but it is coupled to this PR: removing the per-approver WASM work also removes roughly two file reads and two WASM compiles from every
AgentServiceconstruction, which is enough to reorder when that fire-and-forget write lands on slower CI agents.The fix mirrors the pattern already used by the sibling test
retries a transient registry registration failure before reporting creation success, which awaitslistSessions()before injecting failures.listSessions()→_ensureRegistryBackfilled()awaits the in-flight provider backfill, so after it no background registry write can be pending and the budget is deterministically consumed bydisposeSessionalone.Testing
agentSideEffects.test.ts+commandAutoApprover.test.ts: 207 passing.