Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/backends/cmux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ fm_backend_cmux_target_ready() { # <target> [expected-label]
# polling cannot solve this here any more than it could for zellij. Active
# probe instead: print the surface's `$PWD` with a unique marker (atomically
# submitted via send_text_line), briefly settle, then capture and read only
# that marker line. Scoped to fm-spawn.sh's own worktree-discovery poll loop.
# that marker line. Scoped to fm-spawn.sh's own worktree-cwd confirmation poll
# loop.
fm_backend_cmux_current_path() { # <target> [expected-label]
local target=$1 expected_label=${2:-} out line marker_begin="__FM_CMUX_CWD_BEGIN__" marker_end="__FM_CMUX_CWD_END__" in_block=0 chunk="" last=""
fm_backend_cmux_target_ready "$target" "$expected_label" || return 0
Expand Down
18 changes: 8 additions & 10 deletions bin/backends/herdr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2520,17 +2520,15 @@ fm_backend_herdr_target_ready() { # <target>
}

# fm_backend_herdr_current_path: the live FOREGROUND process's cwd, or empty on
# any error. Mirrors tmux's pane_current_path poll used for worktree-path
# discovery after `treehouse get`.
# any error. Mirrors tmux's pane_current_path poll fm-spawn.sh uses to confirm
# the pane's shell sits in the leased worktree it was created in.
#
# Verified pitfall: `pane get`'s `.result.pane.cwd` is the pane's cwd AT
# CREATION TIME - the top-level shell's cwd - and does NOT update when that
# shell `cd`s or enters a subshell (as `treehouse get` does). Reading it here
# would make fm-spawn.sh's worktree-discovery poll never see the pane "leave"
# the project directory, since `cwd` stays frozen at the original path forever.
# `.result.pane.foreground_cwd` tracks the ACTUALLY RUNNING foreground
# process's cwd instead, which is what changes when `treehouse get` enters its
# worktree subshell - confirmed live against a real treehouse acquisition.
# shell `cd`s or enters a subshell. `.result.pane.foreground_cwd` tracks the
# ACTUALLY RUNNING foreground process's cwd instead - confirmed live against a
# real treehouse subshell - so it is the read that reports where the shell
# really is rather than where the pane was opened.
fm_backend_herdr_current_path() { # <target>
fm_backend_herdr_target_ready "$1" || return 0
fm_backend_herdr_cli "$FM_BACKEND_HERDR_SESSION" pane get "$FM_BACKEND_HERDR_PANE" 2>/dev/null \
Expand All @@ -2539,8 +2537,8 @@ fm_backend_herdr_current_path() { # <target>

# fm_backend_herdr_send_text_line: send one line of TEXT then submit,
# ATOMICALLY - mirrors tmux's `send-keys -t T text Enter`. Used for the fixed
# spawn-time commands (treehouse get, the GOTMPDIR export). `pane run` types
# the command and submits it in one call (verified).
# spawn-time commands (the GOTMPDIR export). `pane run` types the command and
# submits it in one call (verified).
fm_backend_herdr_send_text_line() { # <target> <text>
fm_backend_herdr_target_ready "$1" || return 1
fm_backend_herdr_cli "$FM_BACKEND_HERDR_SESSION" pane run "$FM_BACKEND_HERDR_PANE" "$2" >/dev/null 2>&1
Expand Down
26 changes: 14 additions & 12 deletions bin/backends/tmux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
# default (tmux, `backend=` absent) path stays byte-identical. Sourced only
# through bin/fm-backend.sh's fm_backend_source, never directly.
#
# Worktree acquisition (running `treehouse get` inside the pane, and polling
# its cwd) is unchanged by this extraction: P1 scopes only the session
# provider, not the worktree provider, so fm-spawn.sh still drives that part
# inline with these same send/current-path primitives.
# Worktree acquisition is not this adapter's concern: bin/fm-spawn.sh leases
# and refreshes the copy itself, creates the window with that path as its
# starting directory, and confirms the pane's cwd through the current-path
# primitive here (fm-spawn.sh's header owns that order).
#
# The verified composer/busy-detection and verify-and-retry-submit primitives
# already live in bin/fm-tmux-lib.sh, shared with the away-mode daemon
Expand Down Expand Up @@ -82,11 +82,13 @@ fm_backend_tmux_container_ensure() {
# at the next free index by targeting the session with a trailing colon
# ("$ses:"), so a non-default base-index (e.g. base-index 1) cannot collide.
# - PIN the window name by disabling automatic-rename and allow-rename on the
# new window: the captain's tmux may rename the window away from fm-<id> once
# treehouse cd's into the worktree, which would break name-based targeting.
# new window: the captain's tmux may rename the window away from fm-<id> as
# soon as its shell starts in the worktree, which would break name-based
# targeting.
# The returned window id lets callers target the window even if its name is ever
# lost, so worktree discovery cannot fall back to the active client's window.
fm_backend_tmux_create_task() { # <session> <window-name> <proj-abs> -> prints window id
# lost, so worktree cwd confirmation cannot fall back to the active client's
# window.
fm_backend_tmux_create_task() { # <session> <window-name> <cwd> -> prints window id
local ses=$1 wname=$2 proj_abs=$3 wid
if tmux list-windows -t "$ses" -F '#{window_name}' | grep -qx "$wname"; then
echo "error: window $ses:$wname already exists" >&2
Expand All @@ -99,16 +101,16 @@ fm_backend_tmux_create_task() { # <session> <window-name> <proj-abs> -> prints
}

# fm_backend_tmux_current_path: the live pane's current working directory, or
# empty on any tmux error. Mirrors fm-spawn.sh's worktree-discovery poll:
# empty on any tmux error. Mirrors fm-spawn.sh's worktree-cwd confirmation poll:
# `tmux display-message -p -t "$T" '#{pane_current_path}'`.
fm_backend_tmux_current_path() { # <target>
tmux display-message -p -t "$1" '#{pane_current_path}' 2>/dev/null
}

# fm_backend_tmux_send_text_line: send one line of TEXT then Enter, with no
# composer verification - used for the fixed spawn-time commands
# (`treehouse get`, the GOTMPDIR export) that already ran this exact sequence
# inline in fm-spawn.sh. Mirrors `tmux send-keys -t "$T" "<text>" Enter`.
# composer verification - used for the fixed spawn-time commands (the GOTMPDIR
# export) that already ran this exact sequence inline in fm-spawn.sh. Mirrors
# `tmux send-keys -t "$T" "<text>" Enter`.
fm_backend_tmux_send_text_line() { # <target> <text>
tmux send-keys -t "$1" "$2" Enter
}
Expand Down
24 changes: 12 additions & 12 deletions bin/backends/zellij.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
# contradicts the design report's assumption ("acceptable for tmux and
# zellij") and required a different implementation strategy - see
# fm_backend_zellij_current_path below and docs/zellij-backend.md
# "Worktree-path discovery: pane_cwd does not track a subshell".
# "Current operation and safety".
# 5. `new-tab` DOES steal focus from an attached client with NO flag to
# suppress it (unlike herdr's --no-focus and tmux's new-window -d).
# Mitigated (fm_backend_zellij_create_task): capture the previously
Expand Down Expand Up @@ -387,24 +387,24 @@ fm_backend_zellij_target_ready() { # <target> [expected-label]
}

# fm_backend_zellij_current_path: the live pane's cwd, or empty on any error.
# Mirrors tmux's pane_current_path poll used for worktree-path discovery after
# `treehouse get`.
# Mirrors tmux's pane_current_path poll fm-spawn.sh uses to confirm the pane's
# shell sits in the leased worktree it was created in.
#
# Verified pitfall (docs/zellij-backend.md "Worktree-path discovery: pane_cwd
# does not track a subshell"): `list-panes --json`'s `pane_cwd` DOES reflect a
# Verified pitfall (docs/zellij-backend.md "Current operation and safety"):
# `list-panes --json`'s `pane_cwd` DOES reflect a
# `cd` run directly in the pane's own top-level shell, but stays FROZEN at
# whatever directory the pane's shell was in when it launched `treehouse get`
# as a foreground command - it never follows that command's own internal `cd`
# into the acquired worktree, even after the subshell is fully interactive and
# whatever directory the pane's shell was in when it launched a foreground
# command such as `treehouse get` - it never follows that command's own internal
# `cd` into the acquired worktree, even after the subshell is fully interactive and
# a `pwd` typed into it prints the correct live path on screen. Zellij's CLI
# exposes no per-pane pid and no live-process cwd field to read instead
# (unlike herdr's `foreground_cwd`), so passive JSON polling cannot solve
# this. Active probe instead: print the pane's `$PWD` with a unique marker
# (atomically submitted, mirroring send_text_line), briefly settle, then capture
# and read only that marker line. Scoped to fm-spawn.sh's own worktree-discovery
# poll loop (the only caller of this op), where injecting a harmless extra
# command before the harness ever launches is an acceptable trade for a reliable
# answer.
# and read only that marker line. Scoped to fm-spawn.sh's own worktree-cwd
# confirmation poll loop (the only caller of this op), where injecting a
# harmless extra command before the harness ever launches is an acceptable
# trade for a reliable answer.
fm_backend_zellij_current_path() { # <target> [expected-label]
local target=$1 expected_label=${2:-} out line marker_begin="__FM_ZELLIJ_CWD_BEGIN__" marker_end="__FM_ZELLIJ_CWD_END__" in_block=0 chunk="" last=""
fm_backend_zellij_target_ready "$target" "$expected_label" || return 0
Expand Down
Loading