fix(ca): stop scanning every environment on startup - #1056
Merged
Conversation
A user with several workspaces got rate limited opening `railway ca`. The
startup sweep asked for agents in every environment of every project of every
workspace, one request each: 6 workspaces of 30 projects with 2 environments is
361 requests before the tree finishes drawing, on every launch, uncached. There
is no workspace- or user-scoped listing to ask instead — `cloudAgents` requires
an environment ID — so the fan-out was forced by the query, but doing it for the
whole account at startup was not.
Startup now loads only what a keypress immediately needs:
- the prompt's target, which New Session reads to decide whether it has an
agent to work on
- the default project, which the tree leads with
- environments this machine has launched an agent in, from the CLI's own
records, so your own work is where you left it
That is two or three requests instead of hundreds. Everything else loads when
its row is expanded, which the tree already did.
`shift+r` runs the old whole-account scan on request, which is how an agent
made elsewhere — another machine, the dashboard, a project you have never
opened — still gets found without expanding projects one at a time. The cost is
the user's to spend rather than charged to every launch.
Rate limits are now handled rather than absorbed:
- a 429 in any background fetch stops the rest of its batch, instead of
spending the remaining budget on requests that will also be refused
- environments still in flight go back to "not loaded", so opening one
retries rather than showing a spinner that never resolves
- `Retry-After`, which backboard sets on every 429, is read and reported:
"Rate limited — try again in 43s" rather than "please try again later"
The real fix is server-side and small: `CloudAgent` rows carry `userId` and
`projectId`, so "every agent I own" is one indexed query. A user-scoped listing
would make counts-everywhere cost one request instead of hundreds, and retire
`shift+r`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A user with several workspaces got rate limited opening
railway ca. Startup asked for agents in every environment of every project of every workspace, one request each — 361 on a six-workspace account, on every launch, uncached.cloudAgentsrequires anenvironmentId, so the fan-out is forced by the query; doing it for the whole account at startup was not.Startup now loads only what a keypress immediately needs: the prompt's target, the default project's environments, and environments this machine has launched an agent in, from the CLI's own config. That's two or three requests, and everything else loads when its row is expanded, which the tree already did.
shift+rruns the old whole-account scan on request — that's how an agent created elsewhere (another machine, the dashboard, a project you've never opened) still gets found, with the cost spent when asked for rather than charged to every launch.Rate limits are handled rather than absorbed. The first 429 stops the rest of the batch instead of spending the remaining budget on requests that will also be refused. Environments still in flight go back to "not loaded," so opening one retries instead of spinning forever. And
Retry-After, which backboard sets on every 429 and the CLI discarded, is now surfaced: "Rate limited — try again in 43s."For context, the limiter is 1,000 requests per hour, keyed on the token's workspace or user, and only applies to API-token auth —
railway loginsessions are exempt,RAILWAY_API_TOKENis not. At 360 environments, three launches on a token exceed it.The real fix is server-side and lands separately: railwayapp/mono#34876 adds
myCloudAgents, a user-scoped listing that answers "where are my agents" in one query. Once it deploys, a follow-up PR makes it the primary startup path and retiresshift+r.Verification
869 tests, fmt and clippy clean. New tests cover: startup loads only target, default-project, and known environments, claiming each once;
shift+rscans the rest; and a rate limit releases what was in flight so a retry works.🤖 Generated with Claude Code