Type: enhancement · Tier: deferred · Tool: wait_for_content_change
Problem
wait_for_text raises ToolError when the pane dies (#{pane_dead}=1), is respawned (#{pane_pid} changes), or has its history shrunk (clear-history — history_size drops below entry baseline). wait_for_content_change does none of this — it polls capture_pane() until content differs or timeout. If the pane dies mid-wait, the tool returns changed=False after the full timeout instead of surfacing the lifecycle event.
An agent using wait_for_content_change to confirm "the pane is doing something" has no signal that the underlying process has exited or been swapped. Same footgun the wait_for_text lifecycle detection was added to address — different tool, identical mechanism.
Proposed fix
Read a small per-tick state snapshot via one display-message round-trip:
"#{pane_pid}|#{pane_dead}"
Cache baseline_pid at entry. Re-read each tick. Raise ToolError on:
state.pane_dead == True — "pane {pane_id} died during wait" (same message/shape as wait_for_text)
state.pane_pid != baseline_pid — "pane {pane_id} was respawned during wait (pid X -> Y)"
No baseline-anchor math needed. wait_for_content_change's "any change anywhere" semantics are unaffected — the lifecycle check is orthogonal to the capture-diff comparison the tool already does.
Source pointers at tmux SHA 134ba6c
#{pane_dead} and #{pane_pid} are stable format strings (see format.c)
- Same detection path
wait_for_text already uses — verified via test_wait_for_text_raises_on_pane_death and test_wait_for_text_raises_on_pane_respawn
Trade-offs
- Adds one
display-message round-trip per tick to wait_for_content_change (currently the tool issues only capture_pane per tick). The cost is symmetric with wait_for_text — same number of subprocesses per poll.
- Adds two regression tests (death + respawn) mirroring the existing
wait_for_text ones.
Recommendation
Defer until an agent flow asks for it OR a small refactor opportunistically lands the parity. Filed as a deferred enhancement so the inconsistency doesn't get lost. The downstream wait_for_text PR documents the gap by analogy; this issue is the standalone tracking point.
Why this is non-duplicative
Cross-checked against the deferred wait-family issues (#50, #51, #52) and libtmux #671:
None of those address wait_for_content_change lifecycle parity.
Type: enhancement · Tier: deferred · Tool:
wait_for_content_changeProblem
wait_for_textraisesToolErrorwhen the pane dies (#{pane_dead}=1), is respawned (#{pane_pid}changes), or has its history shrunk (clear-history—history_sizedrops below entry baseline).wait_for_content_changedoes none of this — it pollscapture_pane()until content differs or timeout. If the pane dies mid-wait, the tool returnschanged=Falseafter the full timeout instead of surfacing the lifecycle event.An agent using
wait_for_content_changeto confirm "the pane is doing something" has no signal that the underlying process has exited or been swapped. Same footgun thewait_for_textlifecycle detection was added to address — different tool, identical mechanism.Proposed fix
Read a small per-tick state snapshot via one
display-messageround-trip:Cache
baseline_pidat entry. Re-read each tick. RaiseToolErroron:state.pane_dead == True— "pane {pane_id} died during wait" (same message/shape aswait_for_text)state.pane_pid != baseline_pid— "pane {pane_id} was respawned during wait (pid X -> Y)"No baseline-anchor math needed.
wait_for_content_change's "any change anywhere" semantics are unaffected — the lifecycle check is orthogonal to the capture-diff comparison the tool already does.Source pointers at tmux SHA
134ba6c#{pane_dead}and#{pane_pid}are stable format strings (seeformat.c)wait_for_textalready uses — verified viatest_wait_for_text_raises_on_pane_deathandtest_wait_for_text_raises_on_pane_respawnTrade-offs
display-messageround-trip per tick towait_for_content_change(currently the tool issues onlycapture_paneper tick). The cost is symmetric withwait_for_text— same number of subprocesses per poll.wait_for_textones.Recommendation
Defer until an agent flow asks for it OR a small refactor opportunistically lands the parity. Filed as a deferred enhancement so the inconsistency doesn't get lost. The downstream
wait_for_textPR documents the gap by analogy; this issue is the standalone tracking point.Why this is non-duplicative
Cross-checked against the deferred wait-family issues (#50, #51, #52) and libtmux #671:
wait_for_text-specific state/capture racewait_for_text-specific same-row blind spotwait_for_text-specific adaptive backoffNone of those address
wait_for_content_changelifecycle parity.