Fail loudly when tree-sitter grammars are unavailable - #328905
Draft
DonJayamanne wants to merge 2 commits into
Draft
Fail loudly when tree-sitter grammars are unavailable#328905DonJayamanne wants to merge 2 commits into
DonJayamanne wants to merge 2 commits into
Conversation
A macOS unit test run failed with 37 assertion mismatches across CommandAutoApprover, SessionPermissionManager and AgentSideEffects. All of them had the same cause: the bash and PowerShell tree-sitter grammars could not be used, so every command degraded to `noMatch`. That degradation is deliberate and fail-closed in production, but it is also silent: grammars are loaded via `Promise.allSettled` and a failure is only logged with `warn`, which tests discard through `NullLogService`. A single environment problem therefore surfaced as dozens of unrelated-looking assertion failures, and was misattributed to the unrelated commit at the head of the build. Make the failure obvious instead: - `CommandAutoApprover.initialize()` now resolves with which pieces are usable. Readiness is determined by analyzing a probe command per shell rather than by trusting that `Language.load()` resolved, because a grammar can load cleanly and still be the wrong grammar or an incompatible ABI -- which produces exactly the same silent `noMatch` behavior. - The test suite asserts full readiness in `setup()`, collapsing 28 confusing failures into one clear one. Also address how an incomplete dependency tree can reach the tests at all. When the pipeline restores its `node_modules` cache it skips `npm ci` entirely, so a truncated or partially written archive is never repaired. Add a post-extract verification that clears the cache-hit variable so the install steps run and rebuild the tree. `listNodeModules.ts` additionally swallowed `statSync` failures, silently dropping files from the cache manifest; it now only skips dangling symlinks and throws otherwise. Production behavior is unchanged: an unavailable grammar still fails closed and requires user confirmation rather than blocking startup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟡 Not ready to approve
Some affected suites still discard readiness, and symlink stat failures can still be silently omitted.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Adds explicit tree-sitter readiness reporting and hardens cached dependency restoration.
Changes:
- Probes bash and PowerShell grammars and exposes readiness.
- Adds fail-fast test assertions.
- Verifies restored caches and surfaces manifest-generation errors.
File summaries
| File | Description |
|---|---|
commandAutoApprover.test.ts |
Asserts grammar readiness. |
sessionPermissions.ts |
Propagates readiness. |
commandAutoApprover.ts |
Probes parser and grammars. |
agentSideEffects.ts |
Adapts initialization return type. |
product-build-win32-compile.yml |
Verifies restored cache. |
product-build-web.yml |
Verifies restored cache. |
product-quality-checks.yml |
Verifies restored cache. |
product-build-linux-compile.yml |
Verifies restored cache. |
product-build-darwin-compile.yml |
Verifies restored cache. |
copilot/setup-steps.yml |
Verifies Copilot build cache. |
verifyNodeModulesCache.ts |
Adds required-file validation. |
listNodeModules.ts |
Tightens stat error handling. |
product-build-alpine.yml |
Verifies restored cache. |
Review details
- Files reviewed: 13/13 changed files
- Comments generated: 4
- Review effort level: Balanced
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
- Assert readiness in the `SessionPermissionManager` and `AgentSideEffects` test setups too. Previously only `CommandAutoApprover` checked it, so those suites still produced the nine downstream assertion mismatches instead of a readiness failure. - Stop discarding the readiness result in `AgentSideEffects.initialize()` so the two grammar-dependent tests can assert it. - Share the assertion through `treeSitterReadinessTestUtils` rather than repeating it per suite. - Only skip a failing `statSync` in `listNodeModules` when it fails with ENOENT on a symlink. Errors such as EACCES, EIO or ELOOP were still silently skipped and could still yield an incomplete cache manifest. - Reference the actual report (microsoft/vscode-engineering#3484) instead of the unrelated PR that was misattributed as the cause. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 macOS unit test failures reported in vscode-engineering#3484, without reverting #328863 — that PR is innocent.
Root cause
Build
20260804.3(macOS, Electron unit tests) failed with 37 assertion mismatches acrossCommandAutoApprover(28),SessionPermissionManager(7) andAgentSideEffects(2). Every failure had the same cause: the bash and PowerShell tree-sitter grammars could not be used, so_extractSubCommandsreturnedundefinedand every command degraded tonoMatch.That degradation is deliberate and fail-closed in production, but it is silent: grammars load via
Promise.allSettledand failures are only_logService.warn, which tests discard throughNullLogService. One environment problem therefore surfaced as dozens of unrelated-looking assertion failures — and CI misattributed it to the unrelated commit at the head of the build.dce00340cb3touches none ofcommandAutoApprover.ts,sessionPermissions.ts, oragentSideEffects.ts.Evidence
Deleting only the two grammar files (leaving core
tree-sitter.wasmintact) reproduces CI exactly:tree-sitter.wasmmissingENOENTThe last row matters: the signature can arise even when
Language.load()succeeds, so a file-existence check is not sufficient. Missing / truncated / corrupt / wrong-ABI are all indistinguishable from the failure alone.Changes
Make it loud (source)
CommandAutoApprover.initialize()now resolves with{ parser, bash, powershell }readiness. Readiness is determined by analyzing a probe command per shell, not by trustingLanguage.load()— this catches the valid-but-wrong-grammar case above.setup(), collapsing 28 confusing failures into 1 clear one.Stop an incomplete dependency tree reaching the tests (build)
node_modulescache it skipsnpm cientirely, so a truncated/partial archive is never repaired. AddedverifyNodeModulesCache.ts, run after every cache extraction (7 pipelines), which clears the cache-hit variable so the install steps run and rebuild the tree.listNodeModules.tsswallowedstatSyncfailures viacatch { continue }, silently dropping files from the cache manifest. It now only skips dangling symlinks and throws otherwise.Production behavior is unchanged: an unavailable grammar still fails closed and requires user confirmation rather than blocking startup.
Verification
225 passingacross all three affected suites (was 224; +1 new readiness test)tree-sitter must be fully available for these tests(was 28)typecheck-client: 7 errors before, 7 after — no regressionsbuild/typecheck clean; ESLint clean on all changed filesverifyNodeModulesCache.tsmanually exercised: passes on a good tree, detects missing and zero-length files, honors the custom variable name used by the copilot pipelineNote on the caching hypothesis
The most plausible source is a poisoned Darwin
node_modulescache (per-platform cache keys explain macOS-only). That remains plausible but unproven without the failed archive — which is precisely why this PR hardens detection at both layers rather than asserting a single physical cause. Recommend also invalidating the darwin cache entry.Analysis methodology
The first root cause I proposed was wrong (I blamed the
product.commitheuristic inappNodeModules.ts). It was falsified by experiment: that path produces loud EmscriptenENOENTerrors and aborts after ~2 tests, and repo-rootproduct.jsonis never stamped with a commit. A proposedisBuiltfix was also rejected — it would have broken the working test harness. Findings were independently re-derived and verified by two separate models before this PR.