Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.

feat(lockfile): BinaryResolution + VariationsResolution (#437 slice A)#457

Merged
zkochan merged 2 commits into
mainfrom
feat/437
May 13, 2026
Merged

feat(lockfile): BinaryResolution + VariationsResolution (#437 slice A)#457
zkochan merged 2 commits into
mainfrom
feat/437

Conversation

@zkochan

@zkochan zkochan commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Slice A of #437. Adds the lockfile types pnpm v11 writes for runtime dependencies (node@runtime:, deno@runtime:, bun@runtime:); the install pipeline doesn't dispatch them yet (subsequent slices). No new workspace deps.

  • BinaryResolution { url, integrity, bin, archive, prefix? } — mirrors upstream's BinaryResolution. bin is BinarySpec::Single(String) | BinarySpec::Map(BTreeMap) (untagged); archive is the lowercase BinaryArchive::Tarball | BinaryArchive::Zip discriminator; prefix (only set on the .zip branch upstream) skips serialization when None.
  • PlatformAssetTarget { os, cpu, libc? } + PlatformAssetResolution { resolution, targets } per resolver-base. libc is Option<String> (not an enum) so an upstream addition beyond the current musl value lands without a serde migration.
  • VariationsResolution { variants } + the LockfileResolution/TaggedResolution arms routing both new shapes through the existing from/into ResolutionSerde round-trip.
  • Lockfile major stays at 9 — confirmed against core/constants/src/index.ts (LOCKFILE_MAJOR_VERSION = '9'), so the existing lockfile_version.major == 9 assertion holds for v11 lockfiles carrying runtime entries.

Install-dispatch stubs. install_package_by_snapshot.rs and create_virtual_store.rs raise InstallPackageBySnapshotError::UnsupportedResolution { kind: "binary" | "variations" } until Slice D wires the fetcher. Adding these arms keeps the workspace compiling without forcing the full install path to land in this slice. The warm-prefetch path returns Ok(None) for Variations, routing through the cold dispatcher where the unsupported-kind error fires; upstream unwraps Variations before this layer ever sees one, so the branch is unreachable on well-formed input.

Test plan

  • just ready — 830 tests pass (2 skipped)
  • taplo format --check — clean
  • just dylint (perfectionist) — clean
  • 4 new serde round-trip tests: tarball-shape BinaryResolution with bin: <string> and no prefix; zip-shape BinaryResolution with bin: <map> and prefix; two-variant VariationsResolution covering (darwin, arm64) and (linux, x64, musl) to pin the libc? omission rule; single-variant VariationsResolution serialise.

Follow-up slices on #437

  • B: pick_variant(variants, host_target) + host platform plumbing.
  • C: Binary fetcher (tarball path through pacquet-tarball with ignore_file_pattern; zip path with zip = "5" workspace dep + path-traversal validation + NODE_EXTRAS_IGNORE_PATTERN).
  • D: Install-pipeline dispatch for Binary / Variations + bin linking via pacquet-cmd-shim.
  • E: --no-runtime CLI flag + Config.skip_runtimes.
  • F: Integration tests with recorded fixture archives + the 12 entries from plans/TEST_PORTING.md Installation Of Runtimes section.

Upstream

pnpm/pnpm@94240bc046.


Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

  • New Features
    • Lockfiles now record downloaded runtime binaries and platform-specific variant entries, with stable serialization ordering for reliable cross-platform installs.
  • Tests
    • Added round-trip serialization tests for binary and platform-variant lockfile entries to ensure format and field ordering stability.
  • Chores
    • Installation/cache logic treats these new resolution types as cold installs; installers will surface “unsupported” for them until fetcher integration is implemented.

Review Change Stack

Copilot AI review requested due to automatic review settings May 13, 2026 14:26
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d37a37b1-2d8e-44fc-8e52-e6f9b9283253

📥 Commits

Reviewing files that changed from the base of the PR and between 240b2af and f3f25ac.

📒 Files selected for processing (2)
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Dylint
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage

📝 Walkthrough

Walkthrough

Adds Binary and Variations resolution types to the lockfile system, extends LockfileResolution and serde integration to handle them, tests round-trip serialization with exact field ordering, and routes them through the install pipeline as unsupported-pending-fetcher-implementation.

Changes

Binary and Variations resolution support

Layer / File(s) Summary
Binary and Variations type definitions and serde integration
crates/lockfile/src/resolution.rs
Introduces BinarySpec, BinaryArchive, BinaryResolution, PlatformAssetTarget, PlatformAssetResolution, and VariationsResolution; extends LockfileResolution enum with Binary and Variations variants; updates integrity() to return integrity for Binary; integrates with TaggedResolution serde intermediates and bidirectional From impls for lockfile deserialization/serialization.
Binary and Variations serialization round-trip tests
crates/lockfile/src/resolution/tests.rs
Tests deserialization of tarball and zip binary shapes with single-string and name-map bin specs, optional prefix handling, and Variations with multiple platform-target-conditioned binary variants; validates serialization output includes exact YAML field ordering for stable round-trips.
Install pipeline dispatch for Binary and Variations resolutions
crates/package-manager/src/create_virtual_store.rs, crates/package-manager/src/install_package_by_snapshot.rs
snapshot_cache_key returns Ok(None) for Binary/Variations to route through cold-install path; InstallPackageBySnapshot::run dispatch explicitly returns UnsupportedResolution errors for both variants; tarball_url_and_integrity helper includes both in non-tarball guard case.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pacquet#446: Modifies related install pipeline lockfile-resolution dispatch paths referenced by these changes.

Poem

I’m a rabbit with a YAML chart,
Hopping crates and types apart,
BTree maps tidy, tests in line,
Binary dreams serialized fine — 🐇📦

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main addition: BinaryResolution and VariationsResolution lockfile types, with a reference to the slice numbering (#437 slice A) that provides context in the broader work.
Description check ✅ Passed The description comprehensively covers all required template sections: clear summary of changes, linked issue (#437), upstream reference, test plan evidence, and checklist completion. Technical details precisely document the new types and their serialization behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/437

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 35.29412% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.28%. Comparing base (99d708a) to head (f3f25ac).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...package-manager/src/install_package_by_snapshot.rs 0.00% 8 Missing ⚠️
crates/lockfile/src/resolution.rs 75.00% 2 Missing ⚠️
crates/package-manager/src/create_virtual_store.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #457      +/-   ##
==========================================
- Coverage   87.35%   87.28%   -0.08%     
==========================================
  Files         113      114       +1     
  Lines        9356     9451      +95     
==========================================
+ Hits         8173     8249      +76     
- Misses       1183     1202      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.02     16.0±0.57ms   271.8 KB/sec    1.00     15.6±0.16ms   278.0 KB/sec

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
crates/lockfile/src/resolution.rs (1)

70-71: ⚡ Quick win

Use pnpm blob/main links in new porting references.

Please replace newly added .../blob/94240bc046/... links with .../blob/main/... so the references stay aligned with upstream tip.

As per coding guidelines: "When porting features... open files from https://github.com/pnpm/pnpm/blob/main/... rather than from a permalinked SHA."

Also applies to: 89-90, 101-102, 117-118, 130-131, 148-149, 165-166, 232-233, 251-252

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lockfile/src/resolution.rs` around lines 70 - 71, Update the hardcoded
permalinked GitHub references to point at the upstream tip by replacing any
occurrences of "blob/94240bc046" with "blob/main" in the comment links (e.g.,
the comment referencing BinaryResolution.bin and the other inline links around
the same area). Search for the exact URL substring
"github.com/pnpm/pnpm/blob/94240bc046" in this file
(crates/lockfile/src/resolution.rs) and update each to
"github.com/pnpm/pnpm/blob/main" so all porting references follow the coding
guideline.
crates/workspace/src/manifest/tests.rs (1)

74-80: ⚡ Quick win

Log the actual error before the assert!(matches!(...)) check.

For this non-assert_eq! assertion, print the error explicitly so failures are easier to diagnose from logs.

💡 Suggested tweak
     let err = read_workspace_manifest(tmp.path()).unwrap_err();
+    eprintln!("workspace manifest parse error: {err:?}");
     assert!(
         matches!(
             err,
             ReadWorkspaceManifestError::Invalid(InvalidWorkspaceManifestError::EmptyPackageEntry),
         ),
         "unexpected error: {err}",
     );

Based on learnings: In Rust test code, when using assertions like assert!, add logging so failures are diagnosable without reruns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/workspace/src/manifest/tests.rs` around lines 74 - 80, Print or log
the actual error value (err) immediately before the assert so test failures show
the concrete error; for example emit err with eprintln!, dbg!(err), or println!
and then keep the existing assertion that checks for
ReadWorkspaceManifestError::Invalid(InvalidWorkspaceManifestError::EmptyPackageEntry)
so failure output includes the error value for easier diagnosis.
crates/workspace/src/projects/tests.rs (1)

72-79: ⚡ Quick win

Add explicit diagnostic logging around non-assert_eq! checks.

These assert! checks should log names explicitly to aid failure triage in CI output.

💡 Suggested tweak
+    eprintln!("workspace project names: {names:?}");
     assert!(
         !names.contains(&"foo".to_string()),
         "node_modules contents must not surface as workspace projects: {names:?}",
     );
     assert!(
         names.contains(&"real".to_string()),
         "expected the `real` project to be enumerated; got {names:?}",
     );

Based on learnings: In Rust test code, when using assertions like assert!, add logging so failures are diagnosable without reruns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/workspace/src/projects/tests.rs` around lines 72 - 79, Add explicit
diagnostic output for the `names` variable around the two `assert!` checks so CI
failures show the enumerated projects; before the `assert!(
!names.contains(&"foo".to_string()) )` and before the `assert!(
names.contains(&"real".to_string()) )` add an explicit log such as
eprintln!("names: {:?}", names) (or dbg!(names) / tracing::debug if tests use
tracing) so the actual `names` contents are printed when an assertion fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/lockfile/src/resolved_dependency/tests.rs`:
- Line 10: The bare assert! calls lack failure context; update each assertion
that checks parsed.as_link_target().is_none() (and the other plain assert!
occurrences in the same test file) to include a diagnostic message or replace
with an assertion macro that prints values (e.g., use assert!(cond, "parsed={:?}
expected none", parsed) or assert_eq!(parsed.as_link_target(), None,
"parsed={:?}", parsed)); alternatively add an eprintln! immediately before the
assertion to print the relevant variables (e.g., parsed and
parsed.as_link_target()) so failures show actual vs expected; target the
assertions using the identifier parsed and the method as_link_target (and the
other plain assert! lines noted) when making the edits.

In `@crates/package-manager/src/install.rs`:
- Line 267: The frozen-workspace preflight only validates the root importer via
satisfies_package_manifest with Lockfile::ROOT_IMPORTER_KEY, so other workspace
importers can be stale; update InstallFrozenLockfile to validate every
materialized importer before proceeding: either (a) when given workspace_root,
enumerate workspace manifests (all importer ids / package.json paths) and call
satisfies_package_manifest for each importer key found in the lockfile, or (b)
gate the existing check to run per-target importer when installing a single
importer. Specifically, modify the InstallFrozenLockfile path that currently
references workspace_root and Lockfile::ROOT_IMPORTER_KEY to iterate all
importer keys (or the actual importer being installed) and ensure
satisfies_package_manifest is invoked for each so the frozen install fails if
any importer is out of date (apply same fix to the analogous block around lines
325–330).

In `@crates/workspace/src/project_manifest.rs`:
- Around line 95-102: The code in read_exact_project_manifest lowercases the
file basename before matching, allowing case variants like "PACKAGE.JSON" to
match; change this to perform an exact match by removing the to_ascii_lowercase
call so basename remains the original file_name string (i.e., compute basename
with file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default()),
then match basename.as_str() against "package.json" and return
PackageManifest::from_path(manifest_path.to_path_buf()).map_err(ReadProjectManifestError::Read)
or Err(ReadProjectManifestError::UnsupportedName { basename }) as before.

In `@crates/workspace/src/projects.rs`:
- Around line 149-171: The loop currently only accumulates matches into
manifest_paths and ignores negated patterns, so implement proper include vs
exclude handling: first iterate patterns and separate them into include_patterns
and exclude_patterns based on whether normalize_pattern(pattern) yields a
leading '!' (or pattern.starts_with('!'))—strip the '!' for exclude entries—then
for each include pattern use Glob::new/ glob.walk(...) to insert matches into
manifest_paths, and for each exclude pattern use Glob::new/ glob.walk(...) to
remove matching paths from manifest_paths (convert walk errors into the same
FindWorkspaceProjectsError variants as existing code). Apply the same change to
the other identical block (the one around the second occurrence) so exclusion
semantics mirror pnpm.

---

Nitpick comments:
In `@crates/lockfile/src/resolution.rs`:
- Around line 70-71: Update the hardcoded permalinked GitHub references to point
at the upstream tip by replacing any occurrences of "blob/94240bc046" with
"blob/main" in the comment links (e.g., the comment referencing
BinaryResolution.bin and the other inline links around the same area). Search
for the exact URL substring "github.com/pnpm/pnpm/blob/94240bc046" in this file
(crates/lockfile/src/resolution.rs) and update each to
"github.com/pnpm/pnpm/blob/main" so all porting references follow the coding
guideline.

In `@crates/workspace/src/manifest/tests.rs`:
- Around line 74-80: Print or log the actual error value (err) immediately
before the assert so test failures show the concrete error; for example emit err
with eprintln!, dbg!(err), or println! and then keep the existing assertion that
checks for
ReadWorkspaceManifestError::Invalid(InvalidWorkspaceManifestError::EmptyPackageEntry)
so failure output includes the error value for easier diagnosis.

In `@crates/workspace/src/projects/tests.rs`:
- Around line 72-79: Add explicit diagnostic output for the `names` variable
around the two `assert!` checks so CI failures show the enumerated projects;
before the `assert!( !names.contains(&"foo".to_string()) )` and before the
`assert!( names.contains(&"real".to_string()) )` add an explicit log such as
eprintln!("names: {:?}", names) (or dbg!(names) / tracing::debug if tests use
tracing) so the actual `names` contents are printed when an assertion fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c556779c-0b26-470c-b26b-98ba59fb3d75

📥 Commits

Reviewing files that changed from the base of the PR and between 8b35794 and 066c3ee.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (30)
  • Cargo.toml
  • crates/config/src/lib.rs
  • crates/lockfile/src/resolution.rs
  • crates/lockfile/src/resolution/tests.rs
  • crates/lockfile/src/resolved_dependency.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/lockfile/src/save_lockfile/tests.rs
  • crates/package-manager/Cargo.toml
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/build_sequence/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/real-hoist/src/tests.rs
  • crates/workspace/Cargo.toml
  • crates/workspace/src/lib.rs
  • crates/workspace/src/manifest.rs
  • crates/workspace/src/manifest/tests.rs
  • crates/workspace/src/project_manifest.rs
  • crates/workspace/src/project_manifest/tests.rs
  • crates/workspace/src/projects.rs
  • crates/workspace/src/projects/tests.rs
  • crates/workspace/src/root_finder.rs
  • crates/workspace/src/root_finder/tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/package-manager/src/build_sequence.rs
  • crates/real-hoist/src/tests.rs
  • crates/package-manager/src/build_sequence/tests.rs
  • crates/workspace/src/project_manifest/tests.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/workspace/src/manifest/tests.rs
  • crates/workspace/src/lib.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/workspace/src/project_manifest.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/lockfile/src/save_lockfile/tests.rs
  • crates/workspace/src/projects/tests.rs
  • crates/lockfile/src/resolution.rs
  • crates/workspace/src/root_finder/tests.rs
  • crates/lockfile/src/resolved_dependency.rs
  • crates/workspace/src/root_finder.rs
  • crates/lockfile/src/resolution/tests.rs
  • crates/config/src/lib.rs
  • crates/workspace/src/projects.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/workspace/src/manifest.rs
🧠 Learnings (3)
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/package-manager/src/build_sequence.rs
  • crates/real-hoist/src/tests.rs
  • crates/package-manager/src/build_sequence/tests.rs
  • crates/workspace/src/project_manifest/tests.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/workspace/src/manifest/tests.rs
  • crates/workspace/src/lib.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/workspace/src/project_manifest.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/lockfile/src/save_lockfile/tests.rs
  • crates/workspace/src/projects/tests.rs
  • crates/lockfile/src/resolution.rs
  • crates/workspace/src/root_finder/tests.rs
  • crates/lockfile/src/resolved_dependency.rs
  • crates/workspace/src/root_finder.rs
  • crates/lockfile/src/resolution/tests.rs
  • crates/config/src/lib.rs
  • crates/workspace/src/projects.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/workspace/src/manifest.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/real-hoist/src/tests.rs
  • crates/package-manager/src/build_sequence/tests.rs
  • crates/workspace/src/project_manifest/tests.rs
  • crates/workspace/src/manifest/tests.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/package-manager/src/installability/tests.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/lockfile/src/save_lockfile/tests.rs
  • crates/workspace/src/projects/tests.rs
  • crates/workspace/src/root_finder/tests.rs
  • crates/lockfile/src/resolution/tests.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In this repo’s Rust tests (run via `cargo nextest`), stdout/stderr from passing tests is captured and is only shown when a test fails. As a result, `eprintln!` (and similar stderr logging) in these tests should generally not be flagged as CI “noise” on the happy path; CI output should only appear for failing tests.

Applied to files:

  • crates/real-hoist/src/tests.rs
🔇 Additional comments (15)
crates/package-manager/src/installability/tests.rs (1)

60-60: LGTM!

crates/package-manager/src/build_modules/tests.rs (1)

253-256: LGTM!

crates/package-manager/src/build_sequence/tests.rs (1)

47-50: LGTM!

crates/lockfile/src/resolution.rs (1)

65-85: LGTM!

Also applies to: 92-126, 128-174, 184-185, 194-201, 212-214, 242-243, 268-273

crates/package-manager/src/create_virtual_store.rs (1)

700-713: LGTM!

crates/lockfile/src/resolution/tests.rs (1)

2-4: LGTM!

Also applies to: 9-9, 363-533

crates/package-manager/src/install_package_by_snapshot.rs (1)

195-211: LGTM!

Also applies to: 281-288

Cargo.toml (1)

37-37: LGTM!

Also applies to: 92-92

crates/lockfile/src/resolved_dependency.rs (1)

21-21: LGTM!

Also applies to: 24-161

crates/lockfile/src/save_lockfile/tests.rs (1)

87-162: LGTM!

crates/lockfile/src/resolved_dependency/tests.rs (1)

1-9: LGTM!

Also applies to: 11-18, 20-27, 29-55, 57-58

crates/real-hoist/src/tests.rs (1)

26-26: LGTM!

crates/package-manager/src/build_sequence.rs (1)

142-151: LGTM!

crates/real-hoist/src/lib.rs (1)

341-351: LGTM!

crates/workspace/src/manifest.rs (1)

1-147: LGTM!

Comment thread crates/lockfile/src/resolved_dependency/tests.rs
Comment thread crates/package-manager/src/install.rs
Comment thread crates/workspace/src/project_manifest.rs
Comment thread crates/workspace/src/projects.rs
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.596 ± 0.089 2.461 2.722 1.00
pacquet@main 2.650 ± 0.065 2.556 2.733 1.02 ± 0.04
pnpm 6.461 ± 0.078 6.322 6.604 2.49 ± 0.09
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.5962747819799996,
      "stddev": 0.08865942580706526,
      "median": 2.59714185138,
      "user": 2.60447104,
      "system": 3.61983306,
      "min": 2.46138070888,
      "max": 2.72179310288,
      "times": [
        2.63889948688,
        2.72179310288,
        2.46138070888,
        2.53155832388,
        2.71234428488,
        2.63019900088,
        2.49508666888,
        2.56408470188,
        2.55481523788,
        2.65258630288
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.64969458678,
      "stddev": 0.06494354745949904,
      "median": 2.64544669538,
      "user": 2.6277687399999996,
      "system": 3.68217406,
      "min": 2.55640683788,
      "max": 2.7333184638800003,
      "times": [
        2.73196790488,
        2.6126393228800002,
        2.55640683788,
        2.7333184638800003,
        2.62402593188,
        2.62332196288,
        2.70280569688,
        2.68684411688,
        2.66686745888,
        2.55874817088
      ]
    },
    {
      "command": "pnpm",
      "mean": 6.460643567379999,
      "stddev": 0.07843185595198221,
      "median": 6.459978277379999,
      "user": 9.51861294,
      "system": 4.59242806,
      "min": 6.3217595608799995,
      "max": 6.60370576588,
      "times": [
        6.46662568688,
        6.50496020588,
        6.38383998688,
        6.5142795078799995,
        6.453330867879999,
        6.500430992879999,
        6.3217595608799995,
        6.60370576588,
        6.404643732879999,
        6.452859365879999
      ]
    }
  ]
}

Scenario: Frozen Lockfile (Hot Cache)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 708.2 ± 49.4 670.2 839.3 1.00
pacquet@main 726.0 ± 46.7 685.0 811.1 1.03 ± 0.10
pnpm 2755.0 ± 125.6 2656.5 3035.3 3.89 ± 0.32
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.7082260775400002,
      "stddev": 0.049438469153299094,
      "median": 0.6920180630400001,
      "user": 0.38427461999999996,
      "system": 1.48750662,
      "min": 0.67016103254,
      "max": 0.8393263795400001,
      "times": [
        0.8393263795400001,
        0.6989926085400001,
        0.70831389554,
        0.6919423625400001,
        0.6808556815400001,
        0.67813965454,
        0.6879979155400001,
        0.7344374815400001,
        0.6920937635400001,
        0.67016103254
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.72595592754,
      "stddev": 0.04667510413223731,
      "median": 0.69879369204,
      "user": 0.38495442,
      "system": 1.51847872,
      "min": 0.6849787835400001,
      "max": 0.8110957915400001,
      "times": [
        0.7395563365400001,
        0.7551873295400001,
        0.6918275315400001,
        0.6932644355400001,
        0.69424667354,
        0.8110957915400001,
        0.70334071054,
        0.6915225505400001,
        0.6849787835400001,
        0.7945391325400001
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.7549885392399998,
      "stddev": 0.12564874780573618,
      "median": 2.6891557535399997,
      "user": 3.362664219999999,
      "system": 2.2878424199999996,
      "min": 2.65652361654,
      "max": 3.03532940754,
      "times": [
        2.6729590005399997,
        2.67495020254,
        2.87990414354,
        2.69666261054,
        2.68152560554,
        2.68164889654,
        2.65652361654,
        2.7178803065399997,
        3.03532940754,
        2.85250160254
      ]
    }
  ]
}

@zkochan

zkochan commented May 13, 2026

Copy link
Copy Markdown
Member Author

The branch was based on a stale local main (pre-#443-merge), so the PR was wrongly showing all of #443's commits as new and CodeRabbit reviewed them. Force-pushed c80fd31b after resetting onto origin/main and cherry-picking only the slice A commit, so the diff now contains just the lockfile-types changes (4 files, +347/-6). The flagged behaviours all live in code that landed via #443 — out of scope for this lockfile-only slice — but the three substantive ones are real and worth tracking as follow-ups against the workspace install:

  • install.rs:267 workspace-importer freshness: the freshness check should validate every importer's manifest against the lockfile, not just ROOT_IMPORTER_KEY. A workspace with a stale packages/*/package.json currently passes --frozen-lockfile then materialises outdated deps. Worth opening as a sister to Frozen-lockfile freshness check: error when package.json drifts from pnpm-lock.yaml #447.
  • projects.rs:171 negated ! patterns: packages: ["packages/*", "!packages/e2e"] should subtract; pacquet currently only unions. Real pnpm workspaces use ! patterns; pacquet diverges.
  • project_manifest.rs:102 lowercase basename: PACKAGE.JSON matches the strict-name branch via the to_ascii_lowercase coercion. Should be exact-match against the literal package.json.

I can file these as separate issues / pick them up as targeted PRs once the runtime-deps slice (#437) work isn't holding the branch. The lockfile-types slice in this PR doesn't touch any of those files.


Written by an agent (Claude Code, claude-opus-4-7).

Add the lockfile types pnpm v11 writes for runtime dependencies
(`node@runtime:`, `deno@runtime:`, `bun@runtime:`):

- `BinaryResolution { url, integrity, bin, archive, prefix? }`
  matching upstream's [`BinaryResolution`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L41-L49).
  `bin` is `BinarySpec::Single(String) | BinarySpec::Map(BTreeMap)` —
  the untagged union pnpm writes. `archive` is the lowercase
  `BinaryArchive::Tarball | BinaryArchive::Zip` discriminator.
  `prefix` (the archive's top-level directory basename, only set on
  the `.zip` branch in upstream's node-resolver) skips serialization
  when `None`.

- `PlatformAssetTarget { os, cpu, libc? }` and
  `PlatformAssetResolution { resolution, targets }` per
  [`resolving/resolver-base/src/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L60-L69).
  `libc` is `Option<String>` (rather than an enum) so an upstream
  addition beyond the current `musl` value lands without a churning
  serde migration.

- `VariationsResolution { variants }` and the `LockfileResolution`
  + `TaggedResolution` arms that route both new shapes through the
  existing `from/into ResolutionSerde` round-trip. The lockfile
  major stays at 9 — confirmed against
  [`core/constants/src/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/core/constants/src/index.ts)
  — so the existing `lockfile_version.major == 9` assertion holds for
  v11 lockfiles carrying runtime entries.

- Install dispatch stubs in `install_package_by_snapshot.rs` and
  `create_virtual_store.rs` raise `UnsupportedResolution { kind:
  "binary" | "variations" }` until Slice D wires the fetcher.
  Adding these arms keeps the workspace compiling without forcing
  the full install path to land in this slice. `Variations` warm-
  prefetch returns `Ok(None)` so the dispatcher routes through the
  cold path where the unsupported-kind error fires; pnpm's pipeline
  unwraps `Variations` before this layer ever sees one, so the
  branch is unreachable on a well-formed install. Slice D replaces
  these stubs with real dispatch.

Tests: serde round-trip for tarball-shape `BinaryResolution` (with
`bin: <string>`, no `prefix`), zip-shape `BinaryResolution` (with
`bin: <map>` and `prefix`), and a two-variant `VariationsResolution`
covering `(darwin, arm64)` and `(linux, x64, musl)` to pin the
`libc?` omission rule.

Part of #437.

---
Written by an agent (Claude Code, claude-opus-4-7).
Copilot AI review requested due to automatic review settings May 13, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread crates/package-manager/src/install_package_by_snapshot.rs Outdated
Comment thread crates/package-manager/src/create_virtual_store.rs Outdated
Two doc-comment wording nits from PR #457 review:

- `install_package_by_snapshot.rs:218` claimed the arms guard
  against a "non-exhaustive-match panic". That's the wrong failure
  mode: adding a `LockfileResolution` variant without the arms would
  surface as a *compile-time* non-exhaustive-match error, not a
  runtime panic. Reword so the comment reflects the actual mechanic
  — these arms are what makes Slice A standalone.

- `create_virtual_store.rs:738` claimed the install dispatcher
  unwraps `Variations` before this helper sees it, but the match arm
  explicitly handles it. Reword to describe what's actually
  happening: `Variations` is a meta-shape with no warm-key shape of
  its own; both `Binary` and `Variations` route through the cold
  path until Slice D lands variant selection + fetcher dispatch.

No behavior change; comment text only.

---
Written by an agent (Claude Code, claude-opus-4-7).
@zkochan zkochan merged commit c614d64 into main May 13, 2026
14 of 16 checks passed
@zkochan zkochan deleted the feat/437 branch May 13, 2026 15:56
KSXGitHub pushed a commit that referenced this pull request May 13, 2026
Pull in 4 commits from upstream main:
- feat(lockfile): BinaryResolution + VariationsResolution (#457)
- feat: hoisting support (hoistPattern + publicHoistPattern) (#445)
- test(git-fetcher): port §E git-fetcher tests (#462)
- feat(real-hoist): ancestor-path-aware peer-shadow refusal (#461)

Conflict: `crates/config/src/lib.rs` — the hoisting PR (#445)
added `pub mod matcher;` adjacent to my `mod env_replace;`. Keep
both module declarations.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants