Fix pnpm modulesDir store being skipped (#661, #696) - #698
Merged
Mikola Lysenko (mikolalysenko) merged 3 commits intoOct 5, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 3, 2026
pnpm 10.12+ with `modulesDir` set installs into `<modulesDir>/.pnpm` and leaves no node_modules, so agent apply skipped every package as "not installed" and exited 0 unpatched, and hosted vex attested not_affected over the unpatched install. The crawler now treats the configured modulesDir (pnpm-workspace.yaml or .npmrc), or a project dir holding pnpm's .modules.yaml, as an install root. Hosted vex also stops excusing a missing npm package as "nothing installed" when pnpm keeps the installed virtual store outside the project (global virtual store, or a virtualStoreDir that climbs out), since transitive deps there are invisible to the crawler. Fixes #661, #696. Assisted-by: Claude Code:claude-opus-5-5
Adds a real-pnpm leg that installs with `modulesDir: deps` and checks agent apply patches the `deps/.pnpm` store copy, and records the new behavior in CLI_CONTRACT.md and the CHANGELOG. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 3, 2026 14:08
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f548ad0. Configure here.
Collaborator
Author
|
[burn-down agent] Ready for review at head
Slack announcement not sent this run (Slack send tool unavailable). Generated by Claude Code |
Tanmay Singla (Tanmay182003)
approved these changes
Oct 5, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-pnpm-modules-dir-crawl
branch
October 5, 2026 11:23
Mikola Lysenko (mikolalysenko)
pushed a commit
that referenced
this pull request
Oct 5, 2026
Resolves conflicts with main's npm alias (#738), first-party link (#634) and pnpm store (#698) changes: - CLI_CONTRACT.md: keep main's hosted row and this PR's agent row. - vex_consumed.rs: drop aliases the installed lookup already found (main), then store-expand only the new ones (this PR), so already expanded copies are not scanned again. - Tests: keep both sides' new multicopy and e2e_vex regressions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174mrknEY9ge42c94RNRRBx
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #661
Fixes #696
Summary
pnpm projects that set
modulesDir(pnpm 10.12+) are now patched and attested correctly:modulesDir: on pnpm 10.12+ every installed package is "not installed", and apply exits 0 leaving it unpatched #661: agentapplypatches the copy pnpm installed under<modulesDir>/.pnpm. Before, it exited 0 with the package unpatched as "not installed".modulesDiris set (pnpm 10.12+), because the missed install is treated as "nothing installed" #696: hosted standalonevexhash-checks that copy and omits an unpatched one. Before, it attestednot_affectedfrom the lock pin.modulesDiris set (pnpm 10.12+), because the missed install is treated as "nothing installed" #696, follow-up variants from the issue comment: hostedvexno longer attests a pinned npm package from the lock when the installed pnpm tree keeps its virtual store outside the project (enableGlobalVirtualStore, or avirtualStoreDirthat climbs out). A transitive dep there is invisible to the crawler, so "not found" doesn't mean "not installed".Root cause
pnpm's
modulesDir(modulesDir:inpnpm-workspace.yaml,modules-dir=in.npmrc) renamesnode_modules. From pnpm 10.12 the virtual store moves with it. The npm crawler only collects directories literally namednode_modules, plus the roots inconfigured_install_roots(yarn--modules-folder, Rush). It never readmodulesDir. The miss surfaced as the lockfile-only "not installed" skip in agent mode (#661), and the hostedlockfile_basisexemption turned it into an attestation invex(#696). The same exemption also excused every other crawler blind spot, such as pnpm stores outside the project.Changes
crawlers/npm_crawler.rsconfigured_install_rootsnow includes pnpm's modules dirs:modulesDir: nearestpnpm-workspace.yamlfirst, else the nearest.npmrcmodules-dir. It is resolved per project, like pnpm does, and honored only strictly inside the project (same guard as yarn's modules folder)..modules.yamlinstall record. This catches amodulesDirthat came from pnpm's global config or the environment.pnpm_store_outside_project: true when a.modules.yamlinnode_modulesor a pnpm modules dir records avirtualStoreDiroutside the project.commands/vex.rs: the hostedlockfile_basisexemption no longer excusespackage_not_foundforpkg:npm/purls whenpnpm_store_outside_projectholds. With nothing installed (no.modules.yaml) the lock basis still attests, as before.CLI_CONTRACT.md(manifest-less VEX hosted row) andCHANGELOG.mddocument the new behavior.npm/,pypi/andgem/only dispatch to the binary.Test evidence
Red → green: each new test was run with the source fix reverted (tests kept) and failed, then passed with the fix.
test_pnpm_modules_dir_is_a_crawl_root(workspace yaml, quoted/commented yaml key,.npmrc,.modules.yamlprobe)test_pnpm_modules_dir_setting_resolution(workspace member, yaml beats.npmrc, out-of-project values refused)in_process_alternate_installers::pnpm_modules_dir_install_is_patched(yaml and.npmrc)in_process_alternate_installers::pnpm_modules_dir_install_then_apply_patches_file(realpnpm install; skips if pnpm < 10.12)e2e_vex_redirect::pnpm_modules_dir_install_is_hash_verified_not_lockfile_attestedhash_mismatch, exit 1; patched copy attests; nothing installed atteststest_pnpm_store_outside_projecte2e_vex_redirect::pnpm_store_outside_project_is_not_lockfile_attested(GVS-style outside store, in-project store control, nothing-installed control)Local runs on Linux, Rust 1.93.1:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt: the changed files are fmt-clean.mainitself isn't fmt-clean under this toolchain and CI doesn't run fmt, so I left unrelated files untouched.cargo test --workspace --all-features --no-fail-fast: 9731 passed, 12 failed, 253 ignored. All 12 failures are write-failure / unremovable-file simulations that depend onchmoddenying writes. This sandbox runs as root (uid 0), which bypasses that. The 4 core-lib ones (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched,pypi_requirements::wire_failure_rolls_back_already_written_files) fail identically onorigin/main. The others are incovgap_commands_vendor,in_process_redirectandrepair, none of which touch the npm crawler or the vex exemption. CI runs non-root.Follow-ups / not covered
🤖 Generated with Claude Code
https://claude.ai/code/session_01JKwbXnQ94oRf1r94V1yupy
Note
Medium Risk
Changes npm discovery and hosted VEX attestation for pnpm layouts; incorrect handling could miss patches or over-attest, but behavior is narrowly scoped and heavily regression-tested.
Overview
Fixes pnpm
modulesDirlayouts (pnpm 10.12+ puts the virtual store under<modulesDir>/.pnpmwith nonode_modules). The npm crawler now treats configuredmodulesDir(pnpm-workspace.yaml/.npmrc) and child dirs with.modules.yamlas install roots, so agentapplypatches those store copies instead of treating them as not installed (#661).Hosted
vexhash-checks installs under that layout. It also stops using lockfile-only attestation forpkg:npm/when.modules.yamlrecords avirtualStoreDiroutside the project (global virtual store or an escaping path), because transitive deps there are invisible to the crawler (#696). With nothing installed, lock-pin attestation is unchanged.CLI_CONTRACT.mdandCHANGELOG.mddocument the behavior; new unit and e2e tests cover crawl roots, setting resolution, apply, and vex outcomes.Reviewed by Cursor Bugbot for commit f548ad0. Configure here.
Generated by Claude Code