From 875b8edce7c9699b763c56b3a6c39f955d7fe2ae Mon Sep 17 00:00:00 2001
From: Ben <70361638+SpecialAgentB3@users.noreply.github.com>
Date: Sun, 5 Jul 2026 17:13:24 -0500
Subject: [PATCH 1/5] fix(backends): resolve physical path in orca repo_ensure
for symlinked projects
Secondmate homes hold project clones as symlinks to the primary's
canonical clones. fm-spawn computes PROJ_ABS with logical pwd, so
the symlink path reached orca repo add --path, minting a duplicate
repo entry instead of reusing the canonical one.
In fm_backend_orca_repo_ensure, resolve the project directory to its
physical path (cd
&& pwd -P) before passing it to orca repo
show and orca repo add. Keeps the fix scoped to the Orca backend;
other backends are unaffected.
Update three existing tests that passed the non-existent path
/repo/path to use real directories, since the physical-path
resolution now requires the directory to exist. Add a new test
proving a symlinked project dir resolves to the same canonical
physical path as the canonical dir itself.
---
bin/backends/orca.sh | 12 ++++++---
tests/fm-backend-orca.test.sh | 49 ++++++++++++++++++++++++++++-------
2 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/bin/backends/orca.sh b/bin/backends/orca.sh
index 1a4311a2..6d3a3363 100644
--- a/bin/backends/orca.sh
+++ b/bin/backends/orca.sh
@@ -107,16 +107,20 @@ fm_backend_orca_run_json() {
}
fm_backend_orca_repo_ensure() { #
- local project=$1 out repo_id
+ local project=$1 out repo_id phys
fm_backend_orca_tool_check || return 1
- out=$(orca repo show --repo "path:$project" --json 2>/dev/null || true)
+ phys=$(cd "$project" && pwd -P) || {
+ echo "error: cannot resolve physical path for $project" >&2
+ return 1
+ }
+ out=$(orca repo show --repo "path:$phys" --json 2>/dev/null || true)
if repo_id=$(printf '%s' "$out" | fm_backend_orca_json_get repo-id 2>/dev/null); then
printf '%s' "$repo_id"
return 0
fi
- out=$(orca repo add --path "$project" --json) || return 1
+ out=$(orca repo add --path "$phys" --json) || return 1
repo_id=$(printf '%s' "$out" | fm_backend_orca_json_get repo-id) || {
- echo "error: orca repo add did not return a repo id for $project" >&2
+ echo "error: orca repo add did not return a repo id for $phys" >&2
return 1
}
printf '%s' "$repo_id"
diff --git a/tests/fm-backend-orca.test.sh b/tests/fm-backend-orca.test.sh
index 6cf67f17..5b8ffed6 100755
--- a/tests/fm-backend-orca.test.sh
+++ b/tests/fm-backend-orca.test.sh
@@ -349,8 +349,10 @@ test_worktree_path_resolves_id() {
}
test_json_get_ignores_undocumented_terminal_id_shapes() {
- local out status wt_id wt_path term
+ local out status wt_id wt_path term proj_dir
orca_case parser-pruned-terminal-shapes
+ proj_dir="$CASE_DIR/project"
+ mkdir -p "$proj_dir"
set +e
out=$( printf '{"ok":true,"result":{"id":"term-root-id"}}\n' | \
@@ -363,7 +365,7 @@ test_json_get_ignores_undocumented_terminal_id_shapes() {
printf '{"ok":true,"result":{"repo":{"id":"repo-123"}}}\n' > "$RESP/2.out"
printf '{"ok":true,"result":{"worktree":{"id":"wt-123","path":"/tmp/orca-wt","terminal":{"handle":"term-nested"}}}}\n' > "$RESP/3.out"
out=$( PATH="$FB:$PATH" FM_ORCA_LOG="$LOG" FM_ORCA_RESPONSES="$RESP" \
- bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create /repo/path fm-task' "$ROOT" )
+ bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create "$1" fm-task' "$ROOT" "$proj_dir" )
wt_id=${out%%$'\t'*}
wt_path=${out#*$'\t'}
term=${wt_path#*$'\t'}
@@ -375,14 +377,17 @@ test_json_get_ignores_undocumented_terminal_id_shapes() {
}
test_worktree_and_terminal_helpers_parse_json() {
- local out wt_id wt_path term
+ local out wt_id wt_path term proj_dir proj_phys
orca_case lifecycle-helpers
+ proj_dir="$CASE_DIR/project"
+ mkdir -p "$proj_dir"
+ proj_phys=$(cd "$proj_dir" && pwd -P)
printf '1\n' > "$RESP/1.exit"
printf '{"ok":true,"result":{"repo":{"id":"repo-123"}}}\n' > "$RESP/2.out"
printf '{"ok":true,"result":{"worktree":{"id":"wt-123","path":"/tmp/orca-wt"}}}\n' > "$RESP/3.out"
printf '{"ok":true,"result":{"terminal":{"handle":"term-123"}}}\n' > "$RESP/4.out"
out=$( PATH="$FB:$PATH" FM_ORCA_LOG="$LOG" FM_ORCA_RESPONSES="$RESP" \
- bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create /repo/path fm-task' "$ROOT" )
+ bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create "$1" fm-task' "$ROOT" "$proj_dir" )
wt_id=${out%%$'\t'*}
wt_path=${out#*$'\t'}
[ "$wt_id" = wt-123 ] || fail "worktree helper should print worktree id, got '$wt_id'"
@@ -390,10 +395,10 @@ test_worktree_and_terminal_helpers_parse_json() {
term=$( PATH="$FB:$PATH" FM_ORCA_LOG="$LOG" FM_ORCA_RESPONSES="$RESP" \
bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_terminal_create wt-123 fm-task' "$ROOT" )
[ "$term" = term-123 ] || fail "terminal helper should print terminal handle, got '$term'"
- assert_contains "$(cat "$LOG")" $'orca\x1f''repo'$'\x1f''show'$'\x1f''--repo'$'\x1f''path:/repo/path'$'\x1f''--json' \
- "worktree helper should first check repo registration"
- assert_contains "$(cat "$LOG")" $'orca\x1f''repo'$'\x1f''add'$'\x1f''--path'$'\x1f''/repo/path'$'\x1f''--json' \
- "worktree helper should register an absent repo"
+ assert_contains "$(cat "$LOG")" "path:$proj_phys" \
+ "worktree helper should pass the physical project path to orca repo show"
+ assert_contains "$(cat "$LOG")" $'orca\x1f''repo'$'\x1f''add'$'\x1f''--path'$'\x1f'"$proj_phys"$'\x1f''--json' \
+ "worktree helper should register an absent repo using the physical path"
assert_contains "$(cat "$LOG")" $'orca\x1f''worktree'$'\x1f''create'$'\x1f''--repo'$'\x1f''id:repo-123'$'\x1f''--name'$'\x1f''fm-task'$'\x1f''--no-parent'$'\x1f''--setup'$'\x1f''skip'$'\x1f''--json' \
"worktree helper did not create an independent no-hook worktree"
assert_contains "$(cat "$LOG")" $'orca\x1f''terminal'$'\x1f''create'$'\x1f''--worktree'$'\x1f''id:wt-123'$'\x1f''--title'$'\x1f''fm-task'$'\x1f''--json' \
@@ -402,13 +407,15 @@ test_worktree_and_terminal_helpers_parse_json() {
}
test_worktree_create_removes_worktree_when_path_missing() {
- local out status
+ local out status proj_dir
orca_case lifecycle-missing-path
+ proj_dir="$CASE_DIR/project"
+ mkdir -p "$proj_dir"
printf '1\n' > "$RESP/1.exit"
printf '{"ok":true,"result":{"repo":{"id":"repo-no-path"}}}\n' > "$RESP/2.out"
printf '{"ok":true,"result":{"worktree":{"id":"wt-no-path"},"terminal":{"handle":"term-no-path"}}}\n' > "$RESP/3.out"
out=$( PATH="$FB:$PATH" FM_ORCA_LOG="$LOG" FM_ORCA_RESPONSES="$RESP" \
- bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create /repo/path fm-task' "$ROOT" 2>&1 )
+ bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_worktree_create "$1" fm-task' "$ROOT" "$proj_dir" 2>&1 )
status=$?
[ "$status" -ne 0 ] || fail "worktree helper should fail when Orca omits the worktree path"
assert_contains "$out" "orca worktree create did not return a path for fm-task" \
@@ -1256,6 +1263,27 @@ test_dispatcher_sources_orca_and_routes_primitives() {
pass "fm-backend dispatcher: accepts orca and routes capture through bin/backends/orca.sh"
}
+test_repo_ensure_resolves_symlinked_project_to_physical_path() {
+ local canonical phys_canonical symlink out log_text
+ orca_case repo-ensure-realpath
+ canonical=$(mktemp -d)
+ phys_canonical=$(cd "$canonical" && pwd -P)
+ symlink="$CASE_DIR/symlink-project"
+ ln -s "$canonical" "$symlink"
+ printf '1\n' > "$RESP/1.exit"
+ printf '{"ok":true,"result":{"repo":{"id":"repo-canonical"}}}\n' > "$RESP/2.out"
+ out=$( PATH="$FB:$PATH" FM_ORCA_LOG="$LOG" FM_ORCA_RESPONSES="$RESP" \
+ bash -c '. "$0/bin/backends/orca.sh"; fm_backend_orca_repo_ensure "$1"' "$ROOT" "$symlink" )
+ [ "$out" = "repo-canonical" ] || fail "repo_ensure should return the repo id, got '$out'"
+ log_text=$(cat "$LOG")
+ assert_not_contains "$log_text" "path:$symlink" \
+ "repo_ensure must not pass the symlink path to orca"
+ assert_contains "$log_text" "path:$phys_canonical" \
+ "repo_ensure must pass the physical canonical path to orca"
+ rm -rf "$canonical"
+ pass "fm_backend_orca_repo_ensure: resolves symlinked project dir to its physical path before Orca calls"
+}
+
test_capture_reads_terminal_tail_json
test_capture_falls_back_to_text_fields
test_capture_fails_on_orca_error_json
@@ -1277,6 +1305,7 @@ test_remove_worktree_refuses_empty_id
test_remove_worktree_rejects_orca_error_json
test_worktree_path_resolves_id
test_dispatcher_sources_orca_and_routes_primitives
+test_repo_ensure_resolves_symlinked_project_to_physical_path
test_json_get_ignores_undocumented_terminal_id_shapes
test_worktree_and_terminal_helpers_parse_json
test_worktree_create_removes_worktree_when_path_missing
From f1c6a8a803b775a9016b31df4a928fb69ba7c1f8 Mon Sep 17 00:00:00 2001
From: Ben <70361638+SpecialAgentB3@users.noreply.github.com>
Date: Sun, 5 Jul 2026 17:20:00 -0500
Subject: [PATCH 2/5] no-mistakes(review): fix test tmpdir leak by using
CASE_DIR instead of mktemp
---
tests/fm-backend-orca.test.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/fm-backend-orca.test.sh b/tests/fm-backend-orca.test.sh
index 5b8ffed6..d8cbd398 100755
--- a/tests/fm-backend-orca.test.sh
+++ b/tests/fm-backend-orca.test.sh
@@ -1266,7 +1266,8 @@ test_dispatcher_sources_orca_and_routes_primitives() {
test_repo_ensure_resolves_symlinked_project_to_physical_path() {
local canonical phys_canonical symlink out log_text
orca_case repo-ensure-realpath
- canonical=$(mktemp -d)
+ canonical="$CASE_DIR/canonical"
+ mkdir "$canonical"
phys_canonical=$(cd "$canonical" && pwd -P)
symlink="$CASE_DIR/symlink-project"
ln -s "$canonical" "$symlink"
@@ -1280,7 +1281,6 @@ test_repo_ensure_resolves_symlinked_project_to_physical_path() {
"repo_ensure must not pass the symlink path to orca"
assert_contains "$log_text" "path:$phys_canonical" \
"repo_ensure must pass the physical canonical path to orca"
- rm -rf "$canonical"
pass "fm_backend_orca_repo_ensure: resolves symlinked project dir to its physical path before Orca calls"
}
From d28ba07bdc5b82df3b4a1f373fdd7a39d65dc466 Mon Sep 17 00:00:00 2001
From: Ben <70361638+SpecialAgentB3@users.noreply.github.com>
Date: Sun, 5 Jul 2026 17:43:37 -0500
Subject: [PATCH 3/5] no-mistakes(test): pass -f /dev/null to private tmux test
servers
---
tests/fm-afk-inject-e2e.test.sh | 30 ++++++++++++++---------------
tests/fm-backend-tmux-smoke.test.sh | 2 +-
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/tests/fm-afk-inject-e2e.test.sh b/tests/fm-afk-inject-e2e.test.sh
index 415c7db4..fb5cea9c 100755
--- a/tests/fm-afk-inject-e2e.test.sh
+++ b/tests/fm-afk-inject-e2e.test.sh
@@ -54,7 +54,7 @@ cleanup_all() {
wait "$DAEMON_PID" 2>/dev/null || true
fi
if [ -n "${SOCKET:-}" ] && [ -n "${REAL_TMUX:-}" ]; then
- "$REAL_TMUX" -L "$SOCKET" kill-server 2>/dev/null || true
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" kill-server 2>/dev/null || true
fi
rm -rf "${TMUX_SHIM_DIR:-}" 2>/dev/null || true
rm -rf "${STATE_DIR:-}" 2>/dev/null || true
@@ -73,8 +73,8 @@ LOG_FILE="$STATE_DIR/submitted.log"
. "$DAEMON"
# Private tmux server with a supervisor session.
-"$REAL_TMUX" -L "$SOCKET" new-session -d -s supervisor -x 200 -y 50
-SUPERVISOR_PANE=$("$REAL_TMUX" -L "$SOCKET" display-message -p -t supervisor '#{pane_id}')
+"$REAL_TMUX" -f /dev/null -L "$SOCKET" new-session -d -s supervisor -x 200 -y 50
+SUPERVISOR_PANE=$("$REAL_TMUX" -f /dev/null -L "$SOCKET" display-message -p -t supervisor '#{pane_id}')
# Supervisor pane loop: a small deterministic composer that logs each submitted
# line verbatim (hex + text + classification). It draws the in-progress input
@@ -126,7 +126,7 @@ LOOP
chmod +x "$LOOP_SCRIPT"
# Start the loop in the supervisor pane.
-"$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" \
+"$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" \
"bash '$LOOP_SCRIPT' '$LOG_FILE'" Enter
sleep 1 # let the loop start and settle
@@ -145,15 +145,15 @@ if [ "\${1:-}" = "send-keys" ] && [ -f "$STATE_DIR/.swallow-enter" ]; then
fi
_args+=("\$_arg")
done
- exec "$REAL_TMUX" -L "$SOCKET" send-keys "\${_args[@]}"
+ exec "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys "\${_args[@]}"
fi
-exec "$REAL_TMUX" -L "$SOCKET" "\$@"
+exec "$REAL_TMUX" -f /dev/null -L "$SOCKET" "\$@"
SHIM
chmod +x "$TMUX_SHIM_DIR/tmux"
# Create a fake crewmate window (the watcher lists fm-* windows for stale
# detection). The pane is an inert shell — it just needs to exist.
-"$REAL_TMUX" -L "$SOCKET" new-window -d -n fm-fake-c1 -t supervisor
+"$REAL_TMUX" -f /dev/null -L "$SOCKET" new-window -d -n fm-fake-c1 -t supervisor
start_daemon() {
PATH="$TMUX_SHIM_DIR:$PATH" \
@@ -217,24 +217,24 @@ reset_state() {
selfcheck_pane_input_pending() {
local check_text="selfcheck-marker-12345"
- "$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" -l "$check_text"
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" -l "$check_text"
sleep 0.5
if PATH="$TMUX_SHIM_DIR:$PATH" pane_input_pending "$SUPERVISOR_PANE"; then
# Detected — clean up the text and proceed.
- "$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
sleep 0.3
return 0
fi
# Not detected - print diagnostics and fail.
echo "pane_input_pending cannot detect typed text in this tmux environment" >&2
local _cy _line
- _cy=$("$REAL_TMUX" -L "$SOCKET" display-message -p -t "$SUPERVISOR_PANE" '#{cursor_y}' 2>/dev/null)
+ _cy=$("$REAL_TMUX" -f /dev/null -L "$SOCKET" display-message -p -t "$SUPERVISOR_PANE" '#{cursor_y}' 2>/dev/null)
echo " cursor_y=$_cy" >&2
echo " pane capture (first 10 lines):" >&2
- "$REAL_TMUX" -L "$SOCKET" capture-pane -p -t "$SUPERVISOR_PANE" 2>/dev/null | head -10 | sed 's/^/ /' >&2
- _line=$("$REAL_TMUX" -L "$SOCKET" capture-pane -p -t "$SUPERVISOR_PANE" 2>/dev/null | sed -n "$((_cy + 1))p")
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" capture-pane -p -t "$SUPERVISOR_PANE" 2>/dev/null | head -10 | sed 's/^/ /' >&2
+ _line=$("$REAL_TMUX" -f /dev/null -L "$SOCKET" capture-pane -p -t "$SUPERVISOR_PANE" 2>/dev/null | sed -n "$((_cy + 1))p")
echo " cursor line: '$_line'" >&2
- "$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
fail "pane_input_pending self-check failed"
}
@@ -249,7 +249,7 @@ test_scenario_a() {
# Type partial text into the supervisor pane with NO Enter. This simulates the
# captain returning and starting to type before afk has been cleared.
- "$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" -l "human draft text"
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" -l "human draft text"
sleep 0.5
# Write a captain-relevant status to trigger a real escalation through the
@@ -271,7 +271,7 @@ test_scenario_a() {
fi
# Now submit the human's text (Enter). The pane goes idle.
- "$REAL_TMUX" -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
+ "$REAL_TMUX" -f /dev/null -L "$SOCKET" send-keys -t "$SUPERVISOR_PANE" Enter
sleep 0.5
# Wait for the daemon to retry injection (housekeeping tick = 1s).
diff --git a/tests/fm-backend-tmux-smoke.test.sh b/tests/fm-backend-tmux-smoke.test.sh
index 0d47b83e..78680131 100755
--- a/tests/fm-backend-tmux-smoke.test.sh
+++ b/tests/fm-backend-tmux-smoke.test.sh
@@ -30,7 +30,7 @@ cleanup_all() {
SHIM_DIR=$(mktemp -d "${TMPDIR:-/tmp}/fm-backend-smoke.XXXXXX")
cat > "$SHIM_DIR/tmux" <
Date: Sun, 5 Jul 2026 18:00:09 -0500
Subject: [PATCH 4/5] no-mistakes(document): document Orca physical path
resolution for symlinked projects
---
docs/orca-backend.md | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/docs/orca-backend.md b/docs/orca-backend.md
index 55142998..f0fe97d3 100644
--- a/docs/orca-backend.md
+++ b/docs/orca-backend.md
@@ -22,6 +22,7 @@ When bootstrap resolves Orca from `FM_BACKEND=orca` or `config/backend=orca`, it
First run: before spawn mutates any repo or worktree state, firstmate runs `orca status --json` and requires the app to report `reachable=true` and `state="ready"` - start the Orca app and wait for it to finish loading before spawning.
Spawn fails closed if the runtime is not ready.
The first spawn against a given project also auto-registers that project's repo in Orca (`orca repo add --path`) if it is not already registered - no manual registration step is needed.
+The project directory is resolved to its physical path (`cd && pwd -P`) before any Orca repo lookup or registration call, so a secondmate home that points at the primary's project clone via a symlink reuses the canonical project's existing Orca registration instead of creating a duplicate entry.
Watching and attaching: Orca owns both the worktree and the terminal for its tasks, so there is nothing to attach to outside the Orca app itself - open the app and find the terminal for the task (recorded as `terminal=` in the task's meta, with `window=fm-` as the shared firstmate alias).
You do not need to open the app for routine supervision: `bin/fm-peek.sh fm-` reads a task's terminal without opening Orca, and `bin/fm-send.sh fm- ""` steers it (Enter and Ctrl-C are supported; Escape is not).
@@ -67,7 +68,8 @@ The recorded `backend=orca` field tells shared call sites to route capture, send
Spawn:
-1. Ensure the project repo is registered in Orca, adding it with `orca repo add --path` when needed.
+1. Resolve the project directory to its physical path via `cd && pwd -P`, then ensure that physical path is registered as a repo in Orca, adding it with `orca repo add --path` when needed.
+ This physical-path resolution prevents secondmate homes - which symlink to the primary's project clone - from registering duplicate Orca repos under the symlink path.
2. Create an independent Orca worktree with `orca worktree create --repo id: --name fm- --no-parent --setup skip`.
3. Reuse the terminal returned by Orca worktree creation only when it appears in the verified `result.terminal.handle` shape, or create a titled terminal in that worktree when Orca returns only the worktree.
4. Install firstmate's per-harness turn-end hooks in the Orca worktree.
@@ -112,7 +114,8 @@ Fake-Orca tests cover:
- `fm-peek.sh`, `fm-send.sh`, and `fm-crew-state.sh` routing through recorded Orca metadata;
- slash-command popup placeholder handling that requires a second Enter before `fm-send.sh` reports submission;
- scout teardown releasing an Orca worktree through `orca worktree rm`;
-- ship teardown failing closed when the recorded Orca worktree id is missing, cannot resolve to a path, or resolves to a different path than `worktree=`.
+- ship teardown failing closed when the recorded Orca worktree id is missing, cannot resolve to a path, or resolves to a different path than `worktree=`;
+- `fm_backend_orca_repo_ensure` resolving a symlinked project directory to its physical path before passing it to Orca, confirming a symlink and its canonical target register under the same physical path.
Run the focused suite with:
From c094ff4c138c826f6a56e566fd754eda4c979d7e Mon Sep 17 00:00:00 2001
From: Ben <70361638+SpecialAgentB3@users.noreply.github.com>
Date: Sun, 5 Jul 2026 18:04:48 -0500
Subject: [PATCH 5/5] no-mistakes(lint): remove unused local `dir` vars in orca
test helpers
---
tests/fm-backend-orca.test.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/fm-backend-orca.test.sh b/tests/fm-backend-orca.test.sh
index d8cbd398..e796ed76 100755
--- a/tests/fm-backend-orca.test.sh
+++ b/tests/fm-backend-orca.test.sh
@@ -9,7 +9,7 @@ set -u
TMP_ROOT=$(fm_test_tmproot fm-backend-orca-tests)
make_orca_fakebin() { # -> echoes fakebin dir
- local dir=$1 fb="$1/fakebin"
+ local fb="$1/fakebin"
mkdir -p "$fb"
cat > "$fb/orca" <<'SH'
#!/usr/bin/env bash
@@ -49,7 +49,7 @@ orca_case() { # -> sets CASE_DIR LOG RESP FB
}
neutral_fm_root() { # -> echoes a minimal root with a quiet guard
- local dir=$1 root="$1/root"
+ local root="$1/root"
mkdir -p "$root/bin"
cat > "$root/bin/fm-guard.sh" <<'SH'
#!/usr/bin/env bash