You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 14, 2026. It is now read-only.
#432 landed Stage 1 of the global virtual store. Frozen-lockfile installs now write package contents to <store_dir>/links/<scope>/<name>/<version>/<hash>/node_modules/<name> and register each importer at <store_dir>/projects/<short-hash> → project root. Without a prune sweep, both directories grow unbounded — old slots and dead-project entries accumulate forever.
The "Out of scope" section of #432 flagged this as a dedicated follow-up.
Get registered projects: list <store_dir>/projects/, resolve each symlink target, drop entries whose target is gone. Upstream's getRegisteredProjects already has the stale-entry cleanup; pacquet only ships register_project from that file today (see crates/store-dir/src/project_registry.rs), so the read side needs porting too.
Mark phase: for each surviving project, find every node_modules/ directory (root + workspace packages), walk every symlink that lands under <store_dir>/links/..., record the resolved slot path in a reachable set.
Sweep phase: walk <store_dir>/links/<scope>/<name>/<version>/, delete any <hash> subdirectory not in the reachable set. Empty <version>/ and <name>/ parents get cleaned up in a second pass.
Today
<store_dir>/links/... keeps growing as projects upgrade dependency versions.
<store_dir>/projects/<hash> symlinks linger for projects the user has deleted.
No CLI surface for either cleanup.
Plan
Port getRegisteredProjects (with the stale-entry cleanup) into pacquet_store_dir::project_registry.
Add pacquet_store_dir::prune_global_virtual_store(store_dir) that runs the mark-and-sweep.
Add a pacquet store prune subcommand (mirror pnpm store prune) that calls it.
Tests:
End-to-end: register two projects, delete one project's dir on disk, run prune, assert the dead project's registry entry is gone and any slots only it referenced are gone.
Mark phase: workspace with two node_modules/ dirs that both reference the same slot — slot is kept after either project is removed.
Sweep phase: orphan slot under <store_dir>/links/... with no project reference gets removed.
Out of scope
Pacquet's per-store-version GC (the legacy non-GVS <store>/v11/files/ shards). That's a different cleanup with different lifetime semantics.
Background
#432 landed Stage 1 of the global virtual store. Frozen-lockfile installs now write package contents to
<store_dir>/links/<scope>/<name>/<version>/<hash>/node_modules/<name>and register each importer at<store_dir>/projects/<short-hash>→ project root. Without a prune sweep, both directories grow unbounded — old slots and dead-project entries accumulate forever.The "Out of scope" section of #432 flagged this as a dedicated follow-up.
What needs to happen
Port upstream's
pruneGlobalVirtualStoreand expose it via apacquet store prunesubcommand.Upstream's mark-and-sweep:
<store_dir>/projects/, resolve each symlink target, drop entries whose target is gone. Upstream'sgetRegisteredProjectsalready has the stale-entry cleanup; pacquet only shipsregister_projectfrom that file today (seecrates/store-dir/src/project_registry.rs), so the read side needs porting too.node_modules/directory (root + workspace packages), walk every symlink that lands under<store_dir>/links/..., record the resolved slot path in areachableset.<store_dir>/links/<scope>/<name>/<version>/, delete any<hash>subdirectory not in thereachableset. Empty<version>/and<name>/parents get cleaned up in a second pass.Today
<store_dir>/links/...keeps growing as projects upgrade dependency versions.<store_dir>/projects/<hash>symlinks linger for projects the user has deleted.Plan
getRegisteredProjects(with the stale-entry cleanup) intopacquet_store_dir::project_registry.pacquet_store_dir::prune_global_virtual_store(store_dir)that runs the mark-and-sweep.pacquet store prunesubcommand (mirrorpnpm store prune) that calls it.node_modules/dirs that both reference the same slot — slot is kept after either project is removed.<store_dir>/links/...with no project reference gets removed.Out of scope
<store>/v11/files/shards). That's a different cleanup with different lifetime semantics.References
pruneGlobalVirtualStoregetRegisteredProjectscleanupcrates/store-dir/src/project_registry.rs(the write half from Add global virtual store support topacquet install --frozen-lockfile#432 / feat: global-virtual-store foundation (#432 partial) #444)pacquet install --frozen-lockfile#432 (Out of scope)Written by an agent (Claude Code, claude-opus-4-7).