Summary
Every method on CloudApiClient (packages/arkor/src/core/client.ts) is single-shot (rg -in "retry" src/core/client.ts → no hits). The SSE stream gets a full reconnect state machine in trainer.ts, but plain reads (job detail, deployment list/get, ...) fail on the first transient 503 / 429 / network blip. Studio surfaces these as errors the user has to manually refresh past, and SDK callers see avoidable throws.
Proposal
A small bounded retry for idempotent READ operations only:
- attempts: 3 total; delay: full-jitter exponential (base 300 ms, ×2, cap 2 s);
- retry on network-level errors and 408 / 429 / 5xx; never on other 4xx;
- explicitly NOT retried:
createJob, cancelJob, deployment mutations, chat, openEventStream (POSTs have no upstream idempotency keys; the stream already has its own reconnect logic in trainer.ts and must not be double-retried).
Injectable sleep via CloudApiClientOptions (next to the existing fetch seam) keeps tests instant and deterministic.
(A PR implementing this, with vitest coverage incl. "createJob is never retried", is ready to follow this issue.)
Summary
Every method on
CloudApiClient(packages/arkor/src/core/client.ts) is single-shot (rg -in "retry" src/core/client.ts→ no hits). The SSE stream gets a full reconnect state machine intrainer.ts, but plain reads (job detail, deployment list/get, ...) fail on the first transient 503 / 429 / network blip. Studio surfaces these as errors the user has to manually refresh past, and SDK callers see avoidable throws.Proposal
A small bounded retry for idempotent READ operations only:
createJob,cancelJob, deployment mutations,chat,openEventStream(POSTs have no upstream idempotency keys; the stream already has its own reconnect logic intrainer.tsand must not be double-retried).Injectable sleep via
CloudApiClientOptions(next to the existingfetchseam) keeps tests instant and deterministic.(A PR implementing this, with vitest coverage incl. "createJob is never retried", is ready to follow this issue.)