fix(vendor): write pnpm overrides to pnpm-workspace.yaml for pnpm >= 11 - #174
Merged
Mikola Lysenko (mikolalysenko) merged 4 commits intoAug 15, 2026
Merged
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Migration wipe of revert wiring
- Fixed by returning AlreadyPatched when package.json and lock are in sync, preventing replacement of ledger entry that would lose revert wiring records.
Or push these changes by commenting:
@cursor push d2439191b3
Preview (d2439191b3)
diff --git a/crates/socket-patch-core/src/vendor/pnpm_lock.rs b/crates/socket-patch-core/src/vendor/pnpm_lock.rs
--- a/crates/socket-patch-core/src/vendor/pnpm_lock.rs
+++ b/crates/socket-patch-core/src/vendor/pnpm_lock.rs
@@ -271,11 +271,14 @@
Err(e) => return done_failure(purl, format!("{PNPM_WORKSPACE} surgery failed: {e}")),
};
- if !pkg_changed && !lock_changed && ws_edit.new_text.is_none() {
- // Everything already carries this uuid + the packed integrity: the
- // project is in sync. The tarball re-pack above was byte-identical
- // by determinism; synthesize AlreadyPatched and record nothing (the
- // existing ledger entry stays authoritative).
+ if !pkg_changed && !lock_changed {
+ // package.json and the lock already carry this uuid + the packed
+ // integrity: the core wiring is in sync. The tarball re-pack above was
+ // byte-identical by determinism. Even if the workspace file (pnpm >= 11
+ // surface) needs first-time creation during migration, that must not
+ // replace the ledger entry and wipe package.json/lock revert wiring.
+ // Synthesize AlreadyPatched and record nothing (the existing ledger
+ // entry stays authoritative).
return done(
already_patched_result(purl, &project_root.join(&rel_tgz), &record.files),
None,You can send follow-ups to the cloud agent here.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a23abb1. Configure here.
…pm >= 11 pnpm 11 stopped reading `overrides` from package.json's `pnpm` field — it moved to `pnpm-workspace.yaml` (https://pnpm.io/settings). The pnpm vendor backend wrote only `package.json` `pnpm.overrides`, so pnpm 11 ignored it and a frozen install of the committable artifact refused with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` — the lock carried an `overrides:` section but pnpm resolved none from config. The vendored tarball and lock were correct (they even pass pnpm 11's tarball-URL supply-chain policy); only the override LOCATION was wrong. Vendoring now mirrors the same versioned `<name>@<version>` → `file:` selector into `pnpm-workspace.yaml` alongside the existing package.json override, so whichever surface the installed pnpm reads matches the lock's `overrides:` section: - no workspace file → create one with a root-only `packages: ['.']` list (pnpm 9 refuses a workspace file whose `packages` field is missing/empty; `.` is the sole importer already and cannot glob a stray `packages/` subtree into the workspace the way `packages/*` would) plus the `overrides:` block; - existing file without `overrides:` → append the section (packages untouched); - existing `overrides:` section → insert our key (or take over a user's exact pin), fail-closed on a conflicting same-name override or an inline mapping. package.json `pnpm.overrides` is kept for pnpm 9/10. Verified across real pnpm 9.15.9 / 10.34.5 / 11.21.0: a cold `pnpm install --frozen-lockfile --offline` from only the committable files installs the vendored (patched) bytes with no config mismatch. `vendor --revert` deletes a file it created (when still the bare scaffold) or splices its override back out of one it edited; the three surfaces are committed override-first / lock-last so a lock-write failure never leaves a desynced override behind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… surface The pnpm >= 11 support (#174) mirrors the tarball override into `pnpm-workspace.yaml` in addition to `package.json` + `pnpm-lock.yaml`. On the UPGRADE path — a project vendored by the pre-workspace code, then re-vendored under the new code — `package.json` and `pnpm-lock.yaml` already carry the override, so `vendor_pnpm` rewrites neither and its fresh `VendorEntry.wiring` names ONLY the newly created `pnpm-workspace.yaml` surface. The CLI vendor flow re-invokes the backend on every installed package (there is no ledger short-circuit; the `already_vendored` classification only happens AFTER the backend runs and returns `AlreadyPatched`). So `persist_vendor_entry` would replace the prior ledger entry wholesale with the workspace-only entry, dropping the `package.json` + `pnpm-lock.yaml` pre-vendor originals the FIRST vendoring recorded. `vendor --revert` could then restore only the workspace file, leaving the override wired into the other two surfaces forever. Fix: extract the re-vendor reconciliation into `vendor::carry_forward_wiring` and extend it to (1) union the prior entry's wiring records for surfaces THIS run left in sync, and (2) OR-merge the pnpm "created this table/file/section" bookkeeping — both scoped to a same-uuid re-vendor so a new-uuid re-vendor still rewires every surface fresh. The existing original-fill and go-takeover carry-forward are unchanged. Revert now byte-restores all three surfaces on the upgrade path. Regression test `revendor_upgrade_adds_workspace_and_revert_restores_all_three_surfaces` stages a pre-workspace vendored project, re-vendors (adding only the workspace mirror), reconciles, then reverts and asserts package.json + pnpm-lock.yaml + pnpm-workspace.yaml are all byte-restored to their pre-vendor originals. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
fix/pnpm11-overrides-location
branch
from
August 14, 2026 14:48
a23abb1 to
1af04da
Compare
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 14, 2026
Mikola Lysenko (mikolalysenko)
enabled auto-merge (squash)
August 14, 2026 20:26
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
deleted the
fix/pnpm11-overrides-location
branch
August 15, 2026 00:03
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Aug 15, 2026
#174 extracted the re-vendor reconciliation out of the CLI's persist_vendor_entry into vendor::carry_forward_wiring and extended it (wiring union for untouched surfaces, pnpm created-surface merge). This branch had added the cargo lock originals carry-forward to the same inline block, so both sides rewrote it. Resolved by keeping main's extraction and moving this branch's cargo carry-forward into carry_forward_wiring beside the other unconditional, identity-matched reconciliations: a re-vendor over already-detached wiring records lock: None, and the crates.io source/checksum pair it replaced is unrecoverable offline, so the prior entry is its only home. Placed ahead of the same-uuid early return because that loss happens on a new-uuid re-vendor too. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Aug 15, 2026
Conflict: crates/socket-patch-core/src/vendor/mod.rs — both sides widened adjacent re-export lines. Resolved as a union: * main (#174) exports state::carry_forward_wiring, the extracted re-vendor ledger reconciliation persist_vendor_entry now calls; * this branch exports verify::artifact_is_file_shaped and verify::compute_dir_inventory, which the CLI needs because verify is pub(crate). Both hunks are kept verbatim; neither side's symbol set changes. The gem sibling (#177) was already in this branch's base, so main brings only pnpm/redirect work. carry_forward_wiring reconciles wiring, pnpm meta and the go-takeover flag and never touches entry.artifact, so the new fileInventory on a re-vendor keeps the freshly computed inventory — no interaction. Main added no VendorArtifact literal, so the mechanical file_inventory field addition stays complete. Verified: core lib 2215/0, cli lib 376/0, repair_vendor_e2e 24/0, in_process_vendor 30/0, e2e_vex_vendor 8/0, setup_contract_gaps 10/0, e2e_gem 8/0, in_process_gem_apply 7/0, e2e_vendor_gem_build 5/0, e2e_vendor_pnpm_build 6/0. vendor/mod.rs is rustfmt-clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.


Problem
In vendored mode for the npm/pnpm ecosystem, the CLI pinned the vendored tarball by writing a
pnpm.overridesentry intopackage.json. pnpm >= 11 no longer readsoverridesfrom the package.jsonpnpmfield — it moved topnpm-workspace.yaml(https://pnpm.io/settings). So pnpm 11 ignored the override, resolved no overrides from config, and a frozen install of the committable artifact refused:The vendored tarball + lock were correct (they even pass pnpm 11's tarball-URL supply-chain policy) — only the override location was wrong. Surfaced by PR #169's real-production vendored e2e (
e2e_vendored_production.rs, pnpm leg) and documented indocs/testing/vendored-production-e2e.md.Reproduced on this host (pnpm 11.21.0):
pnpm add minimist@1.2.2→scan --mode vendored→ copy only the committable files → coldpnpm install --frozen-lockfile --offline→ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.Fix
The pnpm vendor backend now mirrors the same versioned
<name>@<version>→file:<rel-tgz>selector intopnpm-workspace.yaml, alongside the existing package.json override, so whichever surface the installed pnpm reads matches the lock'soverrides:section (what pnpm compares for the mismatch):packages: ['.']list (pnpm 9 refuses a workspace file whosepackagesfield is missing/empty;.is already the sole importer and, unlikepackages/*, cannot glob a straypackages/subtree into the workspace) plus theoverrides:block;overrides:→ append the section,packages:untouched;overrides:section → insert our key (or take over a user's exact-version pin), fail-closed on a conflicting same-name override or an inline/flow mapping.package.json
pnpm.overridesis kept for pnpm 9/10 back-compat. Edits are line splices (byte-stable), tracked with wiring records +PnpmMetaflags. The three surfaces are committed override-first / lock-last so a lock-write failure never strands a desynced override.vendor --revertdeletes a file it created (when still the bare scaffold) or splices its override back out of one it edited (byte-for-byte), restoring a taken-over value and dropping an emptied section it created. Scoped to the pnpm vendored path; no other ecosystem touched.Test
Verified on real pnpm 9.15.9 / 10.34.5 / 11.21.0: a cold
pnpm install --frozen-lockfile --offlinefrom only the committable files installs the vendored (patched)minimist@1.2.2bytes — noERR_PNPM_LOCKFILE_CONFIG_MISMATCH— andvendor --revertremoves the createdpnpm-workspace.yamland restorespackage.json/pnpm-lock.yaml.vendor::pnpm_lock): create-scaffold + revert-deletes; append-section + byte-restore; insert-beside-existing + remove-only-ours; inline-mapping refused; conflicting-override refused; commit unwind deletes a created workspace file.state.rsround-trip covers the newPnpmMetafields. All 602vendor::*lib tests pass.e2e_vendor_pnpm_build.rs(real corepack pnpm@10/9): now asserts the workspace file is created with the override + root-onlypackages:, is copied into the fresh-checkout proof, stays byte-stable on re-vendor, and is deleted on revert. Passes.e2e_vendored_production.rs(real Socket production + real npm registry, host pnpm 11): the pnpm leg's xfail workaround is removed — the frozen install now succeeds directly and byte-checks the patched bytes.pnpm_vendored_install_proofpasses.cargo build+cargo clippyclean (the only clippy warnings are pre-existingdoc_lazy_continuationnotes in an unrelated module-doc header, present onmain).🤖 Generated with Claude Code
Note
Medium Risk
Changes pnpm vendoring commit/revert behavior and creates or edits
pnpm-workspace.yaml, which affects install correctness across pnpm 9–11; scope is limited to the pnpm vendor path with extensive tests.Overview
Fixes pnpm >= 11 frozen installs failing with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCHbecause overrides were only written topackage.jsonpnpm.overrides, while pnpm 11 reads them frompnpm-workspace.yaml.The pnpm vendor backend now mirrors the same versioned
<name>@<version>→file:override intopnpm-workspace.yaml(still updatingpackage.jsonfor pnpm 9/10). If no workspace file exists, it creates one with a root-onlypackages: ['.']list plusoverrides:so pnpm 9 accepts the file. Edits use line splices for byte-stable revert;commit_surfacesunwinds override files if the lock write fails.vendor --revertdeletes a scaffold file it created or removes only its override from an edited file.PnpmMetagainscreatedWorkspaceFile/createdWorkspaceOverrides. E2E and production tests no longer use a manual workspace workaround; docs mark the pnpm 11 gap as fixed.Reviewed by Cursor Bugbot for commit a23abb1. Configure here.