fix(host): flush queued output before closing clients on shutdown - #49
Open
aksOps wants to merge 1 commit into
Open
fix(host): flush queued output before closing clients on shutdown#49aksOps wants to merge 1 commit into
aksOps wants to merge 1 commit into
Conversation
pumpPTY has already returned by the time shutdown runs, so everything the agent ever wrote is sitting in each client's queue. Closing the connection there truncated the agent's last screen, and cutting a frame in half reached the viewer as "attach output: unexpected EOF" with a non-zero exit instead of a clean "[uam: session ended]". Clients now carry a second signal: done still means stop now, flush means finish first. Shutdown asks every client to flush, then waits up to shutdownFlushWindow for each writer to drain its queue and close the connection itself. A viewer that has stopped reading cannot hold teardown open — the same window bounds both the socket write deadline and the wait — and a client with no writer running is force-closed when the window expires. Verified end to end against the built binary: an agent that prints 60 lines and exits while a client is attached now delivers every line, prints the session-ended note, and the attach client exits 0. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01X3PrstXFvYdemQrz8Vb1b1
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.
First of the three findings #48 verified but deferred.
The defect
pumpPTYruns to EOF on the host's main goroutine, so by the timeshutdownruns, everything the agent ever wrote is already queued on each client.shutdownthen calledclient.drop(), which closes the connection immediately — the writer never got to put the queue on the wire.Two symptoms, both on the viewer:
io.ErrUnexpectedEOF, which is notio.EOF, socopyAttachOutputConfiguredreturns it and the attach client exits non-zero withattach output: unexpected EOFinstead of printing[uam: session ended].The fix
attachClientgains a second signal.donestill means stop now;flushmeans finish, then stop.attachWriterselects on both — onflushit drains whatever is queued, then closes the connection itself.shutdownasks every client to flush and waits up toshutdownFlushWindow(250ms) for each to close. The same window is the socket write deadline, so a viewer that stopped reading cannot hold teardown open, and a client registered without a running writer is force-closed when the window expires.shutdownis split so the client half (shutdownClients) is testable without the record/file side effects.Verification
go test ./...— 1160 pass, 8 skip. The one failure,providerstate.TestResolvedAncestryWarnsForWritableComponent, reproduces unchanged onmain(environment-dependent; it's the next item on the list).go test -race ./internal/session/...— clean.go vet,gofmt,golangci-lint— clean.done(the contrast that makes the distinction meaningful); shutdown bounded against a stalled viewer; shutdown closes a client that has no writer. Removing theflusharm fromattachWritermakes the first test fail — checked.attached delivers every line, prints
[uam: session ended], nounexpected EOF, attach exits 0. Note this casepasses against a pre-fix binary too — on a fast local socket the writer keeps up, so it is a smoke test of the
clean-exit path, not a reproduction. The discriminating evidence is the unit test above.
🤖 Generated with Claude Code
https://claude.ai/code/session_01X3PrstXFvYdemQrz8Vb1b1