Skip to content

Fix repair/prune deleting active patches' restore blobs (#893) - #1316

Merged
Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/v5-gc-keep-before-blobs
Oct 9, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/v5-gc-keep-before-blobs

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #893

Summary

repair / gc and scan --prune no longer delete the beforeHash blobs of patches that are still in the manifest. An offline rollback after a repair works again, and its missing-blob error no longer sends users to repair, which can't download originals.

Root cause

ArtifactReferences had two retention policies for an unchanged manifest:

  • after_removal (used by remove and rollback) kept the beforeHash blobs of every remaining patch.
  • for_apply (used by repair and scan --prune) kept only the afterHash blobs.

So the first repair deleted the originals that get stored.

Changes

  • ArtifactReferences::active(manifest) replaces for_apply and is the one policy. It keeps the afterHash and beforeHash blobs and the diff archive of every manifest patch. repair and scan --prune call it, and after_removal builds on it.
  • Deleted the dead cleanup_unused_blobs, cleanup_unused_archives and format_cleanup_result. Their unit tests now run the same cleanup_dir mechanics through the active policy.
  • New rollback remedy text: offline gate → Re-run without --offline to download the original blobs.; failed download → Re-run once the patch API is reachable….
  • CLI_CONTRACT: the repair row and the scan --prune paragraph describe the shared retention policy.

Per-issue checklist

  • After repair --offline on a project with an active patch and both blobs, rollback --offline exits 0 and restores the file: repair_invariants::repair_keeps_active_patch_before_blob_so_offline_rollback_still_works (red on main: summary.removed swept the before blob).
  • scan --mode agent --prune against a mock API keeps the active patch's beforeHash blob: scan_paths_e2e::prune_keeps_before_blobs_of_active_patches (red on main).
  • Originals that only manifest-absent patches reference are still swept: covered by both tests above.
  • Dead functions gone; cargo test -p socket-patch-core --lib manifest:: passes.
  • The offline rollback message no longer names repair: rollback_invariants assertions updated.
  • CLI_CONTRACT describes one retention policy.
  • cli::output_modes_e2e::repair_non_json_* updated: repair now reports the active patch's beforeHash blob as in use (CI caught the stale expectation).

Commands run

  • cargo test -p socket-patch-core --lib manifest:: (104 passed)
  • cargo test -p socket-patch-cli --test repair --test scan --test rollback --test remove --test covgap_commands_rollback --test remove_rollback_api_overrides --test diff_created_file_e2e (all green)
  • cargo clippy --workspace --all-features -- -D warnings, cargo fmt --all -- --check (the one remaining diff is main's redirect/upstream/mod.rs, which this PR doesn't touch)

Overlap

PR #1273 (GC JSON shape) and #1279 (crate cleanup) also edit repair.rs, scan/gc.rs and rollback.rs. Here those files change only on the one-line policy call and the remedy strings, so a rebase in either direction should be trivial.

🤖 Generated with Claude Code


Generated by Claude Code


Note

Medium Risk
Changes blob GC semantics for active patches and rollback error guidance; incorrect retention could still break offline rollback or leak disk, but behavior is narrowly scoped and heavily tested.

Overview
repair and scan --prune now keep beforeHash blobs for every patch still in the manifest, aligning GC with remove/rollback retention. Previously ArtifactReferences::for_apply swept originals on cleanup, which broke offline rollback after a repair (and repair only re-fetches afterHash blobs).

ArtifactReferences::active(manifest) replaces for_apply as the shared policy: retain each manifest patch’s afterHash, beforeHash, and diff archive; only unreferenced blobs/archives are removed. repair and scan/gc call active; after_removal is built on top of it. Standalone cleanup_unused_blobs / cleanup_unused_archives helpers are removed in favor of the sweep path.

Rollback missing-blob messaging no longer points users at socket-patch repair; it tells them to re-run without --offline or once the patch API is reachable. CLI_CONTRACT.md documents the unified retention rules for repair and scan --prune. Tests cover offline rollback-after-repair, prune retention, and updated repair stdout expectations.

Reviewed by Cursor Bugbot for commit 37bd179. Configure here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`repair` (alias `gc`) and `scan --prune` kept only the afterHash
blobs of patches still in the manifest, so the first repair deleted
the originals `get` stored. A later `rollback --offline` of a still
active patch then failed and told the user to run `repair`, which
only downloads afterHash blobs and can never bring the original back.

Give ArtifactReferences one policy for a manifest's patches,
`active`: afterHash and beforeHash blobs plus the diff archive of
every patch. repair and scan --prune use it, and remove/rollback's
`after_removal` builds on it. Delete the dead cleanup_unused_blobs,
cleanup_unused_archives and format_cleanup_result. The rollback
missing-blob remedy now says to re-run without --offline (or once the
patch API is reachable) instead of naming repair.

Fixes #893

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 9, 2026 17:08
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

The human repair output tests assumed repair sweeps the active
patch's beforeHash blob. It now keeps it (#893), so the no-orphan
case checks both blobs in use and the orphan case removes only the
orphan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 37bd179. Configure here.

Resolve conflicts with #1049 (diff download path removed):
- ArtifactReferences::active keeps the afterHash and beforeHash blobs of
  every active patch (#1316); the patch_uuids/diff-archive retention is
  dropped because #1049 sweeps every diff and package archive as obsolete.
- after_removal builds on active() plus the originals of
  removed-but-not-installed patches.
- cleanup_unused_blobs / format_cleanup_result / cleanup_archives stay
  removed (no callers left); the archive-retention unit tests go with them.
- CLI_CONTRACT.md: repair row and scan --prune paragraph describe the
  combined policy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 579f43a Oct 9, 2026
53 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/v5-gc-keep-before-blobs branch October 9, 2026 23:08
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 10, 2026
Resolve against main's removal of the diff download path (#1049) and
the restore-blob GC fix (#1316): repair drops the created-file blob
pass and keeps the GcReport carrier; remove keeps the archive noun
loop; the contract keeps "update" and drops the removed paidRequired
status; the envelope contract test uses AppliedVia::Blob.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

repair and scan --prune delete the beforeHash blobs of active patches, so a later offline rollback fails and tells the user to run repair

2 participants