Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
This repository was archived by the owner on May 14, 2026. It is now read-only.

Implement pacquet store prune for the global virtual store (#432 follow-up) #458

@zkochan

Description

@zkochan

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 pruneGlobalVirtualStore and expose it via a pacquet store prune subcommand.

Upstream's mark-and-sweep:

  1. 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.
  2. 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.
  3. 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

  1. Port getRegisteredProjects (with the stale-entry cleanup) into pacquet_store_dir::project_registry.
  2. Add pacquet_store_dir::prune_global_virtual_store(store_dir) that runs the mark-and-sweep.
  3. Add a pacquet store prune subcommand (mirror pnpm store prune) that calls it.
  4. 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.

References


Written by an agent (Claude Code, claude-opus-4-7).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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