What Ghostex's PTY-persistence choice does and doesn't teach kolu's in-progress remote-terminals plan — one concrete borrow (honest pane⟂session state), one corroboration, one reframe; the "lean on a mux" swing doesn't survive review.
What Ghostex’s PTY-persistence choice does and doesn’t teach the in-progress kolu plan (remote-terminals + pty-daemon — read as docs/plans HTML at the time, since migrated to those Atlas notes). Sources read this turn: /tmp/Ghostex @ HEAD and both kolu plan docs./lowy + /hickey folded in — my first three recommendations were largely wrong; this is the post-review version, and the headline below says what I retracted.
What Ghostex is (the relevant parts)
A native macOS app: web sidebar in a WKWebView, every terminal a real embedded Ghostty SurfaceView (Metal), not xterm.js. The native host owns PTY creation for live panes; node-pty is explicitly slated for removal once Ghostty owns all PTYs. (docs/native-ghostty-handover.md “Code To Remove”, shared/terminal-host-protocol.ts:1-6.)
Persistence is a pluggable provider, decoupled from the app process entirely:
The mux owns the PTY. A native pane can be unmounted while the provider session still exists — that orthogonality is a first-class part of the state model (see below). (shared/session-grid-contract-sidebar.ts:188-203.)
They forked zmx as a pinned submodule because “native zmx pane refresh depends on a first-party zmx IPC protocol that older PATH installs do not implement” — i.e. stock mux couldn’t give them repaint / metadata / full-replay. (.gitmodules (zmx).)
Remote = persistence, same mechanism. Android/iOS/TUI connect to the Mac over SSH and attach to the very same named mux session that provides local survival; the phone asks the Mac-side ghostex CLI for session inventory + attach commands. First Android release is “ZMX only,” with a warm pool of recently-tapped sessions and an app-owned SSHJ transport. (docs/android-handover.md “Current State”; AllFeatures.md:77, :186.)
The architectural contrast
Concern
Ghostex
kolu (planned)
Who owns PTY lifetime
External mux (forked zmx / tmux / zellij). App is a pure attach/detach client.
First-party @kolu/pty-host daemon the app spawns and supervises (own cgroup, survives systemctl restart).
Survive app/server restart
Free — the app never owned the PTY, so a restart is never a daemon restart. No hazard.
Engineered — pid-gate, cgroup-escape, reattach-by-id, recovery sequence. The #1034 hazard class lives here.
Metadata / repaint / replay IPC
Custom — required forking zmx to add it. Now owns mux source + IPC + a user-session model.
Custom — typed oRPC surface + build-time staleness hash + a provider DAG that runs fresh in kolu-server off the daemon’s stream. Owns only a headless PTY.
Local-survival vs remote-reach
One primitive: a named durable session. Remote = ssh mac → attach <name>. (Still two transports underneath: zmx + SSH.)
One TerminalBackend interface, two implementations (loopback now, ssh in R-2) behind one stable unix socket. Same shape, different name.
Terminal renderer
Native Ghostty/Metal. No web terminal.
@xterm/headless mirror in the daemon + xterm in the client.
Being built into B (status chip, degraded canvas) — the lesson #1034 paid for.
What survives review — the actual influence
Two reasoning errors I made (so the next pass doesn’t)
State-model ⟂ transport-architecture. I used Ghostex’s two-field liveness model (a state-decomposition fact) as evidence about how many transports kolu should have (a transport-architecture claim). Non-sequitur — a system can have the exact two-field model with one transport or two. The honest-state borrow (#1) and the transport-convergence note (#2) are independent claims with independent evidence. (Hickey)
“Convergence validates it” is an easiness claim, not a simplicity one. Two teams reaching the same decomposition shows it’s reachable/natural, not that it’s correct. I’ve reworded #1 accordingly. (Hickey)
Net: the influence is one concrete borrow (Ghostex’s honest pane⟂session state vocabulary into kolu’s B-phase), one corroboration (reattach-by-id is a natural cut), and one reframe (#1034 is a lifecycle bug to pay down, not a seam to redraw). The tempting big swing — “lean on a mux instead of building the daemon” — does not survive a volatility/simplicity pass: Ghostex’s own zmx fork is the counter-evidence.