muxr
muxr was a session manager for AI coding work built on top of tmux and written in Rust. Every session had a two-level address, <repo>/<campaign>, and that address had to mean the same thing in three places that don’t normally agree with each other: the tmux session name, a directory of campaign.md/log.md files on disk, and the AI runtime’s own notion of a conversation (Claude Code’s session id, tied to a working directory). muxr’s job was to create all three together and, when one of them changed, carry that change into the other two without leaving a dangling reference behind.
The tmux half of a rename is one call (rename-session). The interesting half was the runtime, because a rename or a resume there isn’t an API call, it’s keystrokes typed into a live pane. muxr rename did three things in order: rename the tmux session, move campaigns/<old>/ to campaigns/<new>/ on disk, and send the runtime’s own /rename slash command into the pane as literal text so the tool’s internal state matched too. Early on (v0.9.3) that last step shipped a bug worth remembering: the interpolated value was shell-escaped before being sent, correct for a subprocess argument and wrong for a keystroke stream. Shell-escaping wrapped a model name in single quotes, so Claude read /model 'claude-opus-4-7' and rejected the literal quote characters as part of the name. The fix split one interpolation function into two, one escaped for launch arguments and one raw for anything typed into a live pane, because those are two different trust boundaries that happened to share a string type.
Highlights
- 3.0.0 replaced every per-runtime special case with one subprocess extension contract (JSON on stdin, JSON back on stdout, fail-closed), earned from real extractions rather than designed up front. Adding a new coding tool became a TOML block (
bin,args,resume_args,session_discovery) instead of a code branch; the opencode adapter shipped as a worked example that never touched core. muxr upgraderelaunched every live session onto a freshly installed binary in place, resuming the same conversation id, so a new Claude Code release didn’t mean closing and reopening every open session by hand.- A reboot once lost 10 of 11 sessions, because
restoreresumed by conversation id and a session recycled just beforesavewas a zero-turn conversation with no transcript, so--resumedropped its pane to a bare shell. The fix:restorenow always rehydrates from the on-disk campaign/log pointer, never an id. - A CI job enforced that every production file read routed through one module, so a stray raw read could never again reintroduce the bug it was written to catch: one unescaped quote in
log.mdhad made the file unparseable, and the launch command silently rebuilt itself with an empty prompt, on a live session, with no error surfaced. - MIT licensed, published to crates.io as
nomograph-muxrwith prebuilt macOS and Linux binaries per release.
The three-way address was the part of muxr worth building as its own tool: a rename that happens once and lands correctly in tmux, on disk, and inside the runtime, instead of three renames done by hand and occasionally forgotten. Most of what grew up around that core, the extension contract, the readiness machinery, the TUI, was infrastructure for infrastructure, and its retirement note above is the rest of that story.
kit and rune were retired six weeks earlier in the same consolidation, for the same reason: three bespoke binaries maintaining a personal toolchain, where the surface anyone actually used was small enough to be a config file and a shim.