Repository navigation
Cargo rollback after an agent→vendored takeover leaves the shared registry cache patched, reports success, and deletes the revert blobs #336
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:cargoCargoCargo
on Sep 30, 2026 mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Triaged:
priority:p2(Cargo). Not a duplicate, and no existing fix PR. The cause is thatrollback.rspartitions vendor-owned purls out of the in-place restore, and then its manifest cleanup and GC treat the purl as fully reverted, even though the agent→vendored takeover invendornever undid the earlier in-place edit.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triage on main
2463257(v5 consolidation #277, Linux, cargo 1.97.0): still reproduces, twice.v5
vendorneeds the patch service for artifacts, so I ran it against the repo'sprebuilt_commonfixture server (SOCKET_VENDOR_URL) and kept the same hand-staged agent patch forcfg-if@1.0.0:apply --offline -> success; registry/src/.../cfg-if-1.0.0/src/lib.rs patched (grep -c socket_patched = 1) vendor -> success: [applied, skipped vendor_prebuilt_downloaded]; cache still patched (1) rollback --offline -> exit 0, status "success", rolledBack 0, vendoredReverted ["pkg:cargo/cfg-if@1.0.0"], manifest.removedEntries ["pkg:cargo/cfg-if@1.0.0"], gc.removedBlobs 2 warnings: [reinstall_required: "unwired packages keep their patched bytes in installed trees until the next package-manager install"] cache after rollback -> still patched (1); .socket/manifest.json = {"patches": {}} other project, same CARGO_HOME: cargo run --offline -> patched=1 second rollback -> success, rolledBack 0 (nothing left to restore)What's new on v5 is the
reinstall_requiredwarning. For Cargo it doesn't help: no latercargo build/cargo fetchre-extracts a crate that's already inregistry/src, so "the next package-manager install" never restores the bytes. The revert blobs are still garbage-collected while the patched copy remains on disk.
Generated by Claude Code
mikolalysenko commented
on Oct 4, 2026 CollaboratorAuthorMore actions[agent] Same root cause on npm / yarn classic (from the Yarn classic bug-hunt, ledger #304). The closing note above says "other ecosystems' installers usually overwrite the installed copy on the next install". That's not true for yarn classic: its next install doesn't restore the bytes either.
Main
045d7ec, Linux, Node 22, local mock patch API (agent blobs + service tarball),left-pad@1.3.0, marker/* SOCKET-PATCHED */prepended toindex.js:yarn install # pristine lock + node_modules socket-patch scan --mode agent --yes # node_modules/left-pad/index.js patched socket-patch scan --mode vendored --yes # lock -> file:./.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz; node_modules untouched (still agent-patched) socket-patch rollback --json # status success, rolledBack 0, vendoredReverted [left-pad], warnings [reinstall_required] # yarn.lock byte-identical to the pre-agent lock; .socket/ = only manifest.json {"patches": {}} (blobs gone) yarn install --frozen-lockfile # "Already up-to-date": .yarn-integrity matches the restored lock head -c 20 node_modules/left-pad/index.js # /* SOCKET-PATCHED */ <- still patched yarn check --integrity # passes yarn install --check-files # the only thing that restores the upstream bytesyarn rollback lock next yarn install --frozen-lockfilenode_modules after 1.7.0 success, exit 0 byte-exact Already up-to-date patched 1.10.1 success, exit 0 byte-exact Already up-to-date patched 1.22.22 (2 runs) success, exit 0 byte-exact Already up-to-date patched remove pkg:npm/left-pad@1.3.0andvendor --revertafter the takeover end the same way: the manifest entry is dropped and node_modules stays patched.On yarn classic, the
reinstall_requiredremedy ("…until the next package-manager install") is wrong for this path. The rollback puts the lock back byte-for-byte to the state yarn last installed from, so yarn's integrity check short-circuits and never re-extracts the package. The leftover is project-local here, not machine-wide as with Cargo. It still means a "successful" rollback leaves patched code installed with no socket-patch record of it.For comparison, agent→hosted then
rollbackrestores bothyarn.lockand node_modules, because hosted purls aren't partitioned out of the in-place leg.
Generated by Claude Code
mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] From the Yarn classic bug-hunt (ledger #304): the yarn classic leg above also reproduces on macOS and Windows, so it doesn't depend on the OS.
Main
045d7ec, probe run 37248870997 (macos-latest and windows-latest × yarn 1.7.0 / 1.10.1 / 1.22.22, Node 22, local mock patch API). The flow is the same as in my comment above:scan --mode agent --apply, thenscan --mode vendored, thenrollback, thenyarn install --frozen-lockfile.OS yarn 1.7.0 yarn 1.10.1 yarn 1.22.22 macOS fail fail fail Windows fail fail fail Linux (sandbox, re-run today) — — fail In all 7 cells,
rollbackexits 0 andyarn.lockis restored byte-exact (CRLF on Windows). yarn then printssuccess Already up-to-date., andnode_modules/left-pad/index.jskeeps the patched marker. The other agent cells in the same probe pass on both OSes: agent apply, vex, plain agent rollback and an agent re-run.
Generated by Claude Code
[agent] Found by the scheduled Cargo bug-hunt routine (ledger #315).
Summary
Take a crate that was first patched in agent mode, which writes into the shared
$CARGO_HOME/registry/srccache, and later moved to vendored mode withsocket-patch vendor. A barerollbackon that crate then does three things:The shared registry copy stays patched, and
rollbackexits 0 withsuccess. Worse, the revert data is gone: a secondrollbackhas nothing to do, and every other project on the machine that usescfg-if 1.0.0keeps building the patched bytes, with no socket-patch record left anywhere.vendoritself also leaves the in-place patch behind without saying anything: no warning and no event. So an agent→vendored takeover never cleans up the cache edit.Impact
rollbackis documented as moving the system "toward fully unpatched". It also promises that "everything that leaves the system still patched DOES flip it topartial_failureexit 1". Here it reportssuccesswhile a patched shared cache remains, and it deletes the only blobs that could restore it.CARGO_HOME, which the second repro below shows. The only recovery iscargo clean/cache prune or deletingregistry/srcby hand.Repro
The patch is a hand-staged
.socket/manifest.jsonplus both blobs (stage.pyis below). It appendspub fn socket_patched()tocfg-if 1.0.0.stage.py:This was reproduced twice on the current main, with the same output both times.
Expected vs actual
Expected (CLI_CONTRACT.md, "Default behavior: full-state rollback"): a bare
rollback"restores the SYSTEM to unpatched". GC keeps before-blobs for any entry whose revert is still needed, and anything left patched meanspartial_failure/exit 1. So either:vendorreverts the in-place edit when it takes over an agent-applied purl (it has the before-blob), orrollback's agent leg still restores installed copies whose on-disk hash equals the recordedafterHash, even when the purl is vendor-owned.At a minimum, it shouldn't drop the manifest entry and GC the blobs while a patched copy is still on disk.
Actual: exit 0
success, the shared cache stays patched, and the revert data is deleted.Matrix
f6b7fb9rollbackreverts nothing (no vendored leg in 4.0.0) and the cache stays patched, but the manifest entry and blobs are kept, so the revert data survivesThe logic isn't OS-specific (it's the rollback partition and GC). macOS and Windows weren't probed.
First bad
The data loss (manifest entry and blobs removed while the cache is still patched) is new since 4.0.0. It comes from the v5 full-state rollback (vendored leg + manifest cleanup + GC, #231). The leftover in-place edit after
vendoris present in 4.0.0 too.Suspect code
crates/socket-patch-cli/src/commands/rollback.rs:2283-2292: vendor-owned purls are partitioned out of the in-place restore. The comment's premise ("their patch lives in the committed.socket/vendor/artifact … not in the installed tree") doesn't hold after an agent→vendored takeover.crates/socket-patch-core/src/vendor/cargo.rs(vendor takeover) doesn't revert, or warn about, an existing in-place patch of the same purl.This isn't strictly Cargo-only, since the rollback partition is ecosystem-agnostic. But Cargo is where the leftover is persistent and shared across projects: other ecosystems' installers usually overwrite the installed copy on the next install.
Backlog review — 2026-10-08
Priority: P2 → P1. Rollback deletes restore blobs while the shared Cargo cache remains patched. This is a loss-of-recovery-data issue, not ordinary compatibility cleanup.