Repository navigation
Fix vendored member refusal not naming root (#1904) - #1913
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Running a vendored scan, get or vendor from a workspace member with no lockfile of its own refused with "no lockfile ... run your package manager's install first". Following that advice only updated the workspace root's lock, so the next run refused the same way. The vendored refusal now keeps its vendor_lockfile_missing code but says which lock installs the member and which directory to vendor from, the same lookup hosted mode's refusal already used. The lookup (package.json and vlt workspaces, pnpm workspace or lockfile-dir) moves out of the hosted refusal into one shared helper; hosted messages are unchanged. Fixes #1904 Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups to the workspace-member refusal: - A pnpm project whose lock lives elsewhere through lockfile-dir was told to vendor from the lock's directory, which holds no project. It now names the setting and points at hosted mode instead. - The message showed "." for the default --cwd; it now shows the member's own path. - Callers that keep only the refusal code skip the workspace lookup. - The run-from wording follows the stray-lock refusal's convention. 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 |
Final review brief (
|
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1904
Summary
When a vendored run (
scan --mode vendored,get --mode vendoredorvendor) starts in a workspace member that has no lockfile of its own, it refused with "no package-lock.json … run your package manager's install first". Following that advice only updates the workspace root's lock, so the next run refused the same way. The refusal now keeps thevendor_lockfile_missingcode (so the JSON contract doesn't change), but it says which lock installs the member and which directory to vendor from:This covers npm, yarn (classic and berry) and Bun
package.jsonworkspaces, vlt workspaces, and pnpm workspaces. For a pnpmlockfile-dirproject outside a pnpm workspace, the lock's directory holds no project, so the refusal names the setting and points at hosted mode rather than sending the user somewhere else that also doesn't work.Root cause
Step 4 of the lock-flavor probe (
vendor/npm_flavor.rs) always returned the generic detail. It never ran the "the lock lives in another directory" lookup that hosted'sredirect_*_lockfile_elsewhererefusal uses inhosted/governing_root.rs.Change
governing_root: the npm part of hosted'srefusal()(package.json workspaces, vlt workspaces, the pnpm workspace orlockfile-dirlock, with the nearer root winning) moves into onenpm_lock_elsewhere()helper. It returns aLockElsewhere { dir, dir_kind, vendorable_dir, detail }. Hosted builds its refusal from that, and its messages are byte-for-byte unchanged (the existing governing_root tests pass untouched).npm_flavor:detect_vendorable_npm_flavor(forward vendoring;vendor_npm_anyand the yarn berry takeover preflight) swaps the generic detail for the governing-root one.preflight_packagesandlock_text_refusalskeep only the code, so they use a code-only probe and skip the lookup.Test evidence
vendor::npm_flavor::tests::lockless_workspace_member_refusal_names_the_workspace_rootvendor::npm_flavor::tests::lockless_pnpm_member_refusal_names_the_lock_directoryvendor --json)vendor_failure_paths::lockless_workspace_member_refusal_names_the_workspace_root(--test vendor)lockfile_dir_project_refusal_names_the_setting_not_a_dead_endlockless_standalone_project_keeps_the_install_remedyTo get the red runs I disabled the detail swap locally; with it disabled, both core tests and the CLI test failed.
Commands run locally:
cargo fmt --all -- --check: okcargo clippy --workspace --all-features -- -D warnings: okcargo test -p socket-patch-core --all-features: 6998 passed, 4 failed. The 4 failures are chmod-based fault-injection tests (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…,pypi_requirements::wire_failure_rolls_back_…). They can't fail a write when run as root, and this container runs as uid 0. None of them touch this change. CI runs as non-root.cargo test -p socket-patch-cli --all-features --test vendor -- vendor_failure_paths: 28 passedcargo test --workspacelocally: the container ran out of disk building every test binary. CI covers it.No wrapper changes: the npm/PyPI/gem wrappers only pass the CLI's output through.
Review findings not fixed (from
/code-review high)detect_npm_lock_flavor, used byvendor --check/--revert, the lock inventory, VEX, crawlers) still has the generic text. Left as is on purpose. Those consumers either keep only the code or don't show this refusal to the user as a remedy. Adding the ancestor walk there would put filesystem reads on every lockless crawl and VEX snapshot, and the VEX snapshot reads are recorded.has_own_npm_family_lockis checked again insidenpm_lock_elsewhereandpnpm_lock_elsewhere. Each check is a handful of stats on a refusal path only, and the guard keeps thepub(crate)helper correct for any caller.https://claude.ai/code/session_011f9aeR3rq51BGZkfNJkypH
Note
Medium Risk
Refactors shared workspace-root detection used by both hosted and vendored refusal paths; behavior is heavily tested but touches many npm workspace edge cases.
Overview
Fixes #1904: vendored runs from an npm workspace member with no local lock no longer tell users to “run install first” (which only updates the root lock and loops). The
vendor_lockfile_missingcode is unchanged; the error text now names which lock governs the member and where to run vendor—e.g. vendor from the workspace root.governing_root: npm “lock lives elsewhere” logic (package.json / vlt / pnpm workspaces andlockfile-dir, with the same nearer-root rules) is consolidated intonpm_lock_elsewhereand a sharedLockElsewheretype. Hosted refusals still use the same wording viahosted_refusal().npm_flavor: forward vendoring (detect_vendorable_npm_flavor) replaces the generic missing-lock detail withlock_elsewhere_detail, which reuses that lookup. For pnpmlockfile-dirsetups where the lock directory isn’t a vendorable project, the message points to hosted scan instead of a dead-end “vendor from” hint.preflight_packages/lock_text_refusalsuse a code-only probe so they don’t pay for the ancestor walk.Tests cover npm/yarn/Bun/pnpm members,
lockfile-dir, standalone lockless projects (install remedy unchanged), and a CLIvendor --offlinepath.Reviewed by Cursor Bugbot for commit 8a97c8d. Configure here.
Generated by Claude Code