Repository navigation
Honor HTTP-date Retry-After on vendor-service retries through api::retry (#677) - #889
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A vendor-service 429 or 503 that sent Retry-After as an HTTP-date was retried on the 400 ms backoff, because the vendor path kept its own delta-seconds-only parser. Vendor retries now read Retry-After through api::retry::parse_retry_after on the client's RetryHooks clock (still capped at VendorRetryPolicy::max_delay), and draw their ±25% jitter from the seeded api::retry::jitter_sample, keyed by request and attempt, and wait on the hooks' sleep. The private retry_after_secs and jitter_sample copies in client.rs are deleted. New tests run the reference POST, the archive GET, the capped artifact GET and a resumed deferred GET through the shared parser, and pin the cap and the seeded jitter. Assisted-by: Claude Code:claude-opus-5-5
#646 landed inline sha1/sha256 computations in patch/jvm_jar.rs, patch/sidecars/maven.rs and crawlers/gradle_cache.rs after the utils::digest ratchet, so production_digests_go_through_the_helpers fails on main. jvm_jar's private sha1_hex/sha256_hex copies and the Maven sidecar's inline sha1 now call the shared helpers; gradle_cache.rs, which an open PR also edits, joins the pending list for now. Hashes are byte-identical. Ported from #876 so this PR's CI is not red on the base-red ratchet. (cherry picked from commit 28d4d52) Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] CI on Generated by Claude Code |
|
Burn-down agent: labeled Ready for review at
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
* Start fix for #424 Assisted-by: Claude Code:claude-opus-5-5 * Add failing tests for apply failures in --json 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 * Report apply failures in scan/get --json 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 * Keep uninstalled patches as warnings in --json 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 * Check composer/gem docker sync via the manifest 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 * Count only patches apply really applied 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 * Add the Gradle and Maven inline digests to the pending list 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
gradle_cache.rs, jvm_jar.rs and sidecars/maven.rs no longer compute digests inline on main (they go through utils::digest), so production_digests_go_through_the_helpers fails on the stale list. The test's own doc says to drop a file once it moves onto the helpers. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
main is red on production_digests_go_through_the_helpers: #690 moved gradle_cache.rs, jvm_jar.rs and sidecars/maven.rs onto the utils::digest helpers, but PENDING_INLINE_DIGESTS still lists them, and the ratchet fails on a stale entry. Same three-line change as #889 and #980; it no-ops once main carries it. Assisted-by: Claude Code:claude-opus-5-5
|
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 5077cab. Configure here.
|
[agent] Ready for review at 5077cab (merged as 835601b while CI was finishing).
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #677
Summary
Vendor-service retries (the package-reference POST, the archive GET, the capped artifact GET and a resumed deferred GET) now read
Retry-Afterthroughapi::retry::parse_retry_afterand draw their jitter from the seededapi::retry::jitter_sample, on the client'sRetryHooks. The private, weaker copies inapi/client.rsare deleted.Why
Register row C15 (child 1 of tracking #676), living document
doc/07-infra-agent.md("Three retry systems in one module"). Leverage: B 0 (no separate bug issue), U 1 (child 2 of #676, which merges the retry loops, needs this), D ≈2 (retry_after_secs,jitter_sample()), R L. Score ≈4. It was the best candidate that no open PR overlaps.What changed
ApiClient::vendor_retry_hint(status, headers): retryable status →parse_retry_after(headers, hooks.now_unix_secs()). It replaces the threevendor_status_retryable(..).then(|| retry_after_secs(..))sites.vendor_backoff(key, retry, retry_after)takes jitter fromretry_jitter(hooks.jitter_seed, key, retry), keyed by the archive or artifact URL, or by a constant key for the reference POST. It waits onhooks.sleep, which is tokio's sleep by default.VendorRetryPolicy::delaystill maps the sample onto ±25% and caps atmax_delay.retry_after_secs(delta-seconds only) andjitter_sample()(unseededRandomState).28d4d52, cherry-picked unchanged):production_digests_go_through_the_helpersfails onmainbecause Full Gradle support in agent, hosted and vendored modes #646 added inline JVM digests. This is the same change Bound registry downloads by ApiTimeouts instead of a 60 s total deadline (#872) #876 carries, so it becomes a no-op once that PR lands.Size (
git diff --stat)api/client.rs: production +36 / −36 (net 0 after rustfmt wrapping; the two helpers are gone), tests +173 / −2.jvm_jar.rsandsidecars/maven.rs, plus 1 line in the test ratchet.Behavior
Retry-Afteris an HTTP-date now waits until that date, still capped atmax_delay(4 s by default). Before this, it fell back to the 400 ms → 4 s backoff.vendor_status_retryable), same attempt counts and request sequences.Test evidence
every_vendor_retry_loop_honors_an_http_date_retry_after: all four former callers wait exactly 2 s onRetry-After: <date 2 s ahead>. Newan_http_date_retry_after_is_capped_at_max_delaycovers the cap. Newvendor_backoff_jitter_replays_from_the_hook_seed: the same seed gives the same waits, another seed gives different ones, and every wait stays within ±25% of nominal.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-core --lib: 5249 passed. The 4 known root-only failures remain (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…,pypi_requirements::wire_failure_…).cargo test -p socket-patch-cli --all-features --test covgap_commands_vendor --test cli_scan_silent --test covgap_commands_get: all pass except 3 tests that need a read-only directory and fail when run as root in the sandbox (*_state_write_failure_*incovgap_commands_vendor; they chmod a directory). These pass in CI.Risk
Low. One file of production code. The vendor retry suites (
vendor_retry_tests,vendor_prefetchtests) pass with unchanged request sequences.🤖 Generated with Claude Code
https://claude.ai/code/session_01HegUKEf6caV6QASRt7z8iX
Note
Low Risk
Retry timing and jitter change for vendor HTTP calls only; behavior aligns with existing api::retry semantics and is covered by new tests.
Overview
Vendor-service retry backoff now goes through the shared
api::retryhelpers instead of local copies inclient.rs.Retry-Afteris parsed withparse_retry_after(delta-seconds and HTTP-date), usingRetryHooks::now_unix_secs, so a date-based header is honored up tomax_delay. Retry hints are centralized invendor_retry_hintfor package-reference POST, archive GET, and capped artifact GET paths.Backoff jitter comes from
retry_jitter(hooks.jitter_seed, key, retry)—a fixed key for the reference POST, the URL for downloads—and waits usehooks.sleeprather than hard-codedtokio::time::sleep. The oldretry_after_secsand unseededjitter_samplehelpers are removed.New tests assert HTTP-date
Retry-After(2 s wait) across all four retry loops, cap behavior, and deterministic jitter from the hook seed.Reviewed by Cursor Bugbot for commit 5077cab. Configure here.