Repository navigation
Fix npm store children classified two ways (#1899) - #1986
Conversation
Assisted-by: Claude Code:claude-opus-5-5
apply could patch a package copy that scan never reported. Inside a store entry's own node_modules (for example .pnpm/<entry>/node_modules), the scan never treats a .pnpm, .bun, .deno, .vlt, .store, .modules.yaml or .registry.* child as a store, but the apply/vex resolver did and walked it. Both walks now classify a node_modules child through one StoreKind::classify(name, file_type, in_store_entry), and the resolver passes the store-entry flag the scan already used. An empty .modules.yaml store no longer queues an empty entry list. A regression test covers every store name nested in a store entry. Refs #1899 Assisted-by: Claude Code:claude-opus-5-5
apply's peer-variant fan-out walked up from a patched copy and probed every ancestor node_modules for a store, including a store entry's own node_modules. A peer variant in a nested .pnpm there was patched although scan never reports it. That walk now skips a store entry's node_modules, matching the scan and the resolver. The test oracle for find_by_purls follows the same store-entry rule, and the regression test now also covers .vlt, a .modules.yaml relocated store and the peer fan-out. Review cleanups: the resolver's store listing returns a plain Vec, a misplaced #362 doc comment is back on its own test, and the walk docs point at StoreKind::classify. Refs #1899 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The peer fan-out's nested-store check only recognised a <store>/<entry>/node_modules path. A store nested in a scoped npm .store entry, a pnpm <=3 .registry.* entry or an entry of a store relocated by virtualStoreDir was still probed, so apply could patch a peer copy scan never reports. The fan-out now drops any found store that sits inside another found store or inside a store-named dir. Refs #1899 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Burn-down agent: labeled Ready for review.
Generated by Claude Code |
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Reviewed ce351db6451ed448eb6d153ca0ec9b61d5d300f9. Checked unified store classification, resolver/scan traversal and the repaired scoped/relocated peer-fan-out gate. No major correctness issues found. Validation: source and regression-test review plus the available exact-head CI checks; no local test pass is claimed.
|
Final reviewer: claim released. Generated by Claude Code |
is_inside_another_store matched store names on every ancestor, and the ancestor walk also collects any `.pnpm`/`.bun`/`.deno`/`.vlt` dir above the project into `stores`. A project under `~/.bun` (Bun's global install root), `~/.deno` or `~/.vlt` therefore had its own store dropped, so apply and rollback silently skipped peer-variant copies the scan still reports, the reverse of the #1899 disagreement. A fixed store name now counts only on a `node_modules` child, and the `stores` comparison only counts stores without a fixed name (those relocated by `virtualStoreDir`). A new test covers projects under `.deno`, `.vlt` and `.bun/install/global`; it fails without this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Final review: one fix pushed (712ce24)I found one blocking problem in the review and fixed it in 712ce24. The problem: the peer fan-out dropped the project's own store when a store-named directory sat above the project.
The fix:
What I ran:
I removed bugbot run Generated by Claude Code |
|
bugbot run 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 712ce24. Configure here.
|
Burn-down agent: labeled Ready for review.
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Refs #1899 (first slice: one store classifier and the store-entry rule across all three walks; see "Remaining slices").
Root cause
crawlers/npm_crawler.rsdecided whichnode_moduleschild is a package-manager store (.pnpm/.bun/.deno,.vlt, npm's linked.store,.modules.yaml, pnpm <=3.registry.*) in separate hand-written copies, and they drifted. The scan (gather_node_modules) never treated a store-named child of a store entry's ownnode_modulesas a store. The resolver (nested_node_modules_of, used by apply,apply --checkand vex) and apply's peer-variant fan-out (find_store_peer_variant_copies_reusing) did walk it. Soapplycould resolve and patch a copyscannever reported.Fix
StoreKindenum with oneStoreKind::classify(name, file_type, in_store_entry). The scan and the resolver both classify children through it, and the resolver now getsstore_entryfromvisit_resolver_dir, as the scan does.resolver_store_entries.is_inside_another_store). That covers pnpm entries, scoped npm.storeentries, pnpm <=3.registry.*entries and entries of a store relocated byvirtualStoreDir.npm_crawler/oracle.rs) follows the same store-entry rule, so it still matches the production resolver.Tests
test_store_named_child_of_a_store_entry_is_not_a_storenests a copy ofhidden@1.0.0under each of.pnpm,.bun,.deno,.store,.registry.npmjs.org,.vltand a.modules.yaml-relocated store, all inside a pnpm store entry'snode_modules. It also puts a peer variant of the entry's own package in a nested.pnpm. The test asserts thatcrawl_alldoesn't report the copy,find_by_purlsdoesn't resolve it, andfind_store_peer_variant_copiesdoesn't reach the peer variant.Red without the fix:
main:.pnpm: resolver walked a nested store: {"pkg:npm/hidden@1.0.0": [... node_modules/.pnpm/outer@1.0.0/node_modules/.pnpm/hidden@1.0.0/node_modules/hidden]}.pnpm: peer fan-out walked a nested store.vltcase, then only.vs):.vlt: scan reported a nested store's copyand.vs: scan reported a nested store's copy. The scan now shares the classifier, so dropping that check breaks the scan too.Green with the fix. Local runs:
cargo fmt --all -- --check: passcargo clippy --workspace --all-features -- -D warnings: passcargo test -p socket-patch-core --all-features --no-fail-fast: 6997 lib tests pass and every integration suite passes, includingcrawler_npm_e2eand the oracle equivalence tests. Four lib tests fail locally: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_untouchedandpypi_requirements::wire_failure_rolls_back_already_written_files. All four depend on read-only file modes (0o555), which root ignores, and this container runs as root. None of them touch code this PR changes. CI runs them unprivileged.cargo test --workspacelocally because the container's disk allowance ran out while building it. CI runs it.Remaining slices (#1899 stays open)
StoreEntryDir) instead of four shapes, decoding each name once.package_childreniterator for the ~7 package-dir filters and the 5@scope/nameexpansions.Review
test_peer_fan_out_skips_a_store_nested_in_any_store_entryfails without the fix and passes with it./code-review highraised 6 findings and all 6 are fixed: peer fan-out gate,.vlt/.modules.yamltest cases, the misplaced Agent mode ignores pnpm's virtualStoreDir: transitive dependencies are reported package_not_installed with a custom virtualStoreDir or the global virtual store #362 doc, oracle drift,Option→Vec, and stale docs.https://claude.ai/code/session_011f9aeRrMp15bL56hH58ghD
Note
Medium Risk
Touches core npm
node_modulestraversal for scan, resolve, and apply peer fan-out; wrong classification could miss packages or skip valid peer variants, but behavior outside store-entry trees is intentionally unchanged and coverage is extensive.Overview
Fixes #1899, where scan, apply resolver, and peer-variant fan-out disagreed on whether a store-named directory under a store entry's own
node_modules(e.g..pnpm/outer@1.0.0/node_modules/.pnpm/...) counts as a real package-manager store. The resolver and fan-out could patch copies that crawl never reported.The change introduces a shared
StoreKind::classify(name, file type,in_store_entry) and routes both scan deferral and resolver probing through it, with listing logic consolidated inresolver_store_entries.is_inside_another_storedrops stores nested inside another store (including relocatedvirtualStoreDirlayouts) from peer fan-out, while store-named ancestors that are notnode_moduleschildren (e.g. Bun/Deno install roots) still allow the project's own store to be walked.The test oracle mirrors the
store_entrygate so it stays aligned with production. New tests cover nested-store rejection across store shapes and regression #1986 for ancestors that look like store names but are not stores.Reviewed by Cursor Bugbot for commit 712ce24. Configure here.
Generated by Claude Code