feat(cli): pin the pnpm store directory for the lockfile prune [RED-931] [ship] - #1474
Merged
Merged
Conversation
pnpm chooses its content-addressable store per project directory: the home-directory store when the directory can hard-link into it, otherwise an empty store at the root of the directory's mount. The lockfile pruner runs its lockfile-only install in an OS temp dir, so on a temp dir that sits on another filesystem than the home directory (tmpfs /tmp, a TMPDIR on another volume, containers) the re-resolution ran against an empty store: thousands of registry requests that time out, or, served by --prefer-offline from a stale metadata cache, versions the subset verification rejects. The pruner now runs `pnpm store path` in the workspace root (a new PackageManager.storeDirCommand), validates the printed versioned path and pins its parent on the install via --config.storeDir. The parent, not the path: pnpm appends its own version segment unless the path already ends in it, and the lookup and the install can resolve to different pnpm majors through corepack, mise or volta. An unusable answer is a notable skip. Probe time is charged to the prune budget through a PruneBudget shared with the yarn version probe, and the Windows missing-executable classification is shared between probes and the install. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the subset check [RED-931] When the regenerated lockfile resolves entries the original does not have, the warning used to say only that; the entries themselves were nowhere to be seen, which is what made an empty-store prune (a temp dir on another filesystem, see the previous commit) hard to diagnose. The reason now lists the unexpected entries by a short per-format name (pnpm `name@version` with peer suffixes stripped so the packages and snapshots records of one package count once, npm path plus version, bun the tuple's name, yarn the entry's resolution), each redacted on its own since tarball and git entries are URLs, capped with the usual `and N more`; the complete list goes to the debug channel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rence [RED-931] Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion holds on Windows [RED-931] The pruner renders the failing command through shellQuote, which wraps an argument containing a backslash in quotes; a Windows temp path in the stub's trailing store argument therefore no longer matched the expected bare form. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…CRLF checkouts [RED-931] The stub install script anchored its injection on LF newlines, which never match the fixture on a Windows checkout with CRLF; the injection silently no-oped and the prune succeeded. The shared rewrite helper with newline-free anchors fails loudly when an anchor is absent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sorccu
deleted the
simo/red-931-pin-pnpm-store-dir-for-lockfile-prune
branch
September 4, 2026 19:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear: RED-931
Problem
The lockfile pruner runs
pnpm install --lockfile-onlyin a directory under the OS temp dir. pnpm picks its content-addressable store per project directory: the home-directory store when the directory can hard-link into it, otherwise an empty store at the root of the directory's mount. When the temp dir sits on a different filesystem than the home directory (tmpfs/tmpas in WSL2,TMPDIRon another volume, containers), the prune re-resolves against an empty store. Every package then needs registry metadata: without--prefer-offlinethe prune makes thousands of requests and times out; with it (#1471), a stale metadata cache can resolve a different version, which the subset check rejects, so the prune falls back either way.Changes
PackageManager.storeDirCommand()(pnpm store pathfor pnpm, undefined for npm, yarn and bun, whose caches do not depend on the project location) runs in the workspace root, where it reads the workspace's own config and applies pnpm's same-mount rule to the real project dir. The pruner validates the printed versioned path (<storeDir>/v10) and passes its parent to the install as--config.storeDir=. The parent, not the path: pnpm appends its own version segment unless the path already ends in it, and the lookup and the install can resolve to different pnpm majors (corepack, mise, volta pick per directory), so pnpm 11 handed a…/v10path would use…/v10/v11, an empty store again. Verified on pnpm 10.33.4 and 11.22.0; the setting is not recorded in the lockfile'ssettingssection.pnpmis reported as such on every platform (the Windows PATH classification is now shared between probes and the install).PruneBudgetthat also replaces the inline budget logic of the yarn version probe, so the documented timeout still bounds the whole prune.packagesandsnapshots), each redacted, with the full list at debug level. Previously the warning only said that entries were unexpected.Tests
storeDirplanted in the workspace'spnpm-workspace.yamlversus one materialized into the prune dir; the pinned run creates the former and not the latter, and the unpinned control the reverse. Passes with pnpm 10 and pnpm 11 on PATH.🤖 Generated with Claude Code