Bounded out of #246 deliberately rather than fixed silently. That PR hardened endpoint identity checks and the fm-control interrupt path; this is the same defect on the text-delivery path, which the PR was explicitly scoped not to widen into.
The exposure
fm_backend_zellij_send_key calls fm_backend_zellij_target_ready "$1" "${3:-}" with the label only, and the same holds for fm_backend_zellij_send_literal and fm_backend_zellij_send_text_line. bin/fm-send.sh passes only EXPECTED_LABEL, so no recorded tab id reaches the check.
Concrete failure sequence, from the PR body
Two firstmate homes share one zellij session and both carry pre-home-scoping bare tabs titled fm-A. Home1's task A tab is closed, so home1's fm-A is gone and home2's fm-A becomes the unique bare match. Zellij reuses pane id 7 for a pane inside home2's fm-A tab.
FM_HOME=<home1> bin/fm-send.sh A '<text>' resolves target sess:7; target_ready finds tab_for_pane=<home2 tab>; tab_matches_label falls back to the bare title, counts one match, and returns 0. The text is typed into home2's agent.
bin/fm-control.sh A interrupt on identical state now correctly refuses, which is what makes the inconsistency visible.
Why this matters more than the interrupt case it mirrors
An interrupt delivered to the wrong worker is disruptive and obvious. Instructions typed into the wrong worker are neither: that agent reads them as its own steering and acts on them, and nothing in either home reports it.
The fix the PR identifies
The earliest shared boundary is the already-parameterized fm_backend_zellij_target_ready. Give fm_backend_send_key / fm_backend_send_literal / fm_backend_send_text_submit an optional expected-tab-id that bin/fm-send.sh fills from fm_meta_get "$META" zellij_tab_id, rather than repeating fm-control's per-call-site fm_backend_target_exists precheck.
Keep the legacy behaviour: a task with no recorded tab id must continue to work on label-only identity.
Also worth checking in the same pass
Whether any teardown or cleanup path targets by label alone. That one destroys rather than misdelivers, so if it has the same shape it outranks this.
Bounded out of #246 deliberately rather than fixed silently. That PR hardened endpoint identity checks and the fm-control interrupt path; this is the same defect on the text-delivery path, which the PR was explicitly scoped not to widen into.
The exposure
fm_backend_zellij_send_keycallsfm_backend_zellij_target_ready "$1" "${3:-}"with the label only, and the same holds forfm_backend_zellij_send_literalandfm_backend_zellij_send_text_line.bin/fm-send.shpasses onlyEXPECTED_LABEL, so no recorded tab id reaches the check.Concrete failure sequence, from the PR body
Two firstmate homes share one zellij session and both carry pre-home-scoping bare tabs titled
fm-A. Home1's task A tab is closed, so home1'sfm-Ais gone and home2'sfm-Abecomes the unique bare match. Zellij reuses pane id 7 for a pane inside home2'sfm-Atab.FM_HOME=<home1> bin/fm-send.sh A '<text>'resolves targetsess:7;target_readyfindstab_for_pane=<home2 tab>;tab_matches_labelfalls back to the bare title, counts one match, and returns 0. The text is typed into home2's agent.bin/fm-control.sh A interrupton identical state now correctly refuses, which is what makes the inconsistency visible.Why this matters more than the interrupt case it mirrors
An interrupt delivered to the wrong worker is disruptive and obvious. Instructions typed into the wrong worker are neither: that agent reads them as its own steering and acts on them, and nothing in either home reports it.
The fix the PR identifies
The earliest shared boundary is the already-parameterized
fm_backend_zellij_target_ready. Givefm_backend_send_key/fm_backend_send_literal/fm_backend_send_text_submitan optional expected-tab-id thatbin/fm-send.shfills fromfm_meta_get "$META" zellij_tab_id, rather than repeating fm-control's per-call-sitefm_backend_target_existsprecheck.Keep the legacy behaviour: a task with no recorded tab id must continue to work on label-only identity.
Also worth checking in the same pass
Whether any teardown or cleanup path targets by label alone. That one destroys rather than misdelivers, so if it has the same shape it outranks this.