Repository navigation
Publish terminal exit at once, and survive feedback that crosses a close - #48
Open
felixhromadko wants to merge 3 commits into
Open
felixhromadko wants to merge 3 commits into
felixhromadko wants to merge 3 commits into
Conversation
A PTY's exit reached clients only through the terminal catalogue, which a connection refreshed after writing a view's final frame or on its 100 ms poll. The first frame waits out the 20 ms initial grace, so every short command looked about 20 ms slower than it ran. cleanup_pty_internal now bumps a retained watch after the output is drained and the exit status collected. Every Terminal connection subscribes before its initial snapshot and republishes the catalogue when it changes. Display frames keep their own schedule.
A client can present and acknowledge a frame, or type into a view, after the server has already removed that view: the client closed the terminal, closed the view, or the catalogue retired it. The server rejected feedback for any unknown view, and the reliable event loop treats that as a connection failure, so a valid late FRAME_ACK dropped the whole connection with every request in flight. Removed terminal views now leave a bounded record of their last acknowledged and last sent sequence. Late feedback for one is accepted and its input dropped, as Surface already does for retired views; feedback naming an unsent frame or an unknown view still ends the session. Publishing exit state ahead of the final frame makes this race much more frequent, because callers close a finished terminal while its last frame is still in flight.
Workspace focus falls back to another session whenever one closes, and that session can close in the same moment. SET_FOCUS then fails with NotFound, and because the request was fire-and-forget the rejection went unhandled, which terminates a Node embedder.
Coverage
|
pcarrier
approved these changes
Oct 9, 2026
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 short command run through a YAS PTY took about 25 ms longer than the command itself. The server published the exit only after the view's first display frame, and that frame waits out the 20 ms
INITIAL_FRAME_GRACE. This PR publishes the exit as soon as the output is drained. It also fixes two races that the faster exit makes more frequent.Commits
cleanup_pty_internalbumps a retainedwatchafter the output drain and exit-status collection. Each Terminal connection subscribes before its initial snapshot, then refreshes and publishes the catalogue when the watch changes. Display frames keep their own schedule.reliable frame handler failed: family=16 kind=34). Removed views now leave a bounded record (256) of their acked and sent sequences. Late feedback is accepted and its input dropped, as Surface already does. Feedback for an unsent frame or an unknown view still ends the session. The spec is updated (docs/design/yas.md, Frame feedback).SET_FOCUSthen rejects with NotFound, and nothing handles the rejection, which kills a Node embedder.Commit 1 on its own made the disconnect in commit 2 about 3× more frequent under parallel load (7 → 24 drops in 2,400 commands on a v0.3.0 backport). Without commit 2, it is not safe to merge.
Latency
Local Unix socket on an 8-CPU Linux sandbox, 500 runs plus 5 warmups, medians. Commands run through neo's real
BlitShellClient, with the published@yas-run/core0.3.0.BlitShellClient.run("uptime")bash -c uptimebash -lc uptimeThe exit wait drops from ~25 ms to the command's own run time. On the same box,
bash -lc uptimewithout a PTY takes 11–13 ms.Stress: output capture and disconnects
20 command shapes, 30 rounds, ~1,200 commands per run. A third of the runs go 8 at a time on one connection. The shapes include no output, no trailing newline, 99/100/101 lines (tail boundary), 3,000 and 5,000 lines, a 20,000-char line, Unicode, stderr, exit 3, SIGKILL, CR overwrite, colors, alt screen, a background child holding the PTY, and output written right before exit. Each result is compared byte for byte with a golden capture.
FRAME_ACK(4 runs)The runs use both SDKs (0.3.0 and this branch's
js/core), with and without a second client that opens a view on every terminal, like a browser panel. The fixes also hold on a v0.3.0 backport through neo's path: 0 drops in 2,400 commands.Display
uplink.specfails in both because the uplink fixture is not built here.reconnect.specis flaky on main as well: in a 3× repeat of reconnect and terminal specs, main failed 1 of 33 and this PR passed 33 of 33.Checks
cargo test -p yas-server --lib: 944 passed. This includes the newterminal_exit_update_follows_drained_output_onceandterminal_feedback_crossing_close_is_ignored_but_unsent_frames_are_rejected.cargo clippy -p yas-server --all-targets -D warningsandcargo fmt --checkare clean.js/corevitest: 1,528 passed. The new SET_FOCUS test fails without the fix.tsc --noEmitis clean.Not covered: macOS, which has a separate ~95 ms fd-close cost in
close_fds_except.Tag
@indentto continue the conversation here.