[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: register comment.
Kind: refactor. Source: review Part 7.2 and R12; register row C15, child 1 of the C15 tracking issue.
Problem
api/client.rs keeps private copies of two retry helpers that api/retry.rs already provides publicly, and the copies are weaker:
Proof by execution on 045d7ec (a temporary unit test inside client.rs, run twice, not committed). Given Retry-After: Fri, 27 Mar 2026 19:12:42 GMT and "now" set 62 s before that date:
AUDIT vendor=None api=Some(62s)
AUDIT status 500 vendor_retryable=true api_retryable=false
AUDIT status 502 vendor_retryable=true api_retryable=false
AUDIT status 504 vendor_retryable=true api_retryable=false
So a vendor-service 429 or 503 carrying an HTTP-date Retry-After is retried on the vendor backoff (400 ms → 4 s) instead of at the time the server asked for.
Symptoms
None filed. Impact: low risk, small. This is the first, mechanical step of C15, and it shrinks the second retry implementation to its policy plus its classifier.
Proposed change
VendorRetryPolicy::delay takes api::retry::parse_retry_after(headers, now), still capped at max_delay.
- Jitter comes from
api::retry::jitter_sample, keyed by URL and attempt, with a seed from ApiRetry. Map the sample onto the ±25% spread so the range stays the same.
- Delete
retry_after_secs and jitter_sample() from client.rs.
- Keep
vendor_status_retryable as the vendor classifier. Its 5xx set differs on purpose, and child 2 of the tracking issue unifies the classifiers.
Behavior change: HTTP-date Retry-After is now honored on vendor calls, still capped at max_delay (4 s). Nothing else changes.
Size and scope
api/client.rs only, roughly −30/+15 production lines plus tests. Out of scope: the loops themselves (child 2) and fetch_binary retry (child 3).
Acceptance criteria
Dependencies
None. This blocks child 2 of the tracking issue.
[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: register comment.
Kind: refactor. Source: review Part 7.2 and R12; register row C15, child 1 of the C15 tracking issue.
Problem
api/client.rskeeps private copies of two retry helpers thatapi/retry.rsalready provides publicly, and the copies are weaker:Retry-After.retry_after_secsreads delta-seconds only. Its doc comment says "the HTTP-date form is ignored."api::retry::parse_retry_afterreads both forms, usingapi/date.rs.jitter_sample()draws from aRandomStatehasher, so its waits can't be replayed in tests.api::retry::jitter_sample(seed, key, retry)is seeded and deterministic.Proof by execution on
045d7ec(a temporary unit test insideclient.rs, run twice, not committed). GivenRetry-After: Fri, 27 Mar 2026 19:12:42 GMTand "now" set 62 s before that date:So a vendor-service 429 or 503 carrying an HTTP-date
Retry-Afteris retried on the vendor backoff (400 ms → 4 s) instead of at the time the server asked for.Symptoms
None filed. Impact: low risk, small. This is the first, mechanical step of C15, and it shrinks the second retry implementation to its policy plus its classifier.
Proposed change
VendorRetryPolicy::delaytakesapi::retry::parse_retry_after(headers, now), still capped atmax_delay.api::retry::jitter_sample, keyed by URL and attempt, with a seed fromApiRetry. Map the sample onto the ±25% spread so the range stays the same.retry_after_secsandjitter_sample()fromclient.rs.vendor_status_retryableas the vendor classifier. Its 5xx set differs on purpose, and child 2 of the tracking issue unifies the classifiers.Behavior change: HTTP-date
Retry-Afteris now honored on vendor calls, still capped atmax_delay(4 s). Nothing else changes.Size and scope
api/client.rsonly, roughly −30/+15 production lines plus tests. Out of scope: the loops themselves (child 2) andfetch_binaryretry (child 3).Acceptance criteria
grep -n "fn retry_after_secs\|fn jitter_sample" crates/socket-patch-core/src/api/client.rsfinds nothing.429with an HTTP-dateRetry-After2 s ahead waits about 2 s, not the policy backoff, using a wiremock server and a shortmax_delayoverride.client.rsandvendor_prefetch.rs(with_vendor_retry) stay green, with no change to their request sequences.Dependencies
None. This blocks child 2 of the tracking issue.