Skip to content
Open
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: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ After an autonomous merge, give the captain a one-line full-URL or local-main ou

### Validate

For a no-mistakes ship, trigger validation on the same worker after its implementation commit, using the harness invocation owned by `harness-adapters`.
For a no-mistakes ship, the brief has the same worker carry straight from its implementation commit into validation without waiting, so expect no handoff pause and no mid-task `done:` line; trigger validation yourself with the harness invocation owned by `harness-adapters` only when a worker has stopped without starting the run.
The task worker that starts a no-mistakes run drives the pipeline and owns every `no-mistakes axi run` and `no-mistakes axi respond` call through the next gate or outcome.
Firstmate never invokes `no-mistakes axi respond` for a crew-owned run.
When the captain adds or changes an ask mid-task, append the captain's words to that brief's `## Captain's intent` and steer the worker; Firstmate build constraints stay in `## Firstmate spec` or the steer.
Expand Down
6 changes: 3 additions & 3 deletions bin/fm-dod-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ EOF
cat <<EOF
# Definition of done
Delivery contract: mode=no-mistakes
The task is complete only when committed on your branch.
When you believe it is complete, append \`done: {summary}\` to the status file and stop.
Firstmate will then instruct you to run /no-mistakes to validate and ship a PR.
This task ships **no-mistakes**: you validate and raise the PR yourself, in one continuous pass.
When the implementation is committed on your branch, append \`working: implemented, starting validation\` to the status file and invoke /no-mistakes right away - do not stop and do not wait to be told to start it.
The terminal report below is the only \`done:\` line this task ever gets.

You drive no-mistakes by responding to its gates, not by implementing fixes.
Follow the guidance no-mistakes itself provides for the mechanics: it loads when you invoke /no-mistakes, and \`no-mistakes axi run --help\` plus the \`help\` lines in each \`axi\` response are authoritative and version-matched to the installed binary.
Expand Down
54 changes: 53 additions & 1 deletion tests/fm-brief.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ test_ship_mode_is_explicit_not_registry() {
brief="$home/data/brief-explicit-a5/brief.md"
grep -qx "Delivery contract: mode=no-mistakes" "$brief" \
|| fail "registered direct-PR posture overrode the explicit --mode"
assert_grep "Firstmate will then instruct you to run /no-mistakes" "$brief" \
assert_grep "invoke /no-mistakes right away" "$brief" \
"explicit no-mistakes brief did not render the pipeline definition of done"

# An unregistered project is not a blocker either, because nothing is looked up.
Expand Down Expand Up @@ -424,6 +424,57 @@ test_ask_user_escalation_format() {
pass "fm-brief.sh: no-mistakes ask-user findings use one event plus a verbatim snapshot"
}

# A no-mistakes ship brief carries the worker straight from its implementation
# commit into validation. Each ship mode is covered so its definition of done
# continues to instruct exactly one terminal `done:` line.
test_ship_briefs_never_instruct_a_mid_task_stop() {
local home id brief dod count
home="$TMP_ROOT/mid-task-stop-home"
mkdir -p "$home/data"

id="brief-carry-through-c1"
FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" some-proj --mode no-mistakes >/dev/null 2>&1
brief="$home/data/$id/brief.md"
assert_present "$brief" "no-mistakes brief was not scaffolded"
assert_no_grep "When you believe it is complete, append \`done: {summary}\` to the status file and stop." "$brief" \
"no-mistakes brief still instructs a mid-task stop after the implementation commit"
assert_no_grep "Firstmate will then instruct you to run /no-mistakes" "$brief" \
"no-mistakes brief still waits for a firstmate handoff before validating"
assert_grep "invoke /no-mistakes right away - do not stop and do not wait to be told to start it" "$brief" \
"no-mistakes brief lost the carry-through instruction into validation"
assert_grep "append \`working: implemented, starting validation\`" "$brief" \
"no-mistakes brief must report the implementation milestone with a nonterminal state"
# The surviving `done:` must be the terminal CI-green report, and the
# hard-won gate rules around it must be untouched.
assert_grep "append \`done: PR {url} checks green\` and stop. You are finished." "$brief" \
"no-mistakes brief lost its terminal CI-green report"
assert_grep "ask-user findings are never yours to answer" "$brief" \
"no-mistakes brief lost the ask-user escalation rule"
assert_grep "NEVER pass \`--yes\` (or \`-y\`)" "$brief" \
"no-mistakes brief lost the --yes prohibition"
assert_grep "Do not hand-edit, commit, or fix findings yourself while a run is active" "$brief" \
"no-mistakes brief lost the active-run hands-off rule"
assert_grep "do not wait for it to keep monitoring in the background until merge" "$brief" \
"no-mistakes brief lost its CI-green return point"

# Each mode's definition of done instructs exactly one `done:` line, so no
# ship brief can ask for a terminal state twice for one task.
local mode ids
ids=0
for mode in no-mistakes direct-PR local-only; do
ids=$((ids + 1))
id="brief-single-done-c$ids"
FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" some-proj --mode "$mode" >/dev/null 2>&1
brief="$home/data/$id/brief.md"
assert_present "$brief" "$mode brief was not scaffolded"
dod=$(sed -n '/^# Definition of done$/,$p' "$brief")
count=$(printf '%s\n' "$dod" | grep -c -F 'append `done:' || true)
[ "$count" -eq 1 ] \
|| fail "$mode definition of done instructs $count \`done:\` lines, expected exactly 1"
done
pass "fm-brief.sh: ship briefs carry through to their single terminal report"
}

test_ship_project_memory_wording() {
local home id brief
home="$TMP_ROOT/project-memory-home"
Expand Down Expand Up @@ -912,6 +963,7 @@ test_delivery_flags_are_refused_where_they_do_not_apply
test_faster_paths_use_configured_authority_without_stacked_review
test_no_mistakes_dod_wording
test_ask_user_escalation_format
test_ship_briefs_never_instruct_a_mid_task_stop
test_ship_project_memory_wording
test_herdr_lab_contract_is_explicit_and_complete
test_herdr_lab_contract_quotes_foreign_firstmate_path
Expand Down
17 changes: 16 additions & 1 deletion tests/fm-watch-triage.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,22 @@ record_pi_busy() { # <state-dir> <id>
--source pi-ext --event agent-start
}

reap() { kill "$1" 2>/dev/null || true; wait "$1" 2>/dev/null || true; }
# Teardown only: callers have finished all assertions about this watcher.
# fm-watch.sh can defer TERM while foreground work unwinds, so bound its grace
# period and then escalate to KILL to keep test teardown deterministic.
reap() {
local pid=$1 i=0
kill "$pid" 2>/dev/null || true
# 100 ticks matches the wait_for_exit budget documented above: far longer than
# a healthy watcher needs to honour TERM, short enough to stay bounded.
while [ "$i" -lt 100 ]; do
kill -0 "$pid" 2>/dev/null || break
sleep 0.1
i=$((i + 1))
done
kill -9 "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
}

# --- pure classifier predicates (fm-classify-lib.sh) ------------------------

Expand Down