Problem
scan --mode vendored (default) and the standalone vendor command default to DIFFERENT vendor-sources, producing DIFFERENT committed bytes / lock integrity for the same patch.
crates/socket-patch-cli/src/commands/scan/vendor_flow.rs unconditionally passed service = None to vendor_records (build-only), and --vendor-source (a GLOBAL flag) was parsed but silently ignored by scan.
crates/socket-patch-cli/src/commands/vendor.rs:292 builds a VendorServiceConfig from --vendor-source (default auto = service download).
Confirmed against real prod ([email protected] / patch 80630680): scan --mode vendored produced tarball sha256 d5050fe… + lock integrity sha512-N1gyz3… (== vendor --vendor-source build), while plain vendor produced sha256 607e4e3… + sha512-YzCNS0… (== vendor --vendor-source service). Both are deterministic, so committing a lock written by one entry point then running the other rewrites the integrity though nothing changed — lock churn / merge conflicts. (Content installs identically either way.)
Fix
Make scan --mode vendored build the SAME VendorServiceConfig the vendor command does and thread it through the vendored path, defaulting to the same default (auto/service).
- New pure assembler
scan_vendor_service_config (vendor_flow.rs) mirrors vendor::run's config construction (source from --vendor-source, plus --vendor-url / --patch-server-url / --offline).
run_scan_vendor_step (vendor_flow.rs) now builds the run-level client via get_api_client_with_overrides and passes Some(&service) into boxed_vendor_records (vendor_flow.rs:646), which forwards it to vendor_records instead of the hardcoded None. Dry runs short-circuit before this step, so no client is built in preview mode.
After the fix, scan --mode vendored and vendor produce byte-identical committed artifacts + lock integrity by default, and scan --mode vendored --vendor-source build matches vendor --vendor-source build.
Test
cargo test -p socket-patch-cli --lib service_config_tests — 4 hermetic tests in vendor_flow.rs asserting the vendored flow builds its service config FROM --vendor-source (not hardcoded None):
default_source_permits_service_like_vendor_command (regression: default auto must permit the service, matching vendor)
service_source_requires_service, build_source_never_uses_service, overrides_thread_through
Full commands::scan:: suite: 42 passed. cargo clippy -p socket-patch-cli --lib clean.
Scope
Touches only scan/vendor_flow.rs (config construction + threading) and a one-line stale doc comment on vendor_records' service param in vendor.rs. No signature changes to the JSON/interactive path or mod.rs, to minimize conflict surface with parallel PRs. The client is rebuilt in the vendored step (mirroring vendor::run) rather than threaded down from scan::run; a follow-up could thread the already-resolved client to avoid the second get_api_client_with_overrides call.
🤖 Generated with Claude Code
Note
Medium Risk
Changes how vendored artifacts are produced for scan (default shifts from local build to service-capable auto), which affects committed bytes and lockfiles but aligns scan with vendor and is covered by regression tests.
Overview
Fixes a parity bug where scan --mode vendored always vendored build-only (service = None) while the standalone vendor command honored --vendor-source (default auto, service download). The same patch could therefore produce different tarball hashes and lock integrity, causing lock churn when teams mixed entry points.
scan_vendor_service_config mirrors vendor::run: it assembles VendorServiceConfig from --vendor-source, --vendor-url, --patch-server-url, and --offline. run_scan_vendor_step resolves the API client via get_api_client_with_overrides, passes Some(&service) through boxed_vendor_records into vendor_records (dry runs still skip this path). Four hermetic unit tests lock in auto / service / build and override threading.
Reviewed by Cursor Bugbot for commit 8bab316. Configure here.