Skip to content

Vendored scan / get --dry-run on an already-vendored npm project says it "Would download and vendor N patches", while its own JSON and the wet run say there is nothing to do #1850

Open

Description

[agent] Found by the scheduled npm bug-hunt routine (ledger #302).

Summary

Run scan --mode vendored --dry-run (or get <uuid> --mode vendored --dry-run) on a project whose patches are already vendored and in sync. The human output lists every patch under "Patches to vendor:" and ends with [dry-run] Would download and vendor N patches. No changes made. The same preview in --json correctly reports each patch as skipped / already_vendored, and the wet run prints [skip] … (already vendored) and All N packages are already vendored; nothing to do.

The other modes already get this right on the same project:

  • agent: [skip] pkg:npm/left-pad@1.3.0 (already recorded: …) and All selected patches are already recorded in the manifest …
  • hosted: 1 package is already on hosted patches; nothing to rewrite.

The preview already classifies the rows (PreviewVerdict::AlreadyVendored). Only the human renderer ignores that verdict: the headline subtracts would-refuse rows but not already-vendored ones, and no [skip] line is printed.

Impact

  • A user or CI step that previews before vendoring sees work that the wet run won't do, and can't tell from the human output that the project is already in sync.
  • This contradicts the stated intent of the headline (scan/render.rs: "so the headline never promises to vendor what the wet run refuses"), and the human and JSON outputs of the same dry run disagree.
  • Nothing is written, so this is a reporting bug only.

Repro (Linux, npm 10.9.4; same on 8.19.4 and 12.2.1)

The patch API is a local mock (--api-url / --patch-server-url) serving a free patch for left-pad@1.3.0. Any free npm patch shows the same behaviour.

mkdir dr && cd dr
echo '{"name":"dr","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
npm install
socket-patch scan --mode vendored --yes          # Vendored 1 package.
socket-patch scan --mode vendored --dry-run
#   Patches to vendor:
#     pkg:npm/left-pad@1.3.0 [FREE] high ...
#   [dry-run] Would download and vendor 1 patch. No changes made.
socket-patch scan --mode vendored --dry-run --json | jq '[.events[].errorCode], .summary.skipped'
#   ["already_vendored"]  1
socket-patch get <uuid> --mode vendored --dry-run
#   [dry-run] Would download and vendor 1 patch. No changes made.
socket-patch scan --mode vendored --yes
#   [skip] pkg:npm/left-pad@1.3.0 (already vendored)
#   1 package is already vendored; nothing to do.

A hosted→vendored eject of two packages (left-pad, is-number) followed by scan --mode vendored --dry-run gives Would download and vendor 2 patches, and the wet re-run gives All 2 packages are already vendored; nothing to do.

Expected vs actual

  • Expected: the human dry-run matches its own JSON and the wet run, as agent and hosted dry runs already do. Already-vendored rows should print as [skip] <purl> (already vendored) and be left out of the "would download and vendor" count, e.g. [dry-run] Would download and vendor 0 of 1 patch (1 already vendored), or the wet run's "already vendored; nothing to do" wording. CLI_CONTRACT.md describes --dry-run as a non-mutating preview of the run.
  • Actual: the human headline counts selected.len() minus refusals only, so in-sync patches show as work to do.

OS × version

OS npm scan --mode vendored --dry-run (human) same, --json get <uuid> --mode vendored --dry-run wet re-run
Linux 8.19.4 wrong ("Would … vendor 1 patch") already_vendored (right) not run nothing to do
Linux 10.9.4 wrong (2/2 runs, 1- and 2-package projects) already_vendored wrong nothing to do
Linux 12.2.1 wrong already_vendored not run nothing to do
macOS / Windows — not probed (probe branches are blocked for this routine); the code path is OS-independent

Not a regression: published v5.0.1 prints the same output. Tested on main c07af61. The renderer is shared by every vendored ecosystem, so other package managers are probably affected too. Only npm was tested.

Suspect code

  • crates/socket-patch-cli/src/commands/scan/mod.rs:3410: render::Plan::Vendor(selected.len()), and :3442-3443 pass only refused_count() to dry_run_line.
  • crates/socket-patch-cli/src/commands/scan/render.rs:259 (dry_run_line) knows only refusals.
  • crates/socket-patch-cli/src/commands/scan/vendor_flow.rs:157 (print_refusals) prints WouldRefuse rows but drops AlreadyVendored rows. record_into (:118) handles them for JSON.
  • crates/socket-patch-cli/src/commands/get.rs:2303: format_dry_run("download and vendor", selected.len()).
  • crates/socket-patch-cli/src/commands/scan/mod.rs:3368 lists in-sync patches under "Patches to vendor:".

Activity

  1. mikolalysenko commented on Oct 29, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p1 (npm). This is a reporting-only bug in the vendored dry-run human renderer (scan/render.rs dry_run_line, vendor_flow.rs print_refusals, get.rs format_dry_run). Those paths are shared by every vendored ecosystem. No open PR covers it, and it doesn't duplicate an existing issue.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 29, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: vendored dry-run human renderer ignores the already-vendored verdict). Branch: agent/fix-vendored-dry-run-already-vendored. Claim-ID: 2026-10-29T21:20:55Z-86f891


    Generated by Claude Code

  3. mikolalysenko commented on Oct 29, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1856


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 29, 2026
    66d68c4
    015ca58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions