Skip to content

Fix vendor -g rewiring the cwd project (#498) - #499

Merged
Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-vendor-global-scope
Oct 2, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-vendor-global-scope

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #498

Summary

vendor under --global / --global-prefix (or SOCKET_GLOBAL / SOCKET_GLOBAL_PREFIX) is now a usage error: exit 2, human Error: --global cannot be used with vendor[ --revert| --check]: global installs have no project lockfile to …, JSON {status: "error", error: {code: "global_scope_unsupported", message}}. This matches scan / get --mode vendored -g. The check runs before the project is read or locked.

Root cause

#446 made get, scan, apply, rollback and remove leave the cwd project's hosted pins and vendor ledger alone under global scope, using commands::project_state_in_scope / global_mode_conflict. The standalone vendor command (commands/vendor.rs) never consulted either one. Only its manifest-less eject path checked is_global(). As a result:

  • vendor --revert -g reverted the project's vendoring and exited 0, so the next frozen install was silently unpatched.
  • vendor -g vendored the manifest's records into the project and rewired its lockfile, including a hosted→vendored takeover.

The bug isn't specific to vlt. The reporter's npm control reproduces it too, because the code is package-manager independent.

Design choice

The issue allowed either "refuse" or "no-op on the project". I chose refuse, for three reasons:

  • Vendored state never exists for global installs, so there is nothing a global vendor could legitimately act on.
  • A silent no-op exit 0 would hide that the user's intent was misrouted.
  • It is consistent with the existing --mode vendored -g usage error.

vendor --check -g is refused for the same reason: it would report on the project, which isn't the global run's target. The now-unreachable is_global() guard on the eject path was removed.

Changes

  • commands/vendor.rs: global_scope_conflict plus an early exit-2 refusal in run.
  • commands/mod.rs: the shared global_scope_flag helper, so both usage errors name the flag identically.
  • CLI_CONTRACT.md ("Global scope never touches the project's state") and CHANGELOG.md.

Test evidence

New tests in crates/socket-patch-cli/tests/global_scope_project_state.rs (section 5) cover --global, -g, --global-prefix, SOCKET_GLOBAL=1 and SOCKET_GLOBAL_PREFIX, each in human and --json mode:

  • global_vendor_revert_leaves_vendored_project_state: the lock, artifact, ledger and installed copy stay byte-identical. A control shows a project-scope revert still unwinds.
  • global_vendor_does_not_vendor_into_the_project: the lock and manifest are unchanged and no .socket/vendor is created. A control shows project-scope vendoring still works.
  • global_vendor_check_is_refused

Red → green:

  • Before the fix (4749634): all 3 failed with left: 0, right: 2. The command exited 0 and touched the project.
  • After the fix (0e617ff): cargo test -p socket-patch-cli --all-features --test global_scope_project_state passed 29/29.

Local checks:

  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test --workspace --all-features --no-fail-fast: all green. Twelve tests across four targets (covgap_commands_vendor, in_process_redirect, repair, core --lib) fail only because this sandbox runs as uid 0, where their chmod 0o555 write-failure injection can't block writes. Re-run as an unprivileged user, all four targets pass in full (core lib 4719/4719, repair 116/116, in_process_redirect 104/104, covgap_commands_vendor 3/3 of the affected tests). None of them touch this change.
  • cargo fmt --all -- --check is already red on main across ~127 unrelated files (CI doesn't run it). The hunks in this PR are rustfmt-formatted, and I left the rest of the tree alone so the diff stays reviewable.
  • No wrapper changes are needed: npm/, pypi/ and gem/ only dispatch to the binary.

Per-issue checklist

Follow-ups


Note

Low Risk
CLI guard only; refuses a mis-scoped command before writes. No change to project-scoped vendoring behavior.

Overview
vendor with global scope is now rejected before any project read or lock, matching scan / get --mode vendored. Exit code 2, JSON code global_scope_unsupported, with messages that name --global vs --global-prefix (or env equivalents) and the subform (vendor, vendor --revert, vendor --check).

Previously, vendor -g could wire vendoring into the cwd project and vendor --revert -g could unwind the project’s vendoring while exiting 0—because vendor never used project_state_in_scope like the other commands after #446.

A shared global_scope_flag helper formats usage errors consistently with scan/get. The old is_global() skip on the hosted eject path is removed since global runs fail upfront. CLI contract, changelog, and integration tests (all global scope spellings, human + JSON) assert the project lock, manifest, and vendor tree stay untouched.

Reviewed by Cursor Bugbot for commit 0e617ff. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Run inside a project, `vendor -g` vendored the manifest's records into
that project and `vendor --revert -g` unwound the project's vendoring.
These tests pin that every form of `vendor` under --global,
--global-prefix, SOCKET_GLOBAL and SOCKET_GLOBAL_PREFIX leaves the
project's lockfile, artifacts and ledger byte-identical.

Assisted-by: Claude Code:claude-opus-5-5
A global run never targets the project it starts in, but the standalone
`vendor` command ignored global scope: `vendor --revert -g` silently
unpatched a vendored project on its next frozen install, and `vendor -g`
rewired the project's lockfile. Every form of `vendor` is now the same
exit-2 usage error `scan`/`get --mode vendored -g` give, raised before
the project is read or locked.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 1, 2026 20:48
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 0e617ff. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 1, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: ready for review at 0e617ff (0e617ff0efed1d745c70f8379547df8c6102b324).

  • CI: 410/410 non-skipped check runs green (6 skipped) on the head.
  • Bugbot: reviewed 0e617ff, no findings; no unresolved review threads.
  • Reviewer focus: vendor.rs now refuses vendor under -g instead of rewiring or reverting the cwd project; the change is also noted in CLI_CONTRACT.md and CHANGELOG.md.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Reviewed 0e617ff0efed. Ready to merge from this code review; no changes requested.

No blocking findings. The global-scope guard precedes check, project discovery, manifest access, and locking, covers flags and environment-derived scope, and leaves normal project vendoring behavior intact.

Validation: Ran cargo test --locked -p socket-patch-cli --test global_scope_project_state: 29 passed, including all new vendor/global refusal cases and ordinary project controls. Current CI checks pass.

@mikolalysenko
Mikola Lysenko (mikolalysenko) merged commit 39c9232 into main Oct 2, 2026
416 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-vendor-global-scope branch October 2, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

3 participants