Fix npm crawler oracle flake from symlink-cycle trees - #582
Conversation
The randomized npm-crawler oracle generator gave some pnpm store entries a node_modules symlink to nm_dirs.first(), which is usually the importer node_modules that holds the store. Both crawlers follow an entry's node_modules link, so they walked .pnpm/<e>/node_modules/.pnpm/<e>/... until the OS refused the path. Both treat every I/O error as an empty dir, so the oracle comparison came down to where each walker gave up, not what it found. Seeds 5, 7, 12 and 41 drew this shape; seed 12 failed CI once with the sequential walker stopping 6 levels short of the new one. Point the link at a fresh node_modules outside the tree instead, the same way the vlt store case and the symlinked-scope case already do. The followed-symlink shape is still generated (16 of 64 seeds), and the two oracle tests run about 30% faster. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
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 a1d819b. Configure here.
|
[burn-down agent] Labeled Ready for review at
Generated by Claude Code |
|
Reviewed The generated pnpm link still exercises a symlinked Validation: the exact head has 331 successful checks, 6 skipped, and 0 failures. I verified Ubuntu’s test log contains passing results for all four npm oracle tests, including the previously failing no-pool case; Windows/macOS, release tests, clippy, and Bugbot also pass. The diff passes whitespace checks and merges cleanly with Runtime detection of actual directory cycles remains the separate follow-up described in the PR; this change fixes the randomized fixture. |
Problem
test (ubuntu-latest)in CI failed on run 37020762793, attempt 1 (job 110882935639, PR #442 headdce7d1af). The re-run on the same SHA passed:This was the only real test failure among the last 100 CI runs (the other 57 non-green runs were concurrency cancels). It's in a required job, and the only fix available today is a manual re-run of a ~12 min job.
Root cause
The test bug is in the generator, not in either crawler. In
Gen::pnpm_store, case76..=79("an entry whose node_modules is a symlink") links.pnpm/<entry>/node_modulestoself.nm_dirs.first(). That is usually the importernode_modulesholding the store, so the link points back at one of its own ancestors. Both crawlers follow an entry'snode_moduleslink (is_dirfollows symlinks), sofind_by_purlswalkeduntil the OS refused the path (about 40 levels: the Linux symlink limit). Both walkers treat every I/O error as an empty dir, so the assertion ended up comparing where each walker gave up, not what it found. In the failing run the new walker returned 87 copies (40 levels) and the sequential oracle 75 (34 levels). I could not reproduce the early stop locally, so I don't know which errno ended the oracle walk early. The test only depends on it because the tree has a cycle, and the tree should not have one.
The generator already tries to avoid this. The symlinked-scope case says "to a scope living outside the tree, so the followed walk cannot cycle", and the vlt store's symlinked-
node_modulescase points intoscratch. Only the pnpm case missed it.Seeds that drew the cycle, from dumping every generated tree and checking
.pnpm/*/node_moduleslinks that point at an ancestor:randomized_trees_match_the_sequential_oracle(seeds 0..64): seeds 5, 7, 12, 41walk_without_a_pool_matches_the_sequential_oracle(seeds 0..16): seeds 5, 7, 12Fix
Test-only change in
crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs. The entry'snode_moduleslink now points at a fresh out-of-treescratch/pnpm-nm<N>/holding a package, the same way the vlt case does. The followed-symlink shape is still covered, and the walk can no longer depend on OS limits. No assertion changed and no test was removed.Proof
.pnpm/<e>/node_modules, so that shape is still exercised.oracletests pass. Thenonempty > 32check still holds.rustfmt --checkpasses on the touched file.cargo clippy -p socket-patch-core --all-targets -D warningsreports 6 errors locally, all in files this PR doesn't touch, and unmodified main gives the same result locally. The CIclippyjob passes.test (ubuntu/macos/windows-latest),test-releaseandclippy. Bugbot found no issues.Where tests still run
Nothing moved or removed. Both tests still run in
test (*)inci.yml.Follow-up (not in this PR)
The product crawlers themselves follow a store entry's
node_modulessymlink without cycle detection. A real tree with such a back-link would makefind_by_purlsreport up to about 40 "copies" of the same package. Real pnpm never writes this shape, but adding cycle detection is worth a separate issue. It changes behavior, so it doesn't belong in a test-flake PR.🤖 Generated with Claude Code
https://claude.ai/code/session_0142M5G6kR7NJke3PJ1Ra74p
Generated by Claude Code