Skip to content

fix(bin): bind tmux endpoint presence to exact session/window resolution - #2670

Open
KakkoiDev wants to merge 6 commits into
kunchenguid:mainfrom
KakkoiDev:fm/fm-liveness-bug-r2
Open

fix(bin): bind tmux endpoint presence to exact session/window resolution#2670
KakkoiDev wants to merge 6 commits into
kunchenguid:mainfrom
KakkoiDev:fm/fm-liveness-bug-r2

Conversation

@KakkoiDev

Copy link
Copy Markdown

Root cause

fm_backend_target_exists (bin/fm-backend.sh) and fm_backend_tmux_agent_state (bin/backends/tmux.sh) resolved a recorded session:window endpoint using tmux's default fuzzy/prefix name matching instead of exact matching:

  • tmux list-panes -t <session>:<window> and tmux list-windows -t <session> match a prefix of the session name, and the window-name comparison after inherited a similar prefix/glob laxness.
  • A stale recorded endpoint whose window (or whole session) no longer exists could therefore still resolve against an unrelated window or a differently-named live session, reading ALIVE when the actual recorded endpoint is dead.
  • On the cheap session-start fleet digest, a false ALIVE hides a dead worker from the supervisor.
  • On the deeper recovery classifier (fm_backend_tmux_agent_state), a false ALIVE is worse: it can suppress a legitimate relaunch, or (in the renamed-session case) risk authorizing a duplicate relaunch onto a live worktree.

Fix

  • Bind both the session and window axes to tmux's = exact-match prefix, relying on exit status alone rather than a name comparison against captured output.
  • In the recovery classifier, add a fuzzy re-probe when the exact session is absent: if a live session's name merely prefixes the recorded one, reuse that probe's window list to test membership — recorded window present reports unreadable (protects a renamed-but-live agent from a duplicate relaunch), absent reports missing (safe to recover, since no live session holds that window).
  • A transient failure of the fuzzy re-probe itself (signal/fork hiccup) now reports unreadable, not missing, so a transient tmux glitch never licenses a duplicate relaunch.
  • The cheap digest path (fm_backend_target_exists) intentionally does not re-probe on a renamed session: a renamed-but-live session reads dead there, which is documented in docs/tmux-backend.md as the safe direction for a passive read (false-dead only makes a supervisor look, never authorizes an action).

Files: bin/fm-backend.sh, bin/backends/tmux.sh, docs/tmux-backend.md, docs/verification/runtime-backends.md, plus colocated regression tests under tests/.

Regression tests

Colocated tests fabricate a state/<id>.meta pointing at nonexistent tmux windows/sessions (dead-window-in-live-session and whole-session-gone cases) and assert dead/missing instead of the previous false ALIVE. Each new test fails on the pre-fix code and passes on this branch.

Verification

Full no-mistakes pipeline run (review, test, document, lint) passed at head 51b38755; only the push step failed, due to the invoking machine's GitHub identity (KakkoiDev) lacking write access to kunchenguid/firstmate. Delivered manually via a personal fork per captain's instruction: pushed KakkoiDev:fm/fm-liveness-bug-r2, opening this PR against upstream.

bin/fm-backend.sh:837 probed a tmux endpoint with
`tmux display-message -p -t "$target" '#{pane_id}' >/dev/null 2>&1` and read
that exit status as "the endpoint exists". display-message is declared
CMD_FIND_CANFAIL, so an unresolvable -t never fails: it answers from the
client's active window, or expands to nothing when no client is attached, and
exits 0 either way. Every torn-down tmux endpoint therefore read as present,
including on the session-start fleet digest's per-task liveness line, where a
false present tells a supervisor a dead worker is still working.

The same probe also inherited tmux's window-name resolution, which matches by
fnmatch and by prefix, so a recorded `session:fm-x` could bind a live
`session:fm-x2`.

list-panes does fail on a target it cannot resolve, and asking it which window
it landed on makes the remaining name match exact: a recorded
session:window-name must come back as that same name. A pane id, window id, or
window index already resolves exactly, so its exit status alone is enough,
while a target naming no window at all (empty, or "session:") is refused
because tmux answers those from whatever window is current.

bin/fm-crew-state.sh:150 carried the byte-identical probe and now calls the
single owner. bin/fm-watch.sh reads panes only through capture-pane, which
does fail on an absent target, so it is unchanged.

Regression coverage fails on 733a504 and passes here:
tests/fm-tmux-agent-liveness.test.sh proves the predicate against real tmux
(absent window, gone session, prefix of a live name, live and absent pane ids,
and a record naming no window), and tests/fm-session-start.test.sh proves the
digest line itself reports dead for a gone window, a gone session, and a
prefix match. The tmux fakes in the suites that cross this predicate were
extended to answer list-panes the way tmux 3.7b does, and the real-tmux suites
now start their private servers with -f /dev/null so a host tmux.conf
after-new-session hook cannot rename the fixture session out from under them.
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