Skip to content

feat(pacquet/config): port peersSuffixMaxLength setting#12026

Merged
zkochan merged 5 commits into
mainfrom
peers-max-length
May 28, 2026
Merged

feat(pacquet/config): port peersSuffixMaxLength setting#12026
zkochan merged 5 commits into
mainfrom
peers-max-length

Conversation

@zkochan

@zkochan zkochan commented May 28, 2026

Copy link
Copy Markdown
Member

Summary

Ports the peersSuffixMaxLength setting (default 1000) to pacquet end-to-end, matching pnpm:

  • read from pnpm-workspace.yaml, global config.yaml, and PNPM_CONFIG_PEERS_SUFFIX_MAX_LENGTH
  • threaded into ResolveImporterOptions so the deps resolver caps the rendered peer-suffix at the configured value (was hardcoded 1000 via ResolvePeersOptions::default() before)
  • written to pnpm-lock.yaml's settings.peersSuffixMaxLength only when non-default — mirrors pnpm's strip-on-default in convertToLockfileFile
  • check_lockfile_settings now reports drift as StalenessReason::PeersSuffixMaxLengthChanged
  • joins optimistic_repeat_install's settings_match comparison via current_settings, so a change here invalidates the fast path

Closes one of the checkboxes tracked in #12009.

Test plan

  • cargo nextest run -p pacquet-config -p pacquet-lockfile -p pacquet-resolving-deps-resolver -p pacquet-package-manager — 807 tests pass
  • just fmt / just check / just lint — clean
  • New tests added:
    • pacquet-config: default-1000, yaml override, env-var-wins-over-yaml
    • pacquet-lockfile: lockfile-side drift (PeersSuffixMaxLengthChanged) for both unset-vs-default and explicit-vs-explicit, plus serialization strip-on-default + round-trip when non-default
    • pacquet-package-manager: optimistic-repeat-install drift gate (ports checkDepsStatus.test.ts:175-203)

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

Summary by CodeRabbit

  • New Features

    • Added peersSuffixMaxLength setting (default: 1000). Configurable via workspace config or env; used during peer-resolution and preserved when generating lockfiles.
  • Lockfile

    • Lockfiles can record peersSuffixMaxLength (omitted when default). Installs and optimistic fast-path now validate this setting to detect stale installs.
  • Tests

    • Added tests for default, precedence (workspace vs env), serialization, and drift detection.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds peersSuffixMaxLength support across config defaults/env/workspace parsing, exposes Config.peers_suffix_max_length, rounds the setting through lockfile serialization, extends freshness checks and optimistic-repeat fast-path, threads the value into resolver/lockfile generation, and adds tests.

Changes

peersSuffixMaxLength Configuration and Validation

Layer / File(s) Summary
Configuration defaults and parsing
pacquet/crates/config/src/defaults.rs, pacquet/crates/config/src/lib.rs, pacquet/crates/config/src/env_overlay.rs, pacquet/crates/config/src/workspace_yaml.rs
Added default_peers_suffix_max_length() returning 1000, reformatted defaults re-export, new Config.peers_suffix_max_length: u64 with SmartDefault, environment variable (PEERS_SUFFIX_MAX_LENGTH) parsing, workspace YAML field parsing, and unit tests for default/override precedence.
Lockfile model and serialization
pacquet/crates/lockfile/src/lib.rs, pacquet/crates/lockfile/src/save_lockfile/tests.rs
Added DEFAULT_PEERS_SUFFIX_MAX_LENGTH constant and optional LockfileSettings.peers_suffix_max_length: Option<u64> with serde omission when None. Tests verify omission when unset and preservation when explicitly set.
Freshness validation and staleness detection
pacquet/crates/lockfile/src/freshness.rs, pacquet/crates/lockfile/src/freshness/tests.rs
Extended check_lockfile_settings signature to accept peers_suffix_max_length: u64, added StalenessReason::PeersSuffixMaxLengthChanged { lockfile, config }, implemented fallback-to-default lockfile logic, updated callers, and added tests for unset/default/no-drift and mismatch drift cases and ordering.
Lockfile generation and round-tripping
pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs, pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
Added GraphToLockfileOptions.peers_suffix_max_length: Option<u64> and forwarded it into produced LockfileSettings so the setting round-trips; updated tests to include the new field set to None.
Installation pipeline integration
pacquet/crates/package-manager/src/install.rs, pacquet/crates/package-manager/src/install_package_from_registry/tests.rs, pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
Wired config.peers_suffix_max_length into pacquet_lockfile::check_lockfile_settings, per-importer resolver options (converted to usize with fallback), and conditionally emitted the lockfile setting only when non-default. Test setups now initialize the config field using the default provider.
Optimistic repeat install fast-path settings drift
pacquet/crates/package-manager/src/optimistic_repeat_install.rs, pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
Included peers_suffix_max_length in settings_match equality, removed it from the intentionally-ignored list, persisted it into current settings, and added a regression test asserting cached-state drift on this field causes Skipped.
Peer resolver configuration threading
pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs, pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
Added ResolveImporterOptions.peers_suffix_max_length and threaded it into ResolvePeersOptions used for both the inner fixed-point loop and final resolve_peers call; tests set default to 1000.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

  • #12009: Implements the peersSuffixMaxLength configuration item across config, workspace YAML, lockfile, resolver, and freshness checks.

Possibly related PRs

  • pnpm/pnpm#11943: Related changes to optimistic repeat-install fast-path logic.
  • pnpm/pnpm#12005: Overlapping changes to optimistic repeat-install settings comparison.
  • pnpm/pnpm#11816: Prior work on fresh-lockfile lockfile-writing pipeline this change extends.

Suggested reviewers

  • KSXGitHub

Poem

🐰 I nibble at defaults and stitch a thread so neat,
A thousand-length suffix makes peer-hashes complete,
Through lockfiles and resolvers my little hops trace,
Tests guard the round-trip and installs keep their pace,
Hooray — a hoppity setting landed sweet!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: porting the peersSuffixMaxLength setting from pnpm to pacquet across multiple configuration layers and the dependency resolver.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch peers-max-length

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      8.8±0.37ms   494.4 KB/sec    1.07      9.4±0.43ms   463.2 KB/sec

@codecov-commenter

codecov-commenter commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.41%. Comparing base (7c9a6c2) to head (7869650).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12026      +/-   ##
==========================================
+ Coverage   88.38%   88.41%   +0.03%     
==========================================
  Files         230      230              
  Lines       29116    29177      +61     
==========================================
+ Hits        25734    25798      +64     
+ Misses       3382     3379       -3     

☔ 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.

@zkochan zkochan force-pushed the peers-max-length branch from 1b1ca9d to 7682aa7 Compare May 28, 2026 16:14
@zkochan zkochan marked this pull request as ready for review May 28, 2026 16:16
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Port peersSuffixMaxLength setting end-to-end to match pnpm

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Port peersSuffixMaxLength setting end-to-end matching pnpm behavior
• Read from pnpm-workspace.yaml, global config.yaml, and PNPM_CONFIG_PEERS_SUFFIX_MAX_LENGTH
• Thread into resolver to cap peer-suffix rendering at configured value
• Write to lockfile settings.peersSuffixMaxLength only when non-default
• Detect drift via StalenessReason::PeersSuffixMaxLengthChanged in freshness checks
• Invalidate optimistic-repeat-install fast path on peersSuffixMaxLength changes
Diagram
flowchart LR
  Config["Config reads peersSuffixMaxLength<br/>default 1000"]
  Resolver["ResolveImporterOptions<br/>threads to resolver"]
  PeerSuffix["Peer-suffix capped<br/>at configured value"]
  Lockfile["Lockfile settings<br/>serializes when non-default"]
  Freshness["check_lockfile_settings<br/>detects drift"]
  OptimisticRepeat["optimistic_repeat_install<br/>invalidates on change"]
  
  Config -->|"env > yaml > default"| Resolver
  Resolver -->|"peers_suffix_max_length"| PeerSuffix
  PeerSuffix -->|"strip-on-default"| Lockfile
  Lockfile -->|"compare recorded vs current"| Freshness
  Freshness -->|"PeersSuffixMaxLengthChanged"| OptimisticRepeat

Loading

Grey Divider

File Changes

1. pacquet/crates/config/src/defaults.rs ✨ Enhancement +13/-0

Add default_peers_suffix_max_length function

pacquet/crates/config/src/defaults.rs


2. pacquet/crates/config/src/env_overlay.rs ✨ Enhancement +1/-0

Add PNPM_CONFIG_PEERS_SUFFIX_MAX_LENGTH env var support

pacquet/crates/config/src/env_overlay.rs


3. pacquet/crates/config/src/lib.rs ✨ Enhancement +78/-3

Add peers_suffix_max_length config field with tests

pacquet/crates/config/src/lib.rs


View more (14)
4. pacquet/crates/config/src/workspace_yaml.rs ✨ Enhancement +2/-0

Add peers_suffix_max_length to WorkspaceSettings struct

pacquet/crates/config/src/workspace_yaml.rs


5. pacquet/crates/lockfile/src/freshness.rs ✨ Enhancement +29/-0

Add PeersSuffixMaxLengthChanged staleness reason and check

pacquet/crates/lockfile/src/freshness.rs


6. pacquet/crates/lockfile/src/freshness/tests.rs 🧪 Tests +152/-18

Add comprehensive tests for peers_suffix_max_length drift detection

pacquet/crates/lockfile/src/freshness/tests.rs


7. pacquet/crates/lockfile/src/lib.rs ✨ Enhancement +14/-0

Add DEFAULT_PEERS_SUFFIX_MAX_LENGTH constant and lockfile field

pacquet/crates/lockfile/src/lib.rs


8. pacquet/crates/lockfile/src/save_lockfile/tests.rs 🧪 Tests +73/-0

Add tests for peers_suffix_max_length serialization behavior

pacquet/crates/lockfile/src/save_lockfile/tests.rs


9. pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs ✨ Enhancement +10/-0

Add peers_suffix_max_length to GraphToLockfileOptions struct

pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs


10. pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs 🧪 Tests +6/-0

Update test helpers to pass peers_suffix_max_length option

pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs


11. pacquet/crates/package-manager/src/install.rs ✨ Enhancement +1/-0

Pass peers_suffix_max_length to check_lockfile_settings

pacquet/crates/package-manager/src/install.rs


12. pacquet/crates/package-manager/src/install_package_from_registry/tests.rs 🧪 Tests +1/-0

Initialize peers_suffix_max_length in test config

pacquet/crates/package-manager/src/install_package_from_registry/tests.rs


13. pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs ✨ Enhancement +4/-0

Thread peers_suffix_max_length into resolver and lockfile builder

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs


14. pacquet/crates/package-manager/src/optimistic_repeat_install.rs ✨ Enhancement +5/-4

Add peers_suffix_max_length to settings comparison and current_settings

pacquet/crates/package-manager/src/optimistic_repeat_install.rs


15. pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs 🧪 Tests +45/-4

Add test for optimistic-repeat-install drift on peers_suffix_max_length

pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs


16. pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs ✨ Enhancement +10/-3

Add peers_suffix_max_length to ResolveImporterOptions struct

pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs


17. pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs 🧪 Tests +1/-0

Initialize peers_suffix_max_length in default test options

pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs


Grey Divider

Qodo Logo

@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: 2

🤖 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 `@pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs`:
- Line 585: The code currently does an unchecked narrowing cast of
config.peers_suffix_max_length (u64) to usize when building
ResolveImporterOptions (peers_suffix_max_length: config.peers_suffix_max_length
as usize); replace this with a checked conversion using
usize::try_from(config.peers_suffix_max_length) and handle the Result (e.g.,
return an appropriate error if conversion fails or the value is out of range) so
the resolver behavior can't silently change on 32-bit targets; update the
construction site of ResolveImporterOptions (where peers_suffix_max_length is
set) to perform the try_from and propagate or map the error.

In `@pacquet/crates/package-manager/src/optimistic_repeat_install.rs`:
- Line 277: The code unsafely casts Config.peers_suffix_max_length (u64) to u32
with as u32 which can truncate large values; replace the direct cast in the
workspace state construction (the peers_suffix_max_length field) with a safe
conversion such as using u32::try_from(Config.peers_suffix_max_length).ok() (or
equivalent checked conversion) so the result is an Option<u32> that is None on
overflow rather than silently truncated.
🪄 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: 9cb076c4-245a-4a10-9be7-d9c797634ba8

📥 Commits

Reviewing files that changed from the base of the PR and between 7ecaf3d and 7682aa7.

📒 Files selected for processing (17)
  • pacquet/crates/config/src/defaults.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/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). (2)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: When porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(), or streamParser.write(), mirror the call site, payload, and ordering so the reporter parses pacquet's NDJSON the same way it parses pnpm's.
Declare a newtype wrapper for branded string types. Do not collapse the brand into a plain String or &str.
If upstream always validates before construction, validate in pacquet's wrapper too. The wrapper must construct only via TryFrom<String> and/or FromStr. Do not provide an infallible public constructor.
If upstream never validates, just brand for type-safety. Expose an infallible From<String> (and From<&str> when convenient).
If upstream occasionally constructs without validation, expose from_str_unchecked as an escape hatch alongside the validating constructor.
Match upstream serde behavior for branded types that cross JSON, YAML, or INI boundaries. Use #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization.
Use #[derive(derive_more::From)] and #[derive(derive_more::Into)] for mechanical conversion impls. Fall back to manual impl only when conversion needs custom logic.
String-literal unions should become enums, not newtype wrappers. Model closed sets of valid string values as enums.
Template literal types should be treated as branded strings with validation discipline from rules 2-5.
Choose owned vs. borrowed parameters to minimize copies. Widen to the most encompassing type (&Path over &PathBuf, &str over &String) when it doesn't force extra copies.
Prefer Arc::clone(&x) / Rc::clone(&x) over x.clone() for reference-counted types, so the cost is visible at the call site.
Follow Rust API Guidelines for naming conventions.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*;. Two forms stay allowed: external-crate preludes like use rayon::prelude::*; and root-of-module re-...

Files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/config/src/defaults.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
🧠 Learnings (6)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/config/src/defaults.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/config/src/defaults.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/config/src/defaults.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: When implementing dependency “revisit”/optional-folding in pacquet, mirror pnpm’s `resolveDependencies.ts` behavior: on revisit, update the `optional` flag only for the directly visited package, and do not automatically change `optional` for transitive descendants. pnpm corrects stale optional flags later via `copyDependencySubGraph` (a BFS in `lockfile/pruner/src/index.ts`/`copyDependencySubGraph`). Until pacquet has an equivalent pruner/copy-subgraph step, be aware that the raw `node.optional` can flow through to the lockfile/virtual store via `dependencies_graph_to_lockfile.rs` → `create_virtual_store.rs` → `installability.rs`, so review changes that affect optional flag propagation for this mismatch.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: In pacquet’s Rust resolver layer, the behavior of `ResolvedPackage.optional` “AND-folding”/revisit updates is intentionally aligned with pnpm: when a package is directly revisited, only that package’s `optional` flag is updated; transitive descendants are not re-walked/rescored in this stage. pnpm later corrects any stale optional flags downstream via its pruner/BFS logic (`copyDependencySubGraph` + `nonOptional` reachability stamping in the lockfile pruner). Since pacquet does not yet have that pruner equivalent, review should NOT flag the downstream/stale optional propagation as a bug in pacquet PRs; treat the resolver-layer optional propagation gap as expected parity until the pruning/stamping step is ported.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
📚 Learning: 2026-05-20T23:23:40.606Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11788
File: pacquet/crates/lockfile/src/snapshot_dep_ref.rs:52-56
Timestamp: 2026-05-20T23:23:40.606Z
Learning: In the pacquet lockfile crate, the link target is intentionally represented as a raw `Link(String)` (not a `LinkTarget` newtype) for both `SnapshotDepRef::Link` and `ImporterDepVersion::Link`. pnpm upstream preserves `link:` targets verbatim with only a prefix check (e.g., `refToRelative`), so do not suggest introducing a `LinkTarget` newtype in a bug-fix/targeted PR. If type branding is ever desired, it should be done together for both variants in a separate dedicated refactor PR rather than as part of a small fix.

Applied to files:

  • pacquet/crates/lockfile/src/save_lockfile/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
🔇 Additional comments (17)
pacquet/crates/config/src/defaults.rs (1)

245-256: LGTM!

pacquet/crates/config/src/lib.rs (1)

26-28: LGTM!

Also applies to: 397-415, 2578-2632

pacquet/crates/config/src/env_overlay.rs (1)

133-133: LGTM!

pacquet/crates/config/src/workspace_yaml.rs (1)

118-118: LGTM!

Also applies to: 502-502

pacquet/crates/lockfile/src/lib.rs (1)

48-55: LGTM!

Also applies to: 70-75

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

164-235: LGTM!

pacquet/crates/lockfile/src/freshness.rs (1)

102-113: LGTM!

Also applies to: 207-207, 246-260

pacquet/crates/lockfile/src/freshness/tests.rs (2)

594-601: LGTM!

Also applies to: 617-625, 638-665, 687-701, 719-727, 741-747, 764-766, 783-789, 814-820


827-904: LGTM!

pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs (1)

71-78: LGTM!

Also applies to: 109-110, 130-130

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

41-41: LGTM!

Also applies to: 207-207, 227-227, 872-872, 994-994, 1145-1145

pacquet/crates/package-manager/src/install.rs (1)

1060-1065: LGTM!

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

38-38: LGTM!

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)

1047-1049: LGTM!

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

428-468: LGTM!

Also applies to: 513-516

pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs (1)

100-104: LGTM!

Also applies to: 149-152, 196-197, 277-278

pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs (1)

95-95: LGTM!

Comment thread pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs Outdated
Comment thread pacquet/crates/package-manager/src/optimistic_repeat_install.rs Outdated
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

1 similar comment
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

@github-actions

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.083 ± 0.067 1.965 2.196 1.02 ± 0.05
pacquet@main 2.050 ± 0.068 1.946 2.146 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.0829644863599994,
      "stddev": 0.066850377637756,
      "median": 2.07319647856,
      "user": 2.7405716000000004,
      "system": 3.28341106,
      "min": 1.96466871306,
      "max": 2.19583654706,
      "times": [
        2.03870811706,
        2.07763887206,
        2.04054321106,
        2.19583654706,
        2.16780214506,
        1.96466871306,
        2.0553097500599997,
        2.06875408506,
        2.10502354006,
        2.11535988306
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.0501086858600006,
      "stddev": 0.06773101142726648,
      "median": 2.04265495806,
      "user": 2.7345702,
      "system": 3.24459106,
      "min": 1.94556523906,
      "max": 2.14632614606,
      "times": [
        2.14632614606,
        2.14398202906,
        1.94556523906,
        2.10453131106,
        2.00634076606,
        2.06727412506,
        2.00898186006,
        2.07238725406,
        2.01803579106,
        1.98766233706
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 664.0 ± 16.5 646.6 706.9 1.00
pacquet@main 673.8 ± 15.2 658.5 713.0 1.01 ± 0.03
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.6640286777800001,
      "stddev": 0.01651908171406663,
      "median": 0.65973530138,
      "user": 0.36720569999999997,
      "system": 1.3296974599999998,
      "min": 0.6465930938800001,
      "max": 0.7069451008800001,
      "times": [
        0.7069451008800001,
        0.6679661878800001,
        0.6465930938800001,
        0.65260282688,
        0.6685361068800001,
        0.6601446188800001,
        0.6646459368800001,
        0.6593259838800001,
        0.6562241968800001,
        0.6573027248800001
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.67380093608,
      "stddev": 0.015156202735964724,
      "median": 0.6714170648800001,
      "user": 0.3667261,
      "system": 1.3566348599999998,
      "min": 0.6585095648800001,
      "max": 0.71299072088,
      "times": [
        0.71299072088,
        0.6648468098800001,
        0.6585095648800001,
        0.6717942518800001,
        0.6747359988800001,
        0.6678670988800001,
        0.6802546028800001,
        0.6710398778800001,
        0.67348624988,
        0.66248418488
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.380 ± 0.028 2.338 2.426 1.01 ± 0.02
pacquet@main 2.357 ± 0.025 2.330 2.412 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.37970467302,
      "stddev": 0.028337228508125208,
      "median": 2.3774029937200005,
      "user": 3.9135182199999994,
      "system": 3.05420308,
      "min": 2.33849218972,
      "max": 2.4259499647200005,
      "times": [
        2.4259499647200005,
        2.3447556827200002,
        2.3841794437200003,
        2.4103765427200003,
        2.35926099072,
        2.36952723072,
        2.3706265437200003,
        2.39913974872,
        2.33849218972,
        2.3947383927200003
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.3571826400200004,
      "stddev": 0.024721918657338224,
      "median": 2.35556256572,
      "user": 3.88855052,
      "system": 3.02855998,
      "min": 2.33049934472,
      "max": 2.4120440217200003,
      "times": [
        2.34776603772,
        2.3652732537200003,
        2.33382419672,
        2.37282567672,
        2.35993365372,
        2.36746244972,
        2.4120440217200003,
        2.35119147772,
        2.33049934472,
        2.33100628772
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.594 ± 0.037 1.524 1.649 1.04 ± 0.04
pacquet@main 1.531 ± 0.043 1.488 1.640 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.5940279595800002,
      "stddev": 0.03695883060730748,
      "median": 1.60445512738,
      "user": 1.7826519799999996,
      "system": 1.83808012,
      "min": 1.52357037538,
      "max": 1.64914838638,
      "times": [
        1.60294257538,
        1.61800616538,
        1.64914838638,
        1.56161844738,
        1.61461834838,
        1.6059676793800002,
        1.59694086738,
        1.61372753838,
        1.5537392123800002,
        1.52357037538
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.5305973458800002,
      "stddev": 0.04288695918586298,
      "median": 1.5243736488800002,
      "user": 1.7103001799999997,
      "system": 1.8004232200000003,
      "min": 1.4883538383800001,
      "max": 1.6395307833800001,
      "times": [
        1.53776632838,
        1.6395307833800001,
        1.5274620323800001,
        1.50099136138,
        1.5040994123800002,
        1.5429542203800002,
        1.4883538383800001,
        1.5212852653800002,
        1.54258949738,
        1.5009407193800002
      ]
    }
  ]
}

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12026
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
2,379.70 ms
(+2.19%)Baseline: 2,328.67 ms
2,794.40 ms
(85.16%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,594.03 ms
(+9.53%)Baseline: 1,455.27 ms
1,746.33 ms
(91.28%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
2,082.96 ms
(+1.18%)Baseline: 2,058.58 ms
2,470.30 ms
(84.32%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
664.03 ms
(-2.25%)Baseline: 679.34 ms
815.21 ms
(81.45%)
🐰 View full continuous benchmarking report in Bencher

@zkochan zkochan force-pushed the peers-max-length branch from 907e8e0 to 267a069 Compare May 28, 2026 17:38
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

zkochan added 4 commits May 28, 2026 19:48
Adds `Config.peers_suffix_max_length` (default 1000) plumbed through
the resolver, lockfile settings, and optimistic-repeat-install drift
gate so pacquet matches pnpm's behavior for the
[`peersSuffixMaxLength`](https://pnpm.io/settings#peerssuffixmaxlength)
setting end-to-end:

- read from `pnpm-workspace.yaml`, global `config.yaml`, and
  `PNPM_CONFIG_PEERS_SUFFIX_MAX_LENGTH`
- threaded into `ResolveImporterOptions` so the deps resolver caps
  rendered peer suffixes at the configured value
- written to `pnpm-lock.yaml`'s `settings.peersSuffixMaxLength` only
  when non-default (matching pnpm's strip-on-default in
  `convertToLockfileFile`)
- `check_lockfile_settings` now reports drift as
  `StalenessReason::PeersSuffixMaxLengthChanged`
- joins `optimistic_repeat_install`'s `settings_match` comparison
  via `current_settings`

Tracks #12009.
…x_max_length casts

Address CodeRabbit review feedback: `config.peers_suffix_max_length`
is u64 but two narrowing casts could truncate on extreme values —
`as usize` at the resolver-options boundary (on 32-bit targets) and
`as u32` at the workspace-state writer. Replace both with
`try_from(..).unwrap_or(<TYPE>::MAX)` so a configured value larger
than the target type can't silently change resolver behavior.
`pacquet-config` doesn't depend on `pacquet-deps-path` (and shouldn't,
to avoid a dep cycle), so an intra-doc link to
`pacquet_deps_path::create_peer_dep_graph_hash` couldn't be resolved
and broke the Doc CI job under `-D warnings`. Downgrade to a plain
code reference.
`perfectionist::macro-trailing-comma` flags a trailing comma in a
single-line macro invocation; cargo fmt collapsed the original
multi-line `assert_eq!` into one line without dropping the comma.
Drop it so Dylint passes.
@zkochan zkochan force-pushed the peers-max-length branch from 267a069 to cf12648 Compare May 28, 2026 17:53
Six multi-line `assert!(...)` calls in freshness/tests.rs need a
trailing comma after the final expression, and two single-line
`assert_eq!(...)` calls (one in freshness/tests.rs, one in
save_lockfile/tests.rs) have stray trailing commas. Both flavors
trip Dylint's `perfectionist::macro-trailing-comma`.
@zkochan zkochan merged commit 4b60515 into main May 28, 2026
25 checks passed
@zkochan zkochan deleted the peers-max-length branch May 28, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants