Skip to content

Releases: bmad-code-org/bmad-loop

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 03 Jul 18:54
648b407

Changed

  • BREAKING: the project is renamed bmad-autobmad-loop. The distribution, console script,
    and CLI are now bmad-loop; the Python package is bmad_loop (was automator); the BMAD module
    code and marketplace plugin are bmad-loop (was bauto); per-project state moves from
    .automator/ to .bmad-loop/. The GitHub repo is now
    bmad-code-org/bmad-loop — old web and git URLs
    redirect. Clean break: no compatibility shims.
  • BREAKING: renamed public identifiers. Env vars BMAD_AUTO_*BMAD_LOOP_*; plugin
    entry-point group bmad_auto.pluginsbmad_loop.plugins; hook relays bmad_auto_hook.py /
    bmad_auto_probe_hook.pybmad_loop_hook.py / bmad_loop_probe_hook.py; skills
    /bmad-auto-{setup,sweep,resolve}/bmad-loop-{setup,sweep,resolve}; tmux session/window
    prefixes bmad-auto-*bmad-loop-*; worktree branches automator/<run-id>
    bmad-loop/<run-id>; TUI class BmadAutoAppBmadLoopApp. Custom plugins, CLI profiles, and
    policy files that reference any of these must be updated.

Added

  • New pre_commit_gate plugin workflow-injection stage. Gate workflows can bind to
    pre_commit_gate, which fires unconditionally just before every commit — on the review-converged,
    review-skipped, and review-budget-rescue paths alike — while the phase can still legally defer.
    TEA's trace/nfr/review gates rebind to it, fixing blocking gates that were previously inert
    whenever a dev session recommended no review follow-up (so on_pre_commit fail-opened on the
    missing artifacts).

Fixed

  • A workflow session that finishes its work but never writes a completion marker no longer
    livelocks the run.
    Each result-less Stop used to refill the stall-nudge budget, re-nudging a
    responsive-but-signal-less session until session_timeout_min. The engine now appends an explicit
    completion contract (absolute marker path + frontmatter shape) to every workflow-session prompt,
    and a new limits.workflow_stall_nudges_cap (default 3) caps the total nudges a workflow session
    may receive — degrading a still-missing marker to "stalled" in ~30 min instead of hours. Dev/review
    session nudging is unchanged.
  • On Windows, a live engine whose process identity can't be read now shows UNKNOWN instead of a
    false INTERRUPTED.
    psutil raises ERROR_ACCESS_DENIED for a process in another session or
    elevation, which the identity-aware liveness read had surfaced as dead — mislabeling running runs
    and weakening the resume/delete guards. Liveness is now tri-state (alive/dead/unknown) and
    biased away from false-dead; resume, resolve, delete, archive, and cleanup all surface and
    warn on unknown without ever letting an unverifiable pid block cleanup forever, and resolve
    gains --force to override a squatted-pid block.
  • A review session that appends to the deferred-work ledger no longer leaves a sweep bundle
    unclosed.
    The bundle ledger is reclosed after review (journaled distinctly as
    sweep-bundle-reclosed), and the review prompt now states the ledger is append-only for sessions.
  • Worktree git-exclude patterns now anchor correctly on native Windows. install.py normalizes
    backslashes in the per-worktree exclude paths so the ignore rules match (a no-op on POSIX).

Migration

  • Reinstall the tool under its new name — uv can't rename a package in place:
    uv tool uninstall bmad-auto, then
    uv tool install "bmad-loop[tui] @ git+https://github.com/bmad-code-org/bmad-loop.git".
  • Re-run /bmad-loop-setup (or bmad-loop init directly). init migrates a project in place:
    it strips the old .automator/ Stop hook from each CLI's settings, removes the bmad-auto-*
    skill dirs, and carries .automator/policy.toml over to .bmad-loop/policy.toml. Setup folds the
    old bauto config into bmad-loop and clears the leftover bauto config section, stale
    BMAD Automator Skills help rows, and the _bmad/bauto/ installer dir.
  • Legacy .automator/ is left in place (runs, archives, profiles, plugins) and can be deleted
    or hand-moved once the migration is confirmed; stale .automator/* gitignore lines are left
    untouched.

v0.7.12

Choose a tag to compare

@github-actions github-actions released this 02 Jul 18:12
06ec378

Added

  • The TUI dashboard now shows the cost-proportional weighted token total, with the raw total in a new
    column.
    The tokens column and the run-header summary discount cache-read tokens by the run's
    cache_read_weight — the same weighting the per-story budget enforces — so the headline number
    tracks spend rather than context re-reads; the previous unweighted total moves to a new raw column.

Fixed

  • The dashboard no longer crashes when a background poll lands as the screen is torn down or
    switched away.
    A poll worker delivers its refresh on the UI thread; if that arrived just as the
    app quit or another screen opened, the query for the run table raised NoMatches. The apply now
    drops stale refreshes for a screen that is no longer running (while still updating one merely
    backgrounded under a modal).
  • A failed attempt's work is preserved before an auto-rollback hard reset instead of being silently
    discarded.
    With scm.rollback_on_failure on (or on a resolved re-drive), a deferred or stopped
    attempt's commits above baseline are now parked under an attempt-preserve/<run_id>-<head8> branch,
    and its uncommitted working-tree diff — tracked edits and run-created untracked files alike — under
    refs/attempt-preserve-dirty/; both are recoverable by name and survive gc. A plain rollback that
    cannot create the ref refuses to reset and pauses for manual recovery rather than destroying work.
    The uncommitted snapshot is scoped to this run's own changes (never a pre-existing untracked file),
    commits under a synthetic identity so it works with no git user configured, and is keyed per retry
    so repeated rollbacks against the same baseline no longer overwrite each other's recovery ref.
  • Process liveness is now identity-aware, so a reused PID no longer reads as a live run. A recycled
    pid (common on Windows) used to register as a false "alive" — blocking resume of a dead run,
    stranding worktree reclaim, leaking sessions, and showing dead runs as RUNNING. The pid file now
    carries a process-identity token that resume, stop, and the TUI verify against; on win32 the engine
    also ignores console SIGINT/SIGBREAK during a run so a ConPTY Ctrl+C broadcast can't kill it.
  • A story from a resolved escalation that still can't finish now re-escalates instead of being
    silently deferred.
    When a human-resolved CRITICAL blocked escalation was re-driven and the
    re-drive couldn't reach status: done (e.g. the environment was still broken), the story used to
    exhaust its dev/review budget and plateau-defer — filing an unresolved blocker as deferred work and
    rolling back the implemented code. While resolved_redrive is latched, budget exhaustion now
    re-escalates (pauses for the human) instead of deferring, and the attempt's tree is preserved.
  • A resumed --epic N run stays scoped to its epic and no longer declares the epic "done" while
    stories remain.
    resume rebuilt the engine without the run's --epic/--story/--max-stories,
    so a scoped run silently widened to every epic; with strict file-order story selection, deferring or
    finishing a story in an epic placed out of numeric order in the sprint board (e.g. one appended last)
    bounced selection to an earlier-in-file epic and fired a spurious "epic N complete" boundary,
    stranding the epic's remaining stories. The selector and cap are now persisted and restored on
    resume, and story selection exhausts the current epic before advancing — so an epic boundary fires
    only when that epic has no actionable stories left. Document-order epic execution is unchanged.

v0.7.11

Choose a tag to compare

@github-actions github-actions released this 30 Jun 23:01
f79bd12

Fixed

  • A finalized story the review just won't stop recommending a follow-up for is now committed, not
    rolled back, when the review budget runs out.
    Exhausting limits.max_review_cycles previously
    always deferred + reverted — discarding completed, review-passing work (frontmatter status: done,
    verify green) whose only "failure" was a never-clearing followup_review_recommended. The
    orchestrator now commits that work and re-files the lingering recommendation as a fresh open
    deferred-work entry; a story that itself came from such an entry is committed without re-filing, so
    a second non-convergence reaches a human instead of looping across sweeps. Worktree-isolation runs
    were unaffected — a deferred unit already keeps its worktree.

v0.7.10

Choose a tag to compare

@github-actions github-actions released this 30 Jun 05:14
f32d922

Fixed

  • Completed work left at the transient in-review frontmatter is no longer falsely deferred and
    rolled back.
    A bmad-dev-auto session that dies in its step-04 Finalize tail can leave the spec
    frontmatter at the transient in-review marker while the ## Auto Run Result prose already says
    Status: done — the same stale-frontmatter gate bug as 0.7.8 with a different value. The 0.7.8
    reconcile skipped in-review to protect the legacy bmad-auto-dev review-handoff, but that fork
    is retired and in-review is now only ever a transient marker, so it is reconciled to done
    before the gates run. Every deterministic gate still runs afterward, and a followup_review_recommended: true
    spec still triggers the follow-up review pass. Closes a re-sweep loop that re-ran and discarded the
    same completed bundles (~47M tokens/cycle).

v0.7.9

Choose a tag to compare

@github-actions github-actions released this 30 Jun 04:19
bfe7562

Fixed

  • The multiplexer seam no longer leaks raw subprocess timeouts. Every contract method now raises
    MultiplexerError/TmuxError or returns its documented sentinel instead of letting a 30 s tmux
    hang escape as a raw subprocess.TimeoutExpired.
  • A transient tmux hang no longer crashes a run or mis-reads a working session as dead. The
    wait-loop tolerates an unknowable liveness probe — a persistent hang degrades to an honest
    timeout, never a false crashed.
  • An unexpected engine exception is now recorded instead of being lost to the parked control
    window.
    The orchestrator writes a run-crash journal line + a crash.txt traceback, sets a
    CRASHED run status, and tears down the orphaned agent session — rather than dying with a
    traceback printed only to the pruned control pane.

v0.7.8

Choose a tag to compare

@github-actions github-actions released this 30 Jun 00:49
ef7f77b

Fixed

  • Completed bmad-dev-auto work is no longer falsely deferred when the skill leaves the spec
    frontmatter status stale.
    The skill can finalize a run in its ## Auto Run Result prose
    (Status: done) yet leave the YAML frontmatter at the template default draft; since every gate
    reads frontmatter, the sprint/ledger sync no-op'd and the story or sweep bundle deferred — losing
    tested work on rollback. The orchestrator now reconciles the frontmatter to the success status
    from the terminal prose before the gates run (journaled spec-status-reconciled). It reconciles
    only a done outcome from a non-terminal status, and every deterministic gate (worktree diff,
    dw-ids, verify commands, ledger) still runs — so the bookkeeping is repaired without trusting prose
    to pass a gate.

v0.7.7

Choose a tag to compare

@github-actions github-actions released this 29 Jun 05:58
ec715d4

Fixed

  • Spec-status gates are now case- and whitespace-insensitive. A hand-edited spec whose
    frontmatter carried a stray-cased Done/In-Review silently failed the dev/review gate and the
    story never advanced; every spec-frontmatter status read now normalizes through a single
    verify.status_of helper. The well-formed lowercase path is unchanged. Also fixes the
    manual-rollback notice, which rendered an invalid git reset --hard the run's baseline commit
    when no baseline was recorded — it now shows a <baseline_commit> placeholder.
  • Project-relative path guards reject .. traversal and OS-foreign absolute paths. A CLI
    profile or plugin manifest could declare a config_path/skill_tree/seed_files/module path
    that climbed out of the project with ../ — or, on Windows, a POSIX-absolute /etc/... that
    Path.is_absolute() failed to flag — and slip past the "must be project-relative" check. The
    guards now reject both, on every platform.
  • Persisted relative paths serialize with forward slashes. A worktree run's spec_file and the
    resolve context's resolution_path were written with the host separator, so a state.json or
    context file produced on Windows read back with backslashes. Both now persist via as_posix() for
    a single cross-OS contract (a no-op on POSIX).
  • The TUI no longer shows a stale run after a same-size state rewrite. The dashboard's
    stat-gated cache keyed on (mtime_ns, size), so an atomic state.json rewrite of identical size
    within one coarse mtime tick (e.g. WSL2 drvfs) could be served stale. The engine rewrites
    atomically onto a fresh inode, so the cache signature now includes st_ino.
  • A dev session is no longer mis-stalled while it is actively working or legitimately waiting.
    Building on dev_stall_grace_s (0.7.5), the idle-grace window now measures genuine inactivity
    rather than time-since-last-Stop: any growth of the session's pane log (a long productive turn, a
    streaming subagent) re-arms it, so a session that has finished implementation and is mid-review is
    no longer killed and rolled back. And because bmad-auto cannot re-invoke a turn that ended to await
    a background process, the grace window no longer dead-ends in a stall — on real silence the
    orchestrator wakes the session with up to limits.dev_stall_nudges (new, default 2) nudges before
    giving up; a genuine Stop restores the budget, so a slow-but-cooperative session waits up to
    session_timeout_min while a truly unresponsive one still stalls. 0 restores stall-on-grace-expiry.

v0.7.6

Choose a tag to compare

@github-actions github-actions released this 29 Jun 00:18
15c7b80

Changed

  • The OS is now abstracted behind a registry of seams, so a non-tmux/native-Windows port is new
    files plus one registration line each — no core edits.
    The terminal multiplexer is selected
    through a registry (register_multiplexer, by sys.platform with a BMAD_AUTO_MUX_BACKEND
    override) rather than hardcoded in get_multiplexer(), and the tmux backend split into a reusable
    BaseTmuxBackend extension point — every tmux invocation funnels through one overridable _run()
    primitive — with TmuxMultiplexer as a thin POSIX leaf, so a tmux-family backend (an eventual
    "psmux") overrides only the spawn primitive and the few divergent methods.
  • Process-lifecycle primitives moved behind a ProcessHost seam. Politely-stop / force-kill /
    liveness / PID-reuse-identity now route through get_process_host() (registered like the
    multiplexer, with a BMAD_AUTO_PROCESS_HOST override); a WindowsProcessHost ships ready to
    register. Hook registration no longer hardcodes python3 — it takes the interpreter prefix from
    ProcessHost.hook_interpreter() (POSIX python3; Windows uv run --no-project python), and
    bmad-auto validate runs a platform preflight that reports the selected backend's readiness and
    names the process host. Behavior on Linux/macOS/WSL is unchanged.
  • The bundled Unity plugin's teardown delegates pid lifecycle to ProcessHost and its helper
    scripts run under the orchestrator's own interpreter.
    Plugin helper scripts are spawned via
    sys.executable (not a PATH-resolved python3), so a bundled script may import automator seams;
    unity_teardown.py now reaps leaked Editor/MCP processes through get_process_host() instead of
    re-implementing kill/liveness, gaining Windows behavior for free.

Added

  • A consolidated porting guide maps the four OS seams (terminal
    multiplexer, process lifecycle, hook interpreter, validate preflight), their registries and
    test-override env vars, and exactly what a native-Windows port costs end to end. The
    adapter-/plugin-authoring guides, ROADMAP, README, and FEATURES are updated to the post-registry
    world.

v0.7.5

Choose a tag to compare

@github-actions github-actions released this 28 Jun 19:17
6567efd

Added

  • Select and copy text from the TUI Log and Attention panes. Click-drag highlights and ctrl+c
    copies (those panes are now selectable RichLogs with a working get_selection); y copies the
    whole active pane in one keystroke. The other panes are interactive widgets — hold your terminal's
    bypass modifier (Shift on most Linux terminals, Option on iTerm) to use its native selection. Copy
    rides OSC 52, so under tmux it needs set -g set-clipboard on to reach the system clipboard.
  • bmad-auto diagnose emits a sanitized diagnostic dump of a run/sweep so a user can hand
    maintainers what's needed to debug a run without shipping any code, spec/story content, prompts,
    transcripts, file paths, or PII. It derives the diagnostic shape — phase/token/session
    histograms, escalation counts, adapter/model, env, run-dir file sizes — and routes every
    content-bearing value through the audited sanitize chokepoint: identifiers (story keys,
    branches, SHAs) are pseudonymized to stable per-dump aliases so events still correlate, free text
    collapses to presence booleans, and the rendered output is re-scanned by a fail-closed leak
    self-check before writing. Defaults to the latest run; --all, --out, --json, and a
    local-only --legend are supported.

Changed

  • The sanitize chokepoint now redacts credential-shaped strings. Identifier-shaped secrets
    (ghp_/sk-/AKIA/xoxb-/JWTs and long high-entropy blobs) previously passed the slug gate
    verbatim; they are now <redacted:secret>, closing the same hole for probe-adapter.

Fixed

  • A dev session that ends its turn to await a long-running background process is no longer
    mis-stalled.
    A bmad-dev-auto session that yields to wait on a slow job (a Unity PlayMode run, a
    long test) and expects to be re-invoked on completion fired a result-less Stop — and since the
    generic dev adapter runs zero nudges, that was ruled stalled after only the 15s result grace,
    driving a retry that (with scm.rollback_on_failure off) paused the whole sweep for a manual
    reset. A new limits.dev_stall_grace_s (default 600s) opens an idle-grace window on a result-less
    dev Stop and re-arms it on each re-invocation, so only a genuinely idle gap with no terminal spec —
    or the session timeout — counts as a stall. Non-dev adapters keep zero grace and are unchanged.

v0.7.4

Choose a tag to compare

@github-actions github-actions released this 26 Jun 19:59
df5418e

Fixed

  • Deferred-work sweep no longer defers every bundle it just finished. After the migration to
    the generic upstream bmad-dev-auto primitive, bundle dev sessions completed the work but were
    rejected by verify_dev_bundle with result.json dw_ids [] do not match the bundle's […],
    retried to budget, deferred, and rolled the work back — so a sweep could never close a bundled
    entry. The retired dev fork used to echo the dw ids; the generic skill doesn't. The orchestrator
    already owns the bundle→dw-id binding, so the cross-check now passes when the session claims no
    ids, and the run exports BMAD_AUTO_DW_IDS so the synthesized result still carries them and the
    check stays live.