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.
Summary
A review pass (
bmad-dev-autore-invoked on adonespec) can finalize the spec's frontmatter tostatus: donewhile omitting the terminal## Auto Run Resultsection.devcontract.find_result_artifactrequires that non-fenced heading, so the generic adapter never harvests the spec as a result — every Stop reads asno-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 = trueand (review.trigger = "always", ortrigger = "recommended"— the default — with the dev pass having setfollowup_review_recommended: true). A run with review disabled, or a story whose dev pass recommends no follow-up, routes through_skip_review_and_commitand 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-artifactresultless-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-hardeningdev-1: transient — aresume-restartinterrupted 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-hardeningdev-3: deferred withspec status is 'in-review', expected 'done'— frontmatter wasin-review, notdone, 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-autoon an already-donespec (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_artifactis 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 terminalstatus: donewithout the marker"; in practice, the follow-up review leg.Failure mechanism
The livelock does not converge on its own:
decide_review_sessionmaps the eventualstalled/timeoutto RETRY, and each retry runs_reset_spec_for_review(strips any marker) then re-invokes the same skill, which reproduces the omission. Aftermax_review_cyclesthe 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
20260720-142340-c682, story3-10, review legreview-1.tasks/3-10-.../review-1/resultless-stops.jsonl: 6×{"verdict":"no-artifact"};heartbeat.jsonreachedstall_nudges_sent: 4(workflow_stall_nudges_cap = 3) — the session never went truly silent (spinner pane output re-armeddev_stall_grace_spergeneric.py:561), so it was riding toward the 3hsession_timeout_min, ~1h burned.status: 'done',followup_review_recommended: false, a full## Review Triage Log(two passes, patches applied, DW-130–136 filed) — but zero## Auto Run Resultheadings (grep -c= 0). Completed sibling specs 3-8 and 3-9 each carried exactly 1, confirming the intermittency.pidalive, heartbeats fresh) — the session, not the orchestrator, was stuck one line short of the contract.Root cause
The
bmad-dev-autoreview HALT does not append## Auto Run Resultunconditionally the way the dev HALT does. Intermittent — usually written, occasionally not.Repro / confirmation
Manually appending a
## Auto Run Resultsection (Status: done) to the worktree spec was harvested on the very next Stop → verify passed → story committed and merged (b3d87fa/ merge4b96427), and the run advanced to 3-11 with no other change. Confirmed the gate directly:The pre-append
Noneis evidenced by the 6no-artifactentries inresultless-stops.jsonl— those are the orchestrator's ownfind_result_artifactcalls returningNoneagainst the same spec.Suggested fix
Primary (upstream BMAD-METHOD skill): the review HALT must append
## Auto Run Resulton 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
statusbut no## Auto Run Resultheading 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 frontmatterstatus:(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_sis not a fix — RETRY re-runs the same skill and reproduces the omission, so it only reaches the silent DEFER faster.