Skip to content

rebuild: relocate global virtual store projections instead of building in place #12302

Description

@zkochan

Background

Under the global virtual store, a package's projection lives at <globalVirtualStoreDir>/<hash>/node_modules/<name>, and the hash includes the allowBuilds verdict: an unbuilt package gets an engine-agnostic, maximally shared slot, while a built package gets an engine- and deps-partitioned slot (see slot_dir_engine_agnostic_with_empty_allow_build_policy / slot_dir_engine_specific_when_snapshot_is_built in pacquet's virtual_store_layout tests).

Since #12294, a policy divergence between install time and rebuild time is a routine flow: an install ignores a build (no approval yet), creating the projection under the not-built hash; the user then grants the approval (for git/tarball artifacts, by depPath key) and runs pnpm rebuild. The rebuild recomputes the built hash, which points at a directory the install never created.

Current behavior

_rebuild in building/after-install handles this with a fallback: when the recomputed slot doesn't exist on disk, findLinkedGvsDir() follows the project's node_modules symlink into the GVS and runs the build in place, inside the not-built-hash projection. This is pinned by the test 'rebuilds in the global virtual store when the approval was granted after the install'.

This works, but it has two downsides:

  1. The build mutates a projection that sits at a not-built hash — the slot's address no longer reflects how its contents were produced, so the engine/deps partitioning that built slots are supposed to get doesn't apply to it.
  2. The fallback resolves the directory heuristically (via the project symlink) rather than by construction. It can't be replaced with an exact recomputation because rebuild runs in two flows where "the policy that created the current slots" differs (standalone pnpm rebuild vs. the in-install runUnignoredDependencyBuilds path).

Proposal

Make pnpm rebuild under the global virtual store relocate the projection instead of building in place:

  1. import the package into the slot at the newly computed (built) hash,
  2. run the build scripts there,
  3. relink the project node_modules symlinks to the new slot.

This is what the approve-builds flow already does when enableGlobalVirtualStore is on (it routes through a frozen install rather than rebuild.handler). With relocation, the install/rebuild hash divergence window disappears, findLinkedGvsDir() and its existence-check fallback can be deleted, and built projections always live at their built hash with proper engine/deps partitioning.

Notes

  • The pacquet side computes slot dirs with the same policy-dependent hash (VirtualStoreLayout::slot_dir), so when pacquet grows a rebuild command the same design should apply.
  • The relocation should also cover the in-install runUnignoredDependencyBuildsbuildSelectedPkgs path.

Written by an agent (Claude Code, claude-fable-5).

Metadata

Metadata

Assignees

No one assigned

    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