Triage
Confirmed on main (c2fa9fc). A terminal session has a single PTY size, and whichever client last sent its dimensions wins. The other client isn't told, so its local grid drifts away from the PTY. That explains both symptoms you described: a desktop-sized TUI clipped on the phone, and a phone resize leaving the desktop TUI stuck at the phone's column count.
Server side. TerminalResizeInput only carries threadId, terminalId, cols, and rows, with no client identity. resizeLocked in apps/server/src/terminal/Manager.ts resizes the shared PTY, stores session.cols / session.rows, and returns without publishing anything. TerminalSessionSnapshot and TerminalSummary don't include geometry, and TerminalEvent has no resize event, so an attached renderer never learns about the new grid. manager.open does the same last-writer update when an existing session is opened at a different size, and the test "resizes running terminal on open when a different size is requested" locks that behavior in.
Client side. Both clients write their own size into that one slot:
- Native Android sends
cols/rowson attach (ThreadTerminalRouteScreen) and again fromhandleResize. The native view fires that on any size change, including the keyboard opening (T3TerminalView.onSizeChanged→emitResize). - The web drawer doesn't send a size on attach. After layout, Ghostty's
fit()callsonResize, which sendsterminal.resize(debounced 150 ms insurface.ts). So attaching or resizing the desktop panel overwrites the phone's size, and vice versa.
A container that doesn't change size won't re-fit, so once the phone is the last writer, stty size on the wide client stays at the phone's grid. That matches your 15×54 reading from the 1146 px view while a 430 px view was attached.
This isn't the same as #8253 (open issue, Android Backspace) or #12756 (open issue, per-key echo latency), and the open PRs #11235 (incremental output) and #9456 (input batching) don't touch geometry. Nothing else currently tracks shared PTY size.
Since a PTY can only have one size, the fix needs a policy. Either one client owns the size and the others scroll or scale to that grid, or there's an explicit shared grid plus a geometry event that every attached renderer applies. Until then, your workaround is the right one: keep only one terminal view attached at a time. Thanks for the careful reproduction.