Skip to content

Review HALT that omits ## Auto Run Result re-triggers the #149 livelock, then DEFER-drops a finished story #224

Description

@tjoignant

Summary

A review pass (bmad-dev-auto re-invoked on a done spec) can finalize the spec's frontmatter to status: done while omitting the terminal ## Auto Run Result section. devcontract.find_result_artifact requires that non-fenced heading, so the generic adapter never harvests the spec as a result — every Stop reads as no-artifact, re-triggering the #149 nudge-livelock. This is not covered by #160 (which handled a stale marker being present); here the marker is absent while the frontmatter is already terminal.

When it triggers (scope)

Observed only on the follow-up review leg, which runs only when review.enabled = true and (review.trigger = "always", or trigger = "recommended" — the default — with the dev pass having set followup_review_recommended: true). A run with review disabled, or a story whose dev pass recommends no follow-up, routes through _skip_review_and_commit and never exposes this. Both observed hits (3-10, 3-11) recommended a follow-up, so a review session ran.

Review-exclusive on all evidence in this project. Every no-artifact resultless-stop across all runs was checked — 4 legs total: the 2 review legs above (this bug), and 2 dev legs that are not this bug:

  • dw-ci-gate-tripwire-hardening dev-1: transient — a resume-restart interrupted it mid-work, a fresh dev-2 completed and committed. The spec was simply not finalized yet at those stops.
  • dw-ci-gate-tripwire-followup-hardening dev-3: deferred with spec status is 'in-review', expected 'done' — frontmatter was in-review, not done, so it fails this fingerprint (mid-review-interrupt family, a different failure).

Mechanistic reason it lands on the review leg: the review is a re-invocation of bmad-dev-auto on an already-done spec (the step-04 review pass), and that path is where the marker-append does not reliably fire; the dev path (primary implementation) writes it reliably. find_result_artifact is shared, so a dev HALT omitting the marker would livelock identically — but the dev path has never produced the signature here. Precise scope: "any generic-adapter session reaching terminal status: done without the marker"; in practice, the follow-up review leg.

Failure mechanism

The livelock does not converge on its own: decide_review_session maps the eventual stalled/timeout to RETRY, and each retry runs _reset_spec_for_review (strips any marker) then re-invokes the same skill, which reproduces the omission. After max_review_cycles the code path reaches DEFER — silently skipping an otherwise-complete, verify-passing story. (The DEFER endpoint is traced from the code, not witnessed here: the run was manually unblocked first — see Repro. What was observed is the livelock and the omitted marker.)

Observed

  • Run 20260720-142340-c682, story 3-10, review leg review-1.
  • tasks/3-10-.../review-1/resultless-stops.jsonl: 6× {"verdict":"no-artifact"}; heartbeat.json reached stall_nudges_sent: 4 (workflow_stall_nudges_cap = 3) — the session never went truly silent (spinner pane output re-armed dev_stall_grace_s per generic.py:561), so it was riding toward the 3h session_timeout_min, ~1h burned.
  • Worktree spec at that point: frontmatter status: 'done', followup_review_recommended: false, a full ## Review Triage Log (two passes, patches applied, DW-130–136 filed) — but zero ## Auto Run Result headings (grep -c = 0). Completed sibling specs 3-8 and 3-9 each carried exactly 1, confirming the intermittency.
  • Engine healthy throughout (pid alive, heartbeats fresh) — the session, not the orchestrator, was stuck one line short of the contract.

Root cause

The bmad-dev-auto review HALT does not append ## Auto Run Result unconditionally the way the dev HALT does. Intermittent — usually written, occasionally not.

Repro / confirmation

Manually appending a ## Auto Run Result section (Status: done) to the worktree spec was harvested on the very next Stop → verify passed → story committed and merged (b3d87fa / merge 4b96427), and the run advanced to 3-11 with no other change. Confirmed the gate directly:

from bmad_loop import devcontract
devcontract.find_result_artifact(spec_dir, since_ns=0)  # returns the spec path after the append

The pre-append None is evidenced by the 6 no-artifact entries in resultless-stops.jsonl — those are the orchestrator's own find_result_artifact calls returning None against the same spec.

Suggested fix

Primary (upstream BMAD-METHOD skill): the review HALT must append ## Auto Run Result on every terminal exit (done and blocked), matching the dev HALT.

Orchestrator-side hardening (this repo): the fingerprint is unambiguous and already half-detected — a spec with terminal frontmatter status but no ## Auto Run Result heading across ≥2 resultless stops is the missing-marker case, distinct from #52 (which is about a first-save spec with no terminal frontmatter at all). On that fingerprint, either fail-fast to RETRY immediately or synthesize the result from the authoritative frontmatter status: (the reconcile-seam direction of #61), instead of nudging to the stall/timeout budget and then DEFER-dropping a finished story. Related: #149 (livelock mechanism), #160 (stale-marker sibling), #123 (dev-leg residue).

Note: lowering workflow_stall_nudges_cap / dev_stall_grace_s is not a fix — RETRY re-runs the same skill and reproduces the omission, so it only reaches the silent DEFER faster.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions