* fix: isolate codeman tmux sessions
* fix(tmux): unify all sessions onto a single dedicated socket
Remove the per-session `tmuxSocket` field that recorded which tmux server
each session lived on (default vs the `codeman` socket). That field was a
persisted cache of physical reality and could drift — causing live sessions
to be wrongly marked dead ("tab shows no session found") and spawning
duplicate "Restored:" tabs.
All Codeman sessions now live on one process-wide socket (`tmux -L codeman`,
overridable via CODEMAN_TMUX_SOCKET), exposed via TmuxManager.muxSocket on
the TerminalMultiplexer interface. reconcileSessions() collapses from a
multi-socket scan (locate / re-pin / cross-socket dedup) to a single
`list-panes` query. loadSessions() strips the obsolete field from on-disk
records so it stops being written back.
Also fix two sibling bare-`tmux` call sites the unification would otherwise
leave broken (same #80 regression class — bare tmux hits the user's default
server and never finds a session on the codeman socket):
- session.ts queryTmuxWindowSize(): add `-L <socket>` (was silently falling
back to 120x40 on re-attach, losing scrollback)
- session-routes.ts send-key (Shift+Enter / Ctrl+Enter newline): route
through ctx.mux.muxSocket
SSH chooser scripts (tmux-manager.sh, tmux-chooser.sh) route every tmux call
through `tmux -L $CODEMAN_TMUX_SOCKET`, matching the TS default.
---------
Co-authored-by: Teigen <teigen@TeigendeMac-mini.local>
Backfill the two regression gaps flagged on master after the recent
hostname-title and tmux-flicker fixes shipped without server-side
assertions.
* test/server-index-title.test.ts (8 tests) — exercises WebServer's
index.html templating path: default os.hostname(), --title-hostname
override, HTML-escape against `<script>`-style breakout, ampersand
non-double-encoding, exact-once substitution, and byte-identical
template-tail invariance.
* test/tmux-window-size-query.test.ts (15 tests) — mocks
child_process.execFileSync and walks the helper through the
browser-resize-between-attaches happy path, query-then-die race,
zero/negative/empty/non-numeric output, plus argv-form/timeout
assertions to lock down the no-shell-interpolation guarantee.
* src/session.ts — extracts the inline 14-line tmux size query into
a named `queryTmuxWindowSize()` export so the test surface is a
pure function. Behavior unchanged.
* src/web/public/notification-manager.js — Browser Notification API
(layer 3) now uses `${this.originalTitle}: ${title}` so OS-level
desktop pop-ups carry the same `codeman:<host>` prefix that the
tab title and Web Push payloads already do, finishing the
hostname plumb-through started in #82.
* CLAUDE.md, README.md — document the dual-CLI env-prefix discipline
(CLAUDE_CODE_* vs OPENCODE_*), expand the xterm-zerolag-input
duplication gotcha to mention the published-package side-effect,
and note that the hostname prefix now applies uniformly to tab
title, tab-flash, and OS notifications.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>