Skip to content

Coding sessions no longer use tmux, but the names, diagnostics, kill-tmux control and docs all still say they do #247

Description

@serge-ivo

The Coder engine stopped using tmux — packages/browser-runner/src/coding/headless.ts:29:

The coding engine — Claude Code driven through its structured stream-json interface, NOT a
scraped tmux TUI
Survives a runner restart without tmux … routes and console keep working
unchanged — only the guts move from tmux to structured JSON.

It spawns a child process (spawn(this.cmdBin, …), :241/:321) and tracks it via
proc.exitCode/signalCode. Nothing in that path calls createSession from coding/tmux.ts.

tmux is still genuinely used — but only by the terminal-operator family (coding/terminal.ts,
server.ts:351-363), i.e. the tmux / Terminal / kitty / iTerm2 Operator agents. Those create real
sessions. Repo Coders do not.

The surface around the coding path was never cleaned up, and it now actively misleads.

What's stale

1. A session name that names nothing. headless.ts:115 still computes
this.sessionName = pags-${clientType}-${id}, which is persisted to coding_sessions.tmux_session
and surfaced through the API and console. On a live instance today: 13 sessions each recording
pags-claude-csess_…, and zero such tmux sessions have ever existed. A user who tries
tmux attach -t pags-claude-csess_… — the obvious thing to do with a name like that — gets
"session not found" and reasonably concludes their engine is broken.

2. Diagnostics measure something structurally always zero. coding_diagnostics returns
tmux: { trackedSessions, orphanedSessions, tmuxTotal, pagsTmuxTotal }. Real output from an
instance whose runner is healthy:

tmux: { trackedSessions: 0, orphanedSessions: [], tmuxTotal: 4, pagsTmuxTotal: 0 }

pagsTmuxTotal: 0 is not a symptom — it can never be anything else for a coding agent. tmuxTotal: 4
counts the user's own unrelated tmux sessions. This is the panel someone opens because something
is wrong, and it points them at a false cause.

3. A "Kill tmux" control that cannot help. POST /:instanceId/coding/kill-tmux (routes/coding.ts:1413)
proxies to the runner's /coding/kill-tmux. For a coding session there is no tmux to kill, so the
diagnostics panel offers a cleanup action that structurally no-ops for the sessions it is shown
beside.

4. The docs describe the old architecture. pags/CLAUDE.md still says "Engine = the actual CLI
running in a tmux pane on the user's machine"
, "tmux addressed by session name (not :0.0)",
and "The runner reattaches to existing tmux coding sessions on reconnect". The last one is
wrong in a load-bearing way: reattach works by re-spawning with --resume <claudeSessionId> from
~/.claude, which is why it survives a restart without tmux.

5. Stale comments in live code. lib/coding-store.ts:444 explains the driver-claim expiry in
terms of "the RUNNER reports no tmux". The reconciler is fine — trackedIds comes from the runner's
in-memory tracked[] registry (routes/coding.ts:1495-1497), not from tmux — but the comment sends
the next reader down the wrong path. (Verified: not a functional bug.)

Suggested shape

Why bother

This is what made a maintainer (and the product owner) ask "are Coder agents connected to tmux?"
and start debugging in the wrong place. The names, the diagnostics panel and the docs all assert an
architecture the code deliberately replaced.


Verified, and the three forks resolved

Every claim above checks out against the code, plus the live-data assertion: this machine has 4 tmux sessions and zero named pags-* — one is tmux-operator-runner, i.e. the operator family, exactly as described.

Two findings that decide the open questions:

Nothing consumes the stale surfaces. No console or coder-web code reads tmuxSession, pagsTmuxTotal, or calls kill-tmux. The only consumer of /coding/diagnostics at all is MCP coding_diagnostics (workers/mcp/src/instance-tools/coding.ts:27), which passes the JSON through. So all three forks lean the same way — remove — and none is a breaking change for a real caller.

kill-tmux is NOT a no-op, contrary to point 3. Its no-args branch calls runner.coding.closeAll() (server.ts:237), which really does close the tracked engine processes. Only the tmux targeting is dead. Retiring the route wholesale would delete the one working "close everything" escape hatch — it needs re-scoping, not removal.

Decisions

  1. sessionNameengineLabel, and drop the pags- prefix. The prefix is what makes a user type tmux attach -t pags-claude-…. In the coding path this value addresses nothing — runtime.ts:193/211 only reports it. The persisted column coding_sessions.tmux_session stays (a D1 column rename is a table rewrite for a cosmetic gain); it gets a comment saying it holds an engine label under a legacy name, and stops being surfaced as tmuxSession in API responses.
  2. Remove the tmux block from the coding diagnostics rather than scoping it. trackedSessions/orphanedSessions/pagsTmuxTotal are structurally zero for coding agents, and tmuxTotal counts the user's unrelated sessions. The operator agents have their own endpoints and are untouched.
  3. Re-scope kill-tmux to what works. Runner keeps the path (an older runner must still answer a newer API — version skew), with the tmux targeting stripped so it just closes tracked coding sessions. The API gains the honest name POST /:instanceId/coding/close-sessions; kill-tmux stays as a deprecated alias since removing a route nothing calls buys nothing.

Also needed, not in the original scope

coding/tmux.ts also hosts ensureRepo (:166, does the git clone) and sanitizeSessionName (:153), both imported by runtime.ts:6 and neither anything to do with tmux. The file cannot simply shrink to nothing; those two move out first.

The docs pointer at the end should not say #208/#211 — those are the two docs tickets whose numbers unrelated commits have been misusing, so it sends the reader to the wrong place.

Acceptance criteria

  • No coding session records or reports a pags-* value that looks like a tmux target.
  • /coding/diagnostics returns nothing tmux-shaped for a coding agent; the operator agents' tmux endpoints are unchanged and still work.
  • "Close all coding sessions" still works, under a name that says so.
  • ensureRepo/sanitizeSessionName no longer live in a file named tmux.
  • coding-store.ts:444 no longer explains the claim expiry in terms of tmux.
  • pags/CLAUDE.md describes the stream-json engine and --resume reattach, not tmux panes.
  • Tests cover the label shape and the diagnostics payload.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions