Repository navigation
Read and plan pnpm locks through one core (lock-model 2) - #2009
Conversation
Lockfile discovery (vex/discover), the lock inventory (vendor/lock_inventory) and the hosted and vendored writers each read the same project files their own way: two Socket-origin classifiers, three Python precedence rules, second walks of package-lock, yarn, vlt and pnpm locks, a second parse of uv/pylock/script locks, five Cargo.toml dependency-table walks, three readers of Maven's checksums.sha256. The inventory also has no NuGet, JVM, Hatch or Deno models. docs/design/lock-model-unification.md describes the target, one per-project model whose entries carry an Origin (Registry, Hosted, Vendored, Other, Rejected), file selection by install tree (precedence within a tree, union across trees), and the stack of PRs that gets there. scripts/loc-budget.py counts implementation lines (no comments, no cfg(test) items, no test-only modules) for two groups in scripts/loc-budget.json: lock-readers (30,898 lines today) and lock-writers (88,363). The lint-ecosystems job runs it with --warn, which annotates a group over budget but never fails, so bug fixes in these files are not blocked; each PR in the stack lowers the budget as it deletes code. No product code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
formats/ models are guarded to stay free of I/O and of the host policy (formats/mod.rs architecture_tests bans hosted_patch_uuid there), so the classifier is a top-level `origin` module and a project layer applies it. Spell out the three layers and where each part of the classifier moves from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decisions: inconsistencies between discovery, the inventory and the two modes are bugs to remove, not behavior to preserve; hosted and vendored share one core and differ only in where a rewired entry points; when in doubt the hosted mode's behavior wins, and every such call is listed in the doc so it can be overruled. Behavior changes are allowed per PR, listed, pinned by tests and shown as golden diffs. First decision recorded: pnpm 11's env-lockfile document counts as its own install tree, as the hosted planner already treats it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/design/lock-model-divergences.md lists every question on which discovery, the inventory, hosted and vendored mode answer differently today, across all ecosystems, with the resolution under the principles and flags for false attestations and target-specific differences. Owner decisions recorded: the modes are ground truth over the readers; vendored mode's Maven profile and reactor support and its scala-cli wiring are back-ported to hosted as the planners merge (the JVM PR splits into readers, Maven planner, and Gradle/sbt/scala-cli). One call is pending: whether hosted keeps rewriting a shrinkwrap.yaml that pnpm never reads. Corrects the install-tree section: npm-shrinkwrap.json does not shadow package-lock.json; every consumer but the inventory already reads both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decisions: a dependency the user patched themselves is never patched or attested by default, in every mode and package manager, unless the user explicitly overrides; and no reader or writer reads project files outside the git repository root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Discovery's classifier (the memoized hosted_uuid fast path, vendor_ref and its variants, locate, Wired, the socket-patch-<uuid> name grammar) and the redirect's hosted URL rule (hosted_patch_uuid, hosted_patch_url_uuids, SOCKET_PATCH_SERVER_HOST) now live in crates/socket-patch-core/src/origin.rs, with their tests. The code moves verbatim. DiscoverCtx holds an OriginPolicy (the per-run origin allowlist and its memoized host set) and delegates hosted_uuid and locate to it. vex::discover and patch::redirect re-export the moved items, so no caller changes. origin.rs sits outside formats/ on purpose: format models are guarded to apply no host policy (formats/mod.rs architecture_tests). Budget: lock-readers 30,898 -> 30,976 (49 lines moved in from patch/redirect, ~30 lines of OriginPolicy and re-export glue); lock-writers 88,363 -> 88,314. No behavior change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
4b96421 to
613ca3a
Compare
Add formats/pnpm/plan.rs, the planning core hosted and vendored mode share. They now differ only in where a rewired entry points: * Target::check_lock: one lock-version table. Hosted takes every lock but pnpm 1's early shrinkwrap. Vendored takes 5.4 / 6.0 / 9.0 and routes each dialect from it. This replaces unsupported_early_shrinkwrap, check_v9_lock_version and the legacy check_lock_version. * match_key: one lookup of how a key names name@version, used by the hosted lookup, the hosted "vendored?" hint, and both vendored dialects' target, split-entry and rekey checks. It finds the registry entry with its peer suffix, or a vendored rekey read with origin::vendor_ref plus the artifact leaf. * Target::repoints: the all-or-nothing gate's per-instance predicate. Hosted splices any parsable peer context; vendored rekeys the plain entry only. Within the vendored planner, the v9 and legacy dialects now share the dep-pair revert (DepPair), the block revert (revert_block, re-sorting legacy's bare file: rekey), target_blocks, and dep_entries. dep_entries also serves the importer edit, the 6.0 root-dep edit, the ref gate and the lock index. The ref gate's index and scan paths run one set of predicates. Behavior changes, each pinned by a test: * N15: the vendored planner and reverter edit a CRLF pnpm-lock.yaml or pnpm-workspace.yaml like its LF twin and write it back CRLF. Only a file mixing CRLF and LF is still refused (vendor_lockfile_crlf_unsupported). * N5 (vendored planner): a sibling checkout's ../.socket/vendor rekey is no longer taken for this project's entry. * The hosted "vendored" hint now judges a rekey by its artifact leaf, as the vendored planner does, instead of the entry's version: line. N16 is kept as a target-specific refusal: no lock version beyond 5.4 / 6.0 / 9.0 has a captured fixture that proves the vendored rekey. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
N5's vendored-planner part and N15 are resolved in PR 2b. N16 stays target-specific for pnpm vendored mode. scripts/loc-budget.py --lower: lock-readers 31207 -> 31201, lock-writers 88314 -> 88093. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decision on divergence N11: pnpm never reads a pnpm <= 2 shrinkwrap.yaml it has a pnpm-lock.yaml for, so that file is shadowed. The planning core's planned_locks leaves it out, so hosted mode never rewrites it and it no longer takes part in the all-or-nothing gate. Before, an entry the splice cannot repoint, or pnpm 1's early-shrinkwrap head, refused the dependency in every lock. Discovery keeps sweeping it for stale Socket identities, and vendored mode never reads it. A shrinkwrap.yaml that is the only pnpm lock (nested ones are shadowed only by their own sibling) is rewritten as before. No redirect golden changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Read every vendored pnpm text file through one reader, read_pnpm_text. It normalizes CRLF and rejects mixed endings. The revert now reads a CRLF two-document lock: it used to fail on `---\r`. A lock or workspace file that mixes CRLF and LF now fails the revert before any write, as vendoring refuses it; the revert used to rewrite it all-LF. - N11: a project that pins pnpm 1 / 2 (install record or package.json packageManager) reads only shrinkwrap.yaml, so there the stray pnpm-lock.yaml is the shadowed one. Hosted (planned_locks) and discovery share governing_pnpm_lock. The pin reader moves to utils::pnpm_workspace::pinned_pnpm_major, which pnpm_major_from reuses. - sniff_lock_grammar: an unparseable lockfileVersion major keeps the update-socket-patch remedy. - Perf: SectionIndex::build no longer formats a string per block. dep_entries is lazy and borrowed, so the non-index ref gate stops at the first refusal. matching_blocks classifies each block once, and target_block returns it with its rekey flag. - Target::check_lock is split into hosted_refuses_lock and the vendored dialect's sniff_lock_grammar check, with no dead default. Target::repoints is an explicit match per target. Budget, against the base: lock-readers 31206 (base 31207), lock-writers 88115 (base 88314). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vendored mode now reads the same plan::governing_pnpm_lock decision as hosted mode and discovery. When the project pins pnpm 1 or 2 (install record, else package.json packageManager), pnpm installs from shrinkwrap.yaml and ignores pnpm-lock.yaml. Vendored mode does not write the shrinkwrap grammar, so it now refuses with vendor_lockfile_version_unsupported before any write, instead of wiring the stray pnpm-lock.yaml. With no pin, nothing changes. Documented in CLI_CONTRACT.md and the N11 divergence row. The mixed-ending read error is now an io::ErrorKind::Unsupported error instead of a custom type, which keeps lock-writers within budget (88113). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #2001 found the counter miscounting in several shapes, each now pinned by a test in scripts/tests/test_loc_budget.py: - a cfg(test) struct field, enum variant, parameter or match arm drove the scan below the item's depth and swallowed the code after it; the item now ends at a top-level `,` or before the bracket closing its container - `#[cfg(test)] use a::{..};` left its trailing `;` line counted - cfg predicates are parsed: `all(unix, test)` and nested `all` count, `not(test)` doesn't, and a file opening with `#![cfg(test)]` is test-only - `#[path]` is found among any attributes around the item and resolves against the declaring file's directory, as rustc does - `mod x;` inside a test-only inline module (vex::discover's `testing { mod golden; }`) marks that file test-only - `git ls-files -z` keeps paths with spaces whole, and only the group files plus their module declarers are read - `--files` with an unknown group is a usage error, not a KeyError golden.rs (435 lines) and two `};` lines leave the counts, so the budget drops to 30462 / 88362. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #2001: the Decisions table said every read is bounded to the repository root, while G1, J13 and P4 still said to read an ancestor go.work, parent NuGet configs and every include. The owner narrowed the rule on 2026-11-05, and the table now records it with the rest of that day's calls: - the bound applies to workspace runs only; --global / -g / --global-prefix runs are unbounded, and without a git checkout upward lookups behave as before - a marker file above the repository root (below home) makes hosted and vendored refuse with a message naming it - out-of-repository -r includes stay bounded in workspace runs - N11: shrinkwrap.yaml beside pnpm-lock.yaml is shadowed, unless the project pins pnpm 1 / 2, where it governs and vendored refuses - discovery withholds pnpm root and member-lock attestations whenever a pnpm-lock.<branch>.yaml exists - user patches: the override is --allow-patch-conflicts, and vex never attests under a user patch G1, J9, J13, P4, P9, X12, N1, N2, N4, N5, N11, N12 and N14 name the open PRs that fix them, and the fixes-on-main table gains a PR column. The problem table now counts four checksums.sha256 readers, as J9 does. The stack table splits PR 2 into 2a / 2b with their numbers, and the size budget section says why CI only warns and gives the corrected counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the scripts/loc-budget.json conflict: the base fixed the counter's cfg(test) and module bugs, so both budgets are recounted with the fixed counter and lowered to the exact current totals (lock-readers 30259, lock-writers 88313), keeping this PR's 'both groups at their budget' intent. Co-Authored-By: Claude <noreply@anthropic.com>
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>
|
Burn-down agent: merged
Generated by Claude Code |
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>
…g code loc-budget.py only recognized `pub name: Type` as a field, so a #[cfg(test)] on a `crate name: Type` field or a `const N: usize` generic parameter was read as an item. _item_end then didn't stop at the comma and excluded the rest of the struct or parameter list from the implementation count. Treat `crate` followed by a name like `pub`, and read `const` as a parameter when the attribute follows `<` or `,`, where no item can start. A `crate::path` statement and a plain const item keep their current handling. Reported by Bugbot on #2001. Co-Authored-By: Claude <noreply@anthropic.com>
Follow-up PRs grep for the names this doc cites. OriginPolicy::hosted_uuid exists nowhere in the tree; the function is DiscoverCtx::hosted_uuid (its memoized fast path is the hosted_hosts cell). The npm flavor router lives at vendor/lock_inventory/npm_family.rs, since doc paths are relative to src/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aeNYq2GHyrsNnPBepcC
Brings in the counter's crate-field and const-generic fix and the doc symbol fixes. Also restores crates/socket-patch-core/src/origin.rs to the lock-readers group: the previous merge (4242ebc) resolved the loc-budget.json conflict with the base's path list and dropped it, so the 281 lines this PR moves into origin.rs went uncounted (30259). With origin.rs counted, lock-readers is 30540 and lock-writers 88313. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decision on #2009: a config-dependency (env lockfile document) ref is not attested while the project document of the same lock resolves the same name@version from elsewhere. A VEX statement covers the release, not one install tree, so attesting it would claim the release is fixed while node_modules holds upstream. TreePart::withheld_by makes the project tree's copies reach env refs of the same file, through the existing machinery: - a registry entry moves the env ref to `rewirable` (a re-run rewires that entry, and the project ref then replaces the env twin); - a `file:` copy (and any project-tree unpatched copy) shadows it; - a bundled copy marks it Unattested (BundledCopy), like a project ref. The branch-lock marks no longer need a separate env-scoped copy: the project-scoped one now covers env refs. pnpm_env_document_is_its_own_ install_tree's first loop now expects withholding, with registry-only, file:-only and bundled-only cases plus a different-version control. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock-model/2b-pnpm-planner # Conflicts: # docs/design/lock-model-divergences.md
The hosted gate's copy check compared the pin's tree with the copy's, so a config-dependency (env document) pin withheld by the project document's `file:` copy was neither named with redirect_copy_unpatched nor kept out of the in-run VEX. It now asks TreePart::withheld_by, the rule discovery withholds with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock-model/2b-pnpm-planner
- Drop a pnpm env twin before any contest, so it leaves no Unattested mark (keyed by purl and uuid, which withheld the project ref) and no diagnostic behind. - Restore a hosted pin in the env lockfile document bare: pnpm 11+ records config dependencies without `tarball:` whatever lockfileIncludeTarballUrl says, so rollback no longer writes the project document's policy into it. - The Rush reader no longer stats rush.json again; its one caller does. - Divergences row N4: documents are identified by position and content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock-model/2b-pnpm-planner # Conflicts: # docs/design/lock-model-divergences.md
early_pnpm1_refuses_non_durable_shrinkwrap_across_all_locks asserted the pre-N11 rule, where pnpm 1's early shrinkwrap refused the dependency even beside a pnpm-lock.yaml. Beside one it is shadowed now, so it refuses nothing and only pnpm-lock.yaml is rewritten; a lone one still refuses. The test failed on this branch since the N11 change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Review findings (Claude Opus + codex, independent passes):
- Env-document refs are never contested by another lock (
crates/socket-patch-core/src/vex/discover/mod.rs:1410, withwithheld_byat :283; Opus).contest_across_locksonly letsTreePart::Filerefs be contested. Input: rootpnpm-lock.yamlenv document wiringleft-pad@1.3.0to a Socket tarball, project document without left-pad, and apackage-lock.json(or non-shared member / Rush subspace lock) resolvingleft-pad@1.3.0from the registry →pkg:npm/left-pad@1.3.0attested while that tree installs upstream. On main the env document produced no refs, so this path is new. - Env refs skip installed-file verification (
vex/discover/npm.rs:1301, codex). Both npm crawler paths skip.pnpm-config(npm_crawler.rs:3279,3690), so an upstreamnode_modules/.pnpm-config/left-padyieldspackage_not_found, which VEX excuses (commands/vex.rs:857-883) and emitsnot_affected.
Minor: withhold_hosted_pins (npm_family.rs:123) knows only the default host; has_packages_section now scans the env document too; inline name@1.0.0: {} keys are no longer counted (tracked in #2044).
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Approving per maintainer decision; see the findings in the review above.
- The hosted confirmation probe skips a pnpm lock pnpm does not read (N11): a stale Socket URL in a shadowed shrinkwrap.yaml confirmed the dependency, which the attribution gate then dropped as redirect_unattributable. - The pnpm trust/heal post-pass takes the governing root lock from planned_locks, so under a pnpm 1 / 2 pin a stray v9 pnpm-lock.yaml no longer plans a trustLockfile write. - Mixed line endings get their own error type instead of overloading io::ErrorKind::Unsupported, which an OS ENOTSUP read also returns; read_pnpm_text normalizes through utils::line_endings::to_lf. - The pnpm pin is read only when both pnpm-lock.yaml and shrinkwrap.yaml exist (planned_locks, vendoring, discovery). - Docs: divergences N16 names the two checks; read_project refuses mixed endings, not CRLF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Behavior-preserving cleanups over the folded lock-model 2 diff: - The pnpm architecture test reads production code through the shared vendor::test_support::production_sources instead of its own lexer. - The hosted unwind walks PnpmLock::packages once (document tags and resolutions included) instead of two raw entry walks plus its own env-span tagging, so it leaves the raw-walk allowlist. - One PnpmDialect::entry_in_use backs both dialects' in-use checks (memoized; the legacy one no longer parses the whole lock per entry). - plan::shadowed_locks names the pnpm locks pnpm does not read, for the engine's confirmation probe and trust/heal root; planned_locks shares its per-key rule. - Cheaper hot paths: the store crawler's key-only walk skips document tagging; grammar::suffix no longer allocates; drop_env_twins returns early with no env ref; the inventory reads the lockfile setting only when a hosted pin exists; PnpmLock::documents needs no sort. - Comments and the divergences doc state current behavior instead of review history; shared test fixtures (env document head, Rush consts); three vendored helpers made private. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- plan::root_lock picks the root's governing pnpm lock directly: the simplify pass derived it from the shadowed set, which named a missing pnpm-lock.yaml when shrinkwrap.yaml is the only lock. One pnpm_lock_leaf helper backs planned_locks, shadowed_locks and governs. - Remove PnpmLock::vendored_in_use and its cache, which no production code calls since both dialects use PnpmDialect::entry_in_use; the tests read vendored_npm_uuids directly. - parse_keys documents that it leaves every entry tagged Project, and PnpmLock::documents keeps collecting the tags actually present. - drop_env_twins reuses its list array; the unification doc notes that #2029 was folded into #2009. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Final reviewer: re-added to the merge queue at Generated by Claude Code |
#2018's user_patched extractor built UnwiredCopy marks with the old file-only scope; #2009 scopes them by (file, TreePart). Each mark is for exactly one ref, so it takes that ref's own (source_file, part), as npm::mark_user_patched does. A pnpm env-lockfile ref of a user-patched package is marked too: the index is per package, so a missed attestation is the safe side. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged main (9bbb4b7) after the merge-queue dequeue. #2018 added 🤖 Generated with Claude Code |
Problem
Lock-model 2: pnpm locks were read by several separate walks (inventory, discovery, store crawler, hosted planner, vendored planners). Hosted and vendored rewires each had their own planner. They disagreed on the env lockfile document, Rush locks,
lockfile=false, vendored rekeys, CRLF and ashrinkwrap.yamlbesidepnpm-lock.yaml(divergences N4, N5, N11, N12, N14–N16).Fix
formats::pnpm::PnpmLockwalks every document once and tags each entryProjectorEnv(the env document is the first one, when its importers carry only config/packageManager deps). Every reader goes through it, and an architecture test enforces that.TreePart::PnpmEnv), never contested across locks; an env twin of a project ref is dropped.formats/pnpm/plan.rs):match_key, lock-version checks,governing_pnpm_lock. Hosted splices the resolution, vendored rekeys tofile:.shrinkwrap.yamlbesidepnpm-lock.yamlis shadowed unless pnpm 1/2 is pinned. N12 Rush locks always inventoried. N14lockfile=falsedrops hosted integrity. N15 vendored edits CRLF files (mixed endings refused).Owner decision: withhold env-document refs
An env-document (config-dependency) ref is not attested when the project document of the same lock resolves the same
name@versionelsewhere: a VEX statement covers the release, not one tree. ViaTreePart::withheld_by(project copies reach env refs, never the reverse), a registry entry makes itrewirable, afile:copyshadowed, a bundled copyUnattested. The hosted copy check uses the same rule; rollback restores env entries bare.Where to look
vex/discover/mod.rs:TreePart::withheld_by,contest_within_locks,drop_env_twinsformats/pnpm/mod.rsPnpmLock;grammar.rsdocumentsformats/pnpm/plan.rs:match_key,governing_pnpm_lock,planned_locks/shadowed_locks/root_lockvendor/pnpm_lock.rs:PnpmDialect,read_pnpm_text, shared reverthosted/engine.rs:confirm,pnpm_trust, copy checkTesting
vex:: formats:: origin patch::redirect vendor:: hosted:: crawlers::npm_crawler utils::pnpm_workspace): 5254 passed. The discovery golden corpus is unchanged.pnpm_hosted,hosted_inventory,redirect_golden,upstream_restore_golden; CLIin_process_redirect_pnpm,…_vendor_pnpm_takeover,in_process_redirect,hosted_memory_engine,in_process_vendor: pass. clippy, fmt clean.scripts/loc-budget.pyis over budget on both groups, as main already is (31,756 / 89,445); this PR adds +294 / −150 over main. CI only warns;loc-budget.jsonunchanged.Folds #2029
#2029 (pnpm planner) is folded in here and closed.
Follow-ups: #2044
Review history
Fixed:
---\rtwo-document locks; mixed endings now fail before any writetarball:io::ErrorKind::Unsupported; pin read when one lock existed'v10'lost its remedy; stalepnpm_hostedtest; deadvendored_in_use; simplify's lone-shrinkwrap.yamlroot regressionDeclined:
parse_vendor_path"accepts the anchor anywhere": it's root-anchored on mainname@v: {}entries: pnpm never writes them (pnpm reader follow-ups from #2009's review #2044)pnpm_envonLockfileEntry: not derivable,SourceKindis provenance (pnpm reader follow-ups from #2009's review #2044)materializecache clear: none exists; thesplicedclear is neededis_env_document/check_env_document: changes the zero-field rule (pnpm reader follow-ups from #2009's review #2044)is_hostedevaluated twice underlockfile=false: negligible🤖 Generated with Claude Code