Closes#1244.
The 429 handler in `generateVariant` discarded the `Retry-After` response
header and fell straight through to a local exponential schedule (2s/4s/8s).
In image-generation batches, that burns retry attempts inside the provider's
cooldown window and the request never recovers.
Now we parse `Retry-After` per RFC 7231 — both delta-seconds (`Retry-After: 5`)
and HTTP-date (`Retry-After: Fri, 31 Dec 1999 23:59:59 GMT`). Honored waits
are capped at 60s to bound stalls from hostile or buggy headers. Delta-seconds
are validated as digits-only (rejects `2abc`). When `Retry-After` is honored
(including 0 / past-date "retry now"), the next iteration's leading exponential
sleep is skipped so we don't double-wait. Invalid or missing headers fall
through to the existing exponential schedule unchanged.
Behavior matrix:
| Header | Behavior |
|---------------------------------|-------------------------------------------|
| Retry-After: 5 | wait 5s, skip leading on next attempt |
| Retry-After: 999999 | capped to 60s, skip leading |
| Retry-After: 2abc | invalid, fall through to exponential |
| Retry-After: 0 | wait 0, skip leading (retry immediately) |
| Retry-After: <past HTTP-date> | wait 0, skip leading |
| Retry-After: <future date> | wait diff capped at 60s, skip leading |
| no header | fall through to existing exponential |
`generateVariant` now accepts an optional `fetchFn` parameter (defaults to
`globalThis.fetch`) so tests can inject a stub. Production call sites are
unchanged.
Tests cover the five behavior buckets above, asserting both the 1st-to-2nd
call timing gap and call counts. All five pass in ~8s.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>