Tracked in techdebt/3-3-restore-replays-a-torn-alt-screen-snapshot-for-ssh.md (Medium / Medium). Found during code review of #173; not a regression from it.
Issue
#173 stops the daemon replaying a ring buffer into a pane whose child is on the alternate screen, because such a stream carries frame deltas rather than history. The decision reads pane.MouseModes.altScreen, which the daemon learns by scanning the pane's own output.
MouseModes is deliberately not persisted — it starts at the zero value after a daemon restart and only becomes true once this incarnation's child re-emits its enable sequence. On the first attach after a restore, therefore, the daemon cannot know the pane is on the alternate screen, and the ghostsnap replay proceeds using the snapshot loaded from disk.
For claude-code that is harmless: restoresOwnHistory (strategy preassign_id) already skips the restore replay for a separate reason. But ssh and stripe are ghost_buffer = true with strategy rerun and no such skip. A pane running vim, less or htop over ssh when the daemon restarts has its torn alt-screen snapshot replayed on the first attach — the symptom #172 fixes on the live-reconnect path.
Why it wasn't fixed in #173
Pre-existing behaviour: before #173 that replay happened too, and #173 does not make it worse. Closing it needs a design decision, and the options are not obviously ranked:
- Persist the alt-screen bit with the snapshot. Cheapest to reason about, but
MouseModes is documented as broadcast-not-persisted, and adding a field to the workspace format for it invites the question of what else belongs there.
- Track "never scanned this incarnation" and treat an unscanned pane conservatively. But conservative in which direction? Skipping the replay for every freshly restored pane would break the common, correct case — a
terminal pane replaying its history after a restart, which is the feature working as intended.
- Sniff the snapshot itself for an unbalanced
?1049h when loading it from disk. Self-contained, no format change, but it is a second parser over the same bytes and inherits the ring-wrap problem: a snapshot that begins after the enable looks main-screen.
What would settle it
The same method #173 used on its own premise: capture a real ssh-hosted full-screen pane, restart the daemon, and replay the loaded snapshot through the client emulator (PaneModel.AppendOutput + vtRow) to see what the grid actually contains.
Worth checking first whether it needs code at all — rerun re-executes the command on restore, so the respawned child may repaint over the replay anyway. If it does, this is a comment rather than a fix.
Tracked in
techdebt/3-3-restore-replays-a-torn-alt-screen-snapshot-for-ssh.md(Medium / Medium). Found during code review of #173; not a regression from it.Issue
#173 stops the daemon replaying a ring buffer into a pane whose child is on the alternate screen, because such a stream carries frame deltas rather than history. The decision reads
pane.MouseModes.altScreen, which the daemon learns by scanning the pane's own output.MouseModesis deliberately not persisted — it starts at the zero value after a daemon restart and only becomes true once this incarnation's child re-emits its enable sequence. On the first attach after a restore, therefore, the daemon cannot know the pane is on the alternate screen, and theghostsnapreplay proceeds using the snapshot loaded from disk.For
claude-codethat is harmless:restoresOwnHistory(strategypreassign_id) already skips the restore replay for a separate reason. Butsshandstripeareghost_buffer = truewith strategyrerunand no such skip. A pane runningvim,lessorhtopover ssh when the daemon restarts has its torn alt-screen snapshot replayed on the first attach — the symptom #172 fixes on the live-reconnect path.Why it wasn't fixed in #173
Pre-existing behaviour: before #173 that replay happened too, and #173 does not make it worse. Closing it needs a design decision, and the options are not obviously ranked:
MouseModesis documented as broadcast-not-persisted, and adding a field to the workspace format for it invites the question of what else belongs there.terminalpane replaying its history after a restart, which is the feature working as intended.?1049hwhen loading it from disk. Self-contained, no format change, but it is a second parser over the same bytes and inherits the ring-wrap problem: a snapshot that begins after the enable looks main-screen.What would settle it
The same method #173 used on its own premise: capture a real
ssh-hosted full-screen pane, restart the daemon, and replay the loaded snapshot through the client emulator (PaneModel.AppendOutput+vtRow) to see what the grid actually contains.Worth checking first whether it needs code at all —
rerunre-executes the command on restore, so the respawned child may repaint over the replay anyway. If it does, this is a comment rather than a fix.