Repository navigation
Read pnpm locks through one walk (lock-model 2a) - #2009
Draft
Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
Draft
Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
Conversation
Mikola Lysenko (mikolalysenko)
force-pushed
the
lock-model/1-origin
branch
from
November 4, 2026 17:18
6c4869b to
134d40e
Compare
PnpmLock is now the one pnpm entry walk, over every document of the lock. Each entry is tagged with its document (PnpmDocument::Project or Env), and documents are told apart by content: in a multi-document lock, one whose importers carry only configDependencies / packageManagerDependencies is pnpm 11+'s env lockfile, every other document is a project one. grammar::main_document (the text after the last `---`) is gone, so a crafted lock can no longer pick its own "project" and hide the rest (lock-model divergence N4). Behavior changes (owner decision: the env document counts and is its own install tree): - The inventory (PnpmLock::entries), PnpmLock::resolves, the legacy vendored in-use check (vendored_in_use) and InstalledPackages read the env document too, as the hosted planner and the v9 in-use check already did. env_document_is_not_in_the_inventory pinned #1949's skip; it becomes env_document_is_read_as_its_own_tree. - Discovery emits refs for Socket wiring in the env document. Its records are keyed `<lock> (env lockfile)` while the contests run, so the project document's registry entries, file: copies and bundled copies never contest its refs (and the reverse), its wiring does not exempt the project document from contesting another lock, and it is skipped by the cross-lock contest both ways (config dependencies install apart from every node_modules). finalize maps the key back to the lock path. Pinned by pnpm_env_document_is_its_own_install_tree. - A lock with several non-env documents reads all of them as project documents (before: only the last). Pinned by documents_are_identified_by_content_not_position. InstalledPackages::from_lock_text is rebuilt on PnpmLock (keeping its last-wins name:/version: rule and its None cases). pnpm writes the current lockfile (node_modules/.pnpm/lock.yaml) as the project lock alone and installs config dependencies outside the virtual store, so an env document is not expected there; if one appears its entries keep their store entries, the side that never hides an installed package. The vendored set is computed lazily (OnceLock), so the crawler does not pay for it. lock_text_has_member_importers, pnpm_settings_major and the hosted unwind's tarball evidence read documents by content too (the latter through PnpmLock, project documents only, as before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hosted pnpm planner names a dep it cannot find "vendored" instead of "not found" when the lock carries the vendor backend's wiring for it. It decided that with a substring test over every line: any line starting `<name>@file:.socket/vendor/`, or an `<name>@<version>:` line containing both `file:` and `.socket/vendor/`. It now reads the lock's packages entries through PnpmLock and asks crate::origin::vendor_ref (pure path grammar, no I/O, no host policy, so allowed in formats/): a v9 `<name>@file:<path>` key, or a legacy bare `file:<path>` key whose `name:` line names the dep, with `<path>` this project's vendored npm artifact. That is the rule discovery reads vendored pnpm keys with (lock-model divergence N5, pnpm part). Behavior changes, pinned by pnpm_vendored_diagnosis_reads_the_rekeyed_entry_through_origin (the backends' real v9 and legacy shapes stay "vendored", per the existing tests): - A `file:` path without a canonical uuid dir, or under another ecosystem's vendor dir, is redirect_pnpm_entry_not_found now. - An `overrides:` line alone, with no rekeyed packages entry behind it, is redirect_pnpm_entry_not_found now: pnpm mirrors overrides from package.json whether or not anything matches (the vendored in-use check already treats it that way). The depscan-sized equivalence fixture's vendored entry had no uuid dir, a shape the backend never writes; it now carries one, and its golden is re-blessed (both digests change because the input text changed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two npm-family inventory rules now follow lockfile discovery (and so what hosted mode pins): - Rush's common and subspace pnpm locks are separate install trees, read whenever rush.json is present, beside whatever lock the flavor router chose. Before, the inventory read them only as a fallback when the root had no lock (lock-model divergence N12). The listing itself is unchanged (#2003 aligns it separately). root_pnpm_lock_wins_over_ rush_fallback pinned the old shadowing; it becomes rush_locks_are_read_beside_the_root_lock. - pnpm's `lockfile` setting off (lockfile_disabled, not under Rush, as in discovery): pnpm neither reads nor writes the root or member locks. Discovery keeps a hosted ref from such a lock but withholds its lock pin; the inventory now keeps the entry (its coordinates and hosted url, which ledger liveness reads) and drops its integrity, so vendor's fetch and repair never verify against a pin the install does not use. Registry entries keep their integrity: a registry release's hash verifies its content whether or not pnpm reads the lock (N14). Pinned by pnpm_lockfile_off_withholds_hosted_pins. "Hosted" here is the default Socket host (origin::hosted_patch_uuid with no configured origins): the inventory has no per-run origin list yet; the install-tree PR moves it onto the single classifier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Outside formats/pnpm, pnpm lock entries are read through PnpmLock (one walk, every document tagged). A new test in formats/pnpm/mod.rs fails on any production use of grammar::entries / pnpm_packages or a hand-rolled `packages:` line walk elsewhere, except the writers that splice by byte range or line position, each allowlisted with a label and a use count: - patch/redirect/upstream/npm.rs (2): the hosted unwind's resolution splices (restore_pnpm_locks); - vendor/pnpm_lock.rs (3): the v9 vendored planner's preflight and line splices; - vendor/pnpm_lock_legacy.rs (6): the 5.4 / 6.0 vendored planner's preflight, splices and revert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/design/lock-model-divergences.md records what PR 2a resolved. Budget: lock-readers 30,976 -> 31,117 (+141), raised by hand: - vex/discover/mod.rs +46: the env document's tree key (pnpm_document_tree), mapping it back to the lock path in finalize, and skipping it in the cross-lock contest; - formats/pnpm/grammar.rs +51: the content-based document split (documents, is_env_document) replacing main_document; - vendor/lock_inventory/npm_family.rs +35: Rush locks beside the routed lock and the lockfile=false pin withholding; - vex/discover/npm.rs +13: one extraction pass per document; - formats/pnpm/workspace.rs +6, lines.rs +1; - formats/pnpm/mod.rs -4 (InstalledPackages on PnpmLock instead of its own line walk), formats/pnpm/hosted.rs -7 (the vendored check). lock-writers 88,314 -> 88,313 (--lower). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1. The pnpm architecture guard cut each file at its first `#[cfg(test)] mod`, so a file declaring an out-of-line test module near the top (crawlers/npm_crawler.rs, patch/redirect/mod.rs, vendor/mod.rs) went mostly unchecked. It now drops only the gated items: a `mod x;` line, or an inline item through its closing brace, counted outside comments and string, raw string and char literals (which span lines). production_drops_only_test_gated_items pins it, including late production code of those three files. 2. The env document must be the FIRST document as well as env-shaped, as pnpm and split_project_document read it. An env-shaped later document is a project one, so it cannot skip the cross-lock contest, and pnpm_settings_major no longer reports 11 for it. documents_are_identified_by_position_and_content (was ..._by_content_not_position) and pnpm_include_tarball_ignores_the_env_ lockfile_document pin it. 3. The hosted "vendored?" check compares the version too: another version vendored is redirect_pnpm_entry_not_found. The depscan-sized equivalence fixture's vendored entry gains the `version:` line the backend always writes; golden re-blessed. 4. LockfileEntry gains `pnpm_env`, and dedup_prefer_integrity never lets an env-document instance displace a project one (project first, then a verifier). env_document_instances_never_displace_project_instances. 5. Entries are tagged by their KEY line's offset, so an empty-bodied last key of a document keeps its document. an_entry_with_an_empty_body_keeps_its_document. 6. No pseudo path: discovery records carry an explicit TreePart (File or PnpmEnv) beside the real file (PatchedRef, ResolvedElsewhere, UnpatchedCopy, UnwiredCopy scope); the contests compare it, and the finalize remap is gone. An env ref the same file's project document also wires (same purl, uuid, mode) is dropped in finalize, so the two never both survive, whatever their pins. 7. InstalledPackages walks keys only (PnpmLock::parse_keys, no resolution parse) on the store crawler's hot path. 8. The hosted planner builds each lock's vendored (name, version) set once, from the unspliced text (a splice never touches a key or a name:/version: line), dropped when materialize replaces the text. 9. Rush locks are no flavor's: the routed lock keeps its flavor alone (inventory_npm_lock), and inventory_npm_trees_raw_in unions it with Rush's trees; rush.json is stat'd once through the view (the Rush reader no longer re-checks it), no bool/Vec shadowing. Budget: lock-readers 31,117 -> 31,207 (+90: TreePart and its helpers +30 in vex/discover/mod.rs, PnpmTree +22 in vex/discover/npm.rs, the key-only walk +16 in formats/pnpm/mod.rs, the vendored cache +13 in formats/pnpm/hosted.rs, `pnpm_env: false` in 21 LockfileEntry literals, golden render +7, minus 15 in the inventory's npm_family/pnpm/mod). lock-writers 88,313 -> 88,314 (+1: hosted/engine.rs compares the part). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
lock-model/2a-pnpm-readers
branch
from
November 4, 2026 17:50
4b96421 to
613ca3a
Compare
This was referenced Nov 5, 2026
Clears the conflicts left when the base took the 2026-11-05 owner decisions and the loc-budget counter fix. - lock-model-divergences.md: keep this PR's "Resolved in PR 2a" detail for N4, N5, N12 and N14, tagged with #2009, plus the base's PR links (#2025, #2029). N11 takes the base's owner decision (shadowed), which replaces "pending owner decision". - loc-budget.json: the base re-measured lock-readers with the fixed counter (30259). This PR grows the group, so the max is raised by hand to its measured count under the new counter (30482). Co-Authored-By: Claude <noreply@anthropic.com>
Collaborator
Author
|
Burn-down agent: merged
Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
pushed a commit
that referenced
this pull request
Nov 6, 2026
Clears the conflicts left when the base merged lock-model/1-origin. - lock-model-divergences.md: keep this PR's PR 2b resolutions (N5, N11, N15, N16) and tag them #2029, take the base's #2009 tags for the PR 2a rows, its #2025 note on N5, and its P4 note on out-of-root includes. - loc-budget.json: set both groups to their measured counts under the base's fixed counter (this PR shrinks both). Co-Authored-By: Claude <noreply@anthropic.com>
Brings in #2002's review fixes (OriginPolicy imported from crate::origin, the redirect re-export dropped, the shared host matcher, the stricter architecture guards) and #2001's later counter fixes. Restores crates/socket-patch-core/src/origin.rs to the lock-readers group, which the previous merge (1d27a20) inherited the loss of from 1-origin's 4242ebc. Budget at this branch's counts: lock-readers 30767, lock-writers 88315. Co-Authored-By: Claude Opus 5.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.
Stack: PR 2a of the lock-model stack, based on #2002.
What changed
Every pnpm lock reader now reads entries through one walk,
formats::pnpm::PnpmLock, and the readers and modes agree on four questions where they disagreed (lock-model divergences N4, N5 pnpm part, N12, N14).One entry walk.
PnpmLockreads every document of the lock and tags each entry with itsPnpmDocument(ProjectorEnv), by its key line.InstalledPackages::from_lock_text(the store crawler's current-lockfile reader) is rebuilt on it, through a key-only walk (PnpmLock::parse_keys, no resolution parse) for the crawler's hot path. It keeps its last-winsname:/version:rule and itsNonecases. The vendored-uuid set is lazy (OnceLock). The hosted unwind's tarball evidence (patch/redirect/upstream/npm.rs) readsPnpmLock::parse(text).packages_of(Project). It keeps "project documents only" because the env document records bare resolutions whatever the setting.pnpm_packagesis now private.Documents identified by position and content (N4).
grammar::main_document(the text after the last---) is removed.grammar::documentssplits at column-0---lines. In a lock with two or more documents, the first document is the env document when itsimporters:carry onlyconfigDependencies/packageManagerDependencies(at least one). That is pnpm's own position rule, and the onesplit_project_documentin the vendored planners uses. Every other document, including an env-shaped later one, is a project document, so an extra document can't hide entries or opt out of the project tree's contests.lock_text_has_member_importersandpnpm_settings_majoruse the same split.The env document is its own install tree (owner decision, N4). The inventory lists env-document packages (
LockfileEntry::pnpm_env), and discovery emits refs for Socket wiring in it. Both vendored in-use checks read both documents: the v9 check already read the whole text, and the legacy check now does throughPnpmLock. Discovery records carry an explicitTreePart(FileorPnpmEnv) beside the real lock path (PatchedRef,ResolvedElsewhere,UnpatchedCopy,UnwiredCopyscope), so no pseudo path ever exists. The contests compare it:file:copies and bundled copies never contest each other;contest_across_locksskips the env tree in both directions, like PEP 723 script locks, because config dependencies install outside everynode_modules.An env ref that the same file's project document also wires (same purl, uuid and mode) is dropped in
finalize, so the two never both survive. Diagnostics name the env document<lock> (env lockfile).Rush locks always read (N12). The inventory reads Rush's common and subspace locks whenever
rush.jsonexists, beside whatever lock the flavor router chose (inventory_npm_trees_raw_in). Before, it read them only when the root had no lock. The routed lock keeps its flavor alone, since Rush trees are no flavor's, andrush.jsonis stat'd once through the view. The Rush listing code is unchanged (Skip unsafe Rush subspace locks in the lock inventory #2003 aligns it separately).lockfile=false(N14). Discovery keeps a hosted ref from a lock pnpm won't read but withholds its lock pin. The inventory now does the same: underlockfile_disabled(not under Rush, as in discovery), it keeps a Socket-hosted pnpm entry (coordinates and hosted URL, which ledger liveness reads) and drops its integrity. Nothing then fetches or verifies against a pin the install doesn't use. Registry entries keep their integrity, because a registry release's hash verifies its content whether pnpm reads the lock or not. "Hosted" means the default Socket host here, since the inventory has no per-run origin list yet (PR 12).Hosted "vendored?" check uses
origin::vendor_ref(N5, pnpm part). The hosted planner used a substring test over every line to tell a vendored dep from a missing one. It now reads the rekeyed packages entries (v9<name>@file:<path>, or legacy barefile:<path>+name:, each with itsversion:) and checks<path>withcrate::origin::vendor_ref. Both the name and the version must match the dep. The set is built once per lock.vendor_refis pure path grammar, and the formats architecture test bans only I/O andhosted_patch_uuid*. It's the same rule discovery uses for vendored pnpm keys.Architecture guard.
formats::pnpm::architecture_testsfails on any production use ofgrammar::entries/pnpm_packages/ a hand-rolledpackages:line walk outsideformats/pnpm. It drops only#[cfg(test)]items (amod x;line, or an inline item through its closing brace, counted outside comments and literals), so files that declare test modules early are scanned to their end. Writers that splice by position are allowlisted with a label and a use count:patch/redirect/upstream/npm.rs×2: the hosted unwind's resolution splices;vendor/pnpm_lock.rs×3: the v9 vendored planner;vendor/pnpm_lock_legacy.rs×6: the 5.4 / 6.0 vendored planner.None of these changes reads a file outside the project root (owner decision on the repository root).
lockfile_disabledreads the project'spnpm-workspace.yaml,.npmrc,node_modules/.modules.yaml,package.jsonand lock through the view; Rush locks are undercommon/.Behavior changes
Each was checked to fail on the previous production code and pass here, except where noted. The
formats::pnpmenv/document tests and the finding 4 and 6 tests use APIs this PR adds, so on the base they fail to compile; their assertions encode the old behavior being reversed.The env document's packages are read: inventory
entries(),resolves()(get's pnpm-PnP narrowing in hosted mode), the legacy vendored in-use check, andInstalledPackages. Pinned byformats::pnpm::tests::env_document_is_read_as_its_own_tree, which replacesenv_document_is_not_in_the_inventory(it pinned PnpmLock::parse inventories a pnpm 11 env-lockfile document's configDependencies as project packages, while InstalledPackages and the upstream evidence reader drop it #1949's skip, which the owner decision reverses).Discovery emits env-document refs as their own tree, with no contest against the project document or another lock in either direction. Env wiring doesn't exempt the project document, and an env twin of a project ref is dropped. Pinned by
vex::discover::npm::tests::pnpm_env_document_is_its_own_install_tree(LF and CRLF). On the base:refs: [].Documents. A lock with several non-env documents reads all of them as project documents (before: only the last). Only the first document can be the env one. An entry is tagged by its key line. Pinned by
formats::pnpm::tests::documents_are_identified_by_position_and_content,…::an_entry_with_an_empty_body_keeps_its_documentandpatch::redirect::upstream::npm::tests::pnpm_include_tarball_ignores_the_env_lockfile_document.The hosted "vendored?" diagnosis. Each of these now gets
redirect_pnpm_entry_not_foundinstead ofredirect_pnpm_entry_vendored:file:path with no canonical uuid dir;overrides:line with no rekeyed entry behind it;The backends' real v9 and legacy shapes stay "vendored". Pinned by
patch::redirect::tests::pnpm_vendored_diagnosis_reads_the_rekeyed_entry_through_origin.Rush locks are read beside a root lock and beside another family's lock. Pinned by
vendor::lock_inventory::tests::rush_locks_are_read_beside_the_root_lock, which replacesroot_pnpm_lock_wins_over_rush_fallback(it pinned the old shadowing).lockfile=falsedrops a hosted pnpm entry's integrity in the inventory (.npmrcorpnpm-workspace.yaml; not under Rush). Pinned byvendor::lock_inventory::tests::pnpm_lockfile_off_withholds_hosted_pins.The collapsed inventory never lets an env-document instance displace a project instance; the every-instance view keeps both. Pinned by
vendor::lock_inventory::tests::env_document_instances_never_displace_project_instances.Architecture guard. Pinned by
formats::pnpm::architecture_tests::{pnpm_entries_are_read_through_the_model, production_drops_only_test_gated_items}.Golden diffs
vex-discover-goldencorpus (committed_fixture_corpus_matches_golden): no change. No committed fixture has an env document. The renderer prints a ref'spartonly when it isn'tFile.tests/equivalence/pnpm_depscan_sized.golden: re-blessed.vendored-pkg@file:.socket/vendor/npm/vendored-pkg-1.0.0.tgz, with no uuid dir and noversion:line, a shape the backend never writes. It now carries both, so the input digest changes, and the output digest changes with it because the output contains that text. Checked: the previous planner run on the new fixture produces the same blessed digests, so the planner's output on this fixture is unchanged.Restack (2026-11-06)
Merge 097a538 brings in #2002's review fixes and #2001's later counter fixes.
OriginPolicyis now imported fromcrate::origin, thepatch::redirectre-export of the hosted-URL rule is gone, and the extractor,formatsandlock_inventoryguards are stricter. Nothing in this PR's code needed changing.The merge also restores
crates/socket-patch-core/src/origin.rsto thelock-readersgroup. The earlier routine merge had dropped it, inherited from #2002's 4242ebc. Counted with the fixed counter:lock-readers: 30,544 (Move the Socket origin classifier into one module #2002) → 30,767.lock-writers: 88,315 → 88,315.The numbers in the Budget section below are from the old counter.
Tests after the restack:
cargo test -p socket-patch-core --lib -- origin:: vex:: formats:: vendor::lock_inventory:: vendor::pnpm patch::redirect:: hosted::: 2,527 passed. That includes the golden test.test_loc_budget.py: OK.Budget
python3 scripts/loc-budget.py:lock-readers30,976 → 31,207 (+231, raised by hand):vex/discover/mod.rs+76:TreePart, its_inhelpers, contest checks, env-twin dropformats/pnpm/grammar.rs+52: content- and position-baseddocuments/is_env_document, replacingmain_documentvex/discover/npm.rs+35: one pass per document,PnpmTreevendor/lock_inventory/npm_family.rs+31: Rush trees beside the routed lock,lockfile=falsewithholdingformats/pnpm/mod.rs+12: tagging, key-only walkformats/pnpm/hosted.rs+6: the vendored check and its cachepnpm_env: falsein 21LockfileEntryliterals (+16)vex/discover/testing/golden.rs+7formats/pnpm/workspace.rs+6,lines.rs+1vendor/lock_inventory/pnpm.rs−9 (no secondrush.jsonstat),mod.rs−2lock-writers88,314 → 88,314: −1 in the unwind, +1 inhosted/engine.rs(compares the tree part).Tests run
cargo test -p socket-patch-core --lib -- formats::pnpm vendor::lock_inventory:: vex::discover:: crawlers::npm_crawler patch::redirect:: vendor::pnpm hosted::: 2056 passed, 0 failed. This includes the discovery golden corpus and the pnpm equivalence goldens.cargo test -p socket-patch-core --lib -- vex::discover::testing::golden::tests::committed_fixture_corpus_matches_golden formats:: utils::pnpm_workspace vendor::yarn_layering_tests: 322 passed.cargo test -p socket-patch-cli --test in_process_redirect_pnpm --test in_process_vendor_pnpm_takeover: 41 + 15 passed. There is nopnpm_hostedtest target; these are the in-process pnpm hosted and takeover suites.cargo clippy --locked --workspace --all-features -- -D warnings: clean.cargo fmt --all -- --check: clean.python3 scripts/loc-budget.py --check: ok.Review findings (
/code-review high)production()cut each file at its first#[cfg(test)] mod, so files declaring out-of-line test modules near the top went mostly unscanned. It now drops only test-gated items. A self-test checks a violation deep in such a file, and that late production code ofcrawlers/npm_crawler.rs,patch/redirect/mod.rsandvendor/mod.rsis scanned. The fixed guard's count is unchanged.pnpm_settings_majorno longer reports 11 for it. Both pinned; the crafted-extra-document case still reads every document.overrides:line by name@version, so this tightens both.LockfileEntry::pnpm_envand a project-first rank indedup_prefer_integrity;inventory_project_every_lockkeeps both.TreePartis carried beside the real file on every record the contests read. The finalize remap is gone. An env ref twin of a project ref with the same purl/uuid/mode is dropped (the project one is the one an installednode_modulescopy is verified against), so env/project refs no longer both survive with the samesource_file. Not remapped because nothing ever holds a pseudo path:unlocked_pins,read,recognized,diagnostics.unidentified_copiesare file-level (no pnpm extractor records one) and matchFilerefs only.InstalledPackagesperf (fixed). Key-only walk, no resolution parse.materializereplaces the text. The planner no longer materializes every lock for the check: a splice never touches keys orname:/version:lines.inventory_projectignores it). Rush trees are unioned beside it byinventory_npm_trees_raw_in.rush.jsonis stat'd once through the view, and the Rush reader no longer re-checks it.Notes for review
InstalledPackagesreads every document. pnpm writesnode_modules/.pnpm/lock.yamlas the project lock alone, and config dependencies install intonode_modules/.pnpm-config, not the virtual store, so an env document isn't expected there. If one appears, its entries keep their store entries, which never hides an installed package.scan/getsee config dependencies (and, under pnpm 12, thepnpm/@pnpm/exe.*package-manager entries) as lockfile-only packages at a would-benode_modules/<name>path. A Socket patch for a config dependency then shows as not installed in agent mode. This follows from the owner decision; whether the supplement should skip the env tree is a question for the install-tree PR.Out of scope (later PRs): merging the hosted and vendored pnpm planners,
shrinkwrap.yaml(N11, pending),gitBranchLockfile, vendored CRLF / lock-version support (N15, N16).Generated by Claude Code