Skip to content

Fix agent scan/get skipping apply for recorded patches (#454) - #456

Open
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
mainfrom
agent/fix-agent-apply-skipped-records
Open

Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
mainfrom
agent/fix-agent-apply-skipped-records

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #454

Summary

scan --mode agent, scan --sync, and agent-mode get now re-apply a patch that is already recorded in .socket/manifest.json. Before this change, a re-run after a reinstall (a fresh Hatch env, pip install --force-reinstall, a CI cache miss, a failed first apply, a --global-prefix reinstall) exited 0 with the package unpatched.

Root cause

Both agent-mode engines in crates/socket-patch-cli/src/commands/get.rs gated the nested apply on a manifest change:

  • download_and_apply_patches_with (get <purl|CVE|GHSA>, scan --mode agent, scan --sync, -g/--global-prefix): apply_lock and apply_failed both required downloaded > 0.
  • save_and_apply_patch (get <uuid>): the same gate, on changed.

A patch already recorded at the same uuid comes back skipped, so the installed tree was never checked.

Fix

The diff is 3 files: get.rs, one updated test, one new test file.

  • FetchBatch.already_recorded counts the manifest-store skipped patches whose same uuid is already recorded.
  • download_and_apply_patches_with runs the nested apply when downloaded + already_recorded > 0 (and not --save-only). apply_failed and applied use the same count, so applied now means "selected recorded patches confirmed applied after this run". The manifest is still written only when a record changed, and apply is a no-op on already-patched files, so an in-sync re-run changes nothing on disk.
  • save_and_apply_patch applies unless --save-only. The "nothing to update." suffix now prints only when no apply follows, decided by apply_lock.is_none().
  • --save-only keeps its record-only intent.

No wrapper (npm/, pypi/, gem/) changes are needed. They dispatch to the binary.

Test evidence

New crates/socket-patch-cli/tests/in_process_agent_reapply.rs (hermetic npm fixture, wiremock API). Each test records and applies once, reinstalls the pristine file, then re-runs. The "without fix" column comes from the same tests run against get.rs with the fix stashed:

Test Without fix With fix
agent_scan_reapplies_already_recorded_patch_after_reinstall FAIL (file unpatched) ok
scan_sync_reapplies_already_recorded_patch_after_reinstall FAIL ok
get_uuid_agent_reapplies_already_recorded_patch_after_reinstall FAIL ok
get_purl_agent_reapplies_already_recorded_patch_after_reinstall FAIL ok
agent_scan_rerun_fails_when_recorded_patch_cannot_be_applied (--strict, expects exit 1) FAIL (exit 0) ok
agent_scan_in_sync_rerun_is_a_clean_noop (control: manifest bytes unchanged) ok ok
get_save_only_of_recorded_patch_still_does_not_apply (control) ok ok

One existing test pinned the bug and is updated. scan/scan_sync_e2e.rs::scan_apply_with_existing_blob_uses_local_cache pre-staged a same-uuid record over a pristine install and asserted the file stays unpatched with applied: 0. It now asserts the record is still skipped, there's no download, and the manifest is untouched, while the cached blob gets applied (applied: 1, file holds the patched bytes).

Local checks:

  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • rustfmt --check on the new test file: clean. main isn't rustfmt-clean and CI doesn't gate on fmt. A workspace-wide reformat that slipped into 175f3a4 was reverted in the head commit, so only the touched code changes.
  • Before the revert: cargo test -p socket-patch-cli --all-features --lib --bins plus 20 get/scan/apply integration binaries passed 1293, failed 0.
  • After the revert, on the final head: lib, scan, get, in_process_get, in_process_scan, covgap_commands_get, in_process_agent_reapply passed 1133, failed 0.
  • A full cargo test --workspace --all-features couldn't finish locally because the sandbox ran out of disk while linking about 200 test binaries. Before that, the only failures were 3 covgap_commands_vendor read-only-dir tests that need a non-root user (the sandbox runs as uid 0), in vendor code this PR doesn't touch.
  • CodeQL: the test helpers avoid "uuid" in their names. CodeQL treats that word as sensitive, so a helper named get_uuid_args made existing stderr lines look like new cleartext-logging findings.

CI on head 35ee888: all 391 non-skipped checks pass (6 skipped), including coverage, the full e2e matrix, and CodeQL ("No new alerts in code changed by this pull request"). Bugbot found no issues.

Per-issue checklist

Related, not fixed here: #424 (scan JSON drops the apply failure detail on the first run).

🤖 Generated with Claude Code


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A re-run of scan --mode agent, scan --sync, get <uuid> or get <purl>
after the package was reinstalled left it unpatched while exiting 0.
These tests reinstall the pristine file between runs and expect the
recorded patch to be applied again (#454).

Assisted-by: Claude Code:claude-opus-5-5
scan --mode agent, scan --sync and agent-mode get only ran the nested
apply when they downloaded a new or updated record. When the patch was
already in .socket/manifest.json, the installed copy was never checked,
so after a reinstall (a fresh Hatch env, a CI cache miss, a failed
first apply) the run exited 0 with the package unpatched.

Run the nested apply for every selected patch that is recorded,
already-recorded ones included, unless --save-only. Apply is a no-op on
already-patched files, and a failing apply now fails the run.

Fixes #454

Assisted-by: Claude Code:claude-opus-5-5
Comment thread crates/socket-patch-cli/src/commands/get.rs Fixed
CodeQL traced the --save-only flag from GetArgs, which also carries
the API token, into the stderr save summary. Decide the "nothing to
update" suffix from whether an apply follows instead, which is the
same condition without passing argument data into the log line.

Assisted-by: Claude Code:claude-opus-5-5
scan_apply_with_existing_blob_uses_local_cache pinned the #454 bug: a
patch already recorded at the same uuid, over a pristine install, was
left unapplied. The record is still skipped (no download, manifest
untouched), but the cached blob is now applied to the install.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 1, 2026 10:51
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


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.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-cli/src/commands/get.rs Fixed
CodeQL's name heuristics treat "uuid" as sensitive, so the new test
helper get_uuid_args() showed up as a fresh taint source for existing
stderr lines in get and scan. Rename the helpers; behavior unchanged.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


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.

Stale Bugbot comment from a previous run.

A workspace-wide cargo fmt run reformatted 125 files this fix does
not touch (main is not rustfmt-clean and CI does not gate on it).
Restore them to main so the PR only carries the fix and its tests.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


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 35ee888. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 1, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Ready for review on 35ee888: mergeable, 0 commits behind main.

  • CI: 399/405 check runs green, 6 skipped by workflow conditions, 0 failing.
  • Bugbot: reviewed 35ee888, no new issues. Both earlier get.rs threads are outdated and resolved.
  • For the reviewer: agent-mode apply now also runs when the patch is already in the manifest (already_recorded). Check that applied counting the recorded patches is the semantics you want.

Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

4 participants