Skip to content

Safety & HALT

This page documents every way the loop can stop. The principle is: nothing silently exits. Every exit goes through the HALT protocol with a typed status and, where applicable, a blocking condition.

The HALT protocol

When the skill ends a turn (whether by completing or by failing), it:

  1. Appends a loop-status entry to {loop_status_file}.
  2. Runs python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete to check for a team-defined terminal hook.
  3. If the resolved on_complete is non-empty, follows it as the final instruction before exiting.
  4. Stops the workflow.

The only sanctioned way to end a turn.

Terminal statuses

StatusMeaningBlocking condition
doneAll stories reached merged.n/a
blockedAn invariant failed. The skill pauses.always set; see table below
halted_dry_runworkflow.dry_run = true. Plan displayed, no work performed.n/a

Blocking conditions

ConditionWhen
no subagentsThe runtime cannot dispatch synchronous subagents. The skill cannot work without them.
no valid story keys foundInvocation prompt contained no N-N or epic-N tokens.
no ready-for-dev stories in input listAll stories were filtered out due to status (none were ready-for-dev or in-progress).
story dev failed: {key} — {details}The dev subagent returned an error or did not complete.
CI failures persisted after {n} retries for story {key} (PR #{n})ci_attempts >= ci_max_retries.
CI timeout for story {key} (PR #{n})Polling exceeded ci_timeout_minutes.
merge failed for story {key} (PR #{n})gh pr merge exited non-zero. Conflicts, branch protection, or auth.
no story keys provided(Rare) variant of no valid story keys found when prompt was empty.

Edge case handling matrix

This is the canonical list of how the loop responds to common issues. Reproduced verbatim from the origin PR.

SituationResponse
Story not in ready-for-dev statusSkipped with warning. Loop continues with the next story.
DEV subagent failsHALT (no skip). Operator must inspect and re-run.
Review finds blocking issuesFix subagent dispatched, then re-reviewed. If still failing, HALT.
CI fails persistentlyHALT after ci_max_retries.
Merge conflictHALT with conflict details for manual resolution.
Branch already exists locallyForce-deleted and recreated.
CI timeout (> ci_timeout_minutes)HALT with timeout condition.
gh not authenticatedSubagent precondition; will surface as a dev-subagent failure.
Working tree dirtySubagent precondition; will surface as a dev-subagent failure.

Resume semantics

Re-running the skill after a HALT is the standard recovery flow:

  1. The skill reads {loop_status_file}.
  2. If current_index is present, it resumes from that index.
  3. The first story that is not merged is the resume target.
  4. Already-merged stories are not re-processed.

To re-run from scratch, delete {loop_status_file} (or set its current_index: 0 and reset all story statuses to pending).

Dry run as a safety net

workflow.dry_run = true is the recommended way to preview any unfamiliar input:

bash
# In _bmad/custom/bmad-dev-loop.toml
[workflow]
dry_run = true

Then invoke normally:

/bmad-dev-loop epic-4

The skill will:

  1. Parse and validate.
  2. Display the plan.
  3. Write loop-status.yaml with all stories pending and a # DRY RUN marker.
  4. HALT with status halted_dry_run.

No subagents. No git operations. No PRs.

Things the skill does NOT do

It will never:

  • Force-push to main.
  • Skip stories silently (any skip is logged with a warning).
  • Continue after a HALT (status is final; you re-invoke to resume).
  • Run subagents in the background.
  • Auto-merge if CI is not green.
  • Open PRs against the base branch without an explicit --base flag (always defaults to main).

If you need any of these behaviors, modify the skill — don't expect them.

Released under the MIT License.