Repository navigation
Fix vlt stray locks under a vlt.json root (#1584) - #1658
Conversation
Assisted-by: Claude Code:claude-opus-5-5
vlt installs whatever project its root lookup finds: the nearest ancestor vlt.json, else the outermost directory with a package.json or node_modules (stopping at .git, never reading $HOME). A directory with no vlt.json of its own is never a project of its own, whether or not a workspaces field lists it. socket-patch only treated a nested vlt-lock.json as stray when the directory was a listed workspace member. Scans from an unlisted nested directory, a member listed through a `..` glob, or a vlt 1.3.8 pnpm-workspace.yaml directory rewrote a lock vlt never reads, exited 0, and VEX attested not_affected while the install stayed unpatched. Hosted and vendored scans now refuse such a directory (redirect_workspace_lockfile_elsewhere / vendor_lockfile_missing) and name the root to run from, and VEX reads the ignored lock as absent. As for listed members, a root that was never installed (no vlt-lock.json) governs nothing. Fixes #1584 Assisted-by: Claude Code:claude-opus-5-5
Describe in CLI_CONTRACT.md the #1584 case of the workspace refusal: a directory with no vlt.json of its own whose vlt project root is another, installed directory. Assisted-by: Claude Code:claude-opus-5-5
Follow-ups from review of the #1584 fix: - A listed vlt member holding its own .git (a submodule) is its own vlt project, so its lock is no longer refused as stray. - When a vlt.json sits between the member and the listing workspace root, the refusal now names that nearer root, which is the one vlt installs, and says "vlt project root" instead of "workspace root". - The walk resolves symlinks and `..` like vlt's own cwd does. - The message names only the vlt lock as ignored. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The vlt project-root walk compared canonicalized ancestors against the raw HOME / USERPROFILE value. On Windows canonical paths carry a `\\?\` prefix, and on any OS a symlinked or `..`-spelled home never matched, so the walk could climb into the home directory and pick the wrong vlt project root. Resolve the home path the same way. Assisted-by: Claude Code:claude-opus-5-5
|
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 20e3ae5. Configure here.
|
Burn-down agent: labeled Ready for review at
Generated by Claude Code |
Final review briefWhat it does. vlt installs its project root ( Risk: medium. Small, focused code change (4 files), but it widens a refusal: any vlt project with no Look here
Verified
Changes I made: none. Open questions (non-blocking): when there is no Auto-merge is armed (squash), so approving sends it straight to the merge queue. Generated by Claude Code |
|
[final reviewer] Re-added to the merge queue. It was removed at 17:15 UTC; main was then merged in at dadf23b (CLI_CONTRACT.md conflict only), CI is green on dadf23b and the approval still stands. Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1584
What it does
A scan from a directory that vlt never treats as its own project no
longer rewrites that directory's stray
vlt-lock.json. Hosted moderefuses with
redirect_workspace_lockfile_elsewhere, vendored mode withvendor_lockfile_missing, andvexreads the ignored lock as absent.Before this change all three exited 0, and VEX attested
not_affectedwhile the install stayed unpatched.
Root cause
vlt installs whatever project
find("project")in@vltpkg/vlt-jsonreturns, and
ConfigsetsprojectRoot = dirname(find(...)). I checkedthat this code is identical in 1.3.8 and 1.4.0. The walk goes up from the
cwd and works like this:
vlt.json;package.jsonornode_modules;.git, and never reads$HOME.None of that depends on a
workspacesfield.member_stray_lock(hosted/governing_root.rs) only treated a vltlock as stray when an ancestor listed the directory as a workspace
member. That missed three cases from the issue:
..glob;pnpm-workspace.yamldirectory.Fix
vlt_project_rootmirrors vlt's walk, canonicalized the wayNode's cwd is.
member_stray_lockuses it for the vlt family:submodule
.git), the lock is vlt's own and is never stray.vlt-lock.json, thelock is stray.
config-only
vlt.jsonin between), the nearer root is the onenamed.
vlt-lock.json) governs nothing.root", and says a
vlt.jsonin the directory makes it its ownproject. The hosted, vendored and VEX wrappers all share this text.
..glob shapes are covered without normalizing globs: the rootvlt.jsonis the nearest one, so the project-root rule refuses them.redirect_workspace_lockfile_elsewhererow to describe the new case.
Changed expectations in existing tests
Several tests asserted the old premise: that a directory a vlt root
does not list, or excludes with
!, owns itsvlt-lock.json. vlt'ssource and the issue's repros (2/2 runs, 1.3.8 and 1.4.0) show that
premise is false. These tests now expect the lock to be stray:
only_the_locks_whose_manager_lists_the_member_govern_itvlt_extglob_member_is_refused(packages/c)vlt_negated_member_reads_the_root_lock, which still checks thelisting logic on a lockless member
vlt_pnpm_workspace_yaml_reads_the_root_lockThe #1564 VEX fixture gains a root
package.json. Without one, vltfrom
packages/areally does installpackages/a, so the fixturecould not have produced its root lock.
Test evidence
vlt.json(hosted)vlt_project_root_governs_unlisted_directories.././../*/..globsvlt.json, outermostpackage.jsonpnpm-workspace.yamldirvlt_pnpm_workspace_yaml_reads_the_root_lockbun_and_vlt_members_with_stray_lock_are_refused(4 new cases)a_member_lock_its_manager_ignores_attests_nothing(2 new cases).git,$HOME,node_modules)vlt_project_root_walk_bounds.git/ nearervlt.json(review)vlt_project_root_overrides_the_listing_rootRed to green: with the fallback disabled, 6 tests fail
(
vlt_project_root_governs_unlisted_directories,bun_and_vlt_members_with_stray_lock_are_refusedand the 4 updatedones). With the fix, all 91 governing_root / npm_flavor / stray tests
pass.
Local runs:
cargo fmt --all -- --checkandcargo clippy --workspace --all-features -- -D warningsare clean.cargo test -p socket-patch-core --lib --all-features: 6568 passed,4 failed. The 4 failures are chmod-based permission-denial tests
(copy_tree, vlt_heal, pypi_poetry, pypi_requirements) that can't fail
in this sandbox, which runs as root. They don't touch this diff.
in_process_scan,e2e_vlt,e2e_safety_vltandmode_migration_vltpass.in_process_redirecthas 143 passing and3 root-only read-only-permission failures.
cargo test --workspacecouldn't link everything within thissession's disk allowance (30 GB of debug test binaries). The real-vlt
*_vlt_buildsuites couldn't reach npmjs from the sandbox'stest-harness HTTP client. CI covers both.
Review notes not acted on
/code-review highpointed out thatvlt_project_rootuses blockingexists()calls. I kept them, sincemember_stray_lockalreadychecks existence synchronously, and the walk stops at
.git.$HOMEwith no.git. That is vlt's own behavior. The walk test anchors itself witha
.gitso it stays deterministic.Follow-ups (not in this PR)
through the listing-only
vlt_workspace_refusal.refused, which matches the existing listed-member rule.
Note
Medium Risk
Changes governing-root detection for vlt layouts and fails closed on more scan/vendor/VEX paths; behavior is well-tested but affects npm-family refusal logic.
Overview
Treats nested
vlt-lock.jsonas stray when vlt installs from a parent project root, fixing false success and incorrect VEX attestation when running from directories vlt does not treat as their own project (#1584).member_stray_locknow mirrors vlt’sfind("project")viavlt_project_root: nearest ancestorvlt.json, else outermostpackage.json/node_modules, stopping at.gitand below$HOME. Any directory without its ownvlt.jsonwhose lock vlt would ignore is refused withredirect_workspace_lockfile_elsewhere(hosted),vendor_lockfile_missing(vendored), and VEX treats the lock as absent—including unlisted paths,..globs, and vlt 1.3.8pnpm-workspace.yamllayouts, not only explicit workspace members.Refusal text names the correct directory to run from as either workspace root or vlt project root (
MemberStrayLock::root_kind), shared across hosted, vendored, and VEX.CLI_CONTRACT.mddocuments the vlt #1584 case.Reviewed by Cursor Bugbot for commit 20e3ae5. Configure here.
Generated by Claude Code