feat(release): publish a linux-arm64 engine - #20
Merged
Conversation
ARM Linux users had no engine at all: platformBinaryName() returned null for linux-arm64, so npm's postinstall printed "unsupported platform" and exited, and the JetBrains resolver refused too. Issue #15 came from one of the few people who worked around that by building from source. Adds the asset to both client mappings and to the release, and pins how it is built: scripts/build-linux-arm64.sh builds in a container because there is no arm64 Linux build machine, and the compatibility floor is easy to get wrong. It does not only build - it refuses to produce a binary that would narrow support: - __libc_single_threaded must land in writable memory. An immutable definition is read-only and, on aarch64, also exported, so glibc's startup write to it faults before main(). That was issue #15, and this is its regression guard. - the glibc floor must stay <= 2.30 and GLIBCXX <= 3.4.29, the floors measured from the shipped x64 asset. Ubuntu 20.04 + gcc-11 is the only combination measured to give both a working link and those floors. The alternatives failed and the reasons are recorded in the script: Debian 11 (gcc 10) cannot resolve ONNX Runtime's __throw_bad_array_new_length or __aarch64_cas8_sync; SLES 15 SP4's own gcc11 ships no outline atomics; Ubuntu 22.04 links but floors at glibc 2.34, which would have given ARM a narrower support range than x64. Verified by running the built binary: it works on SLES 15 SP4, Ubuntu 22.04+, Debian 12+, RHEL 9+ and Amazon Linux 2023, and not on Ubuntu 20.04, Debian 11, RHEL 8 or Amazon Linux 2 - identical to the x64 asset. Note the binding constraint is GLIBCXX_3.4.29, not glibc, which is why it does not run on its own build host. Also collapses three hand-kept copies of the platform list into one. PUBLISHED_BINARIES in bin/fetch-engine.js is now the source of truth, read by publish-release-assets.sh and package-npm.sh, so a sixth platform cannot be published without every client resolving it - the drift that made this change touch five files. A `while read` loop rather than mapfile: these scripts run under macOS's bash 3.2, where mapfile does not exist and would have silently yielded an empty list. The two tests asserting linux-arm64 was unsupported are inverted rather than deleted; both languages now assert it resolves to its own asset and never to the x64 one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017rVbt7rENTwXkdHt3Bpgb5
The project published no platform requirements anywhere users could read them, while the internal build notes claimed a Linux range that was wrong. A user on RHEL 8 hitting `GLIBCXX_3.4.29 not found` had nothing to consult. Two floors matter and only one of them is obvious. glibc 2.30 is not the binding constraint: the engine embeds ONNX Runtime, built with GCC 11, so it also needs libstdc++ >= GLIBCXX_3.4.29. Checking only glibc is what produced the earlier claim that Debian 11 and Ubuntu 20.04 were supported; both ship 3.4.28 and have never been able to start the engine. The runs / does-not-run table is measured by executing the 0.20.1 binaries on each distribution, not inferred from symbol versions, and is identical for linux-x64 and linux-arm64. Also documents linux-arm64 as supported, and gives the GLIBCXX error the remedy that does not require a distro upgrade. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017rVbt7rENTwXkdHt3Bpgb5
… engine download sizes
🔍 CodeGraph PR Review23 files changed (+761/−97, 12 functions) · Risk: 🔴 high Blast radius4 direct callers affected (2 breaking) across
|
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.
Intent
The developer's overall goal in this session was to ship CodeGraph 0.20.1, and in the later part specifically to add a linux-arm64 engine binary as a supported release platform. They were initially reluctant to build aarch64 because of disk space on the build VM, then decided to proceed once ARM server offerings were shown to be mainstream and the space cost was shown to be small. They cared that the ARM binary match the existing x86_64 compatibility floor rather than shipping an asymmetric support story, motivated by conservative corporate Linux users, and they asked whether telemetry could show how many ARM systems actually run CodeGraph before committing. After merging the two open PRs, they asked to prepare the ARM build for inclusion in the release, to correct the support-matrix documentation, and to build and confirm all remaining 0.20.1 binaries start on their own platforms. Their final instruction was to merge the linux-arm64 branch, do a clean rebuild of all five binaries from a single merge commit so provenance is consistent (the ARM one had been stamped -dirty and the assets disagreed on source commit), and update the docs.
What Changed
linux-arm64as a published engine platform:scripts/build-linux-arm64.shbuilds it in an Ubuntu 20.04 container and asserts the same floors as the x64 build (glibc 2.30,GLIBCXX_3.4.29, the writable__libc_single_threadedshim, and that the binary runs), while the clients now resolve arm64 Linux by exact platform-arch match -fetch-engine.jsgains an exportedPUBLISHED_BINARIESlist andCodeGraphServerResolvermaps arm64 Linux to the new asset instead of returning null.stamp-binary.shnow records the commit beside the version, measured from the binary's own--infowherever the staging host can run it and rejected when it isunknownor-dirty;publish-release-assets.shrefuses a set with a missing or disagreeing commit; and both it andpackage-npm.shread the asset list fromfetch-engine.jsrather than keeping their own copies.build-linux-arm64.shgates its own container build against the hostHEADbefore staging.GLIBCXX_3.4.29failure mode), corrects stale per-platform and download-size claims across the client docs, and restoresvscode/.eslintrc.jsonsonpm run lintruns again - clearing the resulting errors in the touched TypeScript.Risk Assessment
✅ Low: The round-2 items were implemented as instructed and the new manifest format, self-report fallback, and prefix-based agreement logic all hold up under the five-asset release path I traced; what remains is one cheap gate tightening and one stale comment.
Testing
Exercised the change as an arm64 Linux end user and as the release manager: a simulated linux/arm64 install now fetches, checksum-verifies and installs an AArch64
codegraph-server-linux-arm64where the base commit gave that machine no engine at all, and the realstamp-binary.sh/publish-release-assets.shpair was run over a five-asset staging directory holding this host's genuine darwin-arm64 engine - it measures commits from--info, refuses-dirtyand contradicting stamps, blocks a set whose assets disagree on their source commit, and passes with all five binaries traced to one commit. Both client test suites pass (JS suite green; 24 JetBrains tests, 0 failures), and I added one focused install test per channel because both previously covered only the name mapping and not the arm64 download itself. The README's new support matrix is included as a rendered screenshot. Not covered: the arm64 binary was not built or run here, so its glibc/GLIBCXX floors and the #15 startup guard remain unverified locally - reported as a warning.Evidence: arm64 Linux install, before vs after (CLI transcript)
--- BEFORE (base commit 71f0f0f) --- simulated machine: linux / arm64 (uname -m: aarch64) ensureEngine threw: no CodeGraph engine is published RESULT: no engine on this machine. --- AFTER (feat/linux-arm64-engine @ ef37289) --- simulated machine: linux / arm64 (uname -m: aarch64) downloading codegraph-server-linux-arm64 ... installed engine: codegraph-server-linux-arm64 ELF machine: AArch64 (arm64) mode: 755 version marker: 0.20.1 directory: .engine-version, codegraph-server-linux-arm64 RESULT: the arm64 Linux machine has a working, verified arm64 engine.Evidence: Release gate: provenance stamping and one-commit agreement (CLI transcript)
ERROR: the staged binaries were not all built from the same commit. codegraph-server-darwin-arm64 10a6e76 codegraph-server-darwin-x64 10a6e76 codegraph-server-linux-arm64 deadbeef1234 codegraph-server-linux-x64 10a6e76 codegraph-server-win32-x64.exe 10a6e76 --- after re-stamping linux-arm64 from the release commit --- ✓ codegraph-server-darwin-arm64 116M (0.20.1 @ 10a6e76) ✓ codegraph-server-linux-arm64 4.0K (0.20.1 @ 10a6e76fbb00) ... all 5 engine binaries built from 10a6e76/var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T/no-mistakes-evidence/01KZM184HCMTW5K3JF0HM2SQRT/readme-supported-platforms.png)Evidence: Rendered README section (HTML source of the screenshot)
Evidence: Targeted test runs, both client channels
$ node mcp-package/test/fetch-engine.test.js # npm + VS Code channel PASS an arm64 linux install fetches the arm64 engine PASS and it is the arm64 build PASS linux-arm64 resolves to its own engine, not the x64 one PASS linux-arm64 resolves to a published asset (codegraph-server-linux-arm64) 0 failure(s) $ ./gradlew test --tests "*CodeGraphServerResolverTest" --tests "*EngineDownloaderTest" testcase name="test an arm64 linux ide installs the arm64 engine" testcase name="test arm64 linux gets its own engine and arm64 windows emulates x64" tests run: 24, failures: 0Evidence: arm64 build script refuses a dirty tree before building
$ ./scripts/build-linux-arm64.sh ERROR: the working tree is dirty, so this build would be stamped '-dirty' and would disagree with the assets built for the same release. Commit or stash first, or set CODEGRAPH_ALLOW_DIRTY=1 for a throwaway build. [exit 1]Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
scripts/build-linux-arm64.sh:135- The arm64 build has no provenance gate, so the exact failure that motivated the rebuild (an asset stamped-dirty/ disagreeing on source commit) is still reachable.crates/codegraph-server/build.rs:20-31shells out togit rev-parse HEADandgit status --porcelainfrom inside the container, and both failure modes are silent:run_cmdswallows a nonzero exit and substitutes the literal string"unknown". Two concrete triggers: (1) when REPO_ROOT is a git worktree,.gitis a pointer file naming a gitdir path that is not mounted into the container, so every git call fails and the binary shipsGIT_HASH="unknown"; (2) any uncommitted change in the live bind-mounted tree stamps-dirty. The script verifies the glibc/GLIBCXX floors, the shim section class, and that the binary runs, but never checks what commit it claims. stamp-binary.sh records only the version, so publish-release-assets.sh will happily publish it. Recommend asserting provenance at the same boundary as the other floors: pass the host'sgit rev-parse --short HEADin via-e, and after the build compare it against"$BIN" --info(metadata.rs prints GIT_HASH_SHORT), failing onunknown, on a mismatch, or on a-dirtysuffix.jetbrains/src/main/kotlin/ai/codegraph/jetbrains/server/CodeGraphServerResolver.kt:86- The KDoc directly above the changedplatformBinaryNameOrNullstill documents the pre-change behavior: "Only macOS is built for both architectures... Linux has no such layer, and falling back to x64 there installs ~30 MB that cannot execute". The body four lines below now returnscodegraph-server-linux-arm64, so the doc contradicts the code it describes and would mislead the next person deciding whether Linux arm64 is published. Same stale claim at jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/EngineDownloaderTest.kt:67 ("Windows and Linux are published for x64 only"). Also note the fetch-engine.js comment at line 118 calls PUBLISHED_BINARIES "the single list" with "three hand-kept copies", which undercounts: this Kotlin mapping is a fourth copy that cannot import it, and is what actually had to be edited in lockstep here.scripts/build-linux-arm64.sh:113- The issue-codegraph-server SIGSEGVs at startup on Linux/aarch64 (before main, even--version) #15 shim guard fails open when the symbol is absent:case "$SHIM_CLASS" in B|D|b|d|"")accepts the empty string, andnm ... 2>/dev/nullyields empty on any tooling failure (nm missing, a future stripped profile, an unexpected output format), so the aarch64 SIGSEGV regression check silently passes. Separately, awk's$2is only the section class for defined symbols - for an undefinedU __libc_single_threadednm omits the address column, so$2is the symbol name and the guard reports "shim is '__libc_single_threaded' (read-only)". It still fails closed in that case, but the message is nonsense. Recommend treating absent as an error and matching the class positionally (e.g.nmoutput's second-to-last field, ornm -C --defined-only).scripts/build-linux-arm64.sh:20- The header comment says the binary "does not run on Ubuntu 20.04 ... not even on its own build host", but line 135 runs"$BIN" --versioninside the ubuntu:20.04 container and aborts the build if it fails. Both can only be true becauseppa:ubuntu-toolchain-r/testupgrades libstdc++6 past GLIBCXX_3.4.29 in the container; a reader debugging a failure at line 135 would reasonably conclude the check itself is wrong. Recommend qualifying the comment as "stock Ubuntu 20.04, before the toolchain PPA upgrades libstdc++6".scripts/build-linux-arm64.sh:70- The container runs as root against two bind mounts inside the repo ($REPO_ROOT:/srcand$BUILD_DIR:/target, which defaults to$REPO_ROOT/target/linux-arm64). On the natively-supported arm64 Linux host path this leaves root-owned build artifacts in the developer's tree (and a root-owned Cargo.lock if the lock needs updating), which later host-sidecargo/rm -rf targetinvocations cannot clean without sudo. Docker Desktop on macOS hides this via uid mapping. Recommend achown -R "$HOST_UID:$HOST_GID" /targetat the end of the container recipe, with the ids passed in via-e(running the whole container as the host user is not an option, since apt-get needs root).mcp-package/test/fetch-engine.test.js:436- The added assertions duplicate ones already present a few lines above:platformBinaryName("linux", "arm64")is asserted at 418, 436 and again inside the loop at 465, andplatformBinaryName("win32", "arm64")at 423 and 445. Dropping the redundant checks at 435-438 and 444-448 leaves the block asserting each rule once, with the new riscv64 case and the PUBLISHED_BINARIES cross-check carrying the genuinely new coverage.🔧 Fix: gate arm64 build provenance, harden shim check, fix stale docs
3 issues (1 warning, 2 infos) still open:
scripts/build-linux-arm64.sh:253- The new provenance gate stops at the arm64 asset, so "the release assets disagree on their source commit" - the failure the gate names in its own error text at line 227 - is still reachable for the other four. The gate proves the arm64 binary matches the host HEAD, then discards that fact: stamp-binary.sh takes only (binary, version) and writes<version> <binary>(stamp-binary.sh:44), and publish-release-assets.sh accepts an asset ongrep -qxF "$VERSION $bin"alone (publish-release-assets.sh:146). Concrete path: build darwin-x64 natively at commit A and run this script at commit B, both from clean trees; both stamp as 0.20.1, every gate passes, and the release ships assets from two commits. The four native builds have no equivalent check at all. The earliest shared boundary that would make the invariant hold for all five is the manifest itself - have stamp-binary.sh take the commit (or read it from<binary> --infowhen the binary is native to the staging host) and record<version> <commit> <binary>, and have publish-release-assets.sh refuse when the recorded commits disagree - rather than repeating this script's check in each per-platform builder. Note this would change the BUILD-MANIFEST format the other four builds already write, so it is a decision rather than a mechanical fix.scripts/build-linux-arm64.sh:244-CODEGRAPH_ALLOW_DIRTY=1disables the provenance gate but still stages and stamps. Lines 74-75 set CHECK_PROVENANCE=0, line 98 prints "do not publish this build", and then lines 244-253 copy the binary into vscode/bin/ and call stamp-binary.sh with $VERSION anyway - producing a BUILD-MANIFEST entry indistinguishable from a gated one, which is exactly the "stale binary looks as valid as a fresh one" case the manifest exists to prevent (stamp-binary.sh:7-15). The script should enforce the rule it states: when CHECK_PROVENANCE is 0, take the --no-stage path regardless of argv. The env var is also absent from the Usage block at lines 43-45, which documents only the two argv forms.scripts/build-linux-arm64.sh:225- The provenance comparison is an exact string match between two independently abbreviated short hashes: EXPECTED_GIT_SHORT comes from the host'sgit rev-parse --short HEAD(line 77) while GOT_GIT comes from build.rs'sgit rev-parse --short HEAD(build.rs:21) run by the container's git 2.25. Both default to auto-abbreviation over the same object store, so they normally agree, but acore.abbrevset in the host user's gitconfig (the container has none) makes the two differ in length for the same commit and the build fails with "the binary claims commit X, but the host is at Y" - where X is a prefix of Y. Comparing by prefix instead (case "$EXPECTED_GIT_SHORT" in "$GOT_GIT"*), guarded by a minimum length) removes the dependency on both sides picking the same abbreviation.🔧 Fix: record source commit per asset and gate release on agreement
2 infos still open:
scripts/stamp-binary.sh:61- The script now runs the binary and parses the exact line that carries both facts -name vX.Y.Z (commit)from metadata.rs:26 - but extracts only the commit, leaving the version an unverified claim. That leaves one realistic slip uncaught: bump Cargo.toml to 0.20.2, forget to rebuild, then re-stamp the staged binaries as 0.20.2 with no commit argument. Each self-reports the old commit, so the mismatch check at line 64 never fires (nothing was given to contradict), all five agree with each other, and publish-release-assets.sh's agreement gate passes - shipping v0.20.2 assets that are all v0.20.1. Extending the same sed to capture the version and asserting it equals $VERSION when SELF_REPORTED is non-empty closes it for free on every host that can run its own asset. Note the header at lines 9-11 dismisses version scraping, but that referred tostrings-style scraping of the image; running--infois the same measurement this commit already trusts for the commit.scripts/build-linux-arm64.sh:74- This comment is stale as of this commit: "stamp-binary.sh records only the version, so nothing downstream would notice". stamp-binary.sh now records the commit beside the version (stamp-binary.sh:111) and publish-release-assets.sh refuses a set whose commits disagree (publish-release-assets.sh:243), which is precisely what would notice. The block reads as the justification for this script's local gate, so leaving it in place understates what the release path now enforces.scripts/build-linux-arm64.sh:61- The linux-arm64 engine binary itself was never built or executed during this phase, so the support claims that motivate the change are unverified locally: the glibc 2.30 / GLIBCXX_3.4.29 floor assertions, the issue codegraph-server SIGSEGVs at startup on Linux/aarch64 (before main, even--version) #15__libc_single_threadedwritable-section guard, and the README's "both Linux architectures have identical requirements" statement all live insidescripts/build-linux-arm64.shand only run during a full container build. Two things block it here: a fullcargo build --release -p codegraph-serverunder Ubuntu 20.04 is far beyond a targeted test step, and this gate runs from a git worktree whose.gitis a pointer file, so build.rs cannot read git inside the container and the script's own provenance check would fail (the script's comments anticipate exactly this). I did confirm the script's pre-Docker provenance guard refuses a dirty tree, which is the guard that prevents the-dirtystamp from recurring. The author needs to decide whether the arm64 build evidence from their own build host is sufficient, or whether it should be reproduced in CI before release.node mcp-package/test/fetch-engine.test.js- full JS channel suite including the newan arm64 linux install fetches the arm64 enginecase I addedjetbrains/gradlew test --tests "*CodeGraphServerResolverTest" --tests "*EngineDownloaderTest"- 24 tests, 0 failures, including the newtest an arm64 linux ide installs the arm64 enginecase I added (run with JAVA_HOME=/opt/homebrew/opt/openjdk@21; no JDK is on PATH by default)Manual before/after: droveensureEnginewithplatform=linux, arch=arm64against a local release server, once with the base commit'sfetch-engine.js(throws, no engine) and once with the branch's (installs a verified AArch64 ELF, mode 755,.engine-version= 0.20.1)Manual:scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1against the real engine built on this host - commit measured from--inforather than assertedManual:scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1 abc1234def56- refused, binary overrules the argumentManual:scripts/stamp-binary.sh codegraph-server-linux-arm64 0.20.1and... 0.20.1 10a6e76fbb00-dirty- refused for a missing commit and for a-dirtybuildManual:scripts/publish-release-assets.shover a five-asset staging dir (CODEGRAPH_BIN_DIR) - refused when linux-arm64 was stampeddeadbeef1234, passed with checksums once re-stamped to the release commitManual:scripts/build-linux-arm64.sh- dirty-tree provenance guard fires before Docker is touchedManual: rendered README's new## Supported platformssection from source and screenshotted it headless (chrome-devtools-axi)Probedhttps://github.com/codegraph-ai/CodeGraph/releases/download/v0.20.1/*- all six assets 404 today, which is why the client flows were exercised against a local stand-in release🔧 **Document** - 2 issues found → auto-fixed ✅
scripts/publish-release-assets.sh:16- The release scripts point readers atcross-platform-builds.mdin three places (lines 16, 199, and 257 - the last added by this change's commit-agreement error path), but no such file exists anywhere in the repository and none ever has. It is the maintainer's private build-host notes, which contain LAN IPs and SSH key names and so must not be committed to a public repo. I could not resolve this without either creating a new documentation surface (disallowed here, and unsafe with that content) or deleting pointers that are genuinely useful to the maintainer. Suggested resolution: reword the three references to name it as private maintainer notes rather than a repo document, or add a repo-safe build doc that omits host addresses and credentials.vscode/src/engineDownload.ts:110- Follow-up, deliberately left out of scope: the repo's bundling-era rationale comments disagree about how large one engine is. This change settled on ~120 MB (fetch-engine.js:155,CodeGraphServerResolver.kt:88, consistent with the "100-126 MB each" and "116 MB binary" claims elsewhere), butvscode/src/engineDownload.ts:110andjetbrains/.../IndexingStartupActivity.kt:41still call it a ~30 MB download, andEngineDownloader.kt:23plusscripts/publish-release-assets.sh:8still argue against a 498 MB npm package that no longer bundles anything. These predate this change and live in files it never touched, so sweeping them here would multiply edits well beyond the arm64 work; they are worth one consolidating pass of their own.🔧 Fix: replace dangling build-notes pointers, correct engine download sizes
✅ Re-checked - no issues remain.
vscode/package.json:1797- The VS Code extension declares"lint": "eslint src --ext ts"and depends on eslint ^8.55.0, but no eslint configuration exists anywhere in the repository - no .eslintrc*, no eslint.config.*, and noeslintConfigkey in vscode/package.json (verified by find across the tree and by parsing the package.json). Under eslint 8 that script fails with "No ESLint configuration found", so the repo's only configured JS/TS linter cannot run, and I could not lint the two TypeScript files this pass touched (vscode/src/engineDownload.ts, vscode/src/extension.ts). That the code already carries an// eslint-disable-next-line @typescript-eslint/no-var-requirescomment at engineDownload.ts:24 suggests a config existed once and was lost. This is pre-existing and not introduced by the arm64 change, and I did not author a config myself: choosing a ruleset is a judgment call that would likely flag many pre-existing files and amounts to a new configuration surface well outside this change's scope. Suggested resolution: restore an .eslintrc.json for vscode/ with @typescript-eslint, or drop the lint script and the eslint devDependency if linting is no longer intended.🔧 Fix: restore vscode eslint config, clear resulting lint errors
2 infos still open:
vscode/.eslintrc.json:48- The restored ESLint config sets@typescript-eslint/no-explicit-anyto "warn" in extension source and "off" in**/*.test.ts, rather than therecommendeddefault of "error". This is a deliberate, documented choice, not an oversight, and it is disclosed here so the severity is the user's call rather than silently mine. At "error" the rule produced 206 of the 268 pre-existing failures (186 in test files, 20 in source), which would have leftnpm run lintpermanently red and therefore unrunnable - a config nobody can adopt is no better than the missing one this replaces. The 20 source-level sites are concentrated in boundary code handling untyped language-server protocol payloads (extension.ts, reporter.ts, toolManager.ts, commands/index.ts); typing them properly means pinning down the LSP payload surface, which is a design change rather than a lint fix and is unsafe to do mechanically. The 186 test-file sites are in mocks that must mirror the shape of the realvscodeAPI, including its wide types, so enforcing the rule there would push the doubles away from the API they imitate. Net effect:npm run lintnow exits 0 with 20 visible warnings as a typing-debt backlog. No action needed unless the user wants the source-levelanyusages tightened to "error" as a follow-up task.vscode/package-lock.json:3-vscode/package-lock.jsonrecords"version": "0.15.0"(in both the root object andpackages[""]) whilevscode/package.jsonis at0.20.1. I discovered this only because installing the dev dependencies needed to run ESLint caused npm to auto-sync the field, and I reverted that rewrite to keep this lint phase's diff scoped to lint fixes. It is not a lint, format, or static-analysis issue and has no effect on builds or on the extension, but the drift is real and will re-appear as an unrelated diff in the next person'snpm install. Reported as information only; deliberately not fixed here since a lockfile change belongs in its own commit rather than smuggled into a lint pass.✅ **Push** - passed
✅ No issues found.