diff --git a/bin/fm-brief.sh b/bin/fm-brief.sh index 9e5b175e85..f7bb9ac55d 100755 --- a/bin/fm-brief.sh +++ b/bin/fm-brief.sh @@ -278,14 +278,15 @@ Report only true captain-relevant outcomes or a declared external wait by append States: working, needs-decision, blocked, $PAUSED_VERB, done, failed. Use \`$PAUSED_VERB: {why}\` (distinct from \`blocked:\`) only when your domain is deliberately idling on a known external wait you expect to clear on its own; use \`blocked:\` when you are stuck and need firstmate to act. Use this only for material phase changes, a captain decision, a real blocker, a failure, or work ready for review. +For a captain decision, append \`needs-decision [key=]: {summary of options}\`. This is also how you return the answer to a marked from-firstmate request above. A marked request requires one correlated answer after the work; it does not require a separate receipt or start acknowledgement. Never append \`working:\` merely to acknowledge receipt or announce that a marked request has started. When a routed-work phase has a supervisor-actionable material change worth reporting under the rule above, give that reported phase a stable key. If its first reportable event is \`working [key=]: {material phase}\`, use the same key on its later \`$PAUSED_VERB\`, \`done\`, \`failed\`, \`needs-decision\`, or \`blocked\` event so the earlier working phase is superseded. When a keyed phase ends without another reportable state, append \`resolved [key=]: {why it is no longer active}\`. -\`resolved\` separately closes an escalated decision or blocker, and only a \`resolved\` line carrying that decision's exact key closes it: a later \`done\` or \`working\` event never does, even when the answer is what started that work. -The main firstmate's answer normally writes that closing line at answer time; when a blocker or wait clears WITHOUT an answer from the main firstmate, append \`resolved: {how it cleared}\` yourself (keyed with \`[key=]\` if you opened it with one) as your domain resumes. +\`resolved\` separately closes an escalated decision or blocker, and only a \`resolved\` line carrying that decision's exact key (e.g. \`resolved [key=]: {how it cleared}\`) closes it: a later \`done\` or \`working\` event never does, even when the answer is what started that work. +The main firstmate's answer normally writes that closing line at answer time; when a blocker or wait clears WITHOUT an answer from the main firstmate, append \`resolved: {how it cleared}\` yourself (key it with \`[key=]\` if you opened it with one, omit it otherwise) as your domain resumes. Routine internal supervision, heartbeats, retries, and crewmate churn stay inside your own home and must not touch that status file. # Definition of done @@ -368,9 +369,9 @@ The report is the only thing that survives, so anything worth keeping must be in treating it as a possible wedge. Use \`blocked:\` when you are stuck and need help. 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. - A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands; a later \`done:\` or \`working:\` line never closes it, even when the answer is what started that work. - Firstmate's reply normally writes that closing line at answer time; when a blocker or wait clears WITHOUT a firstmate reply, append \`resolved: {how it cleared}\` yourself (same \`[key=]\` if you opened it with one) as you resume. + append \`needs-decision [key=]: {summary of options}\` and stop. Firstmate will reply with the decision. + A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands (e.g. \`resolved [key=]: {how it cleared}\`); a later \`done:\` or \`working:\` line never closes it, even when the answer is what started that work. + Firstmate's reply normally writes that closing line at answer time; when a blocker or wait clears WITHOUT a firstmate reply, append \`resolved: {how it cleared}\` yourself (key it with \`[key=]\` if you opened it with one, omit it otherwise) as you resume. 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. @@ -456,11 +457,11 @@ land in the generation or the report. treating it as a possible wedge. Use \`blocked:\` when you are stuck and need help. 5. If you hit the same obstacle twice, append \`blocked: {why}\` and stop; firstmate will help. 6. If a decision belongs above you (product choices, destructive actions, ask-user findings), - append \`needs-decision: {summary of options}\` and stop. Firstmate will apply the configured authority and reply. - A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands; a later \`done:\` + append \`needs-decision [key=]: {summary of options}\` and stop. Firstmate will apply the configured authority and reply. + A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands (e.g. \`resolved [key=]: {how it cleared}\`); a later \`done:\` or \`working:\` line never closes it, even when the answer is what started that work. Firstmate's reply normally writes that closing line at answer time; when a blocker or wait clears WITHOUT a firstmate reply, - append \`resolved: {how it cleared}\` yourself (same \`[key=]\` if you opened it with one) as you resume. + append \`resolved: {how it cleared}\` yourself (key it with \`[key=]\` if you opened it with one, omit it otherwise) as you resume. 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. @@ -581,9 +582,9 @@ $RULE1 cadence instead of treating it as a possible wedge. Use \`blocked:\` when you are stuck and need help. 5. If you hit the same obstacle twice, append \`blocked: {why}\` and stop; firstmate will help. 6. If a decision belongs above the implementation worker (product choices, destructive actions, ask-user findings), - append \`needs-decision: {summary of options}\` and stop. Firstmate will apply the configured authority and reply with the decision. - A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands; a later \`done:\` or \`working:\` line never closes it, even when the answer is what started that work. - Firstmate's reply normally writes that closing line at answer time; when a blocker or wait clears WITHOUT a firstmate reply, append \`resolved: {how it cleared}\` yourself (same \`[key=]\` if you opened it with one) as you resume. + append \`needs-decision [key=]: {summary of options}\` and stop. Firstmate will apply the configured authority and reply with the decision. + A decision or blocker you opened stays open until a \`resolved\` line carrying its exact key lands (e.g. \`resolved [key=]: {how it cleared}\`); a later \`done:\` or \`working:\` line never closes it, even when the answer is what started that work. + Firstmate's reply normally writes that closing line at answer time; when a blocker or wait clears WITHOUT a firstmate reply, append \`resolved: {how it cleared}\` yourself (key it with \`[key=]\` if you opened it with one, omit it otherwise) as you resume. 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. diff --git a/tests/fm-brief.test.sh b/tests/fm-brief.test.sh index 954930374b..f56cbf2413 100755 --- a/tests/fm-brief.test.sh +++ b/tests/fm-brief.test.sh @@ -816,6 +816,44 @@ test_pause_verb_override_renders_all_brief_scaffolds() { pass "fm-brief.sh: custom pause verb renders in every scaffold" } +test_status_protocol_shows_documented_decision_key_placement() { + local home kind id brief + home="$TMP_ROOT/decision-key-home" + mkdir -p "$home/data" + + for kind in ship scout dreamer secondmate; do + id="brief-decision-key-$kind" + case "$kind" in + ship) + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" firstmate --mode no-mistakes >/dev/null 2>&1 + ;; + scout) + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" firstmate --scout >/dev/null 2>&1 + ;; + dreamer) + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" firstmate --dreamer >/dev/null 2>&1 + ;; + secondmate) + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" --secondmate --no-projects >/dev/null 2>&1 + ;; + esac + brief="$home/data/$id/brief.md" + # shellcheck disable=SC2016 # Literal backticks and braces must remain unexpanded. + assert_grep '`needs-decision [key=]: {summary of options}`' "$brief" \ + "$kind brief did not show the documented before-colon key on needs-decision" + # shellcheck disable=SC2016 # Literal backticks and braces must remain unexpanded. + assert_grep '`resolved [key=]: {how it cleared}`' "$brief" \ + "$kind brief did not show the documented before-colon key on resolved" + # shellcheck disable=SC2016 # Literal backticks and braces must remain unexpanded. + assert_no_grep '`needs-decision: {summary of options}`' "$brief" \ + "$kind brief still shows the colon-first needs-decision template" + # shellcheck disable=SC2016 # Literal backticks and braces must remain unexpanded. + assert_grep '`resolved: {how it cleared}` yourself (key it with `[key=]` if you opened it with one, omit it otherwise)' "$brief" \ + "$kind brief did not restore optional-key self-close phrasing" + done + pass "fm-brief.sh: every scaffold shows documented [key=...] placement on needs-decision and resolved" +} + test_scout_and_secondmate_load_decision_hold_policy() { local home scout charter home="$TMP_ROOT/decision-policy-home" @@ -943,6 +981,7 @@ test_secondmate_no_projects_charter test_secondmate_marked_request_reporting_contract test_secondmate_directory_paths_are_absolute_and_output_is_stable test_pause_verb_override_renders_all_brief_scaffolds +test_status_protocol_shows_documented_decision_key_placement test_scout_and_secondmate_load_decision_hold_policy test_scout_and_secondmate_scaffold test_task_id_reuse_refused_and_preserves_retained_report