Repository navigation
Fix rollback dropping records patched in the other scope (#450) - #1714
Conversation
Assisted-by: Claude Code:claude-opus-5-5
The agent manifest does not record whether a patch went into the project or a global install. A rollback in the wrong scope found an untouched copy, counted it as rolled back, then deleted the manifest record and its blobs. The copy that was really patched stayed patched and nothing could find it again (#450). When every copy in this run's scope is already original, rollback now looks in the other scope (the project for -g runs, the default global install otherwise). If a copy there still holds the patched bytes, the record and blobs are kept and a patched_in_other_scope warning names the copy and the rollback command for its scope. Exit status is unchanged. If nothing is patched anywhere, the record is dropped as before. Assisted-by: Claude Code:claude-opus-5-5
Review follow-up for #450. The first fix only checked the other scope when every copy in this run's scope was already original. Two common shapes still lost the record: - rollback -g where the global install has no copy at all (the purl is not installed in this scope) - a project rollback that restores its own copy while the same manifest's global copy is still patched Now every record about to leave the manifest because it was restored, already original, or not installed here is checked against the other scope. A record kept this way keeps its variant group too. The warning names --cwd when the run used one. The new warning code is documented in CLI_CONTRACT.md. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
Bugbot follow-up for #450. - A copy in the other scope whose patched file exists but cannot be hashed (unreadable, a directory, an I/O error) now keeps the record. Before, it counted as unpatched, so the record and its blobs could be dropped while the copy might still hold the patched bytes. - The patched_in_other_scope warning now names a rollback of just the kept purl (`socket-patch rollback <purl> [-g]`) instead of a bare rollback that would restore every patch in that scope. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ 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 71d22fa. Configure here.
|
Labeled Ready for review by the burn-down agent.
Generated by Claude Code |
Final review briefWhat it does. The agent manifest records no scope, so Risk: low to medium. The change only affects which records survive cleanup, and on doubt it keeps the record. Worst case is a stale record plus a warning, never lost state. The extra crawl runs only when a record is about to be dropped, and a project run now crawls the default global install at that point. Look here
Verified
Changes I made: none. Open questions (non-blocking, both in the description): Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #450
Root cause
The agent manifest (
.socket/manifest.json) records no scope.rollbackcrawls only its own scope: the project, or-g/--global-prefix. Any record it restored, found already original, or found not installed there landed in the removal set. The record was deleted and GC swept its before-blobs, even when the copy the patch was applied to sits in the other scope. That copy stayed patched, the run exited 0, and nothing on the machine could find or revert it again.Fix
commands/rollback.rs, manifest cleanup:--cwdfor a global run, or the default global install (CrawlerOptions { global: true }) for a project run. It looks for copies of the purl whose files still hash to the patch's afterHash. Vendored records are skipped, since vendored state is project-only.patched_in_other_scopewarning names the purl, the copy, and the command that restores it (socket-patch rollbackorsocket-patch rollback -g, plus--cwdwhen the run used one).CLI_CONTRACT.mddocuments the new warning code.Tests: red on main, green here (
tests/global_scope_project_state.rs)rollback --global-prefixwith an original global copy (wet and dry run)global_rollback_keeps_a_record_patched_in_the_projectrollback --global-prefixwith no global copyglobal_rollback_keeps_a_record_not_installed_globally-gapply, then projectrollback(direction 2), thenrollback -grestores it and drops the recordproject_rollback_keeps_a_record_patched_globallyrollbackrestores its own copy; JSON and human warning textproject_rollback_restoring_its_copy_keeps_a_record_patched_globallyglobal_rollback_drops_a_record_original_in_both_scopes(green before and after)All four regression tests fail when
rollback.rsis reverted toorigin/mainand pass with the fix. The direction-2 tests put a stubnpmonPATHthat answersnpm root -gwith a temp root, so they are#[cfg(unix)].Local verification
cargo fmt --all -- --check: cleancargo clippy --workspace --all-features -- -D warnings: cleancargo test --workspace --all-features --no-fail-fast: 14,912 passed, 11 failed, all environmental. None touch rollback:e2e_bun_lockb(2): blob fetch topatches-api.socket.devblocked by the sandbox proxy, plus a bun output difference. The same 2 fail with main'srollback.rs.in_process_redirect(3),repair(2), andsocket-patch-core --lib(4, which this PR doesn't touch): read-only-dir and write-failure tests that can't fail when the sandbox runs as root.Review notes and follow-ups
/code-review highran before this left draft. Its findings were addressed in 558df01: probenot_installedand restored-here records too, keep the variant group, add--cwdto the remedy, and reusecrawler_options().remove -gdelegates to the same rollback engine and still drops the record of a copy patched in the other scope.removeexplicitly asks to drop the record, so whether it should refuse or warn is a separate decision; follow-up.--global-prefixused at apply time. A global copy patched with the same uuid by a different project'sapply -galso keeps this project's record; the warning makes that visible. The extra crawl runs only when a record is about to leave the manifest, so--preserve-stateruns and runs where cleanup is skipped pay nothing.🤖 Generated with Claude Code
Note
Medium Risk
Changes rollback manifest cleanup and blob retention for cross-scope agent patches; behavior is conservative (keep records on doubt) and exit codes stay the same, but incorrect probing could leave stale records or miss warnings.
Overview
Fixes #450:
rollbackno longer drops agent manifest entries (and their blobs) when this scope is restored, already original, or not installed but a project vs global copy still matches the patchafterHash.Before manifest cleanup, rollback crawls the other scope (project for
-g/--global-prefix, default global install for a project run), skips vendored keys and paths already handled in this run, and retains the full variant group when patched or unverifiable bytes are found. It emits a new informationalpatched_in_other_scopewarning with the copy path and a scopedsocket-patch rollbackremedy (including--cwdwhen used). Exit behavior is unchanged when this scope is clean.CLI_CONTRACT.md documents the warning. Integration tests in
global_scope_project_state.rscover both directions, dry run, unverifiable files, and the case where both scopes are original so the record still drops.Reviewed by Cursor Bugbot for commit 71d22fa. Configure here.
Generated by Claude Code