fix(spawn): prove the shell is ready before typing a launch command - #1
Merged
Merged
Conversation
Two registered secondmates, cellarsky-sm and hermes-jarvis-sm, were found on 2026-08-26 as bare zsh prompts rather than running agents. Their launch line had been typed into a shell that was not at a prompt, so the whole `claude --dangerously-skip-permissions "<charter>"` string was consumed as raw text and died on a zsh parse error. firstmate had already recorded a meta and registered them as live supervisors. Nothing noticed for weeks. Root cause: fm-spawn inferred shell readiness from pane_current_path changing after `treehouse get`. That proves a chdir happened; it does not prove the shell returned to a prompt, and tmux send-keys has no acknowledgment channel. The 'sleep 0.3' between the literal text and Enter was a timing guess with no backing signal. Adds two guards in fm-tmux-lib.sh: fm_tmux_wait_shell_ready - a bounded echo round-trip. Send a unique marker through the shell, wait to see it rendered back on its own line. Seeing it is positive proof the shell read a command line, ran it and printed a result. A probe landing in a busy shell is a harmless short printf, unlike a multi-KB launch string. fm_tmux_launch_failed - reads the pane after launch and fails loudly on a shell error rather than recording a meta for a pane holding nothing. Also fixes tests/lib.sh: fm_test_cleanup ended on a falsy test and returned 1, so any suite following the header's advice to call it from its own EXIT trap failed with every test passing. Verified red before green: with the readiness guard stubbed to return 0 (the pre-fix behavior) the deaf-shell case fails; restored, all six pass. Full suite 38/39 - fm-loop-l2 fails identically at 24f6891, before any of this.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The incident
Two registered secondmates —
cellarsky-smandhermes-jarvis-sm— were found on 2026-08-26 as bare zsh prompts, not running agents. Their launch line had been typed into a shell that was not at a prompt:They had never started.
data/secondmates.mdlisted three live supervisors; one was real. firstmate had already recorded their metas. Nothing noticed for weeks.Root cause
fm-spawn.shinferred shell readiness frompane_current_pathchanging aftertreehouse get. That proves a chdir happened — it says nothing about whether the shell returned to a prompt, andtmux send-keyshas no acknowledgment channel to tell the difference:The fix
Two guards in
bin/fm-tmux-lib.sh:fm_tmux_wait_shell_ready— a bounded echo round-trip. Send a unique marker through the shell; wait to see it rendered back on its own line. Seeing it is positive proof the shell read a command line, ran it, and printed a result. A probe that lands in a busy shell is a harmless shortprintf, unlike a multi-KB launch string. Only the output line matches (grep -qx), so the echo of the probe's own keystrokes is not accepted as proof.fm_tmux_launch_failed— reads the pane after launch and fails loudly on a shell error, instead of recording a meta for a pane that holds nothing.Both are bypassable with
FM_SKIP_SHELL_READY=1, which the two suites that exercise spawn's worktree logic over a fake tmux now set — the same way they already setFM_INTAKE_OVERRIDE=1.Drive-by
tests/lib.sh—fm_test_cleanupended on a falsy[ -n "$d" ]and returned 1. Its own header tells suites to call it from a custom EXIT trap, so any suite following that advice failed with every test passing. Now returns 0.Verification
Falsifiable — red before green. With the readiness guard stubbed to
return 0(the pre-fix behavior):Restored, all six pass. New suite
tests/fm-spawn-shell-ready.test.shpins both guards, including the exactzsh: parse error near \do'` text from the incident, and asserts a healthy running-agent pane is not flagged.The one failure,
fm-loop-l2, fails identically at24f6891— before any commit in this branch. Verified in a detached worktree, not assumed.Related
herdrv0.8.2 ships"agent start now properly waits for pane/agent readiness instead of racing"— the same defect class, solved upstream at the multiplexer. This fix makes the tmux driver safe today;docs/plans/cmux-herdr-surface-split.mdremains the longer-term answer.