Skip to content

feat(ca): answer "where are my agents" with one myCloudAgents request - #1062

Merged
codyde merged 3 commits into
masterfrom
cody/ca-my-cloud-agents
Aug 9, 2026
Merged

feat(ca): answer "where are my agents" with one myCloudAgents request#1062
codyde merged 3 commits into
masterfrom
cody/ca-my-cloud-agents

Conversation

@codyde

@codyde codyde commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1056; lands after it and after railwayapp/mono#34876 deploys.

#1056 stopped the startup fan-out by loading less — the price was that a project's agent count only appeared when you opened it, with shift+r as the whole-account escape hatch. This restores full visibility at startup without the fan-out: one myCloudAgents request returns every agent the caller owns, and the tree settles entirely from that answer. Agents land in their environments; an environment absent from the reply is loaded as empty, since absence from a complete answer means "none of yours here" rather than "unknown". An environment that already answered (a launch just filled the target) keeps what it has, which may carry session state the account-wide reply doesn't.

The fallback is the code #1056 shipped, unchanged. Against a backboard that predates the field, the request fails with an unqueryable-field error and startup degrades to the keypress-driven loads, the shift+r sweep, and the 429 handling. A rate limit on the new request itself surfaces the same Retry-After toast rather than silently falling back, which would spend more requests against a refusal. schema.json was regenerated from the mono working tree with dump-public-introspection.ts so this could be written before the backboard change deploys; the CLI must still release after backboard, or every launch takes the fallback path.

shift+r stays for the fallback path. On a current backboard it reports every project as already loaded, which at startup is now simply true.

Verification

873 tests, fmt and clippy clean. New tests: one myCloudAgents reply settles every environment (agents placed by environment, absent environments loaded as empty, unknown environments ignored), and environments that already answered keep their contents.

🤖 Generated with Claude Code

An adversarial review pass then hardened the path (second fix commit): session prefetch is bounded by the same five-permit discipline as the sweep, sessions replies resolve by agent id instead of positional index, the settle no longer overwrites in-flight environments, a stale rate-limit stop can't strand the fallback as permanent spinners, user-driven loads classify 429s like background ones, and re-entry skips the request a settled tree would discard.

@codyde codyde added the release/minor Author minor release label Aug 9, 2026
Base automatically changed from cody/ca-rate-limit to master August 9, 2026 02:56
@codyde codyde added release/patch Author patch release and removed release/minor Author minor release labels Aug 9, 2026
codyde and others added 3 commits August 8, 2026 22:19
Built with backboard's dump-public-introspection.ts against the branch that
adds myCloudAgents (railwayapp/mono#34876), so the CLI change that consumes
the field can be written before the backboard change deploys. The bulk of the
diff is drift between the deployed schema and master plus field ordering; the
whole binary still compiles against it, which is the check that matters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Startup previously loaded only what a keypress immediately needs — the
target, the default project, environments this machine had used — because
asking `cloudAgents` for the whole account was one request per environment,
and hundreds of requests rate limited real users. That kept launches cheap by
giving up on full visibility.

`myCloudAgents` (railwayapp/mono#34876) removes the trade. One request
returns every agent the caller owns across the account, so startup now asks
that first and settles the entire tree from the answer: agents land in their
environments, and every environment absent from the reply is loaded as empty,
because absence means "none of yours here" rather than "unknown". An
environment that already answered — a launch just filled the target — keeps
what it has, which may carry session state the account-wide reply doesn't.

Against a backboard that predates the field, the request fails with an
unqueryable-field error and startup degrades to exactly what it did before:
the keypress-driven loads, the per-environment sweep behind `shift+r`, and
the 429 handling, all unchanged. A rate limit on the new request itself gets
the same Retry-After toast the sweep produces, not a silent fallback that
would spend more requests against a refusal.

`shift+r` stays for the fallback path. On a backboard with the field it
reports every project as already loaded, which is now simply true at startup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An adversarial review pass found four ways the new startup path could
misbehave, all fixed:

Session prefetch is bounded. The account-wide settle names every running
agent at once, and the prefetch fired one sessions request per agent
simultaneously — the burst shape this TUI exists to avoid, reintroduced one
layer down. Prefetch now runs through the same five-permit,
stop-on-first-429 discipline as the environment sweep.

Sessions replies follow their agent. A reply was applied to whatever agent
sat at the index the request went out with; an environment refetch inserting
a newer agent above it would attach the sessions to the wrong row. The reply
now carries the agent id and resolves by it, going nowhere if the agent is
gone.

The settle no longer overwrites in-flight environments. An environment in
Loading has a request issued after the account-wide one, so its reply is
strictly newer; settling over it could briefly hide an agent created since
the snapshot — permanently, if that newer fetch had already failed. Loading
environments keep their spinner, and the settle arm now also runs
expand_pending as the safety net for a launch whose own refetch died.

A stale stop flag can't strand the fallback. If a 429 had set the
stop-everything flag before myCloudAgents failed over, the fallback sweep
marked environments Loading and then aborted without ever answering —
spinners forever. The flag is cleared before the fallback, and when the
fallback has nothing to load the error is surfaced in the status line
instead of vanishing (an expired login previously looked like an account
with no agents anywhere).

Also from review: user-driven environment loads (expand, per-row r, the
post-launch refetch) now classify a 429 like every background fetch does —
row back to "not loaded" plus the Retry-After toast, rather than pinning
"rate limited" to one environment as its own failure — and re-entry from a
session skips the account-wide request the already-settled tree would
discard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codyde
codyde force-pushed the cody/ca-my-cloud-agents branch from cac6b23 to 1df73fe Compare August 9, 2026 05:20
@codyde
codyde merged commit df14f09 into master Aug 9, 2026
9 checks passed
@codyde
codyde deleted the cody/ca-my-cloud-agents branch August 9, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release/patch Author patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant