Skip to content

fm-crew-state reports an open, red PR as merged/closed from the pipeline outcome alone, and supersedes the blocker that disproves it #3306

Description

@Lykhoyda

What happens

bin/fm-crew-state.sh can report a ship task as terminally finished — "PR merged/closed" — for a pull request that is open, red, and carrying an unresolved blocker. The same read then marks that still-open blocker superseded.

Observed in the field: a supervisor received a terminal monitoring result claiming a pull request was merged/closed and its history superseded. The forge disagreed on every count — the PR was open, not merged, mergeable_state: unstable, with a required check in conclusion: failure, and the preserved blocker still open. Cleanup was refused by the operator, which is the only reason the unlanded work survived.

Mechanism

Two adjacent paths compose into the false result.

1. The terminal PR claim is inferred, never verified (bin/fm-crew-state.sh:502):

passed)  RUN_STATE="done"; RUN_DETAIL="run passed: PR merged/closed" ;;

The reader takes the validation pipeline's own outcome field and, on passed, emits a factual claim about the pull request — that it is merged or closed. No forge read happens on this path. When that outcome is wrong, stale, or sourced from a superseded run, the supervisor is told the PR reached a terminal state it never reached.

Contrast bin/fm-pr-poll.sh, which is correctly conservative: it emits merged only on an exact MERGED/merged state read from the forge and stays silent otherwise. The poll cannot produce this claim. The terminal language originates in the state reader, which infers where the poll verifies.

2. The contradicting blocker is invalidated by that same wrong state (bin/fm-crew-state.sh:575-577):

case "$LOG_VERB" in
  needs-decision|blocked)
    if [ "$RUN_STATE" != parked ]; then
      ... RUN_DETAIL="$RUN_DETAIL${SEP}status-log superseded (run $RUN_STATE)"

A preserved blocked record is treated as deterministically stale whenever the run is not parked — sound reasoning, but only when RUN_STATE is itself trustworthy.

3. Why this is one defect rather than two

Step 1 sets RUN_STATE=done from a wrong outcome. Step 2 reads that same wrong value and, because it is not parked, marks the open blocker superseded. A single bad outcome value produces both halves of the false terminal result, and the second half destroys the very record that would have contradicted the first.

That is the discard-risk shape: a false terminal state plus an auto-invalidated blocker is precisely the combination that authorizes teardown of unlanded work.

Why it matters

This fails in the dangerous direction. A reader that overstates trouble costs attention; a reader that reports "finished, nothing blocking" over work that is open, red, and blocked invites destruction of that work.

Relationship to existing issues

Same family — both are fm-crew-state.sh misreads — but neither covers this:

Issue Claim Why it does not cover this
#3216 zero CI checks reported as checks green Different field and path (nm_ci_checks_state, CI log marker)
#3215 superseded cancelled run surfaced, reporting a parked task as failed Fails toward alarm; harmless direction, and no blocker-supersession coupling

The distinguishing property is direction of failure: #3215 and #3216 make a task look worse or busier than it is. This one makes unlanded, red, blocked work look finished and safe to discard.

Reproduction shape

  1. A ship task reaches a validation run whose outcome field reads passed while its pull request is still open with a failing required check.
  2. The task's status log carries an unresolved blocked record.
  3. Read current state with bin/fm-crew-state.sh <id>.

Expected: the reader does not assert a terminal PR state it has not verified against the forge, and does not supersede an open blocker on the strength of an uncorroborated run state.

Actual: run passed: PR merged/closed plus status-log superseded (run done).

Notes

  • Evidence is described by shape and field values; the originating pull request is in a private repository, so its identifiers are withheld. Everything needed to locate the defect is in the two code paths above.
  • Filed as a report only. No fix design is proposed here — two directions worth weighing are verifying the terminal claim against the forge before stating it (or not phrasing it as a claim about the PR at all), and not driving blocker supersession from a RUN_STATE no forge read has corroborated.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions