From 2f92576bdc21791a2de7aeab37257b22d66c9cf2 Mon Sep 17 00:00:00 2001 From: mielyemitchell <249051873+mielyemitchell@users.noreply.github.com> Date: Sat, 4 Jul 2026 16:10:45 -0700 Subject: [PATCH 1/2] brief: add no-mistakes shared-daemon rule to ship and scout scaffolds Crews must never stop, restart, or update the shared no-mistakes daemon since one instance serves every firstmate lane/home; a restart kills other lanes' in-flight pipeline runs and forces expensive re-runs. Encodes this as a new numbered rule in both the ship-task and scout-task brief scaffolds. --- bin/fm-brief.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/fm-brief.sh b/bin/fm-brief.sh index e0f2ec78..bd88c043 100755 --- a/bin/fm-brief.sh +++ b/bin/fm-brief.sh @@ -154,6 +154,9 @@ The report is the only thing that survives, so anything worth keeping must be in 5. If you hit the same obstacle twice, append \`blocked: {why}\` and stop; firstmate will help. 6. If a decision belongs to a human (product choices, destructive actions), append \`needs-decision: {summary of options}\` and stop. Firstmate will reply with the decision. +7. Never stop, restart, or update the shared \`no-mistakes\` daemon - it is one instance serving + every lane/home, so restarting it kills other lanes' in-flight pipeline runs. On ANY no-mistakes + daemon error, append \`blocked: {the daemon error}\` and stop; only firstmate manages the daemon. # Definition of done Write your findings to \`$DATA/$ID/report.md\`. @@ -251,6 +254,9 @@ $RULE1 5. If you hit the same obstacle twice, append \`blocked: {why}\` and stop; firstmate will help. 6. If a decision belongs to a human (product choices, destructive actions, ask-user findings), append \`needs-decision: {summary of options}\` and stop. Firstmate will reply with the decision. +7. Never stop, restart, or update the shared \`no-mistakes\` daemon - it is one instance serving + every lane/home, so restarting it kills other lanes' in-flight pipeline runs. On ANY no-mistakes + daemon error, append \`blocked: {the daemon error}\` and stop; only firstmate manages the daemon. # Project memory If \`AGENTS.md\` or \`CLAUDE.md\` already exists, or if this task produced durable project-intrinsic knowledge, run \`$FM_ROOT/bin/fm-ensure-agents-md.sh .\` in the worktree. From 6d25773dd22423a011a053ea05585dc193c60e2d Mon Sep 17 00:00:00 2001 From: mielyemitchell <249051873+mielyemitchell@users.noreply.github.com> Date: Sun, 5 Jul 2026 00:53:16 -0700 Subject: [PATCH 2/2] no-mistakes(test): Pinning progress; awaiting full test-suite confirmation before final summary --- tests/fm-gotmp.test.sh | 10 ++++++---- tests/fm-turnend-guard.test.sh | 14 +++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/fm-gotmp.test.sh b/tests/fm-gotmp.test.sh index 8ea2a70c..3ebf0b7d 100755 --- a/tests/fm-gotmp.test.sh +++ b/tests/fm-gotmp.test.sh @@ -128,8 +128,10 @@ test_teardown_removes_tasktmp_dir() { fake=$(make_fake_root "$id" "$task_tmp") # Sanity: dir + contents exist before teardown. [ -d "$task_tmp/gotmp" ] || fail "precondition: gotmp missing before teardown" - # Run the REAL teardown against the fake root. - bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ + # Run the REAL teardown against the fake root. FM_HOME is pinned to the fake + # root so an ambient FM_HOME in the caller's environment cannot redirect + # teardown at a real home instead of this fixture. + FM_HOME="$fake" bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ || fail "teardown exited non-zero with a valid tasktmp" [ ! -e "$task_tmp" ] \ || fail "teardown did not remove the tasktmp dir ($task_tmp still exists)" @@ -169,7 +171,7 @@ kind=ship mode=no-mistakes yolo=off META - bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ + FM_HOME="$fake" bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ || fail "teardown exited non-zero when tasktmp= was absent" pass "fm-teardown skips gracefully when tasktmp= is absent (backward compat)" } @@ -182,7 +184,7 @@ test_teardown_skips_gracefully_when_dir_missing() { [ ! -e "$task_tmp" ] || fail "precondition: task_tmp should not exist yet" local fake fake=$(make_fake_root "$id" "$task_tmp") - bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ + FM_HOME="$fake" bash "$fake/bin/fm-teardown.sh" "$id" >/dev/null 2>&1 \ || fail "teardown exited non-zero when tasktmp dir was missing" [ ! -e "$task_tmp" ] || fail "teardown created/left the tasktmp dir unexpectedly" pass "fm-teardown skips gracefully when tasktmp= points to a nonexistent dir" diff --git a/tests/fm-turnend-guard.test.sh b/tests/fm-turnend-guard.test.sh index 5edbc3ac..7161be48 100755 --- a/tests/fm-turnend-guard.test.sh +++ b/tests/fm-turnend-guard.test.sh @@ -94,6 +94,11 @@ install_guard_scripts() { # A primary-shaped checkout: plain (non-worktree) git repo, AGENTS.md, bin/, # state/ - everything the hook's scoping check requires to treat it as primary. +# Returns the CANONICALIZED path (cd && pwd): TMPDIR on macOS ends in a trailing +# slash, so a path built by string concatenation off it carries a double slash +# that string-compares unequal to the same path normalized elsewhere (e.g. +# record_watcher_lock's `cd "$dir" && pwd`), which would falsely fail the +# fm-home identity match the watcher-lock tests depend on. make_primary_dir() { local dir=$1 mkdir -p "$dir/state" @@ -101,14 +106,14 @@ make_primary_dir() { git -C "$dir" commit -q --allow-empty -m init : > "$dir/AGENTS.md" install_guard_scripts "$dir" - printf '%s\n' "$dir" + (cd "$dir" && pwd) } # Same shape as primary, plus the .fm-secondmate-home marker bin/fm-home-seed.sh # writes at seed time (regardless of treehouse-lease or git-clone acquisition). make_secondmate_dir() { local dir=$1 - make_primary_dir "$dir" >/dev/null + dir=$(make_primary_dir "$dir") printf 'sm-test-1\n' > "$dir/.fm-secondmate-home" printf '%s\n' "$dir" } @@ -127,7 +132,10 @@ make_crewmate_worktree_dir() { run_hook() { local dir=$1 stop_active=$2 - printf '{"stop_hook_active":%s}' "$stop_active" | bash "$dir/bin/fm-turnend-guard.sh" 2>&1 + # Pin FM_HOME to the scenario dir so an ambient FM_HOME in the caller's + # environment cannot redirect the hook at a real home's state instead of + # this fixture. + printf '{"stop_hook_active":%s}' "$stop_active" | FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard.sh" 2>&1 } nonexistent_pid() {