diff --git a/bin/fm-dod-lib.sh b/bin/fm-dod-lib.sh index a6d299c6545..7bd4f6fd4db 100755 --- a/bin/fm-dod-lib.sh +++ b/bin/fm-dod-lib.sh @@ -397,6 +397,23 @@ There is no pull request, no \`gh-axi\` call, and no forge CI result to report: EOF } +# fm_dod_compaction_step sets compaction_step: the pause-for-compaction paragraph +# only when this home has idle-compact enabled (the watcher's own +# fm_idle_compact_threshold_minutes verdict on config/idle-compact, with the +# same FM_HOME/config resolution as bin/fm-idle-compact.sh), +# else a line telling the worker to start validation right after its commit. +fm_dod_compaction_step() { + local home config + home="${FM_HOME:-${FM_ROOT_OVERRIDE:-$FM_ROOT}}" + config="${FM_CONFIG_OVERRIDE:-${CONFIG:-$home/config}}" + # shellcheck source=/dev/null + if ( . "$(d=${BASH_SOURCE[0]%/*}; [ "$d" != "${BASH_SOURCE[0]}" ] || d=.; cd "${d:-/}" && pwd)/fm-idle-compact.sh" && fm_idle_compact_threshold_minutes "$config" ) >/dev/null 2>&1; then + compaction_step="Right after that implementation commit lands, append \`paused [at=]: awaiting compaction before validation\` to the status file and stop for this turn - do NOT run \`no-mistakes axi run\` yet. A worker cannot self-trigger compaction (\`/compact\` is a terminal built-in, not a tool you can invoke), so firstmate's idle-compact watcher reads that line - the phrase must START the line, and any detail you want to note (your measured lane size, the commit) may follow it - compacts your context while it is still warm, then rings you with a durable inbox message telling you to start the validation run - resume from that ring instead of waiting on a reply." + else + compaction_step="Right after that implementation commit lands, start validation: measure the lane below, then run \`no-mistakes axi run\`." + fi +} + fm_dod_block() { # [branch] [] local mode=$1 id=$2 forge=${4:-none} local branch=${3:-fm/$id} @@ -479,12 +496,13 @@ The configured merge authority approves the ready branch, then firstmate merges EOF ;; no-mistakes:*) + fm_dod_compaction_step cat <]: awaiting compaction before validation\` to the status file and stop for this turn - do NOT run \`no-mistakes axi run\` yet. A worker cannot self-trigger compaction (\`/compact\` is a terminal built-in, not a tool you can invoke), so firstmate's idle-compact watcher reads that line - the phrase must START the line, and any detail you want to note (your measured lane size, the commit) may follow it - compacts your context while it is still warm, then rings you with a durable inbox message telling you to start the validation run - resume from that ring instead of waiting on a reply. +$compaction_step Before your FIRST \`no-mistakes axi run\`, measure this lane: \`bash $FM_ROOT/bin/fm-diff-size-check.sh .\` diff --git a/docs/configuration.md b/docs/configuration.md index 62bf96d0c62..6e2ce6b2891 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -601,7 +601,8 @@ Eligibility, checked for every task recorded under `state/*.meta`, is strictly t - A live safety gate immediately before typing anything: `bin/fm-busy-lib.sh`'s `fm_busy_classify` reports an exact `idle` verdict (never `busy` and never an unproven `unknown`) and `bin/fm-backend.sh`'s `fm_backend_composer_state` reports exactly `empty`, reusing the identical primitives the away-mode daemon's own injection boundary uses. A busy pane, an unproven verdict, or a non-empty composer defers to the next sweep rather than erroring. -**Declared-state fast path:** a worker cannot self-trigger `/compact` (it is a terminal built-in, not a tool), so a no-mistakes ship brief tells the worker to append the status line `paused: awaiting compaction before validation` right after its implementation commit, then end its turn - before starting `no-mistakes axi run`. +**Declared-state fast path:** a worker cannot self-trigger `/compact` (it is a terminal built-in, not a tool), so, when idle-compact is enabled, a no-mistakes ship brief tells the worker to append the status line `paused: awaiting compaction before validation` right after its implementation commit, then end its turn - before starting `no-mistakes axi run`. +When idle-compact is off (no `config/idle-compact`, or its first content line is not a positive integer) the brief instead tells the worker to start validation right after its commit. The task's own latest status line is read as declaring that pause when it *starts with* that phrase on a word boundary: the same brief asks the worker to note its measured lane size, so the line that actually lands routinely carries trailing detail, and requiring whole-line equality silently dropped such workers onto the ordinary path. When the line declares the pause, eligibility ignores the idle-minutes threshold entirely (every other exclusion and the live safety gate still apply), and the episode skips the notes-save turn below and sends `/compact` directly, naming the branch, the brief path, and the delivery contract. See `bin/fm-idle-compact.sh`'s header comment for the exact mechanics and `bin/fm-dod-lib.sh`'s no-mistakes block for the worker-facing instruction. diff --git a/tests/fm-brief.test.sh b/tests/fm-brief.test.sh index 2479de9cbef..c7646fd1e30 100755 --- a/tests/fm-brief.test.sh +++ b/tests/fm-brief.test.sh @@ -551,10 +551,46 @@ test_ship_briefs_forbid_manual_issue_close_and_board_edits() { # Quota-efficiency worker rules (captain order 2026-09-04) belong only in the # no-mistakes DOD: a direct-PR or local-only brief never runs no-mistakes, and # a scout brief carries no delivery contract at all. +# The compaction pause is only prescribed where idle-compact is enabled; a home +# without config/idle-compact must send the worker straight to validation. +test_no_mistakes_dod_compaction_pause_follows_idle_compact_config() { + local home brief + home="$TMP_ROOT/idle-compact-off-home" + mkdir -p "$home/data" "$home/config" + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" brief-compact-off some-proj --mode no-mistakes >/dev/null 2>&1 + brief="$home/data/brief-compact-off/brief.md" + assert_present "$brief" "brief was not scaffolded" + assert_no_grep "awaiting compaction before validation" "$brief" \ + "idle-compact off: the DOD must not tell the worker to pause for compaction" + assert_grep "start validation: measure the lane below, then run" "$brief" \ + "idle-compact off: the DOD must tell the worker to start validation after its commit" + + : > "$home/config/idle-compact" + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" brief-compact-on some-proj --mode no-mistakes >/dev/null 2>&1 + brief="$home/data/brief-compact-on/brief.md" + assert_grep "awaiting compaction before validation" "$brief" \ + "idle-compact on: the DOD must keep the compaction pause" + assert_no_grep "start validation: measure the lane below" "$brief" \ + "idle-compact on: the DOD must not also say to start validation immediately" + + local bad + for bad in 0 off; do + printf '%s\n' "$bad" > "$home/config/idle-compact" + FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "brief-compact-bad-$bad" some-proj --mode no-mistakes >/dev/null 2>&1 + brief="$home/data/brief-compact-bad-$bad/brief.md" + assert_no_grep "awaiting compaction before validation" "$brief" \ + "idle-compact '$bad' is disabled for the watcher: the DOD must not tell the worker to pause" + assert_grep "start validation: measure the lane below, then run" "$brief" \ + "idle-compact '$bad': the DOD must tell the worker to start validation after its commit" + done + pass "fm-brief.sh: no-mistakes DOD compaction pause follows config/idle-compact" +} + test_no_mistakes_dod_carries_quota_efficiency_rules() { local home id brief home="$TMP_ROOT/quota-efficiency-home" - mkdir -p "$home/data" + mkdir -p "$home/data" "$home/config" + : > "$home/config/idle-compact" id="brief-quota-nm1" FM_HOME="$home" "$ROOT/bin/fm-brief.sh" "$id" some-proj --mode no-mistakes >/dev/null 2>&1 brief="$home/data/$id/brief.md" @@ -1511,6 +1547,7 @@ test_ask_user_escalation_format test_ship_project_memory_wording test_ship_briefs_forbid_manual_issue_close_and_board_edits test_no_mistakes_dod_carries_quota_efficiency_rules +test_no_mistakes_dod_compaction_pause_follows_idle_compact_config test_herdr_lab_contract_is_explicit_and_complete test_herdr_lab_contract_quotes_foreign_firstmate_path test_herdr_lab_omission_is_loud_for_ship_and_scout