Skip to content
Closed
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
200 changes: 128 additions & 72 deletions bin/fm-spawn.sh

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions bin/fm-teardown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@
# through metadata publication, closing the publication
# gap; forced secondmate teardown takes it and runs the same checks for every
# descendant Treehouse slot before touching any child.
# A task worktree is also claimed with a durable Treehouse lease at spawn
# (bin/fm-spawn.sh owns the claim), and the return below releases that lease with
# the slot, so an ordinary teardown is what frees the claim.
# These refusals are not relaxed by --force: --force authorizes discarding THIS
# task's unlanded work, never another task's live work. Nothing of this task's
# own is removed by a refusal; reconcile whichever record is wrong and re-run.
Expand Down
3 changes: 2 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ Codex App support is recorded in `docs/codex-app-backend.md`; it is not selectab
## Worktrees, not branches in your checkout

Crewmates never intentionally touch your project clone; [treehouse](https://github.com/kunchenguid/treehouse) pools clean worktrees for tmux, herdr, zellij, and cmux tasks, while Orca creates its own worktrees for `backend=orca`.
The [`fm-spawn.sh` header](../bin/fm-spawn.sh) owns ship/scout worktree isolation and fresh-base refusal rules, including spawns from linked homes.
A task's pool worktree is claimed with a durable treehouse lease labelled by the task record that owns it, so a copy whose agent is not running is never handed to the next spawn and reset under it, and teardown's return releases the claim.
The [`fm-spawn.sh` header](../bin/fm-spawn.sh) owns ship/scout worktree isolation, the lease claim, and fresh-base refusal rules, including spawns from linked homes.
Portable regressions live in [`tests/fm-spawn-pool-base-freshen.test.sh`](../tests/fm-spawn-pool-base-freshen.test.sh) for spawn isolation and base freshness, and [`tests/fm-control-relaunch.test.sh`](../tests/fm-control-relaunch.test.sh) for preserving the recorded copy on relaunch.

The firstmate repo has one extra exposure because it can dispatch crewmates to work on itself.
Expand Down
2 changes: 1 addition & 1 deletion docs/cmux-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ A genuinely fresh surface returns an internal error from `read-screen` until som
Target readiness therefore uses the structural `list-panes` response instead of a content read.
Capture remains bounded and locally trimmed after `read-screen` becomes available.

`current_directory` follows a top-level shell `cd` but not the foreground subshell opened by `treehouse get`.
`current_directory` follows a top-level shell `cd` but not a foreground subshell.
Spawn-time worktree discovery sends begin and end markers around `pwd`, captures the marked block, and joins wrapped path lines.

An ordinary metadata-routed `fm-send.sh` text steer becomes a durable steering-inbox record, and only its best-effort constant doorbell passes through cmux's submit machinery.
Expand Down
22 changes: 22 additions & 0 deletions docs/verification/runtime-backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,28 @@ The current catch-up reporting boundary is pinned by `tests/fm-afk-return.test.s
The fixture captures submitted input through Pi's `input` extension hook, so the lab agent directory needs no provider credentials.
The daemon injection transport into a live composer keeps its coverage in `tests/fm-afk-inject-herdr-e2e.test.sh` for the harnesses that still run the daemon, and the dedicated Herdr daemon workspace topology is covered by `tests/fm-afk-launch.test.sh` and preserves the captain tab's pane count.

## Treehouse worktree pool

The pool claim `bin/fm-spawn.sh` takes on a task worktree was verified on 2026-09-17 with treehouse v2.1.0 on Linux (WSL2).
A task whose agent is not currently running leaves no process inside its copy, and a lease is what keeps that idle-looking copy from being handed to the next spawn and reset.

```sh
treehouse get --lease --lease-holder 'fm:demo-task@/home/fm/state'
treehouse status --json
printf 'pwd -P\nexit\n' | treehouse get
```

Observed output, with the pool root shortened:

```text
.treehouse/repo-b871d3/1/repo
[{"name":"1","path":".../1/repo","status":"leased","lease_id":"84cf5022d6c3160e356d7a09de20d763","lease_holder":"fm:demo-task@/home/fm/state","leased_at":"2026-09-17T18:37:42.418019618+07:00","processes":[]}]
.treehouse/repo-b871d3/2/repo
```

The leased slot carries no process and is still not handed to the later plain `get`, which allocates a different slot, and the holder label is readable back from the pool's own status.
[`tests/fm-spawn-worktree-lease.test.sh`](../../tests/fm-spawn-worktree-lease.test.sh) is the command that refreshes this record; it skips when treehouse is not installed.

## Zellij

The current compatibility floor and latest verification are Zellij 0.44.0 with `jq` on macOS aarch64.
Expand Down
2 changes: 1 addition & 1 deletion docs/zellij-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ A pane can still disappear between verification and the operation; downstream su

Every pane operation passes an explicit `--pane-id` because a new session can focus its release-notes plugin pane, whose numeric plugin id is in a separate namespace from terminal pane ids.

`pane_cwd` follows a top-level shell `cd` but not the foreground subshell opened by `treehouse get`.
`pane_cwd` follows a top-level shell `cd` but not a foreground subshell.
Worktree discovery therefore sends begin and end markers around `pwd`, captures the marked block, and joins wrapped path lines.
This active probe is scoped to spawn-time worktree discovery and is not advertised as a general live-cwd API.

Expand Down
7 changes: 4 additions & 3 deletions tests/fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,15 @@ EOF
}

# fm_test_make_spawn_fakebin <dir> [extra-exit0-tool...]
# Creates <dir>/fakebin with the spawn tmux stub, a no-op treehouse, and any
# extra exit-0 tools. Echoes the fakebin path.
# Creates <dir>/fakebin with the spawn tmux stub, a lease-answering treehouse,
# and any extra exit-0 tools. Echoes the fakebin path.
fm_test_make_spawn_fakebin() {
local dir=$1 fakebin
shift
fakebin=$(fm_fakebin "$dir")
fm_test_fake_tmux_spawn "$fakebin"
fm_fake_exit0 "$fakebin" treehouse "$@"
fm_fake_exit0 "$fakebin" "$@"
fm_test_fake_treehouse_lease "$fakebin"
printf '%s\n' "$fakebin"
}

Expand Down
49 changes: 36 additions & 13 deletions tests/fm-backend-herdr-presentation-e2e.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,12 @@ set -u
done
printf '\n'
} >> "$TREEHOUSE_CALL_LOG"
# Arm the post-create abort: the worktree claim is taken after the task pane
# exists, so refusing it aborts the spawn exactly where this fixture needs it,
# without involving the real pool or the arrival wait.
if [ -d "$POST_CREATE_ABORT_CONTROL" ] && [ "${1:-}" = get ]; then
exit 0
echo "treehouse: no worktree available (post-create abort fixture)" >&2
exit 1
fi
# Treehouse's pool allocator is outside the Herdr concurrency contract under
# test. Serialize its calls so simultaneous recovery spawns cannot race for
Expand Down Expand Up @@ -412,10 +416,18 @@ spawn_task() { # <id> <home> <project>
"$ROOT/bin/fm-spawn.sh" "$id" "$project" "sh -c 'while :; do sleep 60; done'" --mode no-mistakes --yolo off --backend herdr
}

# A concurrent spawn can lose either serialization race before it does any
# projected work: the home's task-set lock, or the project's pool-slot lock that
# the worktree claim is taken under. Both mean "not my turn yet", so both retry.
concurrent_spawn_lost_a_lock() { # <stderr>
grep -F "task set is locked" "$1" >/dev/null 2>&1 \
|| grep -F "Treehouse slot allocation or return is in progress" "$1" >/dev/null 2>&1
}

finish_concurrent_spawn() { # <id> <status> <stdout> <stderr>
local id=$1 status=$2 out=$3 err=$4
[ "$status" -ne 0 ] || return 0
grep -F "task set is locked" "$err" >/dev/null 2>&1 \
concurrent_spawn_lost_a_lock "$err" \
|| fail "concurrent projected spawn $id failed unexpectedly: $(cat "$err")"
spawn_task "$id" "$HOME_DIR" "$PROJECT_DIR" > "$out" 2> "$err" \
|| fail "projected spawn $id retry failed after task-set publication completed: $(cat "$err")"
Expand All @@ -424,7 +436,7 @@ finish_concurrent_spawn() { # <id> <status> <stdout> <stderr>
finish_concurrent_expected_abort() { # <id> <status> <stdout> <stderr>
local id=$1 status=$2 out=$3 err=$4
[ "$status" -ne 0 ] || fail "post-create abort fixture $id unexpectedly succeeded"
if grep -F "task set is locked" "$err" >/dev/null 2>&1; then
if concurrent_spawn_lost_a_lock "$err"; then
if spawn_task "$id" "$HOME_DIR" "$PROJECT_DIR" > "$out" 2> "$err"; then
fail "post-create abort fixture $id unexpectedly succeeded after task-set publication completed"
fi
Expand Down Expand Up @@ -851,20 +863,31 @@ if wait "$ABORT_A_PID"; then ABORT_A_STATUS=0; else ABORT_A_STATUS=$?; fi
if wait "$ABORT_B_PID"; then ABORT_B_STATUS=0; else ABORT_B_STATUS=$?; fi
finish_concurrent_expected_abort abort-a "$ABORT_A_STATUS" "$TMP_ROOT/abort-a.out" "$TMP_ROOT/abort-a.err"
finish_concurrent_expected_abort abort-b "$ABORT_B_STATUS" "$TMP_ROOT/abort-b.out" "$TMP_ROOT/abort-b.err"
# The forced foreground_cwd is a plain non-git directory, which the discovery
# poll now screens out on every read rather than adopting, so the armed failure
# arrives as the poll's own deadline refusal naming that path.
grep -F "did not enter an isolated worktree" "$TMP_ROOT/abort-a.err" >/dev/null 2>&1 \
# The armed failure is the refused worktree claim, which the spawn takes after
# the task pane exists - so both spawns abort post-create, as this fixture needs.
grep -F "could not lease a worktree" "$TMP_ROOT/abort-a.err" >/dev/null 2>&1 \
|| fail "post-create abort fixture A did not reach the armed validation failure"
grep -F "did not enter an isolated worktree" "$TMP_ROOT/abort-b.err" >/dev/null 2>&1 \
grep -F "could not lease a worktree" "$TMP_ROOT/abort-b.err" >/dev/null 2>&1 \
|| fail "post-create abort fixture B did not reach the armed validation failure"
ABORT_A_PANE=$(cat "$POST_CREATE_ABORT_CONTROL/abort-a/task-pane")
ABORT_B_PANE=$(cat "$POST_CREATE_ABORT_CONTROL/abort-b/task-pane")
ABORT_SEQUENCE=$(sed -n "$((ABORT_FOCUS_START + 1)),\$p" "$FOCUS_AUDIT_LOG" | awk -F '\t' -v a="$ABORT_A_PANE" -v b="$ABORT_B_PANE" '
$1 == "workspace-create" && $4 ~ /^└ abort-a · p:/ { print "create-a" }
$1 == "workspace-create" && $4 ~ /^└ abort-b · p:/ { print "create-b" }
$1 == "pane-close" && $4 == a { print "close-a" }
$1 == "pane-close" && $4 == b { print "close-b" }
# Read the interleaving from the ordered CLI call log rather than the focus
# audit: an aborted spawn's projected pane is a bare idle shell, so the
# focus-preserving close ends that shell instead of calling `pane close`, and
# only the call log records both mechanisms in one order. Either one is that
# task's cleanup; what must hold is that one task's create-then-cleanup does not
# interleave with the other's.
ABORT_SEQUENCE=$(sed -n "$((ABORT_START + 1)),\$p" "$HERDR_CALL_LOG" | awk -F '\t' -v a="$ABORT_A_PANE" -v b="$ABORT_B_PANE" '
$1 == "workspace" && $2 == "create" { for (i = 3; i <= NF; i++) {
if ($i ~ /^└ abort-a · p:/) { print "create-a" }
if ($i ~ /^└ abort-b · p:/) { print "create-b" }
} }
$1 == "pane" && ($2 == "close" || $2 == "process-info") {
for (i = 3; i <= NF; i++) {
if ($i == a && !seen_a) { print "close-a"; seen_a = 1 }
if ($i == b && !seen_b) { print "close-b"; seen_b = 1 }
}
}
')
case "$ABORT_SEQUENCE" in
$'create-a\nclose-a\ncreate-b\nclose-b'|$'create-b\nclose-b\ncreate-a\nclose-a') ;;
Expand Down
20 changes: 18 additions & 2 deletions tests/fm-backend.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,15 @@ esac
exit 0
SH
chmod +x "$fb/tmux"
fm_fake_exit0 "$fb" treehouse
# Stand in for the pool: the spawn leases its worktree, so the stub hands back
# the same fake worktree this fixture's tmux stub reports.
cat > "$fb/treehouse" <<SH
#!/usr/bin/env bash
set -u
case " \$* " in *" --lease "*) printf '%s\\n' "$wt"; exit 0 ;; esac
exit 0
SH
chmod +x "$fb/treehouse"
printf '%s\n' "$fb"
}

Expand Down Expand Up @@ -877,7 +885,15 @@ esac
exit 0
SH
chmod +x "$fb/tmux"
fm_fake_exit0 "$fb" treehouse
# Stand in for the pool: the spawn leases its worktree, so the stub hands back
# the same fake worktree this fixture's tmux stub reports.
cat > "$fb/treehouse" <<SH
#!/usr/bin/env bash
set -u
case " \$* " in *" --lease "*) printf '%s\\n' "$wt"; exit 0 ;; esac
exit 0
SH
chmod +x "$fb/treehouse"
printf '%s\n' "$fb"
}

Expand Down
1 change: 1 addition & 0 deletions tests/fm-backlog-atomicity.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ exit 0
SH
chmod +x "$fakebin/tmux"
fm_fake_exit0 "$fakebin" treehouse gh gh-axi no-mistakes
fm_test_fake_treehouse_lease "$fakebin"

fm_git_init_commit "$case_dir/project"
fm_git_add_origin "$case_dir/project" "$case_dir/project.origin.git"
Expand Down
1 change: 1 addition & 0 deletions tests/fm-bootstrap-network-parallel.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ test_remote_probe_scheduling_keeps_per_mate_lines() { # <parallel|fallback>
git -C "$primary" commit -qm 'seed primary default branch'
fakebin=$(fm_fakebin "$dir")
fm_fake_exit0 "$fakebin" gh treehouse tmux node
fm_test_fake_treehouse_lease "$fakebin"
log="$dir/probe.log"
: > "$log"
install_fake_ssh "$fakebin"
Expand Down
7 changes: 7 additions & 0 deletions tests/fm-captain-hold-lifecycle.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ make_home() { # <name>
EOF
fakebin=$(fm_fakebin "$home")
fm_fake_exit0 "$fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$fakebin"
printf '%s\n' "$home"
}

Expand Down Expand Up @@ -241,6 +242,7 @@ done_keep = 10
EOF
fb=$(fm_fakebin "$home")
fm_fake_exit0 "$fb" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$fb"
printf '%s\n' "$home|$graph/.beads"
}

Expand Down Expand Up @@ -1300,6 +1302,7 @@ test_secondmate_hold_stays_in_authoritative_home() {
EOF
fakebin=$(fm_fakebin "$mate")
fm_fake_exit0 "$fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$fakebin"
origin=sample-mate-review
mkdir -p "$mate/data/$origin"
tasks_in "$mate" add "$origin" "Investigate secondmate sample" --kind scout --repo sample --start >/dev/null
Expand Down Expand Up @@ -1357,6 +1360,7 @@ test_secondmate_home_publishes_holds_and_answers() {
EOF
fakebin=$(fm_fakebin "$mate")
fm_fake_exit0 "$fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$fakebin"
channel="$parent/state/channel-mate.status"
decision="$mate/decision.txt"

Expand Down Expand Up @@ -2902,6 +2906,7 @@ SH
"the deliverable was recorded before destructive cleanup succeeded"

fm_fake_exit0 "$home/fakebin" treehouse
fm_test_fake_treehouse_lease "$home/fakebin"
bootstrap=$(PATH="$home/fakebin:$PATH" FM_ROOT_OVERRIDE="$ROOT" FM_HOME="$home" \
FM_STATE_OVERRIDE="$home/state" FM_DATA_OVERRIDE="$home/data" \
FM_CONFIG_OVERRIDE="$home/config" FM_BOOTSTRAP_NETWORK=skip \
Expand Down Expand Up @@ -2958,6 +2963,7 @@ SH
run_captain "$home" answer "$id" --decision-file "$home/answer.txt" >/dev/null \
|| fail "the captain could not answer before cleanup replay"
fm_fake_exit0 "$home/fakebin" treehouse
fm_test_fake_treehouse_lease "$home/fakebin"
bootstrap=$(PATH="$home/fakebin:$PATH" FM_ROOT_OVERRIDE="$ROOT" FM_HOME="$home" \
FM_STATE_OVERRIDE="$home/state" FM_DATA_OVERRIDE="$home/data" \
FM_CONFIG_OVERRIDE="$home/config" FM_BOOTSTRAP_NETWORK=skip \
Expand Down Expand Up @@ -3083,6 +3089,7 @@ SH
assert_contains "$show" "held: no" "the relocated report kept the answered call held"

fm_fake_exit0 "$home/fakebin" treehouse
fm_test_fake_treehouse_lease "$home/fakebin"
bootstrap=$(PATH="$home/fakebin:$PATH" FM_ROOT_OVERRIDE="$ROOT" FM_HOME="$home" \
FM_STATE_OVERRIDE="$home/state" FM_DATA_OVERRIDE="$data" \
FM_CONFIG_OVERRIDE="$home/config" FM_BOOTSTRAP_NETWORK=skip \
Expand Down
1 change: 1 addition & 0 deletions tests/fm-kimi-harness.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ exit 0
SH
chmod +x "$fakebin/tmux"
fm_fake_exit0 "$fakebin" treehouse gh-axi gh
fm_test_fake_treehouse_lease "$fakebin"
fm_fake_exit0 "$fakebin" kimi
ln -s "$JQ_BIN" "$fakebin/jq"
printf '%s\n' "$fakebin"
Expand Down
1 change: 1 addition & 0 deletions tests/fm-muse-harness.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ exec "$FM_FAKE_MUSE_VERSIONED" -c 'result=$($FM_FAKE_HARNESS_PROBE); printf "%s"
SH
chmod +x "$fakebin/muse"
fm_fake_exit0 "$fakebin" treehouse gh-axi gh
fm_test_fake_treehouse_lease "$fakebin"
printf '%s\n' "$fakebin"
}

Expand Down
9 changes: 9 additions & 0 deletions tests/fm-public-followup.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ EOF
[ "$relay" = relay-off ] || printf 'FMX_PAIRING_TOKEN=test-token\n' > "$home/.env"
make_fake_curl "$home" >/dev/null
fm_fake_exit0 "$home/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$home/fakebin"
printf '%s\n' "$home"
}

Expand Down Expand Up @@ -847,6 +848,7 @@ test_secondmate_teardown_resolves_parent_from_durable_record_when_env_lost() {
parent_resolved=$(cd "$parent" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"

assert_local_secondmate_parent_record "$child" "$parent_resolved"

Expand Down Expand Up @@ -884,6 +886,7 @@ test_secondmate_teardown_durable_record_missing_parent_registration_still_refuse
parent_resolved=$(cd "$parent" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
assert_local_secondmate_parent_record "$child" "$parent_resolved"
fm_write_meta "$child/state/work-child.meta" \
"window=firstmate:fm-work-child" "endpoint_task_id=work-child" \
Expand Down Expand Up @@ -915,6 +918,7 @@ test_secondmate_teardown_durable_record_with_unknown_field_succeeds() {
parent_resolved=$(cd "$parent" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
assert_local_secondmate_parent_record "$child" "$parent_resolved"
printf 'some_future_field=value\n' >> "$child/.fm-secondmate-parent"
parent_alias="$TMP_ROOT/teardown-durable-clean-parent-alias"
Expand Down Expand Up @@ -950,6 +954,7 @@ test_secondmate_teardown_rejects_conflicting_live_and_durable_parent_bindings()
parent_resolved=$(cd "$durable_parent" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
assert_local_secondmate_parent_record "$child" "$parent_resolved"
fm_write_meta "$durable_parent/state/mate.meta" "kind=secondmate" "home=$child"
fm_git_init_commit "$child/projects/worktree"
Expand Down Expand Up @@ -982,6 +987,7 @@ test_secondmate_teardown_rejects_unsafe_durable_parent_records() {
child=$(cd "$child" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
fm_write_meta "$child/state/work-child.meta" \
"window=firstmate:fm-work-child" "endpoint_task_id=work-child" \
"worktree=$child" "project=$child" "kind=ship" "mode=local-only" "spawn_gen=public-followup-fixture"
Expand Down Expand Up @@ -1044,6 +1050,7 @@ test_secondmate_teardown_rejects_nul_bearing_durable_parent_record() {
parent_resolved=$(cd "$parent" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
assert_local_secondmate_parent_record "$child" "$parent_resolved"
fm_write_meta "$parent/state/mate.meta" "kind=secondmate" "home=$child"
fm_git_init_commit "$child/projects/worktree"
Expand Down Expand Up @@ -1475,6 +1482,7 @@ test_dropped_baton_now_surfaces_open_loop() {
child=$(cd "$child" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
log="$TMP_ROOT/curl.log"; : > "$log"

seed_repro_commitment "$parent" public-final-pi-rearm-repro req-pirearm \
Expand Down Expand Up @@ -1525,6 +1533,7 @@ test_control_registered_followon_is_guarded() {
child=$(cd "$child" && pwd -P)
make_fake_curl "$child" >/dev/null
fm_fake_exit0 "$child/fakebin" tmux treehouse no-mistakes gh gh-axi
fm_test_fake_treehouse_lease "$child/fakebin"
seed_repro_commitment "$parent" public-final-pi-rearm-ship req-pirearm2 \
secondmate:mate pi-rearm-loop-fix-r1
fm_write_meta "$parent/state/mate.meta" "kind=secondmate" "home=$child"
Expand Down
1 change: 1 addition & 0 deletions tests/fm-rovo-harness.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ exit 0
SH
chmod +x "$fakebin/tmux"
fm_fake_exit0 "$fakebin" treehouse gh-axi gh
fm_test_fake_treehouse_lease "$fakebin"
fm_fake_exit0 "$fakebin" rovo
printf '%s\n' "$fakebin"
}
Expand Down
Loading
Loading