Repository navigation
fix(cache): leave noisy fields of pnpm's .modules.yaml out of its fingerprint - #823
Merged
Merged
Conversation
wan9chi
added this pull request to stack #826
October 9, 2026 06:06
wan9chi
force-pushed
the
claude/modules-yaml-issues-604e1f
branch
from
October 9, 2026 06:08
3713d39 to
75c9721
Compare
fspy benchmarklinuxmacoswindows |
wan9chi
force-pushed
the
claude/modules-yaml-issues-604e1f
branch
2 times, most recently
from
October 12, 2026 05:25
7c05e67 to
341fdda
Compare
…gerprint pnpm rewrites `node_modules/.modules.yaml` with a new `prunedAt` on fresh installs, and records machine-specific paths in it. Vite reads it on every `resolveConfig`, so cached `vite build` and `vitest` tasks missed after every reinstall and never hit across machines. The manifest is now parsed (JSON, falling back to YAML for pnpm < 10.29) and hashed as canonical JSON without `prunedAt`, `storeDir`, `virtualStoreDir`, and `linkedSkills`. Content in neither format is hashed as-is. Redactions live in `session/execute/redact/`, one module per file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wan9chi
force-pushed
the
claude/modules-yaml-issues-604e1f
branch
from
October 12, 2026 05:33
341fdda to
6ee27ab
Compare
wan9chi
added a commit
that referenced
this pull request
Oct 13, 2026
#850) ## Motivation #823 hashes pnpm's `node_modules/.modules.yaml` without `prunedAt`, `storeDir`, `virtualStoreDir` and `linkedSkills`. What's left protects little and still causes needless misses: - Every cached `vite build` and `vitest` task reads the file because Vite reads it while resolving its config ([`packages/vite/src/node/server/index.ts`](https://github.com/vitejs/vite/blob/dce9fe69d37e957b62f3b5df61a761f7f2f949f5/packages/vite/src/node/server/index.ts#L1332-L1361)). Vite reads only `virtualStoreDir`, to add pnpm's global virtual store to the dev server's `server.fs.allow`, inside a try/catch for non-pnpm projects. `virtualStoreDir` is already left out of the hash, so the hash protects nothing Vite uses, and for `vite build` the file doesn't affect the output. - The other fields describe `node_modules`, which tasks already read. In pnpm 11 they are `hoistedDependencies`, `hoistPattern`, `publicHoistPattern`, `hoistedLocations`, `nodeLinker`, `included`, `skipped`, `injectedDeps`, `layoutVersion`, `virtualStoreDirMaxLength`, `pendingBuilds`, `ignoredBuilds`, `allowBuilds`, `packageManager` and `virtualStoreOnly`. When they change in a way that matters, the files a task reads in `node_modules` change too, and those reads are tracked. - Some of the hashed fields still vary across machines, so tasks missed with `'node_modules/.modules.yaml' modified`: - `packageManager`: the version of the pnpm that installed - `pendingBuilds`: filled after `pnpm install --ignore-scripts` - `virtualStoreOnly`: set after `pnpm fetch` - `skipped`: differs between glibc and musl for platform packages under the same cache key - The list of fields to leave out has to track pnpm's format. ## Changes - `node_modules/.modules.yaml` is now fingerprinted only by whether it exists, the same treatment #824 gives files in `node_modules/.bin`. A missing manifest still differs from an existing one, since tools detect pnpm by the file existing (Vite's try/catch, which-pm). - Trade-off: a task whose output depends on these fields without any file it reads in `node_modules` differing no longer misses. The examples #823 named are LavaMoat's allow-scripts check (`pendingBuilds`/`ignoredBuilds`), which-pm's pnpm version, and Nx's `hoistedDependencies`. These rarely run inside cached vp tasks, and in realistic cases `node_modules` differs too. - The JSON/YAML parsing, the canonical JSON writer and the fallback for unparsable content are removed, along with `vt`'s `serde_norway` dependency. Both files are hashed as empty content without being read, which is the hash files in `node_modules/.bin` already had, so their cache entries stay valid. - The `pnpm_modules_manifest` e2e fixture now shows that changing the manifest's content keeps the cache hit and removing it misses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Motivation
pnpm's
node_modules/.modules.yamlis read by every cachedvite buildandvitesttask, because Vite'sresolveConfigreads it to extend the dev server'sfs.allow(vite-plus#2845). pnpm rewritesprunedAton fresh installs and records machine-specific paths, so those tasks missed the cache after every reinstall with'node_modules/.modules.yaml' modified, and could never hit across machines.Changes
prunedAt: when pnpm last pruned the virtual storestoreDir: absolute path of pnpm's storevirtualStoreDir: absolute on Windows; inside the store with pnpm's global virtual storelinkedSkills: absolute for skills outside the workspacehoistedDependencies, LavaMoat readspendingBuilds/ignoredBuilds, and which-pm readspackageManager. Whether the file exists is fingerprinted as before.io::Writeadapter, so no intermediate string is built.std::hash::Hashisn't used because its output isn't stable across platforms or Rust versions.session/execute/redact/, one module per file, each with amatchescondition and aredactaction called fromhash_file_content.🤖 Generated with Claude Code