Skip to content

pacquet: honor enableGlobalVirtualStore in the fresh-resolve install path #11814

Description

@zkochan

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

  1. 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.
  2. Call pacquet_store_dir::register_project on the resolved importers — same logic as the frozen-lockfile branch at install.rs:593-610.
  3. 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").
  4. 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).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions