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:
- 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.
- 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:
- import the package into the slot at the newly computed (built) hash,
- run the build scripts there,
- 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
runUnignoredDependencyBuilds → buildSelectedPkgs path.
Written by an agent (Claude Code, claude-fable-5).
Background
Under the global virtual store, a package's projection lives at
<globalVirtualStoreDir>/<hash>/node_modules/<name>, and the hash includes theallowBuildsverdict: an unbuilt package gets an engine-agnostic, maximally shared slot, while a built package gets an engine- and deps-partitioned slot (seeslot_dir_engine_agnostic_with_empty_allow_build_policy/slot_dir_engine_specific_when_snapshot_is_builtin pacquet'svirtual_store_layouttests).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
_rebuildinbuilding/after-installhandles this with a fallback: when the recomputed slot doesn't exist on disk,findLinkedGvsDir()follows the project'snode_modulessymlink 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:
pnpm rebuildvs. the in-installrunUnignoredDependencyBuildspath).Proposal
Make
pnpm rebuildunder the global virtual store relocate the projection instead of building in place:node_modulessymlinks to the new slot.This is what the approve-builds flow already does when
enableGlobalVirtualStoreis on (it routes through a frozen install rather thanrebuild.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
VirtualStoreLayout::slot_dir), so when pacquet grows a rebuild command the same design should apply.runUnignoredDependencyBuilds→buildSelectedPkgspath.Written by an agent (Claude Code, claude-fable-5).