Skip to content

fix(tmux-manager): use '|' separator in reconcileSessions#71

Open
TeigenZhang wants to merge 1 commit intoArk0N:masterfrom
TeigenZhang:fix/reconcile-separator-launchd
Open

fix(tmux-manager): use '|' separator in reconcileSessions#71
TeigenZhang wants to merge 1 commit intoArk0N:masterfrom
TeigenZhang:fix/reconcile-separator-launchd

Conversation

@TeigenZhang
Copy link
Copy Markdown
Contributor

Summary

  • reconcileSessions() invokes tmux list-panes -a -F '#{session_name}\t#{pane_pid}' and parses the result with line.indexOf('\t').
  • Under non-tty execution contexts (macOS LaunchAgent, systemd without TTYPath, Docker exec without TTY) tmux emits the \t in FORMAT strings as the literal two characters \ + t, not as a tab (0x09). The parser therefore never matches, activeSessions stays empty, reconcile returns {alive: [], dead: [], discovered: []}, and cleanupStaleSessions() wipes every entry in state.json even though the underlying tmux sessions are still alive.
  • Interactive npm run dev hides this because tmux's FORMAT parser resolves \t when stdout is a TTY.

Root cause

Introduced in 88c415f (perf: batch tmux reconciliation) — the regression only manifests in production deployments under launchd/systemd.

Fix

Switch the separator to |. tmux passes it through verbatim in every environment, and | is rejected by tmux's own session-name validation so it cannot collide with the codeman-<uuid> / claudeman-<uuid> naming scheme.

Test plan

  • Reproduce before fix on macOS LaunchAgent: service restart → [StateStore] Cleaned up N stale session(s) from state while tmux ls shows the sessions alive.
  • After fix: same setup → [Server] Found N alive mux session(s) from previous run → sessions restored with tokens and attached PTYs.
  • Interactive npm run dev still works: create session → kill-restart server → session restored.
  • npm run typecheck / npm run lint / npm run build all pass.

Under non-tty execution contexts (launchd on macOS, systemd without TTY),
tmux emits '\t' in FORMAT strings as the literal two characters `\` + `t`
rather than as a tab. The parser's `line.indexOf('\t')` (a real tab char)
therefore never matches, `activeSessions` stays empty, `reconcileSessions`
returns `alive: []` / `discovered: []`, and `cleanupStaleSessions()` wipes
every entry in `state.json` — even though the underlying tmux sessions are
still alive. On the next startup the user sees an empty session list.

The bug reproduces reliably when codeman is launched via a user LaunchAgent
or a systemd unit without `TTYPath`. Interactive `npm run dev` hides it
because tmux's format parser does interpret `\t` when stdout is a TTY.

Fix: use `|` as the separator. tmux passes it through verbatim in every
environment, and `|` is not a valid tmux session-name character so it
cannot collide with the codeman-<uuid> / claudeman-<uuid> naming scheme.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant