writeonce/docs/plan/exploration/tmux/00-tmux-parity.md
shoney.arickathil 8bcd24969e docs(lang42): story, spec and plan for bounded subprocess
- claim the lang42 prefix; iteration 42 story (readiness: ready), approved
  spec, and the 11-task implementation plan
- board: pending row for 42; graph: node 42 with green edges (11, 24)
- graph: porch track section added (same sweep)
- parity studies that motivated 42: alacritty, tmux, zen-browser under
  docs/plan/exploration/ — staged path, gap lists, refused routes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 22:19:25 +02:00

5.9 KiB
Raw Blame History

tmux parity — the multiplexer as the language's next driving workload

Source: .dev/reference/tmux/ (shallow clone, surveyed 2026-09-01). Companion to alacritty/00-alacritty-parity.md, whose stage C ("tmux-lite") this study scopes for real. tmux is the more instructive reference of the two: it needs no graphics at all, so the entire program sits inside the territory the alacritty study's stages A–C cover — it is the proof target, not a stepping stone to one.

What tmux actually is (measured)

~112,000 lines of C (100,027 in the 153 top-level .c files; the rest is compat/ shims and headers). Exactly two external dependencies — libevent (fd loop + buffers) and ncurses used only as a terminfo(5) reader (tty-term.c calls setupterm/tigetstr and nothing curses-like). Every other need is vendored in compat/: OpenBSD's imsg framing, forkpty for five platforms, daemon.

The architecture is one daemonized server owning every session, window, pane and PTY (server.c:190 forks it), and a deliberately thin client (808 lines): the client connects over a unix socket and passes its own terminal fd to the server with SCM_RIGHTS (compat/imsg-buffer.c:798, proc.c — imsgbuf_allow_fdpass); from then on the server writes escape sequences directly to the client's tty. Detach survives because the client process is disposable — the server never was attached to a terminal it doesn't hold as a passed fd.

Where the lines actually go — the multiplexer kernel is small, the UX is not:

  • Kernel, ~27k: server-client.c 3,280 · input.c 3,745 (the escape parser) · tty.c 3,221 (the output driver) · screen-write.c 3,204 · window.c 2,893 · grid.c 1,839 · layout.c 2,022 (the pane split tree) · tty-keys.c 1,883 (key decoding) · utf8.c 1,043 · plus session/spawn/proc/job plumbing.
  • A command LANGUAGE, ~12k: 63 cmd-*.c files behind a yacc grammar (cmd-parse.y) — the config file is just commands; format.c is 7,182 lines implementing a template DSL with ~380 variables; options-table.c 2,070 lines of typed settings.
  • Interactive UX, ~20k: window-copy.c 7,300 (copy mode is the single biggest file in tmux) · choose-tree/customize/menus/prompt.

What writeonce already answers

  • Client/server over a unix socket — listen_unix landed (iteration 35); the request/actor shape is the porch daily bread.
  • Server = actor tree. tmux multiplexes everything through one single-threaded libevent loop with callbacks; sessions, windows, panes and clients as actors with ownership-move messages is the same topology with the concurrency written down instead of implied. Iteration 24 (chat rooms, fan-out, lifecycle, SIGTERM drain) already proved every piece of the pattern.
  • The DSL layer is free. tmux hand-rolls a command grammar, a format template language and a typed options table (~12k lines) because C has no expression language to lend. writeonce's config/scripting surface can be the language itself — ${ } interpolation replaces format.c, and a @table of settings replaces options-table.c.
  • Durable sessions beyond tmux. tmux state dies with the server; a writeonce multiplexer's session/layout tables can be durable: true for free — scrollback in the WAL is the kind of trick the storage engine exists for. Feature, not parity.

Gaps — mostly shared with the alacritty list, two new, one promoted

  1. PTY + bounded subprocess, signals, arbitrary-fd readiness, unicode width, monotonic timers — identical to alacritty study gaps 1–5; spawn.c/job.c/compat/fdforkpty.c are the reference reading.
  2. fd passing (SCM_RIGHTS) — PROMOTED. The alacritty study placed it in stage D (Wayland). Wrong stage: detach/attach — the whole point of a multiplexer — is built on handing the client's tty fd across a unix socket. Second consumer found; the builtin belongs to the multiplexer stage. (The alacritty study is corrected in place.)
  3. NEW — termios control of an existing terminal. The client must put its own tty into raw mode and restore it on exit (tmux: cfmakeraw, tcgetattr/tcsetattr). The PTY gap covers creating terminals; this is adopting one you were given. Small builtin family, nothing else substitutes.
  4. NEW — the terminfo fork. tmux answers "what escape sequences does THIS client's terminal speak" from the terminfo database. Two honest options: read the compiled terminfo format in pure .wo (a documented binary file — a parser, not a linked library; ncurses would NOT be imported) or emit a fixed xterm-256color profile and refuse exotic terminals by name. Decide at brainstorm; start fixed, the refusal names the gap.
  5. Daemonization — fork-and-detach with the socket handed over. Cheap, and arguably skippable first (a foreground server under systemd was good enough for writeonce.de).

Corrected staged path (supersedes the alacritty study's stage C sizing)

  • Stage A/B unchanged — headless PTY runner, then the VTE grid replayed against recorded sessions (input.c + grid.c are the behaviours to pin).
  • Stage C — the multiplexer now carries its real bill: A + B plus fd passing (gap 2), termios adoption (gap 3), and the terminfo decision (gap 4). Proof: detach, kill the client, reattach from another terminal, scrollback intact — then restart the server and reattach with layout and scrollback replayed from the WAL, which is the demo tmux cannot give.
  • Stage D/E unchanged (Wayland shm, then the GPU fork) — and stage D gets gap 2 for free once C lands.

Scope honesty: parity with tmux the product is ~100k lines including a 7k copy mode and 20k of chooser UX — not the goal. The kernel a driving workload needs is the ~27k-line column, and the DSL third of tmux dissolves into language features writeonce already has.