Repository navigation
pnpm node-linker=pnp with a modulesDir is refused as yarn Plug'n'Play, because the layout detector ignores the modulesDir the crawler honors #1129
Description
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug (one pnpm layout question answered two ways, proven by execution against a real pnpm install). Source: new finding; register E92 (related to E40 and E48).
Problem
"Where does pnpm keep this project's install?" has two answers:
- The npm crawler honors pnpm's
modulesDir:modulesDir:inpnpm-workspace.yaml, ormodules-dirin.npmrcup to pnpm 10. Seepnpm_modules_dirs/pnpm_modules_dir_setting,`` added by Fix pnpm modulesDir store being skipped (#661, #696) #698 for Agent mode ignores pnpm'smodulesDir: on pnpm 10.12+ every installed package is "not installed", and apply exits 0 leaving it unpatched #661 and Hosted pnpm vex attests not_affected over an unpatched install when pnpm'smodulesDiris set (pnpm 10.12+), because the missed install is treated as "nothing installed" #696. - The layout detector
crawlers/pkg_managers.rshard-codesnode_modules. This affects bothdetect_npm_pkg_managerstep 4 and the pnpm Plug'n'Play carve-outpnpm_pnp_layout_in:``(view.is_file("node_modules/.modules.yaml") || view.is_dir("node_modules/.pnpm")) && view.is_file("pnpm-lock.yaml") && !view.is_file("yarn.lock")
pnpm's node-linker=pnp writes a .pnp.cjs. When modulesDir is also set, the store is <modulesDir>/.pnpm, so the carve-out fails and the tree is classified YarnBerryPnP. Three consumers act on that classification:
- agent
applyrefuses withyarn_pnp_unsupportedand "Useyarn patch <pkg>" (apply.rs#L1332-L1341, andapply --checkat#L604-L612); - the vendored router skips its
vendor_pnpm_pnp_unsupporteddiagnosis and refuses withvendor_yarn_berry_unsupportedand the yarn remedy instead (npm_flavor.rs#L181-L210);`` - VEX reads pnpm's
.pnp.cjsas a yarn loader (YarnPnpLoader::detect).``
Meanwhile the crawler finds every copy under deps/.pnpm, and agent mode could patch them.
Proof, run twice on e2d9633 with a real pnpm 10.28.0 install: package.json depends on left-pad@1.3.0, .npmrc = node-linker=pnp + modules-dir=deps, then pnpm install. The install wrote .pnp.cjs, deps/.modules.yaml, deps/.pnpm/ and deps/left-pad; node_modules/ holds only .pnpm-workspace-state-v1.json.
| Probe (throwaway tests) | modules-dir=deps |
control: same project without modules-dir |
|---|---|---|
pnpm_modules_dirs (crawler roots) |
[<root>/deps, <root>/deps] |
n/a |
detect_npm_pkg_manager |
YarnBerryPnP |
Pnpm |
pnpm_pnp_layout |
false | true |
YarnPnpLoader::detect(..).is_some() |
true | false |
socket-patch apply --json --offline with a one-patch npm manifest |
exit 1, yarn_pnp_unsupported, "packages live inside .yarn/cache zips… Use yarn patch <pkg>" |
n/a |
Smaller detail: the crawler lists the deps root twice.
Symptoms
None filed. #698 fixed the crawler half (#661, #696), and #859 is the same pattern for yarn's pnpmStoreFolder. This is one more place where the package-manager layout is spelled outside the crawler (E40/#855, E48).
Impact: a pnpm project using its Plug'n'Play linker with a custom modulesDir can't use agent mode, gets the wrong refusal and remedy in vendored mode, and has VEX consult the wrong loader. This combination is uncommon. Plain pnpm with modulesDir only loses the pnpm notice, because detect falls through to Npm/Unknown.
Proposed change
- Move the modules-dir resolution (
pnpm_modules_dir_setting,resolve_modules_folderand the YAML unquote) out ofnpm_crawler.rsinto one pnpm layout helper, for examplecrawlers/pnpm_layout.rsorformats::pnpm::workspace. Have it answer "the project's pnpm modules dirs" for both a disk root and aProjectView. detect_npm_pkg_managerstep 4 andpnpm_pnp_layout_inlook for.modules.yaml/.pnpmunder those dirs instead of the literalnode_modules.- The crawler's
pnpm_modules_dirscalls the same helper and deduplicates its roots. - Delete the hard-coded
node_modules/.modules.yaml/node_modules/.pnpmprobes inpkg_managers.rs.
Size and scope
- Files:
crawlers/pkg_managers.rs,crawlers/npm_crawler.rs, possiblyvendor/lock_inventory/view.rs(a memory-view read ofpnpm-workspace.yaml/.npmrc). - About 100–200 changed lines.
- Out of scope:
virtualStoreDirset separately frommodulesDir, and yarn'spnpmStoreFolder(Agent mode misses transitive packages in Yarn's pnpm-linker store whenpnpmStoreFoldermoves it out ofnode_modules: skipped aspackage_not_installed, or refused as "first-party source" #859).
Acceptance criteria
- One helper resolves pnpm's modules dirs, used by the crawler and by
pkg_managers. - Regression test: a
node-linker=pnp+modulesDir: depslayout (.pnp.cjs,pnpm-lock.yaml,deps/.modules.yaml,deps/.pnpm/) is detected asPnpm,YarnPnpLoader::detectreturnsNone, and the vendored router returnsvendor_pnpm_pnp_unsupported. Cover both the.npmrcand thepnpm-workspace.yamlspelling. - CLI regression test:
apply --jsonin that layout is not refused withyarn_pnp_unsupported. -
e2e_safety_yarn_pnp, the Yarn 4 node-modules / pnpm-linker projects migrated from Yarn 2 PnP keep a stale.pnp.js, and socket-patch refuses them as Plug'n'Play: agent and vendored exit 1, hosted warns "npm dependencies were NOT scanned" (regression since 3.3.0) #975 stale-loader tests andtest_pnpm_modules_dir_is_a_crawl_rootstay green.
Dependencies
- Blocked by: nothing.
- Related: Tracking: move vex_consumed's per-ecosystem consumed-copy rules from the CLI into core #855 (per-ecosystem locators in core) and Agent mode misses transitive packages in Yarn's pnpm-linker store when
pnpmStoreFoldermoves it out ofnode_modules: skipped aspackage_not_installed, or refused as "first-party source" #859.
Backlog review — 2026-10-08
Priority: unassigned → P2. Keep the reproduced pnpm PnP/custom-modulesDir layout defect. It blocks agent apply and gives the wrong package-manager diagnosis in a specific supported layout. The report does not demonstrate a successful false attestation; P2 fits the conditional compatibility failure.
Activity
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: the top free candidate, one pnpm modules-dir helper replacing the crawler's private resolver and the hard-coded
node_modulesprobes inpkg_managers.rs). Branch: arch-refactor/1129-pnpm-modules-dir. Claim-ID: 2026-10-09T16:55:52Z-7c3a91Generated by Claude Code
Move pnpm modules-dir lookup to pnpm_layout
Detect pnpm stores in the configured modulesDir
mikolalysenko commentedon Oct 9, 2026
[agent] PR: #1347. It adds one helper,
crawlers::pnpm_layout, used by the crawler,detect_npm_pkg_manager, the PnP carve-out (disk, snapshot and memory views),YarnPnpLoader::detectand the vendored router. Thenode_modules-only probes are deleted. All acceptance criteria are covered, including the CLIapply --jsontest, which shows red on main and green on the branch.Generated by Claude Code
refactor: run 2026-10-09T17:45Z (#1347 for #1129)