Repository navigation
Fix scan/get --json dropping apply failures (#424) - #955
Conversation
Assisted-by: Claude Code:claude-opus-5-5
scan --mode agent --json and get --json report a failed nested apply as failed: 0 with the patch listed as added and no error text. These tests pin the expected envelope: the patch record carries action: failed, errorCode and error, and failed counts it. Refs #424 Assisted-by: Claude Code:claude-opus-5-5
When scan --mode agent or get downloads a patch and the in-place apply then fails, the --json output said failed: 0, listed the patch as added and carried no error, so automation reading the JSON could not tell what went wrong. Only the exit code and status hinted at it. The nested apply now hands its failures back to the caller instead of just a pass/fail flag. Each patch that failed to apply is reported as action: failed with the same errorCode/error pair that apply --json prints (apply_failed or package_not_installed), failed counts it, and applied counts only patches that really applied. A failure that no single patch explains (unreadable manifest, yarn PnP refusal, missing patch sources) is reported as a top-level errorCode/error. Fixes #424 Assisted-by: Claude Code:claude-opus-5-5
When one patch fails to apply, apply only warns about other patches that have no installed copy. The JSON report now matches that: those patches are reported as package_not_installed failures only when nothing else failed the run. Adds unit tests for the failure collection. Refs #424 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] CI on
Why these aren't this PR's: the diff only changes the agent-mode nested apply ( No code fix exists or is needed in this PR. I'll re-run the failed jobs once when each run completes. A second failure would be treated as real and investigated. Generated by Claude Code |
The composer and gem docker e2e scripts checked that scan's JSON said "action": "added". In these fixtures scan's own in-place apply fails (the later apply --force patches the file), and scan --json now reports that failure on the patch record (#424). So "added" was only there because of the bug. Check instead that the patch was recorded in .socket/manifest.json, which is what "synced" means here. Refs #424 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The --json apply failure report could blame the wrong patch and miscount applied: - a failure on one PyPI release variant was pinned on a selected sibling variant that applied fine, via a base-purl fallback; - applied was "selected minus failed", so a selected patch that was never installed (only a warning next to a real failure) still counted as applied; - get <uuid> zeroed applied whenever any other manifest patch failed, and its extra failure records had no uuid. The nested apply now also reports which package keys it patched, and the envelope counts applied from that. A failure only marks records it covers: the same purl, or an unqualified key covering its variants. Refs #424 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The digest guard test (#865) fails on main. Gradle support landed with inline sha256/sha1 computations in crawlers/gradle_cache.rs, patch/jvm_jar.rs and patch/sidecars/maven.rs, and the guard's pending list doesn't name them. List them as pending so CI is green until they move onto the utils::digest helpers. Open PRs #876 and #889 add only gradle_cache.rs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjNH36TbmyXCpJPw3EyBZB
|
[agent] I added a minimal fix in Generated by Claude Code |
|
bugbot run 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 c51938b. Configure here.
|
[agent] Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #424
Summary
When
scan --mode agentorget(agent mode) downloads a patch and the in-place apply then fails,--jsonnow says what failed. Before this change the envelope saidfailed: 0andapplied: 0, listed the patch asadded, and had no error text. Only the exit code andstatus: "partial_failure"hinted at the failure. The human output already printed the error.Now the failed patch's record is
{purl, uuid, action: "failed", errorCode, error}, using the same code/text pair the standaloneapply --jsonemits (apply_failed, orpackage_not_installedwhen nothing is installed and the lockfiles don't resolve it).failedcounts it, andappliedcounts only patches that really applied. A failure that no single patch explains (unreadable manifest, yarn PnP refusal, unavailable patch sources) is reported as top-levelerrorCode/erroron the same object (applyin scan's envelope).Root cause
run_nested_applyincrates/socket-patch-cli/src/commands/get.rsreturned only abool. The nested apply never prints JSON (one envelope per command), so its per-patch events were thrown away.download_and_apply_patches_with(behind bothgetandscan --mode agent) and the single-uuidgetpath then filledfailedfrom download failures only. The code is shared by every ecosystem, which is why the report reproduced with Maven, PDM, Pipenv, npm and vlt.Fix
apply::run_lockednow returns anApplyRunReport(exit code, per-patch failures, optional run-level error) instead of a bare exit code.applyitself still uses only the code, so its output is unchanged.get.rsfold_apply_failuresfolds the report into the get/scan envelope. Records are matched by normalized purl, falling back to the base purl for qualified PyPI variants. A failing manifest patch that this run didn't select is added as its ownfailedrecord, because the nested apply covers the whole--ecosystems-scoped manifest.CLI_CONTRACT.mddocuments the newfailedrecords and counters.Tests (red → green)
The regression tests were committed before the fix (9951d28) and failed on that commit with exactly the reported shape:
"failed":0,...,"action":"added"and no error. All of them pass with the fix (94c993c and later).scan --mode agent --jsoncovgap_commands_scan_mod::scan_agent_json_nested_apply_failure_reaches_the_apply_blockapply.failed0)get <uuid> --jsoncovgap_commands_get::get_uuid_json_nested_apply_failure_names_the_patchgetsearch path / scan)covgap_commands_get::engine_nested_apply_failure_reaches_the_json_envelopecovgap_commands_get::engine_nested_apply_not_installed_reaches_the_json_envelopecovgap_commands_get::engine_nested_apply_success_keeps_added_and_counts_appliedapply::tests::collect_apply_failures_names_unresolved_purls_only_when_nothing_else_failedget::tests::fold_apply_failures_*(4),apply::tests::collect_apply_failures_*(2)The apply-failure tests use the first-party-link refusal from the issue thread (
node_modules/<pkg>symlinked topackages/<pkg>), so they're deterministic and don't need a read-only filesystem, which root would bypass anyway. Because they use symlinks they're#[cfg(unix)].Existing test updated: the composer and gem docker e2e verifiers (
docker_e2e_composer.rs,docker_e2e_gem.rs) used to check that scan's JSON said"action": "added". In those fixtures, scan's own in-place apply fails on a hash mismatch and the laterapply --forcepatches the file, soaddedwas only there because of this bug. CI'scoverage-docker (composer)caught the newfailed/apply_failedrecord. The verifiers now check what "synced" actually means: the purl is recorded in.socket/manifest.json.Local checks:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features: not run in full locally: building every test binary ran this container out of disk. Run instead and all green: thesocket-patch-clilib tests (853), theget,scanandapplytargets,in_process_get*(6 targets),covgap_commands_get(89),covgap_commands_scan_mod(52), andcli_{get,scan,apply}_silent. CI runs the full suite.cargo fmt --all -- --checkalready reports diffs in about 120 untouched files onmain. The files this PR touches are clean underrustfmt --edition 2021 --check.🤖 Generated with Claude Code
https://claude.ai/code/session_01HjNH36TbmyXCpJPw3EyBZB
Note
Medium Risk
Changes JSON contract and exit/partial-failure semantics for agent-mode get/scan; apply internals now expose structured failures but standalone apply output is unchanged.
Overview
Fixes #424: when
getorscan --mode agentruns download then nested apply,--jsonnow reports apply failures instead of leaving patches asaddedwithfailed: 0.apply::run_lockedreturns anApplyRunReport(exit code, per-patchApplyFailurelist, optional run-levelerrorCode/error, and which purls actually applied). Standaloneapplystill only uses the exit code.collect_apply_failuresmaps apply results toapply_failedorpackage_not_installed, matching standaloneapply --json.get.rsfolds that report viafold_apply_failures: selected patch rows becomeaction: "failed"with metadata stripped; unselected manifest failures are appended; counters and top-level errors align withCLI_CONTRACT.md. PURL matching uses normalization and base-purl rules for qualified variants.Tests cover engine,
get <uuid> --json, and scan agent JSON; composer/gem docker e2e now treat “synced” as manifest presence when scan’s inline apply can showfailed. Unrelated:digest.rspending-inline list extended.Reviewed by Cursor Bugbot for commit c51938b. Configure here.
Generated by Claude Code