fix(roster): a rate-limited subscription is a 503 with a reason, not a 500

The retry landed and still failed: all four attempts returned 429. A bare
16-token probe with the same token, straight from gw-04, also returned 429
with `x-should-retry: true` — the Claude Code subscription itself is limited
right now, and no amount of backoff inside one HTTP request will outlast it.

So stop pretending it is a server bug. New `ApiError::Unavailable` → 503,
carrying the one sentence the operator can act on ("clears on its own; try
again shortly"), instead of an opaque `internal error` that sends them into
the logs. The harness now prints the response body rather than the generic
"the planner produced no usable proposal", which is what hid both walls —
first the credit balance, now this.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-08 15:50:29 -07:00
co-authored by Claude Opus 5
parent c3c4447810
commit deed591da6
4 changed files with 34 additions and 4 deletions
+3 -1
View File
@@ -126,7 +126,9 @@ pub async fn suggest(
.await
.map_err(|e| {
eprintln!("mission {id}: roster proposal failed: {e}");
ApiError::Internal
// A rate-limited subscription is a 503 the operator can act on, not
// a 500 that reads as "this server is broken".
crate::subscription::as_api_error(&e)
})?;
// A model that answered with prose around its JSON has still answered; a