Remote (Desktop over SSH): reattach replays a bounded output buffer and silently drops the conversation tail — should reconcile from the on-disk transcript
Preflight: searched existing issues — #50982, #58701, #58797, #49790, #26729 are related but distinct (see "How this differs" below); reproduced on the current version.
What's Wrong?
When Claude Desktop is attached to a Claude Code session running on a remote host over SSH, a transient client disconnect (laptop sleep / lid-close / network blip) correctly does not kill the remote session — the claude-ssh daemon keeps the ccd-cli agent running. But on reattach, the tail of the conversation is frequently missing in the Desktop UI — most importantly the final assistant message(s) produced while the client was disconnected — and the "assistant is responding" indicator is not restored. It is intermittent: short turns survive the reconnect, long ones get truncated.
The conversation is not lost: the on-disk transcript on the remote (~/.claude/projects/<proj>/<session>.jsonl) is complete and current. This is purely a reattach re-sync/replay gap in the daemon→client path.
Mechanism (observed via strings on the claude-ssh server binary, package github.com/anthropics/claude-ssh):
- The agent's stdout is streamed to the client as frames; the daemon keeps a bounded in-memory ring of them (
process.bufferedFrame,process.frameSink). - On reconnect,
handlers.(*ProcessHandler).reattachreplays that bounded buffer. The binary contains the string[frameSink] replay write failed, detaching: %v— i.e. replay is written frame-by-frame and aborts on a write failure, dropping everything after that point. - There is no fallback to read the complete transcript on reattach, even though the daemon already exposes
handlers.(*FilesHandler).read.
So output produced during the disconnect that (a) overflows the bounded buffer or (b) trips a mid-replay frameSink write failure is never delivered to the reattached client. The final message of a turn is the most likely casualty, leaving the user staring at a stale last message with no way to know the turn completed — or that an autonomous session did substantial work / a background task finished — without manually reading the remote .jsonl.
How this differs from existing issues
- #50982 (OPEN, Windows — loses history after reboot) and #58701 (CLOSED — "No messages yet" after Desktop restart) are full history loss after a restart. This report is partial tail truncation on a live reattach after a transient disconnect, with the session still running.
- #58797 (CLOSED —
--resumeempty after bridge restart) is the resume path, not live reattach. - #49790 (session should survive disconnect) — the session does survive here; the bug is that the reattach replay is incomplete.
- #26729 (CLOSED — streaming resilience / in-flight snapshots) — this narrows the problem to the concrete reattach-replay path plus a lower-risk fix (read the transcript that already exists, rather than snapshot in-flight state).
What Should Happen?
On reattach the client should always show the complete conversation tail and correct in-flight/idle state, regardless of how much output was produced while disconnected. Concretely: reconcile the reattaching client from the on-disk transcript (the daemon already has FilesHandler.read) — e.g. fall back to a transcript read whenever the buffered replay is incomplete or the frameSink write fails — instead of trusting a bounded, abortable in-memory replay. Restoring the "responding" indicator when the agent is mid-turn on reattach would also resolve the "is it still working?" ambiguity.
Error Messages/Logs
String compiled into the claude-ssh server binary (the silent failure path; nothing is surfaced to the user):
[frameSink] replay write failed, detaching: %v
Relevant reattach/buffer symbols in the binary:
github.com/anthropics/claude-ssh/internal/handlers.(*ProcessHandler).reattach
*handlers.ReattachParams *handlers.ReattachResult
*process.bufferedFrame *[]process.bufferedFrame *process.frameSink
github.com/anthropics/claude-ssh/internal/handlers.(*FilesHandler).read (available, but not used as a reattach fallback)
Steps to Reproduce
- Run a Claude Code session on a remote Linux host; attach from the Claude Desktop app over SSH.
- Have the agent produce a long final message (or a long run of messages / a background task that completes), so a lot of output is generated.
- While that output is being produced (or right after), disconnect the client for a while — close the laptop lid / sleep / drop the network (long enough to exceed the buffer; an overnight disconnect reliably does it).
- Reconnect. The Desktop conversation is missing the tail (often the final assistant message) and the busy indicator is not restored. Confirm
~/.claude/projects/<proj>/<session>.jsonlon the remote holds the full, correct conversation — proving the data exists and only the reattach render is incomplete.
Is this a regression?
Not sure — appears pre-existing across recent versions.
Claude Code Version
Remote ccd-cli 2.1.187; claude-ssh server daemon build dated 2026-06-24.
Platform
Claude Desktop — remote session over SSH.
Operating System
Client: macOS (Claude Desktop). Remote: Linux.
Terminal/Shell
N/A (Claude Desktop GUI; remote bash).
Additional Information
The remote transcript is the source of truth and is always current, so a reattach that reconciles from it (or falls back to it whenever the buffered replay is short or aborts) fully resolves this without the in-flight-snapshot machinery proposed in #26729. Happy to provide per-run daemon-log excerpts (~/.claude/remote/run/<id>/remote-server.log).