perf: content-check modified manifests and fall back to the current lockfile on the repeat-install fast path (pacquet + pnpm)#12315
Conversation
…g the repeat-install fast path Port the modified-manifests branch of pnpm's checkDepsStatus to the optimistic repeat-install check. A package.json whose mtime is newer than the last validation no longer falls straight into the full install pipeline: the check now re-reads the wanted lockfile and confirms the content still matches — assertLockfilesEqual against the current lockfile, getOutdatedLockfileSetting drift, satisfiesPackageManifest per modified importer, and linkedPackagesAreUpToDate for workspace links — before reporting "Already up to date". The workspace branch refreshes lastValidatedTimestamp after a passing content check, like upstream. Ports (at cc4ff817aa): - deps/status/src/checkDepsStatus.ts modified-projects branch and single-project lockfile-mtime branch - deps/status/src/assertLockfilesEqual.ts - lockfile/verification/src/linkedPackagesAreUpToDate.ts (without the isLocalFileDepUpdated branch: file: directory specifiers conservatively fall through to the full install) This closes the dominant gap in the benchmarks.vlt.sh lockfile+node_modules variation, where the harness rewrites package.json (same content, fresh mtime) and wipes the cache dir before every timed run. pnpm absorbs that via the content re-check (~0.5 s); pacquet re-ran the entire install pipeline, and with lockfile-verified.jsonl and the metadata cache wiped, the minimumReleaseAge lockfile-verification gate re-fetched a packument per locked package: 0.94 s on astro and 9.1 s on babylon per run (ranking 8-9/10) against pnpm's 0.50-0.85 s. With the content check the same scenario is a ~40 ms no-op on astro and ~250 ms on babylon. The freshness gate is refactored into reusable pieces (parse_config_overrides, check_lockfile_settings_drift, check_importer_satisfies) shared between the frozen-install dispatch and the new content check, with the per-importer slice no longer hard-wired to the root importer.
Code Review by Qodo
1. Linked check ignores overrides
|
|
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 (6)
✅ Files skipped from review due to trivial changes (2)
📜 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). (8)
🧰 Additional context used📓 Path-based instructions (2)**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.test.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (3)📚 Learning: 2026-05-14T09:04:00.133ZApplied to files:
📚 Learning: 2026-06-05T13:47:05.929ZApplied to files:
📚 Learning: 2026-06-05T13:47:26.046ZApplied to files:
🔇 Additional comments (9)
📝 WalkthroughWalkthroughThis PR refactors the optimistic repeat-install fast-path to accept a struct input, replaces the mtime-unchanged shortcut with manifest-stat + content rechecks against wanted/current lockfiles (with optional regeneration), and extracts override parsing and per-importer satisfiability into pub(crate) helpers; tests updated for new flows and offline scenarios. ChangesOptimistic Repeat-Install Refactor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
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)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. 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 |
…-lock.yaml is missing When `pnpm-lock.yaml` is absent but `node_modules` is intact, the repeat-install fast path now reads `<virtual_store_dir>/lock.yaml` — the record of what the previous install materialized — and treats it as the wanted lockfile: the manifests are content-checked against it and `pnpm-lock.yaml` is regenerated from it (byte-identical to what the full install's synthesize-from-current path would rewrite) before the check reports "Already up to date". Single-project installs with no lockfile on either side still refuse the fast path, and `lockfile: false` skips the regeneration. Previously a deleted `pnpm-lock.yaml` always fell into the full pipeline: the wanted lockfile got synthesized from the current one, but the awaited lockfile-verification gate then missed its verdict cache (`try_lockfile_verification_cache` bails before the content-hash index when the lockfile file can't be stat'd) and re-fetched a packument per locked package. In the benchmarks.vlt.sh `cache+node_modules` / `node_modules` variations this cost 0.7-1.5 s on astro and 6.4-9.0 s on babylon per run; with the fallback both are a ~40 ms (astro) / ~190 ms (babylon) no-op that restores the lockfile.
Integrated-Benchmark Report (Linux)Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 10.250249586119999,
"stddev": 0.08321113889249053,
"median": 10.21988205142,
"user": 3.17478852,
"system": 3.2984231399999997,
"min": 10.17485057092,
"max": 10.39748326792,
"times": [
10.297570589920001,
10.39748326792,
10.182667221920001,
10.21937879592,
10.38346001592,
10.17485057092,
10.18370820392,
10.22038530692,
10.18630914292,
10.25668274492
]
},
{
"command": "pacquet@main",
"mean": 10.29870085852,
"stddev": 0.1543646567154255,
"median": 10.24238042792,
"user": 3.16739262,
"system": 3.2886327399999997,
"min": 10.13212770892,
"max": 10.57336370992,
"times": [
10.227825666920001,
10.13450377192,
10.57336370992,
10.34928879892,
10.20485324592,
10.13212770892,
10.368695305920001,
10.52969467492,
10.20972051292,
10.25693518892
]
},
{
"command": "pnpr@HEAD",
"mean": 5.393506841219999,
"stddev": 0.09925639986558536,
"median": 5.35382475892,
"user": 2.4514180199999998,
"system": 2.9488596399999993,
"min": 5.30461939792,
"max": 5.639826300919999,
"times": [
5.30461939792,
5.44465974292,
5.34164661292,
5.34087191292,
5.33318717292,
5.363681134919999,
5.34841244192,
5.639826300919999,
5.45892661892,
5.359237075919999
]
},
{
"command": "pnpr@main",
"mean": 5.383372208420001,
"stddev": 0.11149464275257254,
"median": 5.34245931092,
"user": 2.4302030199999995,
"system": 2.9678795399999998,
"min": 5.31795368192,
"max": 5.68710968892,
"times": [
5.68710968892,
5.323644252919999,
5.31795368192,
5.3410234579199996,
5.32994962092,
5.41310078492,
5.4008259899199995,
5.35003436292,
5.326185079919999,
5.34389516392
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6482023312,
"stddev": 0.0075624372534389825,
"median": 0.6468382539,
"user": 0.3865014600000001,
"system": 1.31522818,
"min": 0.6378281009,
"max": 0.6643879159,
"times": [
0.6484191839,
0.6378281009,
0.6552784449,
0.6410641699,
0.6433187269,
0.6643879159,
0.6469698149,
0.6517714859,
0.6462787759,
0.6467066929
]
},
{
"command": "pacquet@main",
"mean": 0.6808613608,
"stddev": 0.08764509432998406,
"median": 0.6573317099,
"user": 0.37195596,
"system": 1.32112688,
"min": 0.6305525709,
"max": 0.9265953939,
"times": [
0.6533578529,
0.6644046659,
0.6456491899,
0.6613055669,
0.9265953939,
0.6665474389,
0.6799830639000001,
0.6446915769,
0.6355262879,
0.6305525709
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7771527297,
"stddev": 0.03636939823259288,
"median": 0.7691421514000001,
"user": 0.40929306,
"system": 1.32843358,
"min": 0.7429166229,
"max": 0.8446161109,
"times": [
0.7429166229,
0.7655972109,
0.7726870919000001,
0.7474180659,
0.8393295129,
0.7799929129000001,
0.7505627059000001,
0.7535464839,
0.8446161109,
0.7748605789
]
},
{
"command": "pnpr@main",
"mean": 0.7713410014000001,
"stddev": 0.06312405646409608,
"median": 0.7514588014000001,
"user": 0.39421086,
"system": 1.3144451799999999,
"min": 0.7353625369,
"max": 0.9462116429,
"times": [
0.7533312769,
0.7586677089,
0.7406147389000001,
0.7588764679000001,
0.9462116429,
0.7859342629,
0.7377620139000001,
0.7353625369,
0.7470630389,
0.7495863259000001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 9.2361865565,
"stddev": 0.03943421382690157,
"median": 9.234001786299999,
"user": 3.5936060399999996,
"system": 3.2745829200000003,
"min": 9.1655423223,
"max": 9.3108161133,
"times": [
9.254976987300001,
9.2682791343,
9.3108161133,
9.1655423223,
9.2428634013,
9.2231818003,
9.2086108563,
9.2525228123,
9.2251401713,
9.209931966300001
]
},
{
"command": "pacquet@main",
"mean": 9.2108817493,
"stddev": 0.04670374424342645,
"median": 9.1934899128,
"user": 3.5345855399999997,
"system": 3.28818082,
"min": 9.1631616143,
"max": 9.3179181363,
"times": [
9.3179181363,
9.1760942233,
9.2599904733,
9.1631616143,
9.2057232843,
9.1848938793,
9.1853879213,
9.2286681353,
9.194513992300001,
9.1924658333
]
},
{
"command": "pnpr@HEAD",
"mean": 5.1084145446,
"stddev": 0.10436044844784274,
"median": 5.0765626333,
"user": 2.2767361399999997,
"system": 2.85607462,
"min": 5.0452625723,
"max": 5.3976340363,
"times": [
5.0755161173,
5.105489485300001,
5.1127604523,
5.0452625723,
5.0544525143,
5.0776091493,
5.1019007403000005,
5.0609102393,
5.0526101393000005,
5.3976340363
]
},
{
"command": "pnpr@main",
"mean": 5.1302138009,
"stddev": 0.12646576265401605,
"median": 5.090505605300001,
"user": 2.2755872399999997,
"system": 2.84102492,
"min": 5.0458204313,
"max": 5.4500411533,
"times": [
5.0532381723,
5.0480072233,
5.0730240663,
5.0458204313,
5.1115122513,
5.1225208283,
5.2378288853,
5.4500411533,
5.1079871443,
5.0521578533
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.40856376824,
"stddev": 0.022025648761858682,
"median": 1.41142602684,
"user": 1.6050851800000003,
"system": 1.7553874999999999,
"min": 1.36523586784,
"max": 1.44820557784,
"times": [
1.42411936784,
1.41471889584,
1.41873968184,
1.41048348584,
1.4026996438400001,
1.44820557784,
1.40115573184,
1.41236856784,
1.36523586784,
1.38791086184
]
},
{
"command": "pacquet@main",
"mean": 1.3738036131400002,
"stddev": 0.007806097106627103,
"median": 1.37530083934,
"user": 1.5338213799999998,
"system": 1.7620035,
"min": 1.36269107984,
"max": 1.38511227484,
"times": [
1.38511227484,
1.37526021084,
1.36338126784,
1.36269107984,
1.3770274468400001,
1.38027648384,
1.37202102784,
1.38146957684,
1.36545529484,
1.37534146784
]
},
{
"command": "pnpr@HEAD",
"mean": 0.67711073514,
"stddev": 0.009095088254549764,
"median": 0.6746231713399999,
"user": 0.34612268,
"system": 1.2905897,
"min": 0.66678328884,
"max": 0.7002554708400001,
"times": [
0.7002554708400001,
0.6774577968400001,
0.67783144384,
0.6735115418400001,
0.6818622588400001,
0.67366886484,
0.67557747784,
0.6728404518400001,
0.67131875584,
0.66678328884
]
},
{
"command": "pnpr@main",
"mean": 0.67478137194,
"stddev": 0.017466607436591314,
"median": 0.67003145334,
"user": 0.33475007999999995,
"system": 1.2696439,
"min": 0.65775206084,
"max": 0.7153379588400001,
"times": [
0.68701316384,
0.68105901684,
0.67004886884,
0.7153379588400001,
0.6588741678400001,
0.65775206084,
0.65827547284,
0.66965657784,
0.67978239384,
0.67001403784
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 5.0020508989,
"stddev": 0.01914691217865675,
"median": 5.003364528700001,
"user": 1.7861380400000002,
"system": 1.9010909599999999,
"min": 4.9783028407,
"max": 5.0270478047,
"times": [
5.0183527867,
4.9997946847,
4.9824467407,
4.9783028407,
5.0235762057,
4.9857811657,
5.0069343727,
4.9806081717,
5.0176642157,
5.0270478047
]
},
{
"command": "pacquet@main",
"mean": 4.9852695889000005,
"stddev": 0.020694617136304695,
"median": 4.987522306200001,
"user": 1.7703771400000001,
"system": 1.8883508599999996,
"min": 4.9437941517,
"max": 5.0111509647,
"times": [
4.9961476337,
4.9437941517,
4.9776931717,
5.0111509647,
4.9788545957,
4.9818541107000005,
4.9628946527,
5.0022387667,
5.0048773397,
4.9931905017
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7174659067,
"stddev": 0.07934262420780244,
"median": 0.6901247437,
"user": 0.35191363999999997,
"system": 1.30041496,
"min": 0.6641326697,
"max": 0.9337536837,
"times": [
0.7486656707,
0.6873900907,
0.9337536837,
0.7009255727,
0.6858668947000001,
0.6814154267,
0.7048114197,
0.6641326697,
0.6748382417000001,
0.6928593967
]
},
{
"command": "pnpr@main",
"mean": 0.7066373518000001,
"stddev": 0.0525167253624754,
"median": 0.6829330612,
"user": 0.33574724,
"system": 1.27428916,
"min": 0.6699019347,
"max": 0.8450006827000001,
"times": [
0.7126766987,
0.8450006827000001,
0.7373419847,
0.6944976127,
0.6811593527,
0.6841495977000001,
0.6787847277,
0.6817165247,
0.6699019347,
0.6811444017
]
}
]
} |
|
| Branch | pr/12315 |
| 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 | 9,236.19 ms(+7.41%)Baseline: 8,599.00 ms | 10,318.80 ms (89.51%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 5,002.05 ms(-0.40%)Baseline: 5,022.30 ms | 6,026.76 ms (83.00%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,408.56 ms(-0.97%)Baseline: 1,422.37 ms | 1,706.85 ms (82.52%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 10,250.25 ms(+2.04%)Baseline: 10,045.52 ms | 12,054.63 ms (85.03%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 648.20 ms(-0.82%)Baseline: 653.59 ms | 784.31 ms (82.65%) |
|
| Branch | pr/12315 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 5,108.41 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 717.47 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 677.11 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 5,393.51 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 777.15 ms |
|
Code review by qodo was updated up to the latest commit 21d09e0 |
Micro-Benchmark ResultsLinux |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12315 +/- ##
==========================================
- Coverage 87.70% 87.65% -0.06%
==========================================
Files 288 288
Lines 35177 35475 +298
==========================================
+ Hits 30852 31094 +242
- Misses 4325 4381 +56 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…o-end Add two regression tests that install for real against the mock registry, then drop the registry, wipe the packument/verdict cache, and point the repeat install at a dead port — the exact state the vlt.sh benchmark prepare step produces. Any future change that lets a touched-but-unchanged manifest or a deleted pnpm-lock.yaml fall off the fast path (into resolution or the lockfile-verification fan-out) now fails the install instead of silently regressing the benchmark: - a manifest rewritten with identical content (newer mtime) must short-circuit with "Already up to date" and zero pipeline events; - a deleted pnpm-lock.yaml with intact node_modules must short-circuit via the current-as-wanted fallback and restore pnpm-lock.yaml byte-identically. Both verified discriminating: disabling the content check makes both fail on the dead registry.
|
Code review by qodo was updated up to the latest commit e16083a |
…sing Port of the same optimization that landed on the pacquet side in this branch. When `pnpm-lock.yaml` is absent but `node_modules` is intact, `checkDepsStatus` lets the current lockfile (`node_modules/.pnpm/lock.yaml`) — the record of what the previous install materialized — stand in as the wanted lockfile for the up-to-date checks (settings drift, `satisfiesPackageManifest`, `linkedPackagesAreUpToDate`), and returns it so `installDeps` can restore `pnpm-lock.yaml` from it before reporting "Already up to date". Previously this scenario threw `RUN_CHECK_DEPS_LOCKFILE_NOT_FOUND` (single project) or failed the wanted-lockfile stat (workspace), forcing a full resolution plus a re-verification of every locked package against the registry — in the benchmarks.vlt.sh `node_modules` variations that cost pnpm 2.2 s (astro) to 11.6 s (babylon) per run. Single projects with no lockfile on either side still refuse the fast path, `useLockfile: false` skips the restore, a failed restore falls through to the full install, and the stand-in is disabled under `useGitBranchLockfile` (there a missing `pnpm-lock.yaml` is the steady state and the branch lockfile may legitimately differ from the current one).
|
Code review by qodo was updated up to the latest commit a7bbf85 |
Integrate the 9 commits main gained (#12271, #12294, #12301, #12303, #12305, #12312, #12315, #12316, and the release/version bumps). Conflict resolution: all four conflicts (record_lockfile_verified, build_modules, hoisted_dep_graph, install) were between this branch's lint edits and main's feature changes — took main's authoritative versions; lint compliance is re-derived by re-running clippy in the follow-up commit.
Why
On benchmarks.vlt.sh (2026-06-10 run, pacquet 0.11.2), pacquet ranked 8th–9th of 10 in every
lockfile+node_modulesvariation — slower than pnpm, npm, yarn and vlt — e.g. astro: pacquet 936 ms vs pnpm 502 ms; babylon: pacquet 9.08 s vs pnpm 0.85 s. It also trailed vlt/npm in thenode_modulesandcache+node_modulesvariations (astro 1.5 s / 0.7 s, babylon 8.9 s / 6.4 s).Root cause
Tracing the actual runner (a
pacquetPATH shim logging per-invocation file stats) showed the harness's prepare step rewritespackage.jsonwith identical content but a fresh mtime before every timed run, whileclean_all_cachewipes~/.cache/pnpm(the packument cache andlockfile-verified.jsonl), and thenode_modulesvariations additionally deletepnpm-lock.yaml.checkDepsStatus's modified-manifests branch re-checks the content against the lockfile (assertWantedLockfileUpToDate,assertLockfilesEqual,linkedPackagesAreUpToDate) and reports "Already up to date" with zero network — ~0.5 s is just Node startup. Verified locally: with all caches wiped and the network blocked,pnpm installstill prints "Already up to date" in 228 ms.minimumReleaseAgelockfile-verification gate — its verdict cache wiped — re-fetched one packument per locked package per run: 0.94 s on astro, 9.1 s on babylon.pnpm-lock.yamldeleted, both stacks pay a similar fan-out on the synthesized-from-current lockfile (tryLockfileVerificationCachebails before the content-hash index when the lockfile file can't be stat'd), which is why even pnpm needs 2.2–11.6 s there.What
Commit 1 — port the modified-manifests branch of
checkDepsStatus(at cc4ff817aa) intooptimistic_repeat_install:lastValidatedTimestampis re-checked against the wanted lockfile instead of invalidating the fast path: lockfile-settings drift (getOutdatedLockfileSetting), per-importersatisfiesPackageManifest, and a port oflinkedPackagesAreUpToDatefor workspace links (isLocalFileDepUpdatedforfile:directory specifiers is not ported — those conservatively fall through to the full install);assertLockfilesEqualruns when the wanted lockfile is newer than the reference (workspace:lastValidatedTimestamp; single-project: the current lockfile's mtime, mirroring upstream's branch shapes);lastValidatedTimestampafter a passing content check, like upstream'supdateWorkspaceStatecall;parse_config_overrides,check_lockfile_settings_drift,check_importer_satisfies) shared with the new check, and the per-importer slice is no longer hard-wired to the root importer.Commit 2 — treat the current lockfile as the wanted one when
pnpm-lock.yamlis missing (pacquet) (requested by @zkochan): whennode_modulesis intact,<virtual_store_dir>/lock.yaml— the record of what the previous install materialized — stands in as the wanted lockfile for the same content checks, andpnpm-lock.yamlis regenerated from it (byte-identical to what the full install's synthesize-from-current path would write) before the fast path reports "Already up to date". Single-project installs with no lockfile on either side still refuse the fast path;lockfile: falseskips the regeneration; a manifest that no longer matches (e.g.pacquet add) still takes the full resolve.Validation
Re-ran the actual vlt.sh harness (same scripts, ubuntu-24.04-arm runner) with the patched binary swapped into the npm-installed pacquet; all hyperfine runs exited 0:
lockfile+node_moduleslockfile+node_modulesnode_modulescache+node_modulesnode_modulesAfter this change only aube (~5 ms) and bun (~8 ms) stay ahead in these five variations.
cargo nextest run -p pacquet-package-manager(438 tests),-p pacquet-cliinstall suites, workspace clippy-D warnings, dylint, fmt, taplo andtypos pacquetare clean. New tests cover the touched-but-identical manifest, a manifest that adds a dependency, a diverged wanted-vs-current lockfile, the state-timestamp refresh, linked siblings inside/outside the manifest range, lockfile regeneration (modified and unmodified manifests, workspace state bump), andlockfile: false. Two offline e2e tests additionally pin the "zero network, zero pipeline" property throughInstall::run's real dispatch: a real install, registry dropped, caches wiped, repeat install pointed at a dead port — both verified discriminating by temporarily disabling the content check.Two existing tests were adjusted:
fresh_install_records_lockfile_verification_for_mtime_bypassed_noopnow disables the optimistic check explicitly so it keeps guarding the verification-cache wiring it was written for, andoptimistic_repeat_install_does_not_short_circuit_when_lockfile_missingnow passeslockfile: None(matching the CLI contract for a missing file) and documents that the guard requires both lockfiles to be absent.Commit
1ee88c5107— the same fallback in the pnpm CLI (@pnpm/deps.status+@pnpm/installing.commands):checkDepsStatuslets the current lockfile stand in whenpnpm-lock.yamlis missing (workspace shared-lockfile branch and single-project branch), runs the same content checks against it, and returns it aswantedLockfileToRestore;installDepswritespnpm-lock.yamlback from it before reporting "Already up to date". Guard rails: no lockfile on either side still refuses the fast path,useLockfile: falseskips the restore, a failed restore falls through to the full install, and the stand-in is disabled underuseGitBranchLockfile(there a missing plainpnpm-lock.yamlis the steady state and the branch lockfile may legitimately differ from the current one). Verified with the bundled CLI: install → deletepnpm-lock.yaml→pnpm install --registry=http://127.0.0.1:9/prints "Already up to date" in 29 ms and restores the lockfile byte-identically. Covered by 5 newcheckDepsStatusunit tests and aninstalling/commandsintegration test that runs the repeat install against a dead registry. Changeset bumps@pnpm/deps.status,@pnpm/installing.commands, andpnpm(minor).Not addressed here (follow-ups from the same investigation)
cleanbabylon: pacquet 34.3 s vs pnpm 30.1 s on the runner. Locally the cold install is syscall-bound (open/close churn during store-write + link; no duplicate downloads — 1 669 unique tarballs fetched exactly once; lockfile graph matches pnpm's 1 657±). Needs a Linux-side profile.Written by an agent (Claude Code, claude-fable-5).
Summary by CodeRabbit
New Features
Refactor
Tests
Chore