Skip to content

fix(bin): derive fm-crew-state's passed-outcome PR detail from pr_state - #13

Merged
rub-a-dub-dub merged 4 commits into
mainfrom
fm/firstmate-passed-outcome-claims-merged
Sep 22, 2026
Merged

rub-a-dub-dub merged 4 commits into
mainfrom
fm/firstmate-passed-outcome-claims-merged

Conversation

@rub-a-dub-dub

Copy link
Copy Markdown
Owner

Intent

Observed live on 2026-09-20 on firstmate-lint-debt-blocking-prs, with the
contradicting evidence in the same command output.

THE DEFECT. bin/fm-crew-state.sh maps the pipeline outcome "passed" to the literal
detail string "run passed: PR merged/closed". That assertion is not true in
general and was not true here: no-mistakes reported outcome "passed" AND
"pr_state: open" in the same status block, and the forge independently confirmed
state=open, merged=false, merged_at=null, merged_by=none. Nothing had been merged;
the captain had not even been asked for his word yet.

So firstmate told its own supervisor that a pull request had landed when it
demonstrably had not.

WHY IT MATTERS MORE THAN A WORDING SLIP. A supervisor reading "done - run passed:
PR merged/closed" has every reason to proceed to cleanup. Teardown has its own
landed-work test and would very likely have refused, so this is a near miss rather
than a loss - but the reading is the first domino, and relying on a downstream
guard to catch a false upstream claim is exactly the posture this fleet spent
2026-09-19 and 20 dismantling everywhere else.

SECOND LIVE INSTANCE, SAME MAPPING, OPPOSITE FAILURE. On
firstmate-detect-dropped-ci-event the pipeline reached outcome
"passed-with-override" - a real terminal outcome: every step completed, approved
past one confirmed-unrelated red check. The reader has no arm for it, so it
returned "state: unknown" for a task that had cleanly finished and reported
"done: PR ... holding for merge authority" in its own status log. The first
instance asserts a landing that did not happen; this one refuses to classify a
finish that did. Both come from the same case statement treating the outcome NAME
as the authority.

What Changed

  • bin/fm-crew-state.sh no longer prints the fixed detail run passed: PR merged/closed for a terminal passed outcome. A new nm_outcome_pr_detail helper reads the run record's own pr_state field and maps merged/open/closed/none to PR merged, PR open, not yet merged, PR closed, not merged, and no PR opened; any other value (including an absent field) reads PR merge state unknown rather than guessing in either direction.
  • Added a passed-with-override arm to the same outcome case, classifying it as state: done with detail run passed (approved past a waived check): <pr detail> instead of letting it fall through to the unmapped-outcome unknown default. AGENTS.md's state-reading line now lists passed-with-override alongside passed/checks-passed as done.
  • Extended the header comments in bin/fm-crew-state.sh to record that the outcome name is not proof of a merge, with an audit note on which arms were checked and left unchanged, and added four tests in tests/fm-crew-state.test.sh covering passed + pr_state: open, passed-with-override, a record with no pr_state field, and pr_state: none.

Risk Assessment

✅ Low: A tightly scoped read-only reporting fix whose five pr_state arms match the installed no-mistakes binary's actual vocabulary exactly, with no downstream parser depending on the changed detail text and four genuine regression tests that fail against the pre-fix code.

Testing

Ran the targeted tests/fm-crew-state.test.sh suite (passes at the target commit) and proved the regression property by re-running each of the four new tests against a copy of the tree with the base bin/fm-crew-state.sh restored — all four fail before the fix. For product-level evidence I wrote a driver that executes the real bin/fm-crew-state.sh over throwaway git worktrees with a fake no-mistakes axi status, and captured the supervisor-facing state line for five run records under both the base and target scripts: the base prints the false run passed: PR merged/closed for the pr_state: open incident record and state: unknown for passed-with-override, while the target prints run passed: PR open, not yet merged and state: done · run passed (approved past a waived check): PR open, not yet merged; a merged PR reads PR merged, pr_state: none reads no PR opened, and an absent field reads PR merge state unknown. Quoted YAML values parse identically, and no other script or doc depends on the old wording. This is a CLI/text surface with no rendered UI, so the reviewer-visible artifact is the before/after CLI transcript rather than a screenshot. Temp trees were removed and the worktree is clean.

Evidence: Before/after CLI transcript: fm-crew-state.sh supervisor state line over five run records at base 7ce3cda vs target 2105bf7

Source: Before/after CLI transcript: fm-crew-state.sh supervisor state line over five run records at base 7ce3cda vs target 2105bf7

fm-crew-state.sh supervisor-facing state line, same five run records, before and after the fix
Driver: crew-state-pr-detail-driver.sh (drives the real bin/fm-crew-state.sh over throwaway git worktrees with a fake `no-mistakes axi status`)

################################ BEFORE (base 7ce3cda) ################################
############ fm-crew-state.sh under test: /var/folders/f_/hcy8g0q91p9fdyghn_9ypx5r0000gn/T/tmp.DdmDjKn7e3/bin/fm-crew-state.sh
############ version label: 7ce3cda (base, before the fix)

=== A. live incident: firstmate-lint-debt-blocking-prs (outcome=passed, pr_state=open) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/lint-debt-blocking-prs
      status: completed
      head: "0508744b2736766c521a60304400d24a4eefaf9f"
      pr: "https://github.com/o/r/pull/1"
      pr_state: open
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh lint-debt-blocking-prs ---
    state: done · source: run-step · run passed: PR merged/closed

=== B. live incident: firstmate-detect-dropped-ci-event (outcome=passed-with-override) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/detect-dropped-ci-event
      status: completed
      head: "f55e57bc9fddce63e247a7bae44d2b0329313c84"
      pr: "https://github.com/o/r/pull/2"
      pr_state: open
      findings: none
    outcome: passed-with-override
--- supervisor-facing output: fm-crew-state.sh detect-dropped-ci-event ---
    state: unknown · source: run-step · outcome: passed-with-override

=== C. genuinely merged PR (outcome=passed, pr_state=merged) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/really-merged
      status: completed
      head: "d6ce31e1cef55fd2599db12b70a408a6319471d1"
      pr: "https://github.com/o/r/pull/3"
      pr_state: merged
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh really-merged ---
    state: done · source: run-step · run passed: PR merged/closed

=== D. no PR ever opened (outcome=passed, pr_state=none) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/no-pr-opened
      status: completed
      head: "d6ce31e1cef55fd2599db12b70a408a6319471d1"
      pr: ""
      pr_state: none
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh no-pr-opened ---
    state: done · source: run-step · run passed: PR merged/closed

=== E. older no-mistakes with no pr_state field at all (outcome=passed) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/absent-pr-state
      status: completed
      head: "d6ce31e1cef55fd2599db12b70a408a6319471d1"
      pr: "https://github.com/o/r/pull/5"
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh absent-pr-state ---
    state: done · source: run-step · run passed: PR merged/closed

################################ AFTER  (target 2105bf7) ################################
############ fm-crew-state.sh under test: ~/.no-mistakes/worktrees/00644a303d6a/01M33HXRGTB724W7N4DSXWNGX5/bin/fm-crew-state.sh
############ version label: 2105bf7 no-mistakes(review): correct audit comment's above/below pointers

=== A. live incident: firstmate-lint-debt-blocking-prs (outcome=passed, pr_state=open) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/lint-debt-blocking-prs
      status: completed
      head: "14c96fc695163a109c998cddecd320e9d334d1b8"
      pr: "https://github.com/o/r/pull/1"
      pr_state: open
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh lint-debt-blocking-prs ---
    state: done · source: run-step · run passed: PR open, not yet merged

=== B. live incident: firstmate-detect-dropped-ci-event (outcome=passed-with-override) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/detect-dropped-ci-event
      status: completed
      head: "14c96fc695163a109c998cddecd320e9d334d1b8"
      pr: "https://github.com/o/r/pull/2"
      pr_state: open
      findings: none
    outcome: passed-with-override
--- supervisor-facing output: fm-crew-state.sh detect-dropped-ci-event ---
    state: done · source: run-step · run passed (approved past a waived check): PR open, not yet merged

=== C. genuinely merged PR (outcome=passed, pr_state=merged) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/really-merged
      status: completed
      head: "e1c9e14f909661003dbd526ffdd7c2aa57e0fe27"
      pr: "https://github.com/o/r/pull/3"
      pr_state: merged
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh really-merged ---
    state: done · source: run-step · run passed: PR merged

=== D. no PR ever opened (outcome=passed, pr_state=none) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/no-pr-opened
      status: completed
      head: "e1c9e14f909661003dbd526ffdd7c2aa57e0fe27"
      pr: ""
      pr_state: none
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh no-pr-opened ---
    state: done · source: run-step · run passed: no PR opened

=== E. older no-mistakes with no pr_state field at all (outcome=passed) ===
--- run record fm-crew-state reads (no-mistakes axi status) ---
    run:
      id: "01RUN"
      branch: fm/absent-pr-state
      status: completed
      head: "3c9579f0bd0eaf10ed802da25f4082a8ad242496"
      pr: "https://github.com/o/r/pull/5"
      findings: none
    outcome: passed
--- supervisor-facing output: fm-crew-state.sh absent-pr-state ---
    state: done · source: run-step · run passed: PR merge state unknown
Evidence: Evidence driver: runs the real bin/fm-crew-state.sh over throwaway worktrees with a fake no-mistakes axi status

Source: Evidence driver: runs the real bin/fm-crew-state.sh over throwaway worktrees with a fake no-mistakes axi status

#!/usr/bin/env bash
# Evidence driver: run the real bin/fm-crew-state.sh against the run-record
# shapes from the two 2026-09-20 live incidents and print the exact supervisor-
# facing state line for each. Usage: driver.sh <path-to-fm-crew-state.sh>
set -u
CREW_STATE=$1
ROOT=$(cd "$(dirname "$CREW_STATE")/.." && pwd)
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
export GIT_AUTHOR_NAME=fmtest GIT_AUTHOR_EMAIL=fmtest@example.invalid
export GIT_COMMITTER_NAME=fmtest GIT_COMMITTER_EMAIL=fmtest@example.invalid

mk_fakebin() {  # <dir>
  local fb=$1/fakebin; mkdir -p "$fb"
  cat > "$fb/no-mistakes" <<'SH'
#!/usr/bin/env bash
set -u
case "${1:-}" in
  axi) shift; case "${1:-}" in
      status) shift
        if [ "${1:-}" = --run ]; then printf '%s\n' "${FM_FAKE_AXI_STATUS_RUN:-}"
        else printf '%s\n' "${FM_FAKE_AXI_STATUS:-}"; fi ;;
      logs) printf '%s\n' "${FM_FAKE_CI_LOGS:-}" ;;
    esac ;;
  runs) printf '%s\n' "${FM_FAKE_RUNS_LIST:-}" ;;
  daemon) printf 'daemon running (pid 4242)\n'; exit 0 ;;
esac
exit 0
SH
  cat > "$fb/herdr" <<'SH'
#!/usr/bin/env bash
set -u
case "${1:-}" in
  status) [ "${2:-}" = --json ] && { printf '{"client":{"version":"0.7.1","protocol":14},"server":{"running":true}}\n'; exit 0; } ;;
  server) exit 0 ;;
  pane) case "${2:-}" in
      read) printf 'all quiet\n> \n'; exit 0 ;;
      get) printf '{"result":{"pane":{"pane_id":"%s"}}}\n' "${3:-}"; exit 0 ;;
      process-info)
        pane=""; args=("$@"); for ((i=0; i<${#args[@]}; i++)); do [ "${args[$i]}" = --pane ] && pane=${args[$((i+1))]:-}; done
        printf '{"result":{"type":"pane_process_info","process_info":{"pane_id":"%s","shell_pid":%s,"foreground_process_group_id":424242,"foreground_processes":[{"pid":424242,"name":"claude","argv0":"claude"}]}}}\n' "$pane" "$PPID"
        exit 0 ;;
    esac ;;
  agent) case "${2:-}" in
      get) printf '{"result":{"agent":{"agent_status":"idle"}}}\n'; exit 0 ;;
    esac ;;
esac
exit 0
SH
  cat > "$fb/tmux" <<'SH'
#!/usr/bin/env bash
set -u
case "${1:-}" in
  list-windows) ;;
  display-message) printf '%%1\n' ;;
  capture-pane) printf 'all quiet\n> \n' ;;
esac
exit 0
SH
  chmod +x "$fb/no-mistakes" "$fb/tmux" "$fb/herdr"
}

scenario() {  # <slug> <pr> <pr_state-line> <outcome>
  local slug=$1 pr=$2 pr_state_line=$3 outcome=$4
  local d="$TMP/$slug" branch="fm/$slug"
  mkdir -p "$d/state" "$d/wt"
  git -C "$d/wt" init -q
  git -C "$d/wt" config commit.gpgsign false
  git -C "$d/wt" commit -q --allow-empty -m init
  git -C "$d/wt" checkout -q -b "$branch"
  local head; head=$(git -C "$d/wt" rev-parse HEAD)
  mk_fakebin "$d"
  printf 'window=fm:fm-%s\nworktree=%s\nkind=ship\n' "$slug" "$d/wt" > "$d/state/$slug.meta"
  local status_block
  status_block=$(printf 'run:\n  id: "01RUN"\n  branch: %s\n  status: completed\n  head: "%s"\n  pr: "%s"\n%s  findings: none\noutcome: %s\n' \
    "$branch" "$head" "$pr" "$pr_state_line" "$outcome")
  echo "--- run record fm-crew-state reads (no-mistakes axi status) ---"
  printf '%s\n' "$status_block" | sed 's/^/    /'
  echo "--- supervisor-facing output: fm-crew-state.sh $slug ---"
  FM_FAKE_AXI_STATUS="$status_block" \
    PATH="$d/fakebin:$PATH" FM_STATE_OVERRIDE="$d/state" \
    "$CREW_STATE" "$slug" | sed 's/^/    /'
  echo
}

echo "############ fm-crew-state.sh under test: $CREW_STATE"
echo "############ version label: ${LABEL:-$( (cd "$ROOT" && git log -1 --format='%h %s') 2>/dev/null || echo 'baseline copy' )}"
echo
echo "=== A. live incident: firstmate-lint-debt-blocking-prs (outcome=passed, pr_state=open) ==="
scenario lint-debt-blocking-prs "https://github.com/o/r/pull/1" '  pr_state: open
' passed

echo "=== B. live incident: firstmate-detect-dropped-ci-event (outcome=passed-with-override) ==="
scenario detect-dropped-ci-event "https://github.com/o/r/pull/2" '  pr_state: open
' passed-with-override

echo "=== C. genuinely merged PR (outcome=passed, pr_state=merged) ==="
scenario really-merged "https://github.com/o/r/pull/3" '  pr_state: merged
' passed

echo "=== D. no PR ever opened (outcome=passed, pr_state=none) ==="
scenario no-pr-opened "" '  pr_state: none
' passed

echo "=== E. older no-mistakes with no pr_state field at all (outcome=passed) ==="
scenario absent-pr-state "https://github.com/o/r/pull/5" '' passed
Evidence: Key before/after lines (excerpt)
BEFORE (base 7ce3cda)
A. outcome=passed, pr_state=open -> state: done · run passed: PR merged/closed <-- false landing claim
B. outcome=passed-with-override -> state: unknown · outcome: passed-with-override <-- finished run refused
C. pr_state=merged -> state: done · run passed: PR merged/closed
D. pr_state=none -> state: done · run passed: PR merged/closed <-- no PR existed
E. no pr_state field -> state: done · run passed: PR merged/closed <-- unproven

AFTER (target 2105bf7)
A. outcome=passed, pr_state=open -> state: done · run passed: PR open, not yet merged
B. outcome=passed-with-override -> state: done · run passed (approved past a waived check): PR open, not yet merged
C. pr_state=merged -> state: done · run passed: PR merged
D. pr_state=none -> state: done · run passed: no PR opened
E. no pr_state field -> state: done · run passed: PR merge state unknown
Evidence: Each new test fails against the base script
\### test_terminal_passed_open_pr_reads_honest_detail against base bin/fm-crew-state.sh:
not ok - an open PR's detail must not claim it merged (missing: 'PR open, not yet merged')
\### test_terminal_passed_with_override_reads_done_not_unknown against base bin/fm-crew-state.sh:
not ok - passed-with-override is a real terminal outcome, not unknown (missing: 'state: done')
\### test_terminal_passed_absent_pr_state_reads_unknown against base bin/fm-crew-state.sh:
not ok - an absent pr_state must read unknown (missing: 'PR merge state unknown')
\### test_terminal_passed_pr_state_none_reads_no_pr_opened against base bin/fm-crew-state.sh:
not ok - pr_state none is a known fact, not an unknown (missing: 'no PR opened')

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (3) ✅
  • ⚠️ bin/fm-crew-state.sh:494 - nm_outcome_pr_detail's none|&#34;&#34; arm collapses an ABSENT pr_state with an explicit pr_state=none, and its no-URL leg then prints the positive claim "no PR opened". Concrete path: the helper's own comment (lines 479-481) states it supports "an older no-mistakes without the field", so pr_state is empty there; an empty pr field on a terminal run is a shape this same file already anticipates (line 510 guards [ -n &#34;$pr_url&#34; ] before appending the URL, and tests/fm-crew-state.test.sh's run_failed fixture emits pr: &#34;&#34;). With outcome=passed and both fields empty the supervisor reads "state: done - run passed: no PR opened" for a run that in fact has a PR holding for the captain's merge word - a fact asserted from two absent fields, the same failure class the intent names ("the outcome NAME is not proof"). The pr_url-conditional sub-branch is additionally not required by the intent: the intent requires that the passed detail not assert an unproven PR fact, not that it surface the PR URL, and no sibling arm (merged/open/closed) surfaces it, so its output also reads oddly ("run passed: https://...fix(crew-state): read current state from the newest state-bearing line, not the last line #1, merge state unknown"). Smallest honest remedy is to remove the pr_url read from this helper and split the arm by evidence: none) -> "no PR opened" (the forge said so), &#34;&#34;) -> "PR merge state unknown" (we do not know). Marked ask-user because the remedy removes a component and changes supervisor-facing wording the author chose deliberately.
  • ℹ️ AGENTS.md:380 - AGENTS.md:380 is the fleet-captain contract for reading bin/fm-crew-state.sh and enumerates the mapping as "passed or checks-passed is done; failed or cancelled is failed exactly as bin/fm-crew-state.sh prints it". The change adds a third done-producing outcome (passed-with-override) that this sentence does not list, so the documented enumeration is incomplete for exactly the outcome the intent's second live instance is about. The sentence is not false (it never claims to be exhaustive) and the captain is told to trust the printed state line, so this is informational: add passed-with-override to that enumeration.

🔧 Fix: drop unproven PR claims from passed-outcome detail
2 issues (1 warning, 1 info) still open:

  • ⚠️ bin/fm-crew-state.sh:490 - nm_outcome_pr_detail introduces two distinct "we don't know" paths where the intent needs one: the &#34;&#34;) arm prints "PR merge state unknown" and the *) arm prints the raw field back as "PR state <value>". The intent requires only that a passed/passed-with-override detail stop asserting a landing the outcome name does not prove; it does not require surfacing an unrecognized pr_state spelling verbatim. A default is genuinely needed (without one an unmatched value yields an empty substitution and a dangling "run passed: "), so the strictly narrower form is a single default arm — fold *) into the unknown default so every non-merged/open/closed value reads "PR merge state unknown". Tradeoff worth the author's call: the verbatim arm is honest and never overclaims, and it does preserve the raw word (e.g. pr_state: none currently reads "PR state none"), which folding would discard. Recommending removal of the component rather than hardening it, per the simplification pass; ask-user because it is the author's deliberate supervisor-facing wording.
  • ℹ️ bin/fm-teardown.sh:1800 - This change makes passed-with-override a first-class terminal outcome in the crew-state reader and in AGENTS.md:380, but bin/fm-teardown.sh's task_status_is_terminal_run still enumerates only cancelled|failed|passed|checks-passed, and task_status_is_own_parked_run at line 1755 lists passed|checks-passed among status words. I could not construct a path this change makes reachable: conclude_task_no_mistakes_run only proceeds when the run's outcome field is empty, then issues an abort, so the post-abort status it re-reads is cancelled; a passed-with-override reading there requires the run to have concluded with an override in the window between the parked check and the abort, and that race fails safe ("REFUSED … confirm it stopped"), never toward destructive cleanup. Pre-existing and not introduced here — noted only because this change just made that outcome word significant elsewhere in the repo. No action required.

🔧 Fix: split pr_state none from the single unknown default
1 info still open:

  • ℹ️ bin/fm-crew-state.sh:715 - The new audit comment says "The coarse ledger-fallback case below and the no-outcome status fallback further down were audited the same way". The no-outcome status fallback is indeed below (line 750+), but the coarse ledger-fallback case &#34;$COARSE_STATUS&#34; it names is ABOVE this comment, at lines 686-702 (inside the if [ &#34;$RUN_SOURCE&#34; = coarse ] branch that opens at 677). In a file where these comments are the navigational contract for the next reader, the pointer sends them the wrong way. Mechanical, non-functional: reword to "the coarse ledger-fallback case above and the no-outcome status fallback below".

🔧 Fix: correct audit comment's above/below pointers
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-crew-state.test.sh (full crew-state suite at target commit — all pass)
  • test_terminal_passed_open_pr_reads_honest_detail, test_terminal_passed_with_override_reads_done_not_unknown, test_terminal_passed_absent_pr_state_reads_unknown, test_terminal_passed_pr_state_none_reads_no_pr_opened — each run against a copy of the tree with base bin/fm-crew-state.sh restored, confirming each fails before the fix
  • Manual end-to-end driver crew-state-pr-detail-driver.sh &lt;path-to-bin/fm-crew-state.sh&gt;: runs the real helper over throwaway git worktrees with a fake no-mistakes axi status/tmux/herdr, for five run records (passed+pr_state open, passed-with-override, passed+pr_state merged, passed+pr_state none, passed with no pr_state field), executed against both base 7ce3cda and target 2105bf7
  • Variant of the same driver with quoted YAML values (pr_state: &#34;open&#34;, pr_state: &#34;merged&#34;) to confirm strip_quotes handling
  • grep -rn &#39;PR merged/closed|passed-with-override&#39; --include=&#39;*.sh&#39; --include=&#39;*.md&#39; . to confirm no other script or doc still depends on the old wording
⚠️ **Document** - 1 info
  • ℹ️ docs/architecture.md:86 - Judgment call, left unchanged deliberately. The fm-crew-state paragraph at docs/architecture.md:78-88 enumerates, with safety rationale, the ways the state line departs from the raw run record (ci log-tail override, terminal-failed held-green reclassification with the 2026-09-05 jr-voice rationale, coarse-fallback daemon-down -> unknown). This change adds a sibling invariant of the same class - the terminal outcome NAME is not proof of a merge, so passed and passed-with-override read their PR clause off the run's own pr_state field - and that invariant is not reflected there. I did not add it: no sentence in that paragraph is now false (it never claimed passed meant merged), the paragraph already defers exact rules to the script headers, and the placement policy prefers pointers over synchronizing a fact whose owners (bin/fm-crew-state.sh's header plus AGENTS.md:380 for the agent contract) are both already accurate. Flagging so a reviewer who reads that paragraph as an exhaustive contributor-facing list can disagree and ask for one deferring sentence.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

outcome=passed no longer implies a merge (2026-09-20
firstmate-lint-debt-blocking-prs: passed reported alongside pr_state=open,
merged=false). Read the run's own pr_state instead of asserting
merged/closed from the outcome name, and classify passed-with-override
as done instead of unknown (2026-09-20 firstmate-detect-dropped-ci-event).
@rub-a-dub-dub
rub-a-dub-dub merged commit 2e9903c into main Sep 22, 2026
14 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