Skip to content

fix(bin): let a declared pause outrank a done or cancelled run - #6

Merged
yomi202703 merged 3 commits into
mainfrom
fm/cancelled-run-strips-pause-absorption-replay
Jul 26, 2026
Merged

yomi202703 merged 3 commits into
mainfrom
fm/cancelled-run-strips-pause-absorption-replay

Conversation

@yomi202703

Copy link
Copy Markdown
Owner

Replaces pull request #4, which is closed as superseded. Same change, clean history.

Why this replaces #4

Pull request #4 carried unrelated upstream commits alongside the fix, so GitHub could not compute a merge commit for it (mergeable_state=dirty, refs/pull/4/merge 404) and no checks could run against it. This branch is the same change cherry-picked onto current origin/main, so it carries the fix and nothing else.

Exactly three commits, four files:

6b54d53 no-mistakes(document): point AGENTS.md at crew-state mapping owner for pause override
71597b8 no-mistakes(review): gate pause override on true terminal outcome, keep failures visible
c7f9586 fix(crew-state): let declared pause outrank terminal run outcomes

 AGENTS.md                   |   2 +-
 bin/fm-crew-state.sh        |  66 ++++++++++++++---
 docs/architecture.md        |   5 +-
 tests/fm-crew-state.test.sh | 168 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 228 insertions(+), 13 deletions(-)

Note on the validation record reproduced below: it is the record of the no-mistakes run that produced these commits, executed on #4's branch. The commit shas differ from that run's because the branch was re-cut onto clean origin/main; the content diff is identical. The run was not re-executed on this branch.

The rest of this description is carried over unchanged from #4.


Duplicates upstream pull request kunchenguid#1057 on kunchenguid/firstmate; that PR is left open deliberately.

Intent

Let a crew's declared paused: survive an attributed terminal historical no-mistakes validation run, so a correct pause is not permanently overruled by a stale run record.

Correctness fix only (not a claimed 75-second stale loop): when bin/fm-crew-state.sh has HAVE_RUN=1 for a true terminal outcome of done or cancelled (not the mapped presentation state) and the status log's last verb is paused:, emit state: paused with source: status-log, retaining the terminal run outcome as leading secondary detail. Mirror the existing CI-green status-log override shape for that emit style.

What outranks a pause, by design:

  • Active / working and parked runs still outrank a declared pause (including a CI-green run still monitoring for merge that is only mapped to done).
  • A genuine failed run is not pause-overridable. An abort (cancelled) is a supervisor decision; overriding it with the crew's own pause declaration is sound. A failed run is the work itself failing and must surface whether or not any pause line is fresher. That restriction is the final accepted scope, not a temporary fallback.

Rejected: changing only crew_absorb_class or pause_state_class (dual-authority drift); stopping terminal attribution entirely; TTL on cancelled runs; teardown on abort. Residuals left by design: live-agent first surface, no-verb turn-end while paused, hourly forgotten-pause re-surface.

Tests cover cancelled/done + trailing paused (override), failed + trailing paused (still failed), and negatives that working/parked/ci-green-monitoring still win. No claim this eliminates residual first-surface or signal spam.

What Changed

  • bin/fm-crew-state.sh tracks the attributed run's own terminal outcome (RUN_TERMINAL: none/done/cancelled/failed) alongside the mapped RUN_STATE, set only from the run's reported outcome/status across the coarse, outcome, and bare-status paths (not from CI-green presentation mapping).
  • When that outcome is genuinely terminal done or cancelled and the status log's last verb is paused:, the crew is emitted as state: paused · source: status-log with the run outcome leading the detail and the pause reason following (and no doubled separator when the pause carries no reason).
  • A terminal failed run continues to report failed even if the last status verb is paused:, so a validation failure is not demoted to a quiet external wait. Active, parked, and CI-green-still-monitoring runs likewise outrank pause.
  • tests/fm-crew-state.test.sh adds cases for cancelled/done + paused, bare paused separator, failed-stays-failed, and active/parked/ci-green negatives; docs/architecture.md documents the second status-log exception and AGENTS.md points at the script header as the owner of the run-step-to-state mapping instead of restating it.

Risk Assessment

Low: Review-driven corrections gate the override on true terminal outcomes (so mapped CI-green done stays active authority), keep genuine failures visible by design, put terminal detail first for truncation, fix the empty-note separator, and document the exception. Scope is narrower than the first draft intent: failed is intentionally not pause-overridable.

Testing

Exercised the targeted crew-state suite (all cases pass, including the new pause-vs-terminal-run ones) and the watch-triage suite that consumes the verdict, then demonstrated the change end-to-end on a hermetic fleet fixture: with a cancelled run plus a declared pause the helper flips from state: failed · source: run-step · run cancelled to state: paused · source: status-log · run cancelled · holding for upstream maintainer merge, the watcher's absorb class flips from none (wake surfaces) to paused (absorbed), and the human fm-fleet-view.sh table cell flips from failed / run-step to paused / status-log; the done/checks-passed case behaves the same, while failed, active, and parked runs stay failed/working/parked with or without a trailing pause line. No screenshot applies - the affected surfaces are shell CLI output and a Markdown fleet table, both captured verbatim in the transcript artifact.

Evidence: End-to-end before/after transcript (crew-state verdict, watcher absorb class, rendered fleet view)

SCENARIO: supervisor CANCELLED the validation run, crew declared a pause status log (tail): paused: holding for upstream maintainer merge BEFORE (c64ad1c) crew-state : state: failed · source: run-step · run cancelled BEFORE (c64ad1c) absorb : none AFTER (HEAD) crew-state : state: paused · source: status-log · run cancelled · holding for upstream maintainer merge AFTER (HEAD) absorb : paused SCENARIO: run finished DONE (checks green) , crew declared a pause BEFORE (c64ad1c) crew-state : state: done · source: run-step · checks green: PR ready for review AFTER (HEAD) crew-state : state: paused · source: status-log · checks green: PR ready for review · holding for upstream maintainer merge SCENARIO: run FAILED (must stay visible) + same pause line BEFORE (c64ad1c) crew-state : state: failed · source: run-step · run failed AFTER (HEAD) crew-state : state: failed · source: run-step · run failed SCENARIO: run still ACTIVE (working) + same pause line AFTER (HEAD) crew-state : state: working · source: run-step · validating (running) HUMAN FLEET VIEW (bin/fm-fleet-view.sh) - cancelled run + declared pause --- BEFORE (c64ad1c) --- | upstream-merge | failed / run-step | ship | alpha | tmux | present | https://github.com/o/r/pull/12 | ... | --- AFTER (HEAD) --- | upstream-merge | paused / status-log | ship | alpha | tmux | present | https://github.com/o/r/pull/12 | ... |

==============================================================
SCENARIO: supervisor CANCELLED the validation run, crew declared a pause
  status log (tail): paused: holding for upstream maintainer merge
  no-mistakes axi status run outcome: status cancelled cancelled 
--------------------------------------------------------------
  BEFORE (c64ad1c)  crew-state : state: failed · source: run-step · run cancelled
  BEFORE (c64ad1c)  absorb     : none
  AFTER  (HEAD)     crew-state : state: paused · source: status-log · run cancelled · holding for upstream maintainer merge
  AFTER  (HEAD)     absorb     : paused

==============================================================
SCENARIO: run finished DONE (checks green, PR merged/closed path), crew declared a pause
  status log (tail): paused: holding for upstream maintainer merge
  no-mistakes axi status run outcome: status completed checks-passed 
--------------------------------------------------------------
  BEFORE (c64ad1c)  crew-state : state: done · source: run-step · checks green: PR ready for review
  BEFORE (c64ad1c)  absorb     : none
  AFTER  (HEAD)     crew-state : state: paused · source: status-log · checks green: PR ready for review · holding for upstream maintainer merge
  AFTER  (HEAD)     absorb     : paused

==============================================================
SCENARIO: run FAILED (must stay visible) + same pause line
  status log (tail): paused: holding for upstream maintainer merge
  no-mistakes axi status run outcome: status failed failed 
--------------------------------------------------------------
  BEFORE (c64ad1c)  crew-state : state: failed · source: run-step · run failed
  BEFORE (c64ad1c)  absorb     : none
  AFTER  (HEAD)     crew-state : state: failed · source: run-step · run failed
  AFTER  (HEAD)     absorb     : none

==============================================================
SCENARIO: run still ACTIVE (working) + same pause line
  status log (tail): paused: holding for upstream maintainer merge
  no-mistakes axi status run outcome: status running 
--------------------------------------------------------------
  BEFORE (c64ad1c)  crew-state : state: working · source: run-step · validating (running)
  BEFORE (c64ad1c)  absorb     : working
  AFTER  (HEAD)     crew-state : state: working · source: run-step · validating (running)
  AFTER  (HEAD)     absorb     : working

==============================================================
HUMAN FLEET VIEW (bin/fm-fleet-view.sh) - cancelled run + declared pause
==============================================================
--- BEFORE (c64ad1c) ---
# Fleet View

Schema: fm-fleet-snapshot.v1
Home: /var/folders/1t/12j9b68s5tj28wbq7wd_6fm80000gn/T/no-mistakes-evidence/01KYE4PR9ZWV4TGSRYQ1TQBMNT/work/fmhome

## Under Way
| ID | Current | Kind | Repo/Project | Backend | Endpoint | Artifact | Path | Watch / return channel |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| upstream-merge | failed / run-step | ship | alpha | tmux | present | https://github.com/o/r/pull/12 | /var/folders/1t/12j9b68s5tj28wbq7wd_6fm80000gn/T/no-mistakes-evidence/01KYE4PR9ZWV4TGSRYQ1TQBMNT/work/fmhome/projects/upstream-merge-worktree | bin/fm-peek.sh fm-upstream-merge |

## Queued
No queued backlog records found.

## Done
No done backlog records found.

## Secondmates
For kind=secondmate, bearings selects validated structured state from that registered home; parent events and bounded terminal evidence are fallback-only supplements and never current-state authority.
Evidence: Reproduction driver for the end-to-end evidence (ROOT=<repo> OUT=<dir> bash e2e-pause-vs-terminal-run.sh)
#!/usr/bin/env bash
# End-to-end demo of the "declared pause survives a terminal run record" fix,
# exercised the way a supervisor actually sees it:
#   1. bin/fm-crew-state.sh   - the authoritative one-line crew state
#   2. crew_absorb_class      - the watcher's absorb/surface decision
#   3. bin/fm-fleet-view.sh   - the human-facing fleet table
# Run twice per scenario: the BASE script (c64ad1c) vs the FIXED script (HEAD).
set -u
ROOT=${ROOT:?}
OUT=${OUT:?}
WORK=$OUT/work
rm -rf "$WORK"; mkdir -p "$WORK"

# A full bin/ copy whose fm-crew-state.sh is the BASE (pre-fix) version, so both
# the helper and the fleet renderer can be run against pre-fix behaviour with all
# sibling libraries resolvable.
BASE_BIN=$WORK/base-bin
cp -R "$ROOT/bin" "$BASE_BIN"
BASE_SCRIPT=$BASE_BIN/fm-crew-state.sh
git -C "$ROOT" show c64ad1c:bin/fm-crew-state.sh > "$BASE_SCRIPT"
chmod +x "$BASE_SCRIPT"

FAKEBIN=$WORK/fakebin
mkdir -p "$FAKEBIN"
cat > "$FAKEBIN/no-mistakes" <<'SH'
#!/usr/bin/env bash
set -u
case "${1:-}" in
  axi)
    shift
    case "${1:-}" in
      status) shift; printf '%s\n' "${FM_FAKE_AXI_STATUS:-}" ;;
      logs)   printf '%s\n' "${FM_FAKE_CI_LOGS:-}" ;;
    esac ;;
  runs) printf '%s\n' "${FM_FAKE_RUNS_LIST:-}" ;;
esac
exit 0
SH
cat > "$FAKEBIN/tmux" <<'SH'
#!/usr/bin/env bash
set -u
case "${1:-}" in
  list-windows)   sed -n 's/^window=[^:]*://p' "${FM_HOME:?}"/state/*.meta ;;
  display-message) printf '%%1\n' ;;
  capture-pane)   printf 'all quiet\n> \n' ;;   # idle pane: the crew is waiting
esac
exit 0
SH
chmod +x "$FAKEBIN/no-mistakes" "$FAKEBIN/tmux"

HOME_DIR=$WORK/fmhome
mkdir -p "$HOME_DIR/state" "$HOME_DIR/data" "$HOME_DIR/projects" "$HOME_DIR/config"
WT=$HOME_DIR/projects/upstream-merge-worktree
mkdir -p "$WT"
git -C "$WT" init -q
git -C "$WT" -c user.name=fmtest -c user.email=fmtest@example.invalid commit -q --allow-empty -m init
git -C "$WT" checkout -q -b fm/upstream-merge
HEAD_SHA=$(git -C "$WT" rev-parse HEAD)

cat > "$HOME_DIR/data/backlog.md" <<EOF
## In flight
- [ ] upstream-merge - Upstream Merge https://github.com/o/r/pull/12 (repo: alpha) (kind: ship) (since 2026-07-26)
EOF
{ printf 'window=firstmate:fm-upstream-merge\n'
  printf 'worktree=%s\n' "$WT"
  printf 'project=alpha\nharness=codex\nkind=ship\nmode=ship\nyolo=off\n'
  printf 'pr=https://github.com/o/r/pull/12\n'; } > "$HOME_DIR/state/upstream-merge.meta"

# The crew's own append-only event log: it worked, then declared an external wait.
{ printf 'working: opened PR 12, validation running\n'
  printf 'paused: holding for upstream maintainer merge\n'; } > "$HOME_DIR/state/upstream-merge.status"

run_state() {  # <script>
  PATH="$FAKEBIN:$PATH" FM_HOME="$HOME_DIR" FM_STATE_OVERRIDE="$HOME_DIR/state" \
    FM_DATA_OVERRIDE="$HOME_DIR/data" FM_PROJECTS_OVERRIDE="$HOME_DIR/projects" \
    FM_CONFIG_OVERRIDE="$HOME_DIR/config" "$1" upstream-merge
}

absorb_class() {  # <script>
  PATH="$FAKEBIN:$PATH" FM_HOME="$HOME_DIR" FM_STATE_OVERRIDE="$HOME_DIR/state" \
    FM_DATA_OVERRIDE="$HOME_DIR/data" FM_PROJECTS_OVERRIDE="$HOME_DIR/projects" \
    FM_CONFIG_OVERRIDE="$HOME_DIR/config" FM_CREW_STATE_BIN="$1" \
    bash -c '. "$0"/bin/fm-classify-lib.sh; crew_absorb_class upstream-merge; echo' "$ROOT"
}

fleet_view() {  # <bin-dir>
  PATH="$FAKEBIN:$PATH" FM_HOME="$HOME_DIR" "$1/fm-fleet-view.sh"
}

scenario() {  # <label> <axi-status-fixture>
  FM_FAKE_AXI_STATUS=$2; export FM_FAKE_AXI_STATUS
  echo "=============================================================="
  echo "SCENARIO: $1"
  echo "  status log (tail): $(tail -1 "$HOME_DIR/state/upstream-merge.status")"
  echo "  no-mistakes axi status run outcome: $(printf '%s\n' "$2" | sed -n 's/^outcome: //p;s/^  status: /status /p' | tr '\n' ' ')"
  echo "--------------------------------------------------------------"
  printf '  BEFORE (c64ad1c)  crew-state : %s\n' "$(run_state "$BASE_SCRIPT")"
  printf '  BEFORE (c64ad1c)  absorb     : %s\n' "$(absorb_class "$BASE_SCRIPT")"
  printf '  AFTER  (HEAD)     crew-state : %s\n' "$(run_state "$ROOT/bin/fm-crew-state.sh")"
  printf '  AFTER  (HEAD)     absorb     : %s\n' "$(absorb_class "$ROOT/bin/fm-crew-state.sh")"
  echo
}

run_cancelled=$(cat <<EOF
run:
  id: "01RUN"
  branch: fm/upstream-merge
  status: cancelled
  head: "$HEAD_SHA"
  pr: ""
  findings: none
outcome: cancelled
EOF
)
run_checks_passed=$(cat <<EOF
run:
  id: "01RUN"
  branch: fm/upstream-merge
  status: completed
  head: "$HEAD_SHA"
  pr: "https://github.com/o/r/pull/12"
  findings: none
outcome: checks-passed
EOF
)
run_failed=$(cat <<EOF
run:
  id: "01RUN"
  branch: fm/upstream-merge
  status: failed
  head: "$HEAD_SHA"
  pr: ""
  findings: 2
outcome: failed
EOF
)
run_running=$(cat <<EOF
run:
  id: "01RUN"
  branch: fm/upstream-merge
  status: running
  head: "$HEAD_SHA"
  pr: ""
  findings: none
EOF
)

scenario "supervisor CANCELLED the validation run, crew declared a pause" "$run_cancelled"
scenario "run finished DONE (checks green, PR merged/closed path), crew declared a pause" "$run_checks_passed"
scenario "run FAILED (must stay visible) + same pause line" "$run_failed"
scenario "run still ACTIVE (working) + same pause line" "$run_running"

echo "=============================================================="
echo "HUMAN FLEET VIEW (bin/fm-fleet-view.sh) - cancelled run + declared pause"
echo "=============================================================="
FM_FAKE_AXI_STATUS=$run_cancelled; export FM_FAKE_AXI_STATUS
echo "--- BEFORE (c64ad1c) ---"
fleet_view "$BASE_BIN"
echo
echo "--- AFTER (HEAD) ---"
fleet_view "$ROOT/bin"
- Outcome: ⚠️ 1 info across 1 run (15m22s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 infos
  • ⚠️ bin/fm-crew-state.sh:607 - The new override keys on the mapped RUN_STATE (failed|done), not on whether the run actually reached a terminal outcome. RUN_STATE is also set to done at bin/fm-crew-state.sh:557 for a run whose status is still ci/running when the ci-step log tail reads green (detail: "checks green: PR ready for review (still monitoring for merge/close)") - that run is active, not historical. The intent states as required: "Active/working and parked runs must still outrank a declared pause", and the new header comment itself says the override applies "when the attributed run is already TERMINAL"; neither holds for this path. Failure scenario: a crew appended paused: awaiting maintainer merge while its run is still in the CI-monitor phase; checks go green, so RUN_STATE becomes done at line 557, the new case at line 607 matches, and the crew is reported state: paused · source: status-log instead of done - the fleet/bearings view loses the "PR ready for review" signal for an actively-monitored run. Gating on the terminal branches themselves (outcome passed/checks-passed/failed/cancelled, coarse completed/failed/cancelled, or bare status completed/failed/cancelled) rather than on the post-override RUN_STATE would keep the ci-green case out.
  • ⚠️ bin/fm-crew-state.sh:608 - The override has no ordering or freshness signal: it only asks whether the status log's LAST verb is paused:. It therefore cannot distinguish the motivating case (a pause declared AFTER a historical run ended) from a pause declared BEFORE or DURING a run that subsequently failed - the intent's own framing is "a crew's freshly declared paused:". Because crews append sparsely during validation (AGENTS.md status contract), a pause line stays last for the whole run. Failure scenario: crew appends paused: waiting on vendor rate-limit reset while its no-mistakes run is in ci; CI goes red and the run ends outcome: failed; crew-state now reports state: paused with the failure demoted to trailing detail, so the fleet snapshot/bearings surface a benign external wait and the real validation failure is only re-checked on the hourly pause cadence instead of surfacing as failed. This masking is not among the residuals the intent lists as accepted (live-agent first surface, no-verb turn-end while paused, hourly forgotten-pause re-surface), so it needs an explicit call. Note TTL was rejected, but a status-file-mtime vs run-attribution ordering check is a different mechanism if you want one.
  • ℹ️ bin/fm-crew-state.sh:609 - The retained terminal outcome is appended LAST in the emitted detail (&lt;pause note&gt; · &lt;run detail&gt;), which makes it the first text dropped by downstream truncation. bin/fm-bearings-snapshot.sh:378-379 renders current_state.detail through trunc(90) for the in-flight rows. Failure scenario: a crew with paused: holding until the upstream vendor publishes the 2.4 release and the mirror re-syncs plus a cancelled run emits detail of ~95+ chars, so bearings shows only the pause reason with an ellipsis and the "run cancelled" evidence the intent wants retained as secondary detail never reaches the reader. Putting the run outcome before the pause note, or shortening it, keeps the terminal fact inside the 90-char budget.
  • ℹ️ bin/fm-crew-state.sh:609 - When the pause line carries no reason, status_line_note returns empty and the detail argument becomes &#34; · &lt;run detail&gt;&#34;, producing a doubled separator in the canonical line. Failure scenario: status log's last line is a bare paused: with a cancelled run - the emitted line is state: paused · source: status-log · · run cancelled, and bin/fm-fleet-snapshot.sh:219 parses detail as &#34; · run cancelled&#34; with a leading separator, which then renders verbatim in the bearings doing column. Building the detail conditionally (use $RUN_DETAIL alone when the note is empty) avoids it.
  • ⚠️ docs/architecture.md:31 - docs/architecture.md owns the crew-state mechanism (AGENTS.md:331) and now contradicts the code in two places. Line 31 states the helper "keeps that run-step authoritative even if the pane has closed" with only the ci-log-tail exception spelled out at lines 33-34; line 37 states "In that status-log fallback, a declared external wait reports the distinct paused state with its reason" - i.e. paused is described as reachable ONLY on the no-run fallback path. After this change a declared pause also overrides an attributed terminal run outside that fallback. Failure scenario: a reader (or a future change) follows line 31/37 and concludes an attributed failed/cancelled run always reports failed, then builds surfacing logic on that guarantee, which the new bin/fm-crew-state.sh:601-612 block breaks. The ci exception is documented at that level of detail; this second exception should be too.

🔧 Fix: gate pause override on true terminal outcome, keep failures visible
2 infos still open:

  • ℹ️ bin/fm-crew-state.sh:628 - Flagging per intent-conformance: the change now contradicts the literal User intent text on the failed outcome. The intent states as required: "when bin/fm-crew-state.sh has HAVE_RUN=1 for a terminal outcome (failed including cancelled, or done) and the status log's last verb is paused:, emit state paused with source status-log", and "Tests added for cancelled/failed/done + trailing paused". The contradicting hunk is case &#34;$RUN_TERMINAL&#34; in done|cancelled) at bin/fm-crew-state.sh:627-628, which excludes failed, plus tests/fm-crew-state.test.sh:735-751 where the former test_terminal_failed_plus_paused is replaced by test_terminal_failed_plus_paused_surfaces_failure, now asserting state: failed / source: run-step and assert_not_contains &#34;state: paused&#34;. This divergence is the author's own round-1 decision (key=review-gate: "If NO reliable freshness signal exists, fail SAFE ... Cancelled ... may still be overridden by pause; failed (work failed) must surface. Prefer surfacing."), so the code is almost certainly right and the resolution is to update the intent/PR description rather than the code. Failure scenario if left unresolved: the PR body and the recorded acceptance criteria claim failed + trailing paused: reports paused, while the shipped behavior and its test assert the opposite - a later reader or change re-introduces the failed-override believing it was the agreed contract.
  • ℹ️ bin/fm-crew-state.sh:619 - The load-bearing rationale comment states "no freshness signal exists to prove the pause came AFTER the failure (status-log lines carry no timestamp, and axi status reports no completion time)", mirrored in docs/architecture.md:36 ("neither the status log nor axi status timestamps its events"). That is accurate for the primary axi status path and for status-log lines, but this same file documents at bin/fm-crew-state.sh:387-388 that the coarse no-mistakes runs rows are "newest-first, columns &fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34;<status> <branch> <short-sha> <date> [<pr-url>]&fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34;" - i.e. a date IS present on the coarse fallback path, and the status file's mtime is always readable. Failure scenario: a future change reads the comment as "no timestamp exists anywhere in this script's inputs" and rules out an ordering check that is in fact partially available. No code change needed - a coarse-only freshness rule would give the two attribution paths different pause semantics, which is exactly the dual-authority drift the intent rejects, and the fail-safe (surface failures) is the safer branch either way; only the absoluteness of the claim is worth narrowing to "no per-event timestamp on the attributed-run path".
⚠️ **Test** - 1 info
  • ℹ️ bin/fm-crew-state.sh:614 - Intent text says the pause override should apply to "a terminal outcome (failed including cancelled, or done)", but the shipped behavior (head commit b96b202, "keep failures visible") deliberately excludes terminal failed: a failed run + trailing paused: still reports state: failed · source: run-step. The added test test_terminal_failed_plus_paused_surfaces_failure and the intent's rejected-alternatives section encode that narrower choice, so this reads as a conscious review-driven narrowing rather than a defect - flagged only so the reviewer confirms the narrower scope is what they want.
  • bash tests/fm-crew-state.test.sh (whole file; includes the 7 new cases: cancelled+paused, failed+paused stays failed, cancelled+bare paused separator, done/checks-passed+paused, active outranks paused, parked outranks paused, ci-green monitoring outranks paused)
  • bash tests/fm-watch-triage.test.sh (the only suite exercising crew_absorb_class / crew_is_paused, the downstream consumer of this verdict)
  • Manual end-to-end: hermetic FM_HOME with a real git worktree on fm/upstream-merge, fake no-mistakes axi status and tmux (idle pane), status log ending paused: holding for upstream maintainer merge; ran bin/fm-crew-state.sh upstream-merge and crew_absorb_class upstream-merge against both the base (c64ad1c) and target (b96b202) scripts across cancelled / done / failed / active run fixtures
  • Manual end-to-end: bin/fm-fleet-view.sh rendered over the same fixture with base vs target bin/, showing the human fleet table cell change from failed / run-step to paused / status-log
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

When an attributed no-mistakes run is already terminal (done/failed,
including cancelled) and the status log ends with paused:, report paused
from the status log and keep the terminal run detail as secondary text.

Active and parked runs still win over a pause line. This stops a stale
historical validation record from permanently overruling a fresher
self-declared external wait, without widening absorption for live or
working crews.
@yomi202703
yomi202703 merged commit 4cf8f6a into main Jul 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant