gstack/design/src
Stefan Neamtu 4bdb02070f fix(design): honor Retry-After header in variants 429 handler
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>
2026-05-06 12:01:52 +02:00
..
auth.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
brief.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
check.ts fix: close redundant PRs + friendly error on all design commands (v0.15.8.1) (#817) 2026-04-05 02:02:06 -07:00
cli.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
commands.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
compare.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
design-to-code.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
diff.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
evolve.ts fix: close redundant PRs + friendly error on all design commands (v0.15.8.1) (#817) 2026-04-05 02:02:06 -07:00
gallery.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
generate.ts fix: user-friendly error when OpenAI org is not verified (#776) 2026-04-05 00:09:32 -07:00
iterate.ts fix: close redundant PRs + friendly error on all design commands (v0.15.8.1) (#817) 2026-04-05 02:02:06 -07:00
memory.ts feat: design binary — real UI mockup generation for gstack skills (v0.13.0.0) (#551) 2026-03-27 20:32:59 -06:00
serve.ts security: tunnel dual-listener + SSRF + envelope + path wave (v1.6.0.0) (#1137) 2026-04-21 21:58:27 -07:00
session.ts fix: security wave 3 — 12 fixes, 7 contributors (v0.16.4.0) (#988) 2026-04-13 07:49:37 -10:00
variants.ts fix(design): honor Retry-After header in variants 429 handler 2026-05-06 12:01:52 +02:00