You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#2803 widened the per-agent read cap from 20/min → 240/min, fixing the self-inflicted "every card rate-limited" experience on normal dashboard loads. That removed the immediate bug, but the client-side UX if a 429 does legitimately fire is still weak.
Surface: server/public/dashboard-agents.html.
What's broken today
Parallel fan-out with no concurrency cap.loadAgents fires Promise.allSettled(agents.map(fetchAgentState)) — every request for every card in flight at once. A heavy user (80+ agents, cold page, hard reload spam) can still self-429.
Response body retryAfter is ignored.fetchWithRetry (line ~577) reads the Retry-After header only. fix(dashboard): stop applying bulk-resolve cap to per-agent reads #2803 drops the hardcoded body field because of this, but the underlying client behavior could stand to fall back to the body when the header is stripped by a proxy.
"Retry in a moment" is vague. No countdown, no disabled state on a second retry, no explanation. A user who refreshes hard gets cryptic error cards with no path forward.
Proposed work
Add a p-limit (or hand-rolled) concurrency cap of ~6 in loadAgents so the fan-out is naturally rate-friendly. (Each agent is 2 requests against agentReadRateLimiter + 1 request via the separate auth-status middleware, so 6 in-flight agents = ~18 requests at any instant.)
When a 429 fires on a card, surface actual wait time from the RateLimit-Reset / Retry-After header, render a live countdown ("Retry in 28s…"), auto-retry once when it hits zero, then fall back to a manual retry button with "You've refreshed too quickly — wait 30s" guidance.
As a fallback, parse any numeric hint from the response body if the header is missing.
Context
#2803 widened the per-agent read cap from 20/min → 240/min, fixing the self-inflicted "every card rate-limited" experience on normal dashboard loads. That removed the immediate bug, but the client-side UX if a 429 does legitimately fire is still weak.
Surface:
server/public/dashboard-agents.html.What's broken today
Parallel fan-out with no concurrency cap.
loadAgentsfiresPromise.allSettled(agents.map(fetchAgentState))— every request for every card in flight at once. A heavy user (80+ agents, cold page, hard reload spam) can still self-429.Response body
retryAfteris ignored.fetchWithRetry(line ~577) reads theRetry-Afterheader only. fix(dashboard): stop applying bulk-resolve cap to per-agent reads #2803 drops the hardcoded body field because of this, but the underlying client behavior could stand to fall back to the body when the header is stripped by a proxy."Retry in a moment" is vague. No countdown, no disabled state on a second retry, no explanation. A user who refreshes hard gets cryptic error cards with no path forward.
Proposed work
loadAgentsso the fan-out is naturally rate-friendly. (Each agent is 2 requests againstagentReadRateLimiter+ 1 request via the separate auth-status middleware, so 6 in-flight agents = ~18 requests at any instant.)RateLimit-Reset/Retry-Afterheader, render a live countdown ("Retry in 28s…"), auto-retry once when it hits zero, then fall back to a manual retry button with "You've refreshed too quickly — wait 30s" guidance.Out of scope