>_PARITY VS tmux
ztmux is a port of tmux, so "correct" means tmux itself. The parity suite runs the same inputs
through the vendored tmux (reference) and ztmux (port) and compares byte-for-byte —
the same shape as the sibling ports (zshrs vs
zsh, strykelang vs perl).
The suite has grown from a handful of format cases to 1080 covering formats, arithmetic,
conditionals, string ops, options, buffers, windows, panes, and layouts — and every one now passes
byte-for-byte, with zero known divergences.
>_REPOSITORY LAYOUT
Original code is kept apart from the port so the fidelity gate only judges what is meant to mirror C:
| Path | What |
|---|---|
src/ported/ | The tmux port — 138 modules plus 23 compat shims that mirror vendor/tmux one-to-one. Held to C-name fidelity by the anti-drift gate. |
src/extensions/ | Original ztmux features with no C counterpart — structured output, the dashboard and fuzzy-switcher TUIs, and the family of pipeable client subcommands (see Extensions). Exempt from the gate. |
src/lib.rs, src/main.rs | Crate roots: the module tree (which re-points every ported module's file into ported/) plus the binary entry. |
vendor/tmux | Read-only, SHA-pinned reference — see How the port is built. |
>_EXTENSIONS — BEYOND tmux
Because ztmux owns the whole stack, it adds capabilities upstream tmux does not have. They are plain clients — they re-invoke the ztmux binary and read its own machine-readable output, so they need no linkage to the server internals. The foundation is structured output plus the two interactive TUIs:
| Feature | Invocation | What |
|---|---|---|
| Structured output | list-* -o json|jsonl|csv|tsv|table | Typed, machine-readable output for every list-* command, driven by the same #{…} format engine. table is aligned columns for humans; json feeds scripts and the TUIs below. |
| Live dashboard | ztmux dashboard | A ratatui TUI: the session→window→pane tree, per-pane detail table, live server stats with a pane-count sparkline, kill-with-confirm, and 1 s auto-refresh. |
| Fuzzy switcher | ztmux switcher | Type-to-filter picker over every session, window, and pane; Enter runs the right switch-client / select-window / select-pane to jump there. |
On top of that sits a family of one-shot client subcommands, each resolving the running server over the list-* -o json query layer. The inspection verbs are pipeable tables that also emit -o json / --json; the action verbs are dry-run by default and only mutate when passed -f / --force.
// INSPECTION — read-only, pipeable:
| Command | What |
|---|---|
ztmux active | Focused window/pane of every session. |
ztmux age | Sessions ranked by creation age, oldest first. |
ztmux ahead | How far each repo is ahead of/behind upstream. |
ztmux alerts | Windows with a pending bell/activity/silence alert. |
ztmux autoname | Windows with a pinned name (automatic-rename off). |
ztmux borders | Windows that draw a status line on their pane borders. |
ztmux buffers | The server's paste buffers, largest first. |
ztmux busy | Panes running a program, not idle at a prompt. |
ztmux changes | Uncommitted file count per pane's repo, dirtiest first. |
ztmux cmd | Histogram of commands running across panes. |
ztmux commit | The last commit in every pane's repo. |
ztmux conflicts | Repos with unresolved merge conflicts. |
ztmux connected | Attached clients ranked by connection age. |
ztmux constrain | Attached clients ranked by screen size, smallest first. |
ztmux control | Clients attached in control mode (-CC). |
ztmux cwd | Working directories in use, busiest first. |
ztmux dead | Dead panes still held open in a window. |
ztmux dedup | Find redundant panes (same cwd + command). |
ztmux density | Windows ranked by pane count, most first. |
ztmux destroy | Sessions that self-destruct when the last client detaches. |
ztmux detached | Sessions with no client attached, freshest first. |
ztmux disk | Filesystem usage behind each pane's cwd. |
ztmux doctor | Environment / server health check. |
ztmux elapsed | How long each pane's process has been running. |
ztmux env | Per-session environment overrides. |
ztmux events | Stream server lifecycle events as JSONL. |
ztmux fanout | Sessions ranked by total pane count. |
ztmux finder | Search panes by command/path/title/window. |
ztmux focus | The active pane of every window. |
ztmux git | Git branch + dirty state of every pane's repo. |
ztmux gone | Panes whose working directory no longer exists. |
ztmux graph | Render the server tree as DOT/Mermaid/HTML. |
ztmux grep | Search the live contents of every pane. |
ztmux groups | Cluster sessions by session group. |
ztmux history | Rank panes by scrollback buffer size. |
ztmux hooks | The command hooks configured on each session. |
ztmux idle | Attached clients ranked by time since last activity. |
ztmux info | Deep inspector for a single pane. |
ztmux input | Panes that are ignoring keyboard input. |
ztmux keys | How many key bindings live in each key table. |
ztmux keytable | Clients parked on a non-root key table. |
ztmux layouts | The current layout string of every window. |
ztmux limit | Each session's scrollback capacity, largest first. |
ztmux linked | Windows linked into more than one session. |
ztmux autolock | Sessions set to lock themselves after idle time. |
ztmux marks | The marked pane(s). |
ztmux mem | Panes ranked by process resident memory. |
ztmux mode | Panes currently frozen in a mode. |
ztmux monitor | Windows armed to alert on activity or silence. |
ztmux mouse | Which sessions have mouse mode enabled. |
ztmux named | Windows with a deliberate name, not the running command. |
ztmux nested | Panes running a nested terminal multiplexer. |
ztmux net | Established outbound connections per pane. |
ztmux peek | Dump the visible contents of every pane. |
ztmux piped | Panes with an active pipe-pane capture. |
ztmux ports | Listening TCP ports mapped to panes. |
ztmux project | Project kind and root behind every pane. |
ztmux ps | One-shot pipeable per-pane process table. |
ztmux pstree | Process tree running under every pane. |
ztmux readonly | Clients attached in read-only mode. |
ztmux recent | List sessions ranked by last activity. |
ztmux remain | Windows that keep panes open after they exit. |
ztmux remote | The git remote each pane's repo points at. |
ztmux shells | Panes sitting at a bare shell prompt. |
ztmux size | Report pane geometry, smallest first. |
ztmux snapshot | Dump the whole server as one nested JSON document. |
ztmux solo | Windows holding a single, unsplit pane. |
ztmux ssh | Which panes hold an SSH connection, and where. |
ztmux startcmd | The command line each pane was launched with. |
ztmux stash | Repositories with stashed work behind a pane. |
ztmux state | Panes whose process is in an abnormal state. |
ztmux stats | One-shot server summary report. |
ztmux status | Sessions with the status line turned off. |
ztmux submodules | Repos with submodules and how many are out of sync. |
ztmux sync | Windows with synchronize-panes turned on. |
ztmux tag | The git describe/tag context of every pane's repo. |
ztmux term | Histogram of attached client terminal types. |
ztmux titlebar | Sessions that push a title to the outer terminal. |
ztmux titles | Every pane's advertised title. |
ztmux tree | Print the session/window/pane tree. |
ztmux tty | Map every pane to its terminal device. |
ztmux usage | Per-session CPU/MEM/RSS resource rollup. |
ztmux user | The owner of every pane's process. |
ztmux utf8 | The UTF-8 state of every attached client. |
ztmux vcs | Which version-control system each pane is under. |
ztmux viewers | How many clients are attached to each session. |
ztmux visual | Sessions that show alerts visually instead of just beeping. |
ztmux watch | Top-like live per-pane process monitor. |
ztmux who | Clients attached to the server, by session. |
ztmux winsize | Windows whose sizing mode differs from the default. |
ztmux worktree | Panes sitting in a linked git worktree. |
ztmux writable | Panes whose working directory is read-only. |
ztmux zoom | Windows with a zoomed pane. |
// ACTIONS — dry-run by default, apply with -f:
| Command | What |
|---|---|
ztmux bcast | Broadcast a command to many panes at once. |
ztmux clearall | Free the scrollback of every pane. |
ztmux equalize | Re-balance every multi-pane window's layout. |
ztmux layout | Apply a named layout preset to a window. |
ztmux pick | Batch ops over the multi-pane mark set (sync/unmark/clear/list). |
ztmux prune | Remove dead/empty/idle server objects. |
ztmux revive | Revive every dead pane in place. |
ztmux retitle | Label every pane with its running command. |
>_HOW THE PORT IS BUILT
Two references, both vendored under vendor/ as plain, read-only, SHA-pinned copies so the clone is self-contained:
| Path | What | Role |
|---|---|---|
| vendor/tmux | upstream tmux C sources (next-3.7) | Source of truth — every ported module is diffed against its C counterpart |
| src/ported/ | the ztmux port | The living code we own and evolve toward safe, idiomatic Rust |
Every ported function carries a back-link to its C origin as a doc comment, e.g. /// C . The port report tracks per-function C→Rust coverage: 2,127 of tmux's 2,696 functions ported so far.vendor/tmux/grid.c:320: grid_create()
>_BUGS THE HARNESS CAUGHT
Verifying against C at every step turns "looks right" into "is right." A sample of faults the parity suite and unit tests root-caused to a single line — each now locked by a regression test (the full log is in BUGS.md):
- Mouse-driven TUIs froze mid-pane — a stale
- 1in the SGR-mouse encoder (input_keys.rs) dropped the sequence'sM/mterminator afterxsnprintfwas corrected to exclude the NUL. ztmux wrote\033[<35;69;44with no terminator; crossterm blocked inread()waiting for an end byte that never came, so rich ratatui/crossterm panes locked up on any click or focus change while keyboard input still worked. - ztmux hijacked real tmux's socket — it resolved its default socket from
$TMUX, so launched inside a tmux pane it connected to tmux's server and spoke protocol 8 at it (server exited unexpectedly). Fixed to resolve only from$ZTMUXand advertise both$TMUXand$ZTMUXto panes, so the two multiplexers run side by side. - Server crash on
bind-key hthenl— the red-black tree's delete rebalance (rb_remove_color,compat/tree.rs) rotated around the wrong node. Rebinding a key that already had a default removed the old node first, corrupting the key-bindings tree and segfaulting the server on startup. - Powerline glyphs rendered as
\202\202\202—utf8_strvis's inner loop never advanced the source pointer (the C iswhile (++src < end …)), so each multibyte UTF-8 character was re-read and mangled into raw bytes plus octal escapes. - even-* layouts off by one —
layout_spread_celldumped the leftover column on the last pane instead of handing it to the leading cells; an 80-col two-pane split came out39|40instead of tmux's40|39. - Pane spawn hung on macOS — the ported
closefromloopedclose()up to the server-raisedRLIMIT_NOFILE; replaced with thelibprocPROC_PIDLISTFDSpath tmux actually compiles on macOS, so the forked child reachesexecvp.
>_ANTI-DRIFT GATE
A port can be faked by inventing Rust-only "helper" functions that don't exist in tmux, inflating apparent
completeness. tests/ported_fn_names_match_c.rs fails the build when a free
fn is added under src/ported/ whose name has no counterpart in vendor/tmux.
src/extensions/ is exempt by design — it is original code, not a mirror of C — exactly as
#[cfg(test)] blocks are skipped. Pre-existing exceptions (libc/libevent wrappers, Rust glue) are
frozen in an allowlist: an audit trail to burn down, not a free pass.
>_TESTING
Three layers stack: byte-for-byte parity against the vendored tmux; in-tree unit tests for pure logic (red-black tree invariants under randomized insert/delete, UTF-8 round-trips, layout arithmetic, and the extensions' rendering via ratatui's headless backend); and the anti-drift gate that fails the build on invented functions.
>_"DONE RIGHT"
- Start from a working skeleton — a running program to refactor, not a blank page.
- Shrink the unsafe surface — replace raw-pointer intrusive lists and C-isms with safe Rust where behavior allows.
- Verify against C at every step — a module isn't "ported" until it matches the C reference (parity suite).
- Keep it green —
cargo buildandcargo clippystay clean as code comes over.
>_BUILD & RUN
Requires a C libevent and a terminfo database (ncurses), matching tmux itself. On macOS the build
links Homebrew's libevent automatically (TMUX_RS_DISABLE_HOMEBREW_LIBS=1 to opt out).
Then the binary ztmux speaks the same commands as tmux — on its own socket namespace
(ztmux-<uid>, from $ZTMUX, never $TMUX) so it never collides with a
running tmux — plus the extension subcommands:
| Command | Does |
|---|---|
cargo build --release | Builds the ztmux server + client binary. |
ztmux new-session -s work | Start a session (as tmux). |
ztmux list-panes -a -o table | Every pane, as an aligned table (or -o json). |
ztmux dashboard / ztmux switcher | Launch the live dashboard / the fuzzy switcher. |