feat(pacquet/config): --recursive, --workspace-concurrency#11959
Conversation
…ttings Mirror pnpm's `workspaceConcurrency` (a `.npmrc` / `pnpm-workspace.yaml` / `PNPM_CONFIG_WORKSPACE_CONCURRENCY` config-file key, default `getDefaultWorkspaceConcurrency()`, resolved through `getWorkspaceConcurrency`) and CLI-only `recursive` (`-r`) boolean. - Add `Config::workspace_concurrency` (resolved via the existing `resolve_child_concurrency` port of `getWorkspaceConcurrency`) and `Config::recursive`, plus `default_workspace_concurrency`. - Read `workspaceConcurrency` from workspace yaml, global config.yaml, and the `PNPM_CONFIG_*` env overlay; resolve negative offsets the same way `childConcurrency` does. - Add the `--workspace-concurrency` install flag (overrides the config-resolved value) and the global `-r` / `--recursive` flag (sets `Config::recursive`, matching pnpm's CLI-only nature). `workspaceConcurrency` is parsed and stored for config-surface parity; pacquet's frozen install materializes the whole workspace in one shared pass, so there is no per-project parallel loop for it to throttle yet (same posture as `preferOffline`). `recursive` is likewise a surface flag on install today, since install already spans the workspace.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📜 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). (4)
🧰 Additional context used📓 Path-based instructions (1)pacquet/**/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
🧠 Learnings (3)📚 Learning: 2026-05-20T19:40:55.051ZApplied to files:
📚 Learning: 2026-05-22T00:08:44.646ZApplied to files:
📚 Learning: 2026-05-20T23:07:58.444ZApplied to files:
🔇 Additional comments (3)
📝 WalkthroughWalkthroughAdds Config fields ChangesWorkspace Concurrency and Recursive Flags Configuration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Micro-Benchmark ResultsLinux |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #11959 +/- ##
==========================================
- Coverage 87.94% 87.93% -0.01%
==========================================
Files 227 228 +1
Lines 27720 27810 +90
==========================================
+ Hits 24377 24456 +79
- Misses 3343 3354 +11 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
`default_workspace_concurrency`'s public doc linked the crate-private `default_child_concurrency`, which `rustdoc -D rustdoc::private-intra-doc-links` (implied by `-D warnings`) rejects. Use plain backticks instead.
Integrated-Benchmark Report (Linux)Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.9542126651,
"stddev": 0.059778628980986445,
"median": 1.9653385481,
"user": 2.7601574199999996,
"system": 3.25694284,
"min": 1.8685403341,
"max": 2.0726070611000003,
"times": [
1.9705796921,
1.8685403341,
1.9134690171,
1.8766452701,
2.0726070611000003,
1.9607433471,
1.9786416511,
1.9361649821,
1.9948015471,
1.9699337491
]
},
{
"command": "pacquet@main",
"mean": 1.9212230648000002,
"stddev": 0.04350200692465724,
"median": 1.9162810386,
"user": 2.7820275199999993,
"system": 3.2635453400000003,
"min": 1.8723179571000002,
"max": 2.0246779891,
"times": [
1.9102146081,
1.9122495351,
1.9308702731,
1.9275715261000002,
1.9203125421,
2.0246779891,
1.9468852201,
1.8723179571000002,
1.8908644281,
1.8762665691
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6452588685199999,
"stddev": 0.020722747157525128,
"median": 0.6405704347200001,
"user": 0.35815811999999997,
"system": 1.35249326,
"min": 0.62870929222,
"max": 0.70170546022,
"times": [
0.70170546022,
0.63619677822,
0.6322857442200001,
0.62870929222,
0.64853108622,
0.64645818022,
0.64015620022,
0.64142392122,
0.6409846692200001,
0.63613735322
]
},
{
"command": "pacquet@main",
"mean": 0.69947233022,
"stddev": 0.03663505718071411,
"median": 0.70598324822,
"user": 0.35540392,
"system": 1.3487084599999999,
"min": 0.63756952422,
"max": 0.7485173002200001,
"times": [
0.68790178522,
0.72786832922,
0.6955742622200001,
0.64715022022,
0.7485173002200001,
0.73070790722,
0.71639223422,
0.6805633602200001,
0.63756952422,
0.72247837922
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.2299414914800004,
"stddev": 0.02184420477058646,
"median": 2.2226256380800002,
"user": 3.83613578,
"system": 3.02728276,
"min": 2.20158424358,
"max": 2.26743333458,
"times": [
2.25189331558,
2.21485319758,
2.25702076458,
2.22234082358,
2.20158424358,
2.26743333458,
2.21048112058,
2.23305296858,
2.22291045258,
2.21784469358
]
},
{
"command": "pacquet@main",
"mean": 2.24766427178,
"stddev": 0.028170096547575216,
"median": 2.2542308765800003,
"user": 3.8815918799999993,
"system": 2.9884238599999997,
"min": 2.20472515958,
"max": 2.28518787758,
"times": [
2.21767551558,
2.25303236358,
2.28518787758,
2.26105458358,
2.26782757158,
2.25494830558,
2.20654940858,
2.20472515958,
2.27212848458,
2.25351344758
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.4356077055200003,
"stddev": 0.015211088616632154,
"median": 1.4325464547200002,
"user": 1.7041831199999997,
"system": 1.83640194,
"min": 1.41165773672,
"max": 1.46476633472,
"times": [
1.43428571472,
1.42294942072,
1.42846706072,
1.44913382072,
1.43404208872,
1.44994242972,
1.4310508207200001,
1.41165773672,
1.46476633472,
1.42978162772
]
},
{
"command": "pacquet@main",
"mean": 1.46397821792,
"stddev": 0.03496363564575821,
"median": 1.45454592272,
"user": 1.7195764199999997,
"system": 1.8480519400000002,
"min": 1.4376546997200002,
"max": 1.5600527637200001,
"times": [
1.4376546997200002,
1.45070157172,
1.45683483872,
1.46366721272,
1.5600527637200001,
1.45225700672,
1.44140974672,
1.46313898972,
1.4643659667200002,
1.44969938272
]
}
]
} |
|
| Branch | pr/11959 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,229.94 ms(-24.70%)Baseline: 2,961.46 ms | 3,553.75 ms (62.75%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,435.61 ms(-30.41%)Baseline: 2,062.82 ms | 2,475.39 ms (58.00%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 1,954.21 ms(-6.19%)Baseline: 2,083.24 ms | 2,499.88 ms (78.17%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 645.26 ms(-1.71%)Baseline: 656.45 ms | 787.74 ms (81.91%) |
…estructure bind The sibling `offline: _` / `prefer_offline: _` binds carry no comment; match them for consistency.
The inline `if let Some(value) = args.workspace_concurrency` override body was only reachable when install ran *with* the flag, so the flag-absent integration runs left it uncovered. Extract the resolution into `InstallArgs::resolve_workspace_concurrency` and apply it unconditionally at the dispatch (matching upstream's final `getWorkspaceConcurrency` pass), so the substantive logic is covered by fast unit tests (absent / positive / negative) and the call site is an unconditional assignment the existing install tests already exercise.
KSXGitHub
left a comment
There was a problem hiding this comment.
Some lines could be better with pipe-trait.
…alls Per review: rewrite the `InstallArgsHarness::try_parse_from([...])` calls in the new workspace-concurrency tests as `[...].pipe(InstallArgsHarness::try_parse_from)` so they read left-to-right, matching the codebase's pipe-trait convention.
There was a problem hiding this comment.
Pull request overview
Ports pnpm’s recursive and workspace-concurrency knobs into pacquet’s Rust configuration/CLI surfaces to align config parsing and storage behavior (even if not yet fully consumed by install’s execution pipeline).
Changes:
- Added
Config::workspace_concurrencywith a smart default (min(4, cores)), YAML/env parsing, and CLI override viainstall --workspace-concurrency. - Added
Config::recursiveand wired it to the global-r/--recursiveCLI flag. - Added/updated tests covering YAML/env/CLI parsing and default parity.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pacquet/crates/package-manager/src/install_package_from_registry/tests.rs | Updates test config construction for new Config fields. |
| pacquet/crates/config/src/workspace_yaml/tests.rs | Adds YAML parsing/resolution tests for workspaceConcurrency. |
| pacquet/crates/config/src/workspace_yaml.rs | Adds workspace_concurrency to WorkspaceSettings and applies it via the shared resolver. |
| pacquet/crates/config/src/lib.rs | Adds Config fields/docs and exports default_workspace_concurrency. |
| pacquet/crates/config/src/env_overlay.rs | Adds PNPM_CONFIG_WORKSPACE_CONCURRENCY env overlay support + tests. |
| pacquet/crates/config/src/defaults/tests.rs | Adds a parity test tying workspaceConcurrency default to childConcurrency default. |
| pacquet/crates/config/src/defaults.rs | Introduces default_workspace_concurrency(). |
| pacquet/crates/cli/src/cli_args/tests.rs | Adds tests for global -r/--recursive parsing behavior. |
| pacquet/crates/cli/src/cli_args/install/tests.rs | Adds tests for --workspace-concurrency parsing and resolution. |
| pacquet/crates/cli/src/cli_args/install.rs | Adds the --workspace-concurrency flag and resolution helper. |
| pacquet/crates/cli/src/cli_args.rs | Wires --recursive into Config and applies --workspace-concurrency override for install. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…currency docs workspace_concurrency is populated only from pnpm-workspace.yaml, global config.yaml, and the PNPM_CONFIG_WORKSPACE_CONCURRENCY env overlay. Config::current reads .npmrc but applies only the auth/network subset, so a workspace-concurrency= entry in .npmrc never reaches the field. Align the doc comments and PR summary with actual behavior, matching the sibling childConcurrency docs.
…ral doc - Re-pad the [dependencies] table so every `=` aligns to the widest key (pacquet-resolving-parse-wanted-dependency, added by this PR). The merge from origin/main introduced a new dep that broke the uniform column alignment taplo enforces. - Update exec.rs's deferral doc: the workspace-projects-filter / workspace-projects-graph crates and the global --filter/--recursive flags landed on main (#11959, #12000), so the selection layer now exists; only the recursive runner is still missing. https://claude.ai/code/session_01PPwhryEFfN4iyZkVsjy2Hf
The perfectionist 0.0.0-rc.17 upgrade on main flags bare `#NNN` references as ambiguous (could be issue / PR / colour / etc.). Replace the two #11959 / #12000 references with explicit pull-request URLs, matching pacquet's existing doc-comment convention. https://claude.ai/code/session_01PPwhryEFfN4iyZkVsjy2Hf
Summary
Ports two pnpm settings into pacquet's Rust config layer.
workspace-concurrency— read frompnpm-workspace.yaml, globalconfig.yaml, andPNPM_CONFIG_WORKSPACE_CONCURRENCY, overridable per-invocation by the--workspace-concurrencyinstall flag. Stored asConfig::workspace_concurrency; defaultmin(4, cores); resolved throughresolve_child_concurrency(the port ofgetWorkspaceConcurrency), so a non-positive value meansparallelism - |value|, floored at 1. Not read from.npmrc— pacquet applies only the auth/network subset of.npmrc, same as the siblingchildConcurrency.recursive— the global CLI-only-r/--recursiveboolean, stored asConfig::recursive. Not a yaml / env /.npmrckey, matching pnpm.Both are parsed and stored for config-surface parity. pacquet's frozen install materializes the whole workspace in one shared pass, so neither value has a per-project consumption point yet (same "read now, consume as the architecture lands" posture as
preferOffline).Tests
Default parity; yaml (positive / negative / independent-from-
childConcurrency);PNPM_CONFIG_*env parsing; CLI parsing (including the global-rbefore and after the subcommand); and the--workspace-concurrencyoverride resolution.Written by an agent (Claude Code, claude-opus-4-7).
Summary by CodeRabbit
New Features
--recursive/-rflag and--workspace-concurrencyoption to the CLITests
Documentation