perf(ca): attach by probing the route instead of polling status to RUNNING - #1098
Merged
Conversation
…NNING The launcher and `ca ssh` waited for status RUNNING, which arrives only after route publication, a Kafka projection hop, and a poll interval — none of which the shell needs. Probe the SSH route (asking the guest to echo a marker) every 750ms and attach on first success; status is still read each round to catch crashed/failed/deleting early. Success is the marker round-tripping, never the exit code: the relay answers a session against a target it cannot route yet with a status JSON and a clean exit 0, so `ssh <target> true` reports success seconds before a command can actually run (verified against prod). Pairs with the server routing agents at STARTING_WORKLOAD (mono #35462); without it the probe simply succeeds at RUNNING, which still beats the old 2s poll that `railway code` was using. Removes the now-unused ensure_running and code.rs's private wait_until_running. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
codyde
force-pushed
the
cody/ca-eager-attach
branch
from
August 13, 2026 16:30
daac0ac to
e85aa7e
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
codyde
added a commit
that referenced
this pull request
Aug 15, 2026
* fix(ca): an agent with a pane open is not still "waking" Create a new cloud agent, land in its session, and the tree still calls it "waking" while you are typing into it. The row renders the platform's raw status, and `sorted_agents` already documents the vocabulary: "running first, waking next" — where the middle rank is `starting`. That status lags routability by design. #1098 established the gap: the platform routes a shell as soon as the container exists, while RUNNING additionally waits on route publication, a projection hop, and a poll interval. The launcher stopped gating attach on it and started probing the route instead — but the tree kept rendering the value the CLI itself had already decided not to trust. An attached, unfinished pane is direct evidence the agent is up: there is a shell running on it. So believe the pane over the projection. Scoped to `starting` only. A pane can outlive what it is attached to, and sleeping/crashed/failed/deleting alongside a live pane is news the user needs rather than latency to paper over. Three places, so display and state agree: - the row's status and note read `running` once a pane is open; - `sorted_agents` ranks on the displayed status, so the row does not sit in the waking band while reading `running`; - `attach_session` clears a settled wake op and its watch. A pane opening proves the wake landed, and it stops the 1.5s watch tick polling that environment for up to WAKE_PATIENCE about an agent already taking keystrokes. Sleep and delete are untouched — neither is proven by an attach. Known limitation, measured after the fact: a 10-agent benchmark put the routine lag at a few seconds, closing before the session is usable (status reached RUNNING before the harness answered in all 10 runs). The case that prompted this was an agent that stayed `starting` and had to be deleted — and this change would render that one `running` too, hiding the only signal something was wrong. A follow-up should bound the override to a short grace window after attach and surface a persistent `starting` explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ca): stop excusing a starting agent once the pane is old The override shipped in the previous commit was unbounded: any open pane made a `starting` agent read `running`, forever. That hides the case that prompted the fix — an agent that stays `starting`, routes SSH fine, and has to be deleted. The row would have painted it green and removed the only signal the user had. Bound it to the window the lag actually lives in. Measured over ten `railway code --claude --new` runs, status reached RUNNING 5.3–14.2s after create, before the harness answered every time. 45s from pane open is generously past that, so a slow-but-healthy boot never flickers into looking stuck, while an agent still `starting` past it goes back to reporting what the platform says. `agents_with_live_panes` now carries how long each pane has been open rather than just which agents have one, via a new `Session::open_for`. The decision stays a pure function of (status, pane age), so the boundary is unit-tested directly instead of through a fabricated Session clock. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- 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.
What
railway codeandrailway ca sshpolled agent status until RUNNING before attaching. RUNNING lands ~2.5-3s after the shell is actually connectable: it waits out route publication the tunnel doesn't use, a ~0.7s status-projection hop, and a poll interval (code.rs still had its own 2s poll — the adaptive poll from #1095 never covered it).Now: probe the route directly every 750ms and attach on the first success. Status is still fetched each round, but only to fail fast on crashed/failed/deleting. Applies to create, wake, and
ca sshpaths.The probe is output-verified, not exit-code-verified
Found during live testing: the relay answers a session against a target it can't route yet with a status JSON and a clean exit 0, so
ssh <target> truereports success seconds before a command can run — an exit-code probe attaches prematurely and the session dies with "Provisioning produced no status marker". The probe asks the guest toechoa marker and succeeds only when the marker round-trips.Server pairing
Pairs with mono #35462 (route agents at STARTING_WORKLOAD). Ships safely in either order: against today's servers the probe first succeeds at RUNNING — verified live with a dev build (prompt at 14.5s, same as release; the ~3s win arrives when the server side deploys).
Cost
2-5 probe connections per launch through the relay (each is one route auth + a no-op session), inside the per-IP budget.
Testing
🤖 Generated with Claude Code