Repository navigation
Human-output scan --mode agent / --sync still never re-applies an already-recorded patch after a reinstall (#454 fixed only the --json path) #732
Description
[agent] Found by the scheduled npm bug-hunt routine (ledger #302). The Bun routine noticed it first and handed it over (ledger entry entries/npm/20261003T193043Z-from-bun.md).
Summary
#454 ("scan --sync / --mode agent never re-applies an already-recorded patch") was fixed by #456 in the shared fetch loop: patches already recorded in .socket/manifest.json now count towards the nested apply (get.rs to_apply = downloaded + batch.already_recorded). But the human-output scan path drops those selections before they reach that loop. crates/socket-patch-cli/src/commands/scan/mod.rs:2733-2771 partitions every selection whose uuid is already recorded into already_recorded, prints [skip] … (already recorded: …), and when nothing else is left returns finish_human(0) without applying anything. That partition came in with de316b4 (#358), merged six minutes before the #456 fix, so #454 still reproduces whenever --json is omitted.
So the same scan --mode agent (or scan --sync) command patches the files with --json and leaves them unpatched without it. get <purl> --mode agent (human) does re-apply.
Impact
After any reinstall (npm ci, rm -rf node_modules && npm install, a CI cache miss), a human-output socket-patch scan --mode agent or scan --sync exits 0 and leaves the vulnerable bytes installed. It prints "All selected patches are already recorded in the manifest; run socket-patch apply to re-apply them.", but a CI log or a cron job only sees exit 0. vex afterwards refuses (not_applied), so attestation stays honest. Only the disk state is wrong.
Repro (npm, Linux, main 045d7ec)
A local mock of the patch API serves one free patch for left-pad@1.3.0, prepending /* SOCKET-PATCHED */ to index.js.
mkdir p && cd p && git init -q
echo '{"name":"p","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
npm install
socket-patch scan --mode agent --yes # patched
rm -rf node_modules && npm ci # pristine bytes again
socket-patch scan --mode agent --yes # or: scan --sync --yes
# [skip] pkg:npm/left-pad@1.3.0 (already recorded: 11111111)
# All selected patches are already recorded in the manifest; run `socket-patch apply` to re-apply them.
# exit 0, node_modules/left-pad/index.js still unpatched
socket-patch scan --mode agent --yes --json # apply.applied: 1, file patchedExpected vs actual
- Expected: what scan --sync / --mode agent never re-applies an already-recorded patch, so after a fresh Hatch env (or any reinstall) it exits 0 with the package unpatched #454 describes and Fix agent scan/get skipping apply for recorded patches (#454) #456 implemented. Every selected patch already in the manifest "is owed the nested apply … whose installed copy may be pristine again after a reinstall" (
get.rs:2441-2447), and the output format doesn't change what gets written. The--synchelp (scan/mod.rs:281-283) presents it as the one-shot reconciliation. - Actual: with human output, recorded patches are skipped before the fetch loop. The run exits 0 and the installed copy stays unpatched. If a run also has a new patch, only that one gets applied. The recorded but reverted one is still left out, because
selectedno longer contains it.
Cells
| OS | npm / Node | human scan --mode agent after npm ci |
human scan --sync |
--json |
|---|---|---|---|---|
| Linux | 6.14.18 / 22.22 | unpatched, exit 0 | — | re-applied |
| Linux | 10.9.4 / 22.22 | unpatched, exit 0 (×3) | unpatched, exit 0 | re-applied |
| Linux | 12.2.0 / 24 | unpatched, exit 0 | — | re-applied |
| Linux (Bun 1.4.2, from the handover) | — | unchanged | — | re-applied |
The decision is in OS-independent code, so I didn't run a macOS / Windows probe.
First bad version
- v4.0.0: both formats skip (that's the original scan --sync / --mode agent never re-applies an already-recorded patch, so after a fresh Hatch env (or any reinstall) it exits 0 with the package unpatched #454).
- main since a36432e (Fix agent scan/get skipping apply for recorded patches (#454) #456):
--jsonfixed, human still broken. The human skip itself is de316b4 (Keep Composer redirects and rollback consistent #358). Its testscan_human_does_not_offer_an_already_recorded_patch(tests/covgap_commands_scan_mod.rs:2265) says the selection "would only be downloaded to be skipped", which stopped being true once Fix agent scan/get skipping apply for recorded patches (#454) #456 landed.
Suspect code
crates/socket-patch-cli/src/commands/scan/mod.rs:2733-2771: the already_recorded partition and the early finish_human(0).
Activity
mikolalysenko commentedon Oct 4, 2026
[agent] Triaged as
priority:p1(npm-family; the defect is in ecosystem-independent scan code). Confirmed onmain045d7ec:crates/socket-patch-cli/src/commands/scan/mod.rs:2741-2770partitions manifest-recorded selections out ofselectedbefore the fetch loop and returnsfinish_human(0)when nothing else remains, so the #456batch.already_recordedre-apply never runs on the human path. Not a duplicate: #454 is closed and this is the human-output path #456 didn't cover. No open PR addresses it.Generated by Claude Code
mikolalysenko commentedon Oct 4, 2026
[agent] Claiming this issue (shared root cause: human scan path drops manifest-recorded selections before the nested apply). Branch: agent/fix-scan-human-recorded-reapply. Claim-ID: 2026-10-04T00:27:53Z-1b901e
Generated by Claude Code
mikolalysenko commentedon Oct 4, 2026
[agent] Draft fix PR: #733
Generated by Claude Code
Re-apply recorded patches in human scan output
Keep apply advice for report-only dry runs
Drop workspace-wide rustfmt churn from the #732 fix
mikolalysenko commentedon Oct 4, 2026
[agent] Narrowing (npm 10.9.4, main
045d7ec, two-patch mock). If the re-scan also finds a new patch, the human-outputscan --mode agentre-applies every manifest entry, including the recorded one thatnpm cireverted. The output reads "Added: 1 … 2 of 2 targeted patches applied", and both files are patched. So #732 only bites when every selected patch is already recorded ("All selected patches are already recorded in the manifest", exit 0, files unpatched). That case still reproduces on this main.Generated by Claude Code
npm: run 15 ledger (filed #753, #732 comment)
Fix human scan skipping re-apply of recorded patches (#732) (#733)