[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Both line-splice editors of pnpm-workspace.yaml find an existing top-level key only when the line literally starts with trustLockfile: or overrides:. Valid YAML spellings of the same key are missed:
- a double-quoted key:
"trustLockfile": false, "overrides":
- a single-quoted key:
'trustLockfile': true
- a space before the colon:
trustLockfile : false
When the key is missed, scan --mode hosted appends a second trustLockfile: true, and vendor appends a second overrides: section. pnpm rejects duplicate mapping keys, so the workspace file no longer loads, and every later pnpm install (frozen or not) fails. Both commands report success.
Impact
- The repo's installs break after a "successful" run.
- In the
"trustLockfile": false case, the user's explicit opt-out is overridden rather than respected. CLI_CONTRACT.md says the trust write "preserves explicit user settings", and plan_workspace_trust has a UserSet branch for exactly this case. It just never matches.
- In vendored mode, the user's own quoted
overrides section is treated as absent, so the pre-flight conflict check (check_workspace_override) never examines it.
This is a different defect from #400 (non-block document shapes); here the document is an ordinary block mapping.
Repro
echo '{"name":"proj","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
printf 'packages:\n - '"'"'.'"'"'\n"trustLockfile": false\n' > pnpm-workspace.yaml
pnpm install # loads fine
socket-patch scan --mode hosted --yes --json --api-url $MOCK --org test-org --api-token fake
# exit 0, status success, rewrittenFiles [pnpm-lock.yaml, pnpm-workspace.yaml]
cat pnpm-workspace.yaml
# packages:
# - '.'
# "trustLockfile": false
# trustLockfile: true
pnpm install --frozen-lockfile
# [ERROR] duplicated mapping key (4:1)
Vendored (.socket/manifest.json and blobs staged):
printf 'packages:\n - '"'"'.'"'"'\n"overrides":\n is-number: 7.0.0\n' > pnpm-workspace.yaml
pnpm install && socket-patch vendor --yes --json --offline # exit 0, applied
# ... "overrides":\n is-number: 7.0.0\noverrides:\n [email protected]: file:.socket/vendor/...
pnpm install --frozen-lockfile # duplicated mapping key (5:1)
$MOCK is a local mock of the patch API (batch / by-package / package grant / view routes plus the tarball), modelled on crates/socket-patch-cli/tests/e2e_redirect_pnpm_build.rs.
Expected vs actual
- Expected: a key pnpm reads as
trustLockfile / overrides is recognised as that key. An explicit non-true trustLockfile is kept, with the redirect_pnpm_trust_lockfile warning, as the UserSet branch intends. An existing overrides mapping is edited in place, or the run refuses with a clear code if the splice can't edit it, as is already done for an inline overrides: mapping.
- Actual: a duplicate key is appended, the file can't be parsed, and the command reports success.
OS × version (Linux, Node 22, main f6b7fb9)
| pnpm |
hosted "trustLockfile": false |
hosted 'trustLockfile': true |
hosted trustLockfile : false |
vendored "overrides": |
| 10.34.5 |
fail (duplicated mapping key) |
not run |
not run |
not run |
| 11.27.0 |
fail |
fail |
fail |
fail |
| 12.8.1 |
fail (duplicate mapping) |
not run |
not run |
fail (duplicate mapping) |
control: unquoted trustLockfile: false |
(documented UserSet behaviour; not re-run this time) |
|
|
|
Released 4.0.0 behaves the same (hosted "trustLockfile": false, pnpm 11.27.0). It isn't a regression. macOS and Windows weren't probed (pure string matching).
Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:393-400: line.strip_prefix("trustLockfile:") is the only way an existing key is detected.
crates/socket-patch-core/src/vendor/pnpm_lock.rs:1667 (ws_overrides_section → section_bounds(lines, "overrides")) and the inline check at :1695 (l.starts_with("overrides:")).
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Both line-splice editors of
pnpm-workspace.yamlfind an existing top-level key only when the line literally starts withtrustLockfile:oroverrides:. Valid YAML spellings of the same key are missed:"trustLockfile": false,"overrides":'trustLockfile': truetrustLockfile : falseWhen the key is missed,
scan --mode hostedappends a secondtrustLockfile: true, andvendorappends a secondoverrides:section. pnpm rejects duplicate mapping keys, so the workspace file no longer loads, and every laterpnpm install(frozen or not) fails. Both commands report success.Impact
"trustLockfile": falsecase, the user's explicit opt-out is overridden rather than respected. CLI_CONTRACT.md says the trust write "preserves explicit user settings", andplan_workspace_trusthas aUserSetbranch for exactly this case. It just never matches.overridessection is treated as absent, so the pre-flight conflict check (check_workspace_override) never examines it.This is a different defect from #400 (non-block document shapes); here the document is an ordinary block mapping.
Repro
Vendored (
.socket/manifest.jsonand blobs staged):$MOCKis a local mock of the patch API (batch / by-package / package grant / view routes plus the tarball), modelled oncrates/socket-patch-cli/tests/e2e_redirect_pnpm_build.rs.Expected vs actual
trustLockfile/overridesis recognised as that key. An explicit non-truetrustLockfileis kept, with theredirect_pnpm_trust_lockfilewarning, as theUserSetbranch intends. An existingoverridesmapping is edited in place, or the run refuses with a clear code if the splice can't edit it, as is already done for an inlineoverrides:mapping.OS × version (Linux, Node 22, main
f6b7fb9)"trustLockfile": false'trustLockfile': truetrustLockfile : false"overrides":trustLockfile: falseReleased 4.0.0 behaves the same (hosted
"trustLockfile": false, pnpm 11.27.0). It isn't a regression. macOS and Windows weren't probed (pure string matching).Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:393-400:line.strip_prefix("trustLockfile:")is the only way an existing key is detected.crates/socket-patch-core/src/vendor/pnpm_lock.rs:1667(ws_overrides_section→section_bounds(lines, "overrides")) and the inline check at:1695(l.starts_with("overrides:")).