Skip to content

fix: fire pane-died/pane-exited hooks with remain-on-exit#228

Merged
psmux merged 1 commit into
psmux:masterfrom
tarikguney:fix/remain-on-exit-hooks
Apr 16, 2026
Merged

fix: fire pane-died/pane-exited hooks with remain-on-exit#228
psmux merged 1 commit into
psmux:masterfrom
tarikguney:fix/remain-on-exit-hooks

Conversation

@tarikguney

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes pane-died and pane-exited hooks not firing when remain-on-exit is enabled
  • Hooks now fire when a pane's child process exits, regardless of whether the pane is retained or removed from the tree
  • Layout resize only happens when panes are actually removed (not when they transition to dead state)

Root cause

When remain-on-exit is on, prune_exited() marks panes as dead but keeps them in the tree. The hook-firing logic only checked whether the tree leaf count decreased (leaves_after < leaves_before), which never happens in the remain-on-exit case since dead panes are still counted as leaves. So hooks never fired.

What changed

src/tree.rs:

  • prune_exited() now returns (Option<Node>, newly_dead_count) — the second value tracks panes that transitioned alive→dead in each call
  • reap_children() returns (all_empty, any_pruned, any_newly_dead) — separating "pane removed from tree" from "pane became dead"

src/server/mod.rs:

  • Hooks fire when any_pruned || any_newly_dead (either a pane was removed OR became dead)
  • Layout resize only runs when any_pruned (actual tree structure change)
  • Uses fire_hooks() helper instead of inline hook execution

Why hooks don't fire repeatedly

The has_any_exited() fast-path check (line 688) returns false for already-dead panes (if p.dead { return false; }), so once a pane transitions to dead state and hooks fire, subsequent reap ticks skip it entirely.

Fixes #227

Test plan

  • With remain-on-exit on: start a pane with a short-lived process, set pane-died hook — verify hook fires when process exits
  • With remain-on-exit off: same test — verify existing behavior (hooks fire, pane is removed) still works
  • Verify hooks fire exactly once per pane death, not on every reap tick
  • respawn-pane after death should reset the pane; if it dies again, hooks should fire again

🤖 Generated with Claude Code

When remain-on-exit is on, prune_exited() marks panes as dead but keeps
them in the tree.  The hook-firing logic only checked whether the tree
leaf count decreased (any_pruned), which never happens in the
remain-on-exit case since dead panes are still counted as leaves.

Fix: prune_exited() now returns a newly_dead_count tracking panes that
transitioned alive→dead in each call.  reap_children() propagates this
as a separate any_newly_dead flag alongside any_pruned.  The server
event loop fires pane-died/pane-exited hooks when either flag is true,
but only resizes the layout when panes were actually removed.

This matches tmux semantics: hooks fire when the child process exits,
regardless of whether the pane is retained or removed.  The dead flag
on the pane prevents hooks from firing repeatedly on subsequent reap
ticks — has_any_exited() skips already-dead panes.

Fixes psmux#227

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@psmux

psmux commented Apr 16, 2026

Copy link
Copy Markdown
Owner

Hey @tarikguney, closing this since the same fix already landed via the second commit in #225 (80bb22e), which is now merged into master. The changes in tree.rs and server/mod.rs are identical. Issue #227 is resolved. Thanks for the quick turnaround on both PRs!

@psmux psmux closed this Apr 16, 2026
@psmux psmux reopened this Apr 16, 2026
@psmux psmux merged commit a138a9e into psmux:master Apr 16, 2026
6 checks passed
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.

pane-died / pane-exited hooks with remain-on-exit (tmux parity)

2 participants