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
2 changes: 2 additions & 0 deletions .no-mistakes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ test:
Run live Herdr scenarios only through bin/fm-herdr-lab.sh with a named non-default fm-lab-* session, following that helper's prepare, provision, run, and teardown contract exactly.
Never touch the live default Herdr session or fleet panes.
Prefer a throwaway lab for spawn, long-launch, and Claude-path proofs, and tear it down in the same evidence turn.
Use `LAB=$(bin/fm-lab-home.sh create <label>)` to create the disposable home and `bin/fm-lab-home.sh teardown "$LAB"` to remove it; the helper's header owns the marker and binding contract, and bin/fm-gate-refuse-lib.sh owns gate authorization.
To run a real primary inside the gate: create the lab, write the scenario's opt-in flag (e.g. `touch $LAB/config/supervision-host`), then, from the run worktree, start the harness CLI as the session command on the lab's private tmux socket: `mkdir -p $LAB/tmux && env -u NO_MISTAKES_GATE TMUX_TMPDIR=$LAB/tmux tmux -L fm-lab new-session -d -s primary -c "$PWD" -e FM_HOME=$LAB <cli>`, where <cli> is the harness's own launch command using the machine's existing login: claude -> `claude`, codex -> `codex`, cursor -> `cursor-agent`, opencode -> `opencode`, grok -> `grok`, omp -> `omp`. Drive, inspect, and stop that primary only through the same socket - `TMUX_TMPDIR=$LAB/tmux tmux -L fm-lab send-keys -t primary ...`, `TMUX_TMPDIR=$LAB/tmux tmux -L fm-lab capture-pane -p -t primary`, `TMUX_TMPDIR=$LAB/tmux tmux -L fm-lab kill-server` - never the default tmux server; the firstmate scripts the primary runs inherit $TMUX from its pane, which names that same fm-lab socket inside the lab. For a Herdr primary use a named non-default fm-lab-* session via bin/fm-herdr-lab.sh instead, and record it in the lab as soon as it is provisioned with `bin/fm-lab-home.sh record-herdr-session $LAB <session>` - the gate accepts only a Herdr session recorded for the FM_HOME's own lab. The gate marker is scrubbed (env -u NO_MISTAKES_GATE) because the lab primary is a fixture firstmate, not a gate agent. If the harness CLI is absent or its login is unavailable, report the scenario untested; never fake the CLI, the login, or the evidence.
Do not mutate the operator primary checkout, real fleet FM_HOME state, or production credentials, and keep git changes otherwise inside the run worktree.
Read docs/herdr-backend.md and the bin/fm-herdr-lab.sh header as the owners of Herdr lab mechanics rather than reproducing that manual here.
Ship or scout briefs that will drive Herdr lifecycle still require --herdr-lab at scaffold time; these Test-agent instructions are not a substitute for that brief flag.
Expand Down
13 changes: 13 additions & 0 deletions bin/fm-afk-launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ fm_afk_launch_usage() {
}

fm_afk_launch_primary_harness() {
# Test seam: FM_TEST_HARNESS names the simulated harness for suites that
# invoke this launch path directly (tests/fm-afk-launch.test.sh). It is
# honored only alongside FM_TEST_SEAM=1, the marker test suites set, so a
# leaked variable in a real primary's environment stays inert. Only an
# exact known-harness token is honored, so a stray or mistyped value falls
# through to real detection; bin/fm-harness.sh's production detect_own
# precedence never reads either variable.
case "${FM_TEST_SEAM:-}${FM_TEST_SEAM:+ }${FM_TEST_HARNESS:-}" in
"1 claude" | "1 codex" | "1 opencode" | "1 pi" | "1 pi-signed" | "1 grok" | "1 kimi" | "1 cursor" | "1 gemini" | "1 muse" | "1 rovo" | "1 omp" | "1 agy" | "1 devin" | "1 unknown")
printf '%s' "$FM_TEST_HARNESS"
return
;;
esac
"$FM_AFK_LAUNCH_DIR/fm-harness.sh" 2>/dev/null || printf unknown
}

Expand Down
9 changes: 6 additions & 3 deletions bin/fm-control.sh
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,10 @@ esac

# shellcheck source=bin/fm-gate-refuse-lib.sh
. "$SCRIPT_DIR/fm-gate-refuse-lib.sh"
# Fail closed before any fleet mutation: a no-mistakes gate agent must never
# drive a crewmate's lifecycle (see bin/fm-gate-refuse-lib.sh).
fm_refuse_if_gate_agent
# Refuse gate-context lifecycle calls unless the target is a verified lab
# (see bin/fm-gate-refuse-lib.sh). The backend comes from recorded task
# metadata, not the environment, so the env-backend check is skipped.
fm_refuse_if_gate_agent . '' 1

if [ -z "${FM_HOME+x}" ] || [ -z "${FM_HOME:-}" ]; then
echo "error: FM_HOME is not set; fm-control refuses to resolve a task without an explicit firstmate home" >&2
Expand Down Expand Up @@ -341,6 +342,8 @@ fi
fm_backend_validate_task_endpoint "$META" "$ID" || exit 1
BACKEND=$FM_BACKEND_VALIDATED_BACKEND
T=$FM_BACKEND_VALIDATED_TARGET
fm_gate_lab_assert_backend "$BACKEND"
fm_gate_lab_assert_target "$BACKEND" "$T"
LABEL="fm-$ID"
RECORDED_HARNESS=$(fm_meta_get "$META" harness)
KIND=$(fm_meta_get "$META" kind)
Expand Down
Loading
Loading