a73x

CLAUDE.md

Ref:   Size: 7.4 KiB   History

# mux

Terminal multiplexer in Zig, for Linux and Apple-silicon macOS.
The daemon owns the ghostty-vt engine; clients receive cell snapshots and row
updates. Read the relevant module header and contract document before edits.

## Scope

The terminal wall is frozen: bug fixes only until the native client meets
issue `94ed7dfd`. No new wall features or invariants. `mux web` stays and will
own the layout file. See the 2026-09-05 entry in [decisions](docs/decisions.md).

When the user requests sprint delivery, follow the repository
[sprint-delivery skill](docs/skills/sprint-delivery/SKILL.md). Ordinary isolated
edits do not require that workflow.

## Toolchain and gates

Use `mise` (pinned to Zig 0.15.2 in `mise.toml`) through Make; Make resolves
`ZIG` via `mise which zig` and respects an explicit `ZIG=/path/to/zig`
override. `mise install` provisions this compiler per machine; worktrees reuse
it, so `deps/zig` is not required. The system Zig is incompatible.
Make handles the macOS shadow SDK. See [development commands](docs/development.md)
for setup, releases, cross-OS checks and the full command list.

- `make build`: lint, then build the development CLI.
- `make lint`: fetch and verify pinned zlint, then lint repository Zig sources.
- `make check`: required before commit; lint, formatting, unit tests, shell
  checks, web ABI verification, comment references and source-rule checks.
- `make ci`: required before delivery; check, e2e, agent and throughput gates.
- `make daemon-test client-test native-core-test`: focused component tests.
- `make native native-e2e`: GUI build/tests and real-window checks; the full
  GUI remains outside the default delivery gate.
- `make mac xos`: explicit macOS and cross-OS journeys; see development setup.

## Architecture

`build.zig` owns the module definitions, imports and source-boundary rules.
Use [component ownership](docs/component-ownership.md) to find the affected
component and focused tests. Do not maintain another inventory of module files
here. A Zig file belongs to one build module, even when used by several clients.

- `src/engine/`: terminal grid, protocol, replay and authoritative VT engine.
- `src/server/`: daemon, sessions, PTYs and upgrade handling.
- `src/client/`: reusable client services and browser hub.
- `src/gui/`, `src/tui/`, `web/`: native, terminal and browser frontends.
- `src/os/`: operating-system implementations behind shared interfaces.
- `src/cli/`: command entry points. `mux d`, `mux a`, `mux web` select modes;
  other first words select a client transport. `muxg` is the native GUI.

Source-rule exceptions must explain the immediately following occurrence.
One permitted occurrence must not exempt the rest of a file. Rule definitions
and their diagnostics live in `build.zig`; `test/bans.sh` tests enforcement.

## Contracts

- The daemon owns VT parsing. CLI, wasm and fixtures use one replay core over
  the cell grid. Prediction is an overlay and never enters the replica.
- Proxy and QUIC transport carry opaque bytes, independent of terminal frames.
- Client and daemon plain dumps agree after trimming row-tail whitespace.
- Latest-active client controls session geometry. Agent clients attach at 0×0.
- Read-only discovery never starts a daemon. Start and upgrade behavior belong
  to the daemon; processes are executed as argv without shell interpretation.
- Detach leaves sessions alive. Ending a session and stopping a daemon are
  explicit actions. Never steal a socket owned by a live daemon.
- Agent forwarding is opt-in. Clipboard reads through OSC 52 are refused.

Read the detailed contracts for the area being changed:
| Folder | Row — its child files |
|---|---|
| `src/engine/` | `term`(`term.zig`) — `protocol` `replica` `grid` · `engine`(`engine.zig`) — `delta` — the daemon's ghostty-vt; no client row imports it outside a test |
| `src/server/` | `daemon`(`server.zig`) — `server_agent` `server_sessions` `cmd` `shellint` `quic_server` `upgrade` `server_test_*` · `pty` |
| `src/client/` | `client` — `client_core` `hosts` `handoff` `layout` `keymap` `askpass` `selection` `session_pump` `buffered_wire` `open_wait` `resolver` `discovery` · `webhub` · `wasm_core` `client_core_wasm_check` (wasm roots the build wires outside the table) |
| `src/gui/` | `native_core`(`native_core.zig`) — `workspace` `runtime` `picker` `persistence` `interaction` `font_options` `config` `theme` · `native`(`native.zig`) — `font` `atlas` `quads` `gl` `frame` `bench` |
| `src/tui/` | `wall`(`wallview.zig`) — `interact` `paint` `predict` `wall_host` `wall_picker` `wall_pump` `wall_layout` `wall_test_*` |
| `src/cli/` | `mux`(dispatch) — `main`(daemon) `mux_main`(client) `webhub_main`(hub) · `muxg`(native viewer) · `agent`(`muxa.zig`) · `cliflags`(`flags.zig`) |
| `src/os/` | `server_os`(`server_os.zig`) — `server_os_linux` `server_os_macos` · `client_os`(`client_os.zig`) — `client_os_linux` `client_os_macos` · `spawn` — the platform layer, one row per side so the client never links a fork or a pty; imports nothing of ours (spec 2026-09-03) |
| `src/` | `xdg` `sockpath` `dial` `link` `serve` `proxy` `quic` `testtmp` — what both sides link; `dial` is the client side of a daemon socket and `link` the live connection under it whatever reached it (fd, handoff stdio, QUIC), `serve` the right to bind a socket path and the duty to unlink it, `term` and nothing else under them |

- [Terminal and agent contracts](docs/terminal-contracts.md).
- [Daemon, transport and upgrade contracts](docs/daemon-contracts.md).
- [Terminal wall and browser layout contracts](docs/wall-contracts.md).

## Working rules

- Assert user-visible behavior with real binaries, PTYs and shells. Test
  plural sessions/panes and nonzero origins; single-item fixtures alone miss
  interactions. Ask the OS about process state, rather than trusting the daemon.
- Use `test/os_oracle.sh` for platform queries and the shared e2e helpers for
  fixtures. Linux evidence does not establish macOS behavior.
- For ad hoc rigs, use `tools/isolated-run.sh PATH_TO_MUX COMMAND [ARG ...]`.
  It isolates XDG state and the default daemon socket. Custom sockets need
  their own cleanup. Never run throwaway rigs against the user's daemon/state.
- Capture noisy checks with `tools/run-logged.sh LOG COMMAND [ARG ...]`.
  It preserves the command's exit status; piping a check into tail does not.
- Unit tests must not write to stdout: it carries the Zig test-runner protocol.
  Inject a writer or capture fd 1 in a test-owned pipe.
- Measure performance with ReleaseSafe (`make throughput`), not Debug builds.
- Keep rationale in comments, stated plainly. Put historical narratives in
  decision records. Comment-reference checks enforce valid references, not
  prose length; do not compress explanations merely to satisfy a counter.
- Split code along responsibilities. Keep wrappers only where they preserve
  an existing API; do not create unrelated fragments to reduce a line count.
- Commit subjects are `type: what changed`, without a parenthesized scope.
  Types: feat, fix, refactor, test, docs, build, chore. Use fixups during work
  and autosquash before delivery.
- `git-collab issue list` is the live backlog. The commit-msg hook stamps Patch
  trailers; batch collab writes because each can sync with origin.

See [testing rationale](docs/testing-practices.md) for the incidents behind
these practices. [README](README.md) covers usage; [decisions](docs/decisions.md)
records design history. The old roadmap is historical, not the live queue.