From a65f29dbcb7d9342d79346ca54d2741121d3bbd2 Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Thu, 2 Jul 2026 05:10:05 +0000 Subject: [PATCH 1/6] fix(brief): local-only DoD requires build+install+verify for no-mistakes + elevate task-body criteria Local-only crewmates reported 'done: ready in branch' without the build/install/ verify cycle even when the brief required it: the scaffold's formal DoD said 'ready in branch' so they treated committing as the completion signal and treated the task body's custom criteria as secondary. - bin/fm-brief.sh: for a local-only project named 'no-mistakes' (the binary that runs the pipeline), inject a clause requiring build (make build), install to both ~/.local/bin/no-mistakes and ~/.no-mistakes/bin/no-mistakes, daemon restart, and end-to-end verification before done. Gated on repo name; other local-only projects have no binary to install and are unaffected. - bin/fm-brief.sh: all three ship-mode DoDs now declare task-body acceptance criteria part of the authoritative Definition of done, so committing (or a green pipeline) is necessary but not sufficient. - tests/fm-brief.test.sh: regression tests for the clause gating and elevation. - AGENTS.md: document both refinements as durable scaffold knowledge. --- AGENTS.md | 1 + bin/fm-brief.sh | 22 ++++++++++-- tests/fm-brief.test.sh | 82 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d657a01b64f..c2fefc0dc17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -817,6 +817,7 @@ The ship-brief Setup opens with a worktree-isolation assertion ahead of the bran For a ship task the definition of done is shaped by the project's delivery mode (section 6): `no-mistakes` stops after the implementation commit, then firstmate triggers the harness-appropriate no-mistakes validation pipeline; `direct-PR` has the crewmate push and open the PR itself, and `local-only` has it stop at "ready in branch" for firstmate to review and merge locally. The no-mistakes brief points to no-mistakes' version-matched guidance and keeps only firstmate-specific wrapper rules for `ask-user` escalation, `--yes` avoidance, and the CI-green done line. The scaffold reads the mode via `fm-project-mode.sh`, so you do not pass it. +Two refinements are baked into every ship-mode definition of done and must be preserved when adjusting the scaffold: (1) task-specific acceptance criteria written in the Task body are declared part of the authoritative definition of done - a crewmate is not done merely for committing or for the pipeline going green, but only once those task-specific criteria are met too; this stops a crewmate from treating the formal DoD as the sole completion signal and ignoring the task body. (2) For a `local-only` project whose name is `no-mistakes` (the binary that runs the pipeline), the DoD adds a build+install+verify clause: the rebuilt binary must be installed to both `~/.local/bin/no-mistakes` and `~/.no-mistakes/bin/no-mistakes`, the daemon restarted, and the fix verified end-to-end before `done`, because a branch commit is otherwise unobservable. Other `local-only` projects have no binary to install and get no such clause; gate it on the repo name. Ship briefs also include the project-memory contract: run `bin/fm-ensure-agents-md.sh` when the project already has agent-memory files or when the task produced durable project-intrinsic knowledge, then record proportionate learnings in `AGENTS.md`. For scout tasks add `--scout`: the scaffold swaps the definition of done for the report contract (findings to `data//report.md`, no branch, no push, no PR) and declares the worktree scratch; scout is mode-agnostic. Scout briefs do not include the project-memory step, because their deliverable is a report rather than a committed project change. diff --git a/bin/fm-brief.sh b/bin/fm-brief.sh index 6b21edab1c9..ecb7c65ec97 100755 --- a/bin/fm-brief.sh +++ b/bin/fm-brief.sh @@ -179,7 +179,8 @@ case "$MODE" in # Definition of done This project ships **direct-PR**: you raise the PR yourself, without the no-mistakes pipeline. The task is complete only when committed on your branch. -When it is implemented and committed, push your branch and open a PR with \`gh-axi\`, then append \`done: PR {url}\` to the status file and stop. +Any task-specific acceptance or completion criteria stated in the Task section above are part of this Definition of done - you are not done when you have merely committed; you are done only when those criteria are met as well. +When it is implemented and committed (and any task-specific criteria above are satisfied), push your branch and open a PR with \`gh-axi\`, then append \`done: PR {url}\` to the status file and stop. Do NOT run /no-mistakes. The captain reviews and merges the PR; firstmate relays it. EOF ) @@ -187,12 +188,28 @@ EOF local-only) SETUP2="" RULE1="1. Never push to any remote and never open a PR. Work only on your \`fm/$ID\` branch; firstmate handles the merge into local \`main\`." + # no-mistakes develops the binary that runs the pipeline itself: a branch commit is + # not observable until the rebuilt binary is installed and the fix is verified + # end-to-end. Other local-only projects have no binary to install, so gate on repo. + NM_BUILD_CLAUSE="" + if [ "$REPO" = "no-mistakes" ]; then + NM_BUILD_CLAUSE=$(cat < file < "$home/data/projects.md" <<'EOF' +- no-mistakes [local-only] - the gate tool (added 2026-07-01) +- otherproj [local-only] - a plain local project (added 2026-07-01) +- gatemate [no-mistakes] - a gated project (added 2026-07-01) - direct-proj [direct-PR] - fixture for direct-PR mode (added 2026-07-01) - local-proj [local-only] - fixture for local-only mode (added 2026-07-01) EOF @@ -41,7 +54,7 @@ EOF # one of these DOD blocks, since a broken heredoc corrupts or empties the # generated brief content, not just the script's own syntax. test_ship_modes_generate_clean_briefs() { - local home id brief status + local home id proj brief status home="$TMP_ROOT/ship-home" write_registry "$home" @@ -76,6 +89,73 @@ test_no_mistakes_dod_wording() { pass "fm-brief.sh: no-mistakes DOD wording avoids the apostrophe regression" } +# The local-only DoD must add a build+install+verify clause for no-mistakes +# (the binary that runs the pipeline) because a branch commit is otherwise +# unobservable until the rebuilt binary is installed and verified end-to-end. +# Other local-only projects must NOT get it, and non-local-only modes must NOT +# get it either. +test_local_only_dod_build_clause() { + local home + home="$TMP_ROOT/dod-home" + write_registry "$home" + + # Case 1: no-mistakes local-only gets the build+install+verify clause. + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" dod-nm no-mistakes >/dev/null 2>&1 + local b_nm="$home/data/dod-nm/brief.md" + assert_grep "If this task modifies no-mistakes source code" "$b_nm" \ + "no-mistakes local-only brief missing build+install clause" + assert_grep "make build" "$b_nm" "no-mistakes local-only brief missing build step" + assert_grep "no-mistakes daemon stop && no-mistakes daemon start" "$b_nm" \ + "no-mistakes local-only brief missing daemon restart" + assert_grep "verify the fix end-to-end" "$b_nm" "no-mistakes local-only brief missing end-to-end verify" + + # Case 2: a plain local-only project must NOT get the binary clause. + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" dod-other otherproj >/dev/null 2>&1 + local b_other="$home/data/dod-other/brief.md" + ! grep -E "make build|\.no-mistakes/bin" "$b_other" >/dev/null || \ + fail "plain local-only brief must not get the no-mistakes build clause" + + # Case 3: no-mistakes (default) mode must NOT get the local-only binary clause. + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" dod-gate gatemate >/dev/null 2>&1 + local b_gate="$home/data/dod-gate/brief.md" + ! grep -E "make build|\.no-mistakes/bin/no-mistakes" "$b_gate" >/dev/null || \ + fail "no-mistakes-mode brief must not get the local-only build clause" + + pass "fm-brief.sh: local-only DoD build clause gated to the no-mistakes repo" +} + +# The elevation sentence must be present in every ship mode, making the +# Task-body acceptance criteria part of the authoritative Definition of done. +test_dod_elevates_task_body_criteria() { + local home id + home="$TMP_ROOT/elev-home" + write_registry "$home" + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" elev-nm no-mistakes >/dev/null 2>&1 + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" elev-other otherproj >/dev/null 2>&1 + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" elev-gate gatemate >/dev/null 2>&1 + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" elev-direct direct-proj >/dev/null 2>&1 + for id in elev-nm elev-other elev-gate elev-direct; do + assert_grep "part of this Definition of done" "$home/data/$id/brief.md" \ + "$id brief missing the DoD elevation sentence" + done + pass "fm-brief.sh: all ship-mode briefs elevate task-body criteria into the DoD" +} + +# fm-brief.sh must refuse to clobber an existing brief. +test_refuses_to_overwrite() { + local home id rc + home="$TMP_ROOT/overwrite-home" + write_registry "$home" + id="brief-overwrite-c1" + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" no-mistakes >/dev/null 2>&1 + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" no-mistakes >/dev/null 2>&1; rc=$? + [ "$rc" -ne 0 ] || fail "fm-brief.sh must refuse to overwrite an existing brief" + pass "fm-brief.sh: refuses to overwrite an existing brief" +} + test_script_parses test_ship_modes_generate_clean_briefs test_no_mistakes_dod_wording +test_local_only_dod_build_clause +test_dod_elevates_task_body_criteria +test_refuses_to_overwrite From e932ef8e904242bbae54875479597392b1bb4412 Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:46:27 +0000 Subject: [PATCH 2/6] no-mistakes(document): Document fm-brief test in CONTRIBUTING test catalog --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2de74545a4b..ae1357ab6b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,7 +78,7 @@ tests/fm-grok-harness.test.sh # grok adapter spawn hook, token guard tests/fm-fleet-sync.test.sh # project clone refresh: safe detached recovery, STUCK drift reports, benign skips, and bootstrap relay tests/fm-x-mode.test.sh # X-mode poll, inbox context round-trip, reply threading, dismiss, dry-run preview, and .env-presence activation tests tests/fm-tangle-guard.test.sh # primary-checkout tangle detection, read-only remediation suppression, and spawn/brief isolation tests -tests/fm-brief.test.sh # fm-brief.sh bash -n parse regression guard (issue #166) and clean no-mistakes/direct-PR/local-only brief generation tests +tests/fm-brief.test.sh # fm-brief.sh bash -n parse regression guard (issue #166), clean no-mistakes/direct-PR/local-only brief generation, no-mistakes local-only build+install+verify DoD clause with plain/default exclusion, task-body criteria elevated into the DoD, and overwrite refusal tests/fm-spawn-batch.test.sh # batch dispatch and FM_HOME project-path scoping tests tests/fm-spawn-dispatch-profile.test.sh # concrete dispatch profile flags: active-profile backstop, harness/model/effort meta, launch templates, batch forwarding, and secondmate exemption tests/fm-update.test.sh # fast-forward-only self-update, reread, nudge, dedup, and skip-safety tests From 62b8158756f58ce48490df92f8b463be7474aada Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:07:40 +0000 Subject: [PATCH 3/6] no-mistakes(review): Enumerate no-mistakes install exception in brief Rule 2 --- bin/fm-brief.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/fm-brief.sh b/bin/fm-brief.sh index ecb7c65ec97..85aeaee16d0 100755 --- a/bin/fm-brief.sh +++ b/bin/fm-brief.sh @@ -171,6 +171,8 @@ read -r MODE _ <> $STATUS_FILE\` From 781e81693edcf1f54906f5c09c1b0eac472dbcf4 Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:22:16 +0000 Subject: [PATCH 4/6] no-mistakes(document): Document no-mistakes Rule 2 worktree relaxation --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index c2fefc0dc17..5ed49b906b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -817,7 +817,7 @@ The ship-brief Setup opens with a worktree-isolation assertion ahead of the bran For a ship task the definition of done is shaped by the project's delivery mode (section 6): `no-mistakes` stops after the implementation commit, then firstmate triggers the harness-appropriate no-mistakes validation pipeline; `direct-PR` has the crewmate push and open the PR itself, and `local-only` has it stop at "ready in branch" for firstmate to review and merge locally. The no-mistakes brief points to no-mistakes' version-matched guidance and keeps only firstmate-specific wrapper rules for `ask-user` escalation, `--yes` avoidance, and the CI-green done line. The scaffold reads the mode via `fm-project-mode.sh`, so you do not pass it. -Two refinements are baked into every ship-mode definition of done and must be preserved when adjusting the scaffold: (1) task-specific acceptance criteria written in the Task body are declared part of the authoritative definition of done - a crewmate is not done merely for committing or for the pipeline going green, but only once those task-specific criteria are met too; this stops a crewmate from treating the formal DoD as the sole completion signal and ignoring the task body. (2) For a `local-only` project whose name is `no-mistakes` (the binary that runs the pipeline), the DoD adds a build+install+verify clause: the rebuilt binary must be installed to both `~/.local/bin/no-mistakes` and `~/.no-mistakes/bin/no-mistakes`, the daemon restarted, and the fix verified end-to-end before `done`, because a branch commit is otherwise unobservable. Other `local-only` projects have no binary to install and get no such clause; gate it on the repo name. +Two refinements are baked into every ship-mode definition of done and must be preserved when adjusting the scaffold: (1) task-specific acceptance criteria written in the Task body are declared part of the authoritative definition of done - a crewmate is not done merely for committing or for the pipeline going green, but only once those task-specific criteria are met too; this stops a crewmate from treating the formal DoD as the sole completion signal and ignoring the task body. (2) For a `local-only` project whose name is `no-mistakes` (the binary that runs the pipeline), the DoD adds a build+install+verify clause: the rebuilt binary must be installed to both `~/.local/bin/no-mistakes` and `~/.no-mistakes/bin/no-mistakes`, the daemon restarted, and the fix verified end-to-end before `done`, because a branch commit is otherwise unobservable. Because that clause writes outside the worktree, Rule 2 (the worktree-isolation rule) is correspondingly relaxed for this case to permit only the binary install and daemon restart it requires. Other `local-only` projects have no binary to install and get no such clause or Rule 2 relaxation; gate it on the repo name. Ship briefs also include the project-memory contract: run `bin/fm-ensure-agents-md.sh` when the project already has agent-memory files or when the task produced durable project-intrinsic knowledge, then record proportionate learnings in `AGENTS.md`. For scout tasks add `--scout`: the scaffold swaps the definition of done for the report contract (findings to `data//report.md`, no branch, no push, no PR) and declares the worktree scratch; scout is mode-agnostic. Scout briefs do not include the project-memory step, because their deliverable is a report rather than a committed project change. From cdb417fdfa79daf797bd510dd432cddf431e2935 Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:30:26 +0000 Subject: [PATCH 5/6] no-mistakes(review): guard fm-pr-merge empty array under set -u --- bin/fm-pr-merge.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/fm-pr-merge.sh b/bin/fm-pr-merge.sh index 8ffb32cc6a0..13e48230ba0 100755 --- a/bin/fm-pr-merge.sh +++ b/bin/fm-pr-merge.sh @@ -87,4 +87,4 @@ if ! caller_has_merge_method "$@"; then merge_args=(--squash) fi -gh-axi pr merge "$PR_NUMBER" --repo "$PR_OWNER/$PR_REPO" "${merge_args[@]}" "$@" +gh-axi pr merge "$PR_NUMBER" --repo "$PR_OWNER/$PR_REPO" ${merge_args[@]+"${merge_args[@]}"} "$@" From 578336c8831aabdb581c7533e6e9d5c65f4ddf6c Mon Sep 17 00:00:00 2001 From: e-jung <8334081+e-jung@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:02:42 +0000 Subject: [PATCH 6/6] no-mistakes(test): tests: force MISSING diagnostic via gh-axi not node --- tests/fm-session-start.test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fm-session-start.test.sh b/tests/fm-session-start.test.sh index a5acede4a77..6a4f9ba2e99 100755 --- a/tests/fm-session-start.test.sh +++ b/tests/fm-session-start.test.sh @@ -257,7 +257,7 @@ EOF make_fake_toolchain "$fakebin" make_fake_ps_claude "$fakebin" # Force a MISSING diagnostic line so the bootstrap section is non-trivial. - rm -f "$fakebin/node" + rm -f "$fakebin/gh-axi" printf 'window=fm-sess:w1\nkind=ship\n' > "$home/state/task-a.meta" @@ -280,7 +280,7 @@ EOF [ "$context_line" -lt "$fleet_line" ] || fail "CONTEXT did not precede FLEET STATE" [ "$fleet_line" -lt "$next_line" ] || fail "FLEET STATE did not precede NEXT STEP" - missing_line=$(printf '%s\n' "$out" | grep -n 'MISSING: node' | head -1 | cut -d: -f1) + missing_line=$(printf '%s\n' "$out" | grep -n 'MISSING: gh-axi' | head -1 | cut -d: -f1) [ -n "$missing_line" ] || fail "MISSING diagnostic did not appear at all" [ "$missing_line" -lt "$fleet_line" ] || fail "actionable MISSING diagnostic was buried after the bulk fleet-state digest" @@ -400,7 +400,7 @@ $rec EOF make_fake_toolchain "$fakebin" make_fake_ps_claude "$fakebin" - rm -f "$fakebin/node" + rm -f "$fakebin/gh-axi" append_wake "$home/state" signal task-z "needs-decision: pick a library" @@ -409,7 +409,7 @@ EOF # fm-lock.sh's own exact success text. assert_contains "$out" "lock acquired: harness pid" "fm-lock.sh's real output did not appear (composition, not reimplementation)" # fm-bootstrap.sh's own exact MISSING-tool line format. - assert_contains "$out" "MISSING: node (install:" "fm-bootstrap.sh's real detect line did not appear verbatim" + assert_contains "$out" "MISSING: gh-axi (install:" "fm-bootstrap.sh's real detect line did not appear verbatim" # fm-wake-drain.sh's real drained record (raw tab-separated queue line). assert_contains "$out" "$(printf 'signal\ttask-z\tneeds-decision: pick a library')" "fm-wake-drain.sh's real drained record did not appear"