fix(ssh): stop the reconnect prepaint firing after its mount is spent

Regression I introduced with the snapshot-first reconnect paint. The payload path
consumes mountFollowsTerminalPark — it clears the flag after the first reattach
so a later in-place reconnect on the SAME mount cannot repaint. I replaced the
prepaint's read of that mutable flag with a const snapshot of it, so my combined
flag stayed true for the life of the mount. A snapshot could then be written on a
later reattach, into a terminal that already had live content, and its own
isCurrent() guard could no longer go false either.

The visible symptom was a tab that came up blank with no prompt and stayed
generically titled Terminal N — the title only stays generic when the shell never
printed a prompt for Orca to read one from. Every such tab in my session had been
through a remount; four tabs created cleanly with Cmd+T were all fine.

So the flag is mutable again and is consumed alongside the one it was derived
from. Both reasons a mount paints into an empty terminal — a park and a reconnect
— are spent by the first reattach, which is what the original code meant.

Worth stating plainly: my earlier claim that the snapshot change was
non-regressing was tested only against reconnect scenarios. I never exercised
creating a tab afterwards, which is exactly where this showed up.
This commit is contained in:
Neil
2026-08-15 00:03:11 -07:00
parent e6541fe9b8
commit c497a26788
@@ -7973,7 +7973,11 @@ export function connectPanePty(
// it is only set when a pending or live SSH pane retry matches this connection AND this tab
// generation.
const isSshReconnectRemount = Boolean(directSshRetryAttempt)
const paintsIntoEmptyTerminal = mountFollowsTerminalPark || isSshReconnectRemount
// MUTABLE, and consumed with mountFollowsTerminalPark below. A const here is a bug: the payload
// path clears mountFollowsTerminalPark after the first reattach so a later in-place reconnect on
// the SAME mount cannot repaint, and a snapshot written then lands on a terminal that already
// has live content. Reading a frozen copy reintroduced exactly that.
let paintsIntoEmptyTerminal = mountFollowsTerminalPark || isSshReconnectRemount
let parkedSshSnapshotPrefetch: {
ptyId: string
@@ -8230,6 +8234,9 @@ export function connectPanePty(
mountFollowsTerminalPark &&
(connectResult?.isReattach === true || isRemoteRuntimePtyId(ptyId))
mountFollowsTerminalPark = false
// Consumed together: the prepaint's own guard reads this, and both reasons a mount paints into
// an empty terminal are spent by the first reattach.
paintsIntoEmptyTerminal = false
// Why: ordinary parking destroys xterm. Rebuild from the authoritative
// host snapshot before releasing queued live bytes; null falls back to
// the subscribe screen without keeping the old xterm mounted.