Problem
With enableGlobalVirtualStore: true set globally, pacquet install (no flag, fresh project) materializes packages into the project-local node_modules/.pnpm/ legacy layout instead of the global store under <store_dir>/links/<scope>/<name>/<version>/<hash>/.... The --frozen-lockfile path uses GVS correctly; the fresh-resolve path doesn't.
Repro (with enableGlobalVirtualStore: true in global pnpm config):
mkdir t && cd t
echo '{\"name\":\"t\",\"dependencies\":{\"is-positive\":\"1.0.0\"}}' > package.json
pacquet install
readlink node_modules/is-positive
# .pnpm/is-positive@1.0.0/node_modules/is-positive ← project-local
# Versus, with a lockfile + --frozen-lockfile:
pnpm install --lockfile-only
pacquet install --frozen-lockfile
readlink node_modules/is-positive
# ../../../../<pnpm-home>/store/v11/links/@/is-positive/1.0.0/<sha>/node_modules/is-positive ← global
Root cause
install_without_lockfile.rs hardcodes VirtualStoreLayout::legacy for slot lookup (around line 512). The comment at install.rs:581-585 documents this as deliberate:
InstallWithoutLockfile keeps the project-local virtual store via VirtualStoreLayout::legacy, and a registry entry for it would point at a project that never touches the shared store.
That scoping was defensible when the fresh-resolve path produced no lockfile (nothing to anchor a global-store registry entry against). Once the writable-lockfile path lands (#11813), this constraint goes away: the install will have produced a lockfile, the project will register against the global store, and the existing pacquet_store_dir::register_project call in the frozen-lockfile branch is exactly the right hook to reuse.
Proposed change
- Once the resolver-output →
Lockfile adapter lands (issue 1), switch the fresh-resolve path to use VirtualStoreLayout::gvs(...) when config.enable_global_virtual_store is true, mirroring the existing branch in the frozen path.
- Call
pacquet_store_dir::register_project on the resolved importers — same logic as the frozen-lockfile branch at install.rs:593-610.
- Drop or rewrite the "deliberately project-local" comment at
install.rs:581-585 and the matching one at the top of VirtualStoreLayout::legacy (virtual_store_layout.rs:89-101) so the new contract is documented ("GVS is scoped to install paths that produce a lockfile," not "GVS is scoped to frozen-lockfile installs").
- Add a fresh-install fixture that asserts the GVS symlink shape when
enableGlobalVirtualStore: true.
Dependencies
Blocked by issue #11813 (writable-lockfile adapter). This issue is meaningless without a lockfile being produced — that's the precondition for safely registering against the global store.
Out of scope
- The store-registry pruning logic for projects that switch GVS state between installs. The existing
pacquet store prune plumbing already covers it.
Written by an agent (Claude Code, claude-opus-4-7).
Problem
With
enableGlobalVirtualStore: trueset globally,pacquet install(no flag, fresh project) materializes packages into the project-localnode_modules/.pnpm/legacy layout instead of the global store under<store_dir>/links/<scope>/<name>/<version>/<hash>/.... The--frozen-lockfilepath uses GVS correctly; the fresh-resolve path doesn't.Repro (with
enableGlobalVirtualStore: truein global pnpm config):Root cause
install_without_lockfile.rshardcodesVirtualStoreLayout::legacyfor slot lookup (around line 512). The comment atinstall.rs:581-585documents this as deliberate:That scoping was defensible when the fresh-resolve path produced no lockfile (nothing to anchor a global-store registry entry against). Once the writable-lockfile path lands (#11813), this constraint goes away: the install will have produced a lockfile, the project will register against the global store, and the existing
pacquet_store_dir::register_projectcall in the frozen-lockfile branch is exactly the right hook to reuse.Proposed change
Lockfileadapter lands (issue 1), switch the fresh-resolve path to useVirtualStoreLayout::gvs(...)whenconfig.enable_global_virtual_storeis true, mirroring the existing branch in the frozen path.pacquet_store_dir::register_projecton the resolved importers — same logic as the frozen-lockfile branch atinstall.rs:593-610.install.rs:581-585and the matching one at the top ofVirtualStoreLayout::legacy(virtual_store_layout.rs:89-101) so the new contract is documented ("GVS is scoped to install paths that produce a lockfile," not "GVS is scoped to frozen-lockfile installs").enableGlobalVirtualStore: true.Dependencies
Blocked by issue #11813 (writable-lockfile adapter). This issue is meaningless without a lockfile being produced — that's the precondition for safely registering against the global store.
Out of scope
pacquet store pruneplumbing already covers it.Written by an agent (Claude Code, claude-opus-4-7).