[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Since #888 (fix for #880 / #881), hosted and vendored modes refuse any pnpm project with its own v9 pnpm-lock.yaml and no pnpm-workspace.yaml of its own whenever any ancestor directory has a pnpm-workspace.yaml. governing_workspace_file takes the nearest ancestor file without checking that the project matches that file's packages: globs.
pnpm 11 and 12 don't treat such a directory as a workspace project. Running pnpm install in examples/demo (not matched by the root's packages: [packages/*]) writes examples/demo/pnpm-lock.yaml and reads only a pnpm-workspace.yaml in examples/demo, never the root one. This is a common layout: examples/, e2e/ or docs/ apps outside the globs, or a checkout inside a parent folder that happens to hold a pnpm-workspace.yaml.
So on main:
- hosted exits 1 with
redirect_pnpm_settings_elsewhere. Its remedy ("add trustLockfile: true to /pnpm-workspace.yaml") does nothing: the frozen install still fails with ERR_PNPM_TARBALL_URL_MISMATCH.
- vendored fails each package with
vendor_pnpm_settings_elsewhere and tells the user to use --mode hosted, which also refuses. Neither mode can patch the project.
- Release 4.0.0 patches the same project in both modes. The nested
pnpm-workspace.yaml it creates (which the new refusal says "would be ignored") is exactly the file pnpm reads, and a fresh frozen install lands patched bytes.
Impact
Every pnpm 11/12 project nested below an unrelated workspace file stops being patchable in hosted and vendored modes, and both error messages point at remedies that don't work.
Repro (pnpm 12.10.1, Node 22, mock patch API as in the pnpm compatibility e2e)
mkdir -p ws/packages/a ws/examples/demo && cd ws
printf 'packages:\n - packages/*\n' > pnpm-workspace.yaml
echo '{"name":"root","private":true}' > package.json
echo '{"name":"a","version":"1.0.0"}' > packages/a/package.json
echo '{"name":"demo","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0","ms":"2.1.3"}}' > examples/demo/package.json
cd examples/demo && pnpm install # pnpm 11/12: lock written to examples/demo/pnpm-lock.yaml
socket-patch scan --mode hosted --json --yes # exit 1, redirect_pnpm_settings_elsewhere
socket-patch scan --mode vendored --json --yes # exit 1, vendor_pnpm_settings_elsewhere x2
# Proof that the root file doesn't govern demo (pins written with --no-trust-lockfile-config,
# then a fresh frozen install of a copy, empty store):
# C: no trust anywhere -> ERR_PNPM_TARBALL_URL_MISMATCH
# A: `trustLockfile: true` in ws/pnpm-workspace.yaml -> ERR_PNPM_TARBALL_URL_MISMATCH (the advised fix)
# B: `trustLockfile: true` in examples/demo/pnpm-workspace.yaml -> exit 0, left-pad patched
Expected vs actual
CLI_CONTRACT.md scopes both codes to "a pnpm workspace member with its own … pnpm-lock.yaml (sharedWorkspaceLockfile: false)". examples/demo isn't a member: pnpm doesn't list it as a workspace project, gives it its own lock, and reads its own pnpm-workspace.yaml. Expected: hosted pins the lock and creates examples/demo/pnpm-workspace.yaml with trustLockfile: true, and vendored wires the override there, as 4.0.0 does. Actual: both modes refuse, and nothing is written.
OS × version
| OS |
pnpm |
layout (lock location) |
hosted on main |
vendored on main |
4.0.0 |
| Linux |
12.10.1 |
own lock in examples/demo |
refused (reproduced twice), advised fix fails, nested file works |
refused |
hosted + vendored pass, fresh frozen install patched |
| Linux |
11.28.5 |
own lock in examples/demo |
refused, advised fix fails, nested file works |
not run |
not run |
| Linux |
10.34.6 / 9.15.9 |
pnpm installs the root workspace instead (no lock in demo) |
n/a |
n/a |
n/a |
macOS and Windows weren't probed. The logic is path-only and OS-independent.
First bad commit
8e8daf0 (#888). Release 4.0.0 is good.
Suspect code
crates/socket-patch-core/src/utils/pnpm_workspace.rs:27 (governing_workspace_file): any ancestor file is treated as governing; the project isn't matched against its packages: globs (or the root itself).
crates/socket-patch-core/src/hosted/governing_root.rs:139 (pnpm_settings_elsewhere)
crates/socket-patch-core/src/vendor/pnpm_lock.rs:594 (vendored refusal)
No probe runs; Linux only.
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Since #888 (fix for #880 / #881), hosted and vendored modes refuse any pnpm project with its own v9
pnpm-lock.yamland nopnpm-workspace.yamlof its own whenever any ancestor directory has apnpm-workspace.yaml.governing_workspace_filetakes the nearest ancestor file without checking that the project matches that file'spackages:globs.pnpm 11 and 12 don't treat such a directory as a workspace project. Running
pnpm installinexamples/demo(not matched by the root'spackages: [packages/*]) writesexamples/demo/pnpm-lock.yamland reads only apnpm-workspace.yamlinexamples/demo, never the root one. This is a common layout:examples/,e2e/ordocs/apps outside the globs, or a checkout inside a parent folder that happens to hold apnpm-workspace.yaml.So on main:
redirect_pnpm_settings_elsewhere. Its remedy ("addtrustLockfile: trueto /pnpm-workspace.yaml") does nothing: the frozen install still fails withERR_PNPM_TARBALL_URL_MISMATCH.vendor_pnpm_settings_elsewhereand tells the user to use--mode hosted, which also refuses. Neither mode can patch the project.pnpm-workspace.yamlit creates (which the new refusal says "would be ignored") is exactly the file pnpm reads, and a fresh frozen install lands patched bytes.Impact
Every pnpm 11/12 project nested below an unrelated workspace file stops being patchable in hosted and vendored modes, and both error messages point at remedies that don't work.
Repro (pnpm 12.10.1, Node 22, mock patch API as in the pnpm compatibility e2e)
Expected vs actual
CLI_CONTRACT.md scopes both codes to "a pnpm workspace member with its own …
pnpm-lock.yaml(sharedWorkspaceLockfile: false)".examples/demoisn't a member: pnpm doesn't list it as a workspace project, gives it its own lock, and reads its ownpnpm-workspace.yaml. Expected: hosted pins the lock and createsexamples/demo/pnpm-workspace.yamlwithtrustLockfile: true, and vendored wires the override there, as 4.0.0 does. Actual: both modes refuse, and nothing is written.OS × version
examples/demoexamples/demodemo)macOS and Windows weren't probed. The logic is path-only and OS-independent.
First bad commit
8e8daf0(#888). Release 4.0.0 is good.Suspect code
crates/socket-patch-core/src/utils/pnpm_workspace.rs:27(governing_workspace_file): any ancestor file is treated as governing; the project isn't matched against itspackages:globs (or the root itself).crates/socket-patch-core/src/hosted/governing_root.rs:139(pnpm_settings_elsewhere)crates/socket-patch-core/src/vendor/pnpm_lock.rs:594(vendored refusal)No probe runs; Linux only.