GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Agent mode ignores pnpm's virtualStoreDir: transitive dependencies are reported package_not_installed with a custom virtualStoreDir or the global virtual store · Issue #362 · SocketDev/socket-patch Closed Labels agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:pnpmpnpmpnpmpriority:p1 Description[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303). SummaryThe npm crawler only looks for pnpm's virtual store at
pnpm records the location in The failure is loud ( Repromkdir p && cd p echo '{"name":"p","version":"0.0.0","private":true,"dependencies":{"is-odd":"3.0.1"}}' > package.json # is-odd -> [email protected] (transitive) printf 'virtualStoreDir: .vstore\n' > pnpm-workspace.yaml # pnpm <=10 also: printf 'virtual-store-dir=.vstore\n' > .npmrc pnpm install ls .vstore/[email protected]/node_modules/is-number/index.js # hand-stage .socket/manifest.json + blobs for pkg:npm/[email protected] (before = that file) socket-patch apply --offline --json # "status": "partialFailure", "action": "skipped", "reason": "No installed package matches this PURL", "errorCode": "package_not_installed" node -e "console.log(require('fs').readFileSync(require.resolve('is-number',{paths:[require('path').dirname(require.resolve('is-odd'))]}),'utf8').slice(0,18))" # still the original bytes; the same project without virtualStoreDir -> applied, node sees the patch For the global virtual store, replace the workspace line with Expected vs actual
Matrix (current main f6b7fb9;
|
| OS | pnpm | custom virtualStoreDir | global virtual store |
|---|---|---|---|
| Linux | 8.15.9 | fail | n/a |
| Linux | 9.15.9 | fail | n/a |
| Linux | 10.34.5 | fail | fail (sandbox); not engaged on GH runner (pnpm 10 ignores the global virtual store under CI) |
| Linux | 11.27.0 / 12.8.1 | fail | fail |
| macOS (GH runner) | 10.34.5 / 11.27.0 / 12.8.1 | fail | fail on 11 / 12 (10: not engaged) |
| Windows (GH runner) | 10.34.5 / 11.27.0 / 12.8.1 | fail | fail on 11 / 12 (10: not engaged) |
Release 4.0.0 behaves the same.
crates/socket-patch-core/src/crawlers/npm_crawler.rs:1033 and :1362: .pnpm is matched by name only, and node_modules/.modules.yaml's virtualStoreDir is never read.
Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36759597534
[agent] Shares root cause with #359: npm_crawler.rs finds a dependency store only by its hard-coded directory name (.pnpm, pnpm ≤3 .<host>, .vlt) in both gather_node_modules (scan, :1362) and the :1033). Any other store location (npm's find_by_purls resolver (.store, or a pnpm virtualStoreDir recorded in node_modules/.modules.yaml) falls through to the hidden-entry skip. Will be fixed together. Triaged priority:p1 (pnpm).
Note on the global-virtual-store half: making <store>/links visible without the isolation #361 asks for would make agent mode write into the shared store for transitive deps too. So that half is best fixed together with, or after, #361.
Generated by Claude Code
[agent] Claiming this issue (with #359; shared root cause: npm crawler recognizes dependency stores only by hard-coded names, so npm's .store and a pnpm virtualStoreDir are skipped). Branch: agent/fix-npm-crawler-store-discovery. Claim-ID: 2026-09-30T19:21:12Z-aa553b
Generated by Claude Code
[agent] Scope note for #365: it fixes the custom virtualStoreDir half. A store recorded in node_modules/.modules.yaml inside the project, such as .vstore or node_modules/.custom, is now walked by scan, apply, rollback and the peer-copy fan-out. Covered by unit tests plus a real pnpm 10 e2e, both red on main.
The global virtual store half is deliberately left out, so this PR says Refs #362, not Fixes. pnpm records that store as <store-dir>/v10/links, outside the project, and every project on the machine with the same graph loads it. Making agent mode see it would write patches into files other projects share, which is #361's problem. It should be fixed together with #361, for example by materializing a private copy, or by refusing with a clear "shared global virtual store" reason instead of package_not_installed. This issue stays open for that part.
Generated by Claude Code
[agent] New information from the pnpm bug-hunt routine (ledger #303): this issue also covers the default global (-g) layout on pnpm 7–10, and the global-virtual-store half also applies to pnpm 11's default -g layout.
$PNPM_HOME/global/5/node_modules with virtualStoreDir: ../.pnpm recorded in .modules.yaml. That's not an opt-in setting; it's what every pnpm add -g produces. So every transitive dependency of a globally installed tool is invisible. scan -g doesn't list it, and get -g pkg:npm/[email protected] (transitive through a global [email protected]) ends partial_failure / package_not_installed, with the file unchanged.global/v11/<hash>/node_modules/* → store/v11/links/…), with the same result for transitive deps. Direct global deps are patched through the link into the shared links dir (the Agent-mode apply and rollback on a pnpm project with enableGlobalVirtualStore patch (and unpatch) every other project that shares the store #361 concern).node_modules/.pnpm, so transitive deps are found. That layout has a different problem with duplicate copies, now filed as Global agent mode on pnpm 12 (and 11 without the global virtual store) patches only one of the per-install copies of a package, reports success, and VEX attests not_affected #435.I checked PR #365's head (80f4a71) against these layouts. It fixes the pnpm 7–10 global case: scan -g lists is-number, get -g patches the copy is-odd loads, and rollback restores it. pnpm 11 global is still not found, as intended by the PR's global-virtual-store exclusion.
main 2463257 |
scan -g lists transitive | get -g transitive |
|---|---|---|
| pnpm 7.33.7 / 8.15.9 / 9.15.9 / 10.34.5 | no (PR #365: yes) | not installed (PR #365: patched) |
| pnpm 11.27.0 | no (PR #365: no) | not installed (PR #365: not installed) |
| pnpm 12.8.1 | yes | patched |
Release 4.0.0 behaves the same as main on all of these.
Side note, not pnpm-specific, so it isn't filed: when .socket/manifest.json already holds another patch that applies, get -g <uninstalled purl> reports status: success, applied: 1, exit 0 (the nested apply only fails when no manifest entry matches). So on pnpm 7–11, a get -g for a global transitive dep can even claim success.
Generated by Claude Code
[agent] Re-triage on main 61cfb9b (PR #365 merged), with real pnpm installs on Linux:
| pnpm | custom virtualStoreDir: .vstore (transitive [email protected]) |
global virtual store (enableGlobalVirtualStore: true) |
|---|---|---|
8.15.9 / 9.15.9 (.npmrc virtual-store-dir) |
fixed: apply success, Node loads the patched bytes, rollback restores them |
n/a |
10.34.5 / 11.28.3 / 12.8.1 (pnpm-workspace.yaml) |
fixed (same result) | 11.28.3 / 12.8.1: still partialFailure / package_not_installed for …/v11/links/@/is-number/6.0.0/<hash>/… |
So the custom-virtualStoreDir half is fixed. As PR #365 said it would, the global-virtual-store half is still open, so I'm leaving this issue open for that part, which goes with #361.
Generated by Claude Code
[agent] Update from the pnpm bug-hunt routine (ledger #303), main 203e092, Linux, pnpm 12.8.1: the global-virtual-store half now exits 0 with success.
Setup: enableGlobalVirtualStore: true, with the transitive [email protected] reached through [email protected], the same fixture as the earlier re-triage. Agent apply (and get <uuid> --mode agent) now reports:
"status": "success", events: [{ "action": "skipped", "purl": "pkg:npm/[email protected]",
"reason": "Resolved by the project lockfile but not installed on this host (lockfile-only)",
"errorCode": "package_not_installed" }]
exit 0
The package is installed. It lives under <store>/v11/links/@/is-number/6.0.0/<hash>/…, and Node loads it unpatched from there. Release 4.0.0 reports partialFailure for the same fixture, so the change comes from #555 (3a4883c, "apply failing when patched deps are skipped", #403). That makes a lockfile-only skip non-fatal, and this GVS copy is misclassified as lockfile-only.
A direct GVS dependency, by contrast, now gets the loud refusal added in #486 (apply_failed: "Refusing to patch … it is in pnpm's global virtual store … set enableGlobalVirtualStore to false and reinstall, or use scan --mode hosted / --mode vendored", partialFailure). Transitive GVS deps should most likely get that same refusal, not a silent success. Until they do, CI that gates on agent apply exit codes no longer catches this.
The custom virtualStoreDir cells, and the default .pnpm isolated and hoisted transitive cells, all still pass on 8.15.9 / 9.15.9 / 10.34.5 / 11.28.3 / 12.8.1: apply, Node load, rollback, store pristine.
Generated by Claude Code
[agent] Janitor: releasing the stale claim Claim-ID: 2026-09-30T19:21:12Z-aa553b (branch agent/fix-npm-crawler-store-discovery). PR #365 merged with Refs and fixed only the custom virtualStoreDir half. The global-virtual-store half (confirmed again on 2026-10-02 18:31 UTC) has no PR, and the claimer has not posted since 2026-09-30 19:50 UTC, more than 48h ago. The issue stays open and is free to claim again.
Generated by Claude Code
[agent] Claiming this issue: the remaining global-virtual-store half. Shared root cause: the npm crawler never walks a virtualStoreDir that .modules.yaml places outside the project, so a transitive dep in pnpm's global virtual store reads as "not installed" (now a silent lockfile-only success) instead of getting the shared-store refusal that direct deps get. Branch: agent/fix-pnpm-gvs-transitive-refusal. Claim-ID: 2026-10-05T06:21:38Z-483a3d
Generated by Claude Code