Skip to content

fix(jobs): subshell-guard exit/exec so the sentinel always prints - #46

Merged
tomfordweb merged 1 commit into
mainfrom
fix/exit-kills-sentinel
Jul 30, 2026
Merged

fix(jobs): subshell-guard exit/exec so the sentinel always prints#46
tomfordweb merged 1 commit into
mainfrom
fix/exit-kills-sentinel

Conversation

@tomfordweb

Copy link
Copy Markdown
Owner

Closes beads issue sidemux-6e2.

Problem

A command containing exit or exec as a word terminates (or replaces) the pane's shell before the appended ; printf '<<SMUX…>>' sentinel runs. The sentinel then exists nowhere — not in the pane, not in the pipe-pane log — so the #29 log fallback can't help, and the job reports running until its full timeout. Observed live: a 360s wait burned on a job that finished in ~90s (grep -c SMUX <log> = 0).

Fix

buildLaunchBody wraps only commands matching exit/exec as a word in a posix subshell:

( echo bye; exit 7 ); printf '\n<<SMUX:%s:%d>>\n' 'jxxxxxx' $?
  • exit/exec end the subshell; the outer shell survives to print the sentinel.
  • Exit code passes through unchanged (( exit 7 ) → 7).
  • The pane survives and stays reusable — previously it died and the run reported unknown with no code.
  • Everything else launches exactly as before (no blanket wrapping; the never-wrap design holds for normal commands). fish is left unwrapped — it has no subshell operator, and a fish exit closing the pane is settled by pane-death handling.
  • Known cosmetic trade-off, documented: a guarded command's cd/env changes don't persist in the pane.

Detection-based alternatives (settle when the shell is childless and quiet) were rejected: they false-settle pure-builtin loops and bare read prompts.

Tests

  • Unit: wrap decisions incl. word-boundary cases (pnpm run exit-lint, echo \"exit\" stay unwrapped; fish never wraps).
  • Integration (real tmux): echo pre-exit-probe; exit 7 settles status=exit, code 7, pane intact — this exact case previously ran to wait-timeout.
  • Updated the service test that asserted the old unknown/dead-pane behavior to the new, strictly better outcome.

Full gate green (typecheck, lint, test, build).

A command containing exit or exec as a word terminated or replaced the
pane shell before the appended sentinel printf ran. The sentinel then
existed nowhere — not in the pane, not in the pipe-pane log — so the
log-fallback from #29 could not help and the job sat 'running' until
its full timeout (sidemux-6e2; observed live with a 360s wait on a
finished 90s job).

Just those commands are now wrapped in a posix subshell before the
suffix is appended: exit/exec end the subshell, the outer shell
survives to print the sentinel, and the exit code passes through
unchanged (( exit 7 ) is 7). As a bonus the pane now survives and
reports the real code where it previously died with status unknown.
fish is left unwrapped (no subshell operator); a fish exit closes the
pane, which pane-death handling already settles.

Closes sidemux-6e2
@tomfordweb
tomfordweb merged commit c3e6a96 into main Jul 30, 2026
1 check passed
@tomfordweb
tomfordweb deleted the fix/exit-kills-sentinel branch July 30, 2026 17:56
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