Skip to content

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 15:44
6bbe469

Added

  • Implement [gates] retrospective = "auto": at each epic boundary, and at run end for
    the last epic once every story is done, run one headless
    /bmad-retrospective -H <epic> session on the new retro adapter role
    ([adapter.retro], inheriting from [adapter]), before the auto-sweep. Success needs a
    completed session, result.json done, the board at epic-N-retrospective: done and
    an epic-N-retro-*.md doc; only the doc and board are committed, and leftover dirt
    pauses. The verdict rides retro-auto-finished and the done notice (a rejected epic
    adds an ATTENTION line). validate and status --json name the retro role, and
    config_digest covers it (DW-389, DW-487, DW-488).
  • Ingest sprint-status retro action_items into the deferred-work ledger: a fresh
    bmad-loop sweep files each not-done item as a DW-<n> entry
    (origin: retro action item <id>) before triage, never re-filing a twin (DW-388).
  • Document the _bmad/custom/bmad-retrospective.toml override that points the retro at
    run-dir journals and session logs (setup guide) (DW-387).
  • Add bmad-loop resolve <run> --adopt-branch: finish an escalated worktree story from
    its kept branch (spec set done, board mirrored, branch squashed and merged) with no
    new session. Review, [verify] and pre_commit_gate are not re-run. A resume that
    drains the queue past an ESCALATED story now pauses instead of recording the run
    finished (DW-386).
  • Add bmad-loop resume <run> --accept-baseline: restarted in-place stories adopt the
    current HEAD as their baseline, so commits above the old one are kept rather than
    parked and reset (baseline-accepted). Restart resets that do park work now notify
    (ATTENTION) naming the attempt-preserve/* / attempt-preserve-dirty/* refs with
    recovery hints, and a failed preserve leg journals attempt-preserve-fallthrough
    (DW-371, DW-480, DW-481, DW-482).
  • Add [operator] on_review_demotion = "escalate" | "park" (default escalate): under
    park, a review that finalizes a done story at awaiting-operator moves the board
    back, commits and parks (DW-383).
  • Seed antigravity workspace trust per unit worktree via a profile's [workspace_trust]
    table, so isolation = "worktree" works with antigravity (journals
    worktree-trust-seeded / -unseeded); add probe-adapter --workspace PATH for a
    read-only trust verdict (DW-390).
  • Add parked-session detection: a canonical Notification event with
    PermissionPrompt/IdlePrompt/QuotaPrompt kinds, a
    [hooks.notification_types] profile table, and parked_prompt_patterns matched
    against the pane at every stall-grace expiry. A parked session ends stalled +
    parked and pauses instead of being nudged. The claude profile maps its permission,
    idle, quota and MCP elicitation dialogs; re-run bmad-loop init to register the
    relay (DW-348, DW-350, DW-433, DW-434).
  • Add a post_review_verify plugin hook stage publishing every review verify gate's
    [verify] commands results (DW-357).
  • Expose ctx.delivery_id on post_migrate and ctx.rollback_outcome on
    post_rollback (declarative: BMAD_LOOP_DELIVERY_ID, BMAD_LOOP_ROLLBACK_OUTCOME);
    post_rollback now fires once for every pre_rollback, also on a paused or failed
    rollback (DW-317, DW-322).
  • Add a free-form effort key to [adapter] and [adapter.<stage>]; opencode-http
    sends it as the per-prompt variant (shown on the run --dry-run launch line), and
    validate warns (policy.effort-unsupported) for tmux stages (#643).
  • Journal a session's idle stretches as session-idle / session-active (threshold
    limits.dev_stall_grace_s), with transcript_idle_s on heartbeat.json and the TUI
    agent line (#680).
  • Add opt-in bmad-loop validate --render-probe: runs each renderer stub's render
    command in a throwaway copy and fails on a render HALT: (DW-381).
  • Add validate warnings: policy.bypass-dropped when adapter.extra_args drops the
    profile's bypass_args (also warned and journaled at real launches);
    policy.isolation-shared-artifact-dir for artifact dirs shared with the main checkout
    under worktree isolation; deferred.ledger-untracked and
    deferred.ledger-ignored-isolated for a ledger that bmad-loop's commits would sweep in
    or lose; hooks.relay-path-unsafe for a relay path with shell metacharacters on
    Windows; policy.extra-args-unservable for opencode-http extra_args repeating an
    adapter-owned serve flag (DW-349, DW-410, DW-485, DW-361, DW-375, DW-346, DW-483).
  • Show auto-sweep outcomes (refused with reason, or ran) in the TUI run header (DW-366).

Changed

  • Hooks now register through the installed bmad-loop relay <Event> command.
    Re-run bmad-loop init after upgrading.
    Codex treats the new commands as untrusted
    and silently skips them until you accept them at the next launch. validate warns
    when a hook still points to another installation, and (hooks.relay-stale) while a
    Windows backslash registration remains (#773; Windows paths are now forward-slash);
    init replaces a relay installed for the other OS.
  • Support isolation = "worktree" when the project sits inside a repo_root: override
    (a monorepo's <repo>/app): unit worktrees provision at the project's offset. The
    policy.isolation-repo-root refusal now fires only when the project is outside
    repo_root, and validate/the TUI launch guard now require a clean repo_root, as
    run and sweep do (DW-379).
  • For the claude profile, an idle dev/review/workflow session now pauses as parked at
    stall-grace expiry instead of receiving the stall wake nudge; opt out by dropping
    idle_prompt from [hooks.notification_types] in a project overlay (DW-348).
  • Commit hooks in the ledger-publication candidate worktree now see only the target
    file: a hook that reads other tracked files may fail the publication (DW-401).
  • Name an earlier attempt's parked work in the retry dev prompt once Git confirms the
    ref still resolves on this task's baseline (#777).
  • Name every mux session's window 0 shell on tmux and psmux (DW-351).
  • Document the live-session removal guard's ceiling: delete, archive and clean can
    still remove a run whose live session a listing omits (#732).

Fixed

  • Pause a dev session with no confirmed work (a permission dialog, a login, a
    dead-on-arrival window) instead of retrying into the same wall; dev-decision and
    session-end carry produced_work (#727).
  • Stop charging an attempt for a session that times out having consumed zero tokens (a
    provider quota stall): it is now an environment fault that pauses, and usage with no
    signal is journaled as untracked (null), not 0 (DW-364).
  • Count Copilot shutdown metrics, increased Codex output-token totals and new or changed
    Gemini model messages as work when a dev session exits before the next transcript
    heartbeat (#822).
  • Honor a hard bmad-loop stop inside verify commands and declarative plugin hooks, and
    kill the whole process tree (including descendants that outlive their shell) on stop or
    timeout; on Windows the post-kill drain no longer blocks on a held pipe (DW-353,
    DW-477, DW-478).
  • Escalate an isolated unit before a Codex session starts in a worktree Codex does not
    trust, instead of starting a session whose Stop hook is silently skipped; trust the
    worktree, then resolve --no-interactive and resume (DW-340, DW-341).
  • Report stale or unverifiable Codex hook trust in validate and probe-adapter before
    a live probe launches (#461).
  • Tighten hook-registration checks: validate/probe-adapter report hooks registered
    only when every Stop-mapped event runs an executed relay naming Stop, and refuse a
    managed relay reporting Stop (or a wrongly mapped event) under another native event
    or any antigravity hook group; init strips or warns about them (DW-342, DW-343,
    DW-344, DW-345, DW-391, DW-392, DW-409, DW-490).
  • Preflight the complete bundled bmad-loop-sweep skill before sweep, a sweep resume
    or an auto-sweep, naming bmad-loop init --force-skills, instead of every triage
    session stalling at Unknown command; validate reports skills.sweep* (DW-367).
  • Re-merge on resume a worktree story saved DONE before its merge started, instead of
    letting GC discard the unit branch (DW-385).
  • Stop worktree teardown and the run-end GC from silently discarding a story's edit to a
    skip-worktree-pinned hook config: success paths pause with the worktree kept
    (pinned-config-edit-refused), and DEFERRED or merge-escalated units carry the edit in
    changes.patch. A GC pause now leaves the run resumable (DW-368, DW-479, DW-501,
    DW-502).
  • Protect the tracked ledger and board at the merge pre-flight only when the task writes
    them, and park an operator's uncommitted edit to a path the branch changes under
    refs/merge-preflight-preserve/<sha> before restoring it (DW-354, DW-356).
  • Prove ledger ownership on every harvested-deferral carry retry, pausing
    (harvest-carry-foreign-dirt) rather than committing an operator's ledger edit
    (DW-355, DW-413).
  • Seed a leaf-symlinked ledger or board at its configured path, ending the #426 defer
    loop, and name a link to outside the project in worktree-seed-dropped (DW-377,
    DW-432).
  • Keep deferred-work ledger ids and dedupe keys stable: mint ids from ### DW-<n>
    headings only, refuse a migration that edits an entry's origin:/source_spec:,
    ignore fenced examples when checking for a field, and restore severity when reopening
    an old archive stub (DW-334, DW-363, DW-384, DW-394, DW-408).
  • Harden sweep-migration recovery: resume a migration interrupted mid-retry, mid-triage
    or after escalating from committing by restoring its durable snapshot (or replaying
    its idempotent commit tail) instead of escalating or triaging over unaccepted text;
    replay sweep-migrated / post_migrate after a host death past DONE; re-raise a
    ledger publish whose git status fails (DW-313, DW-314, DW-315, DW-317, DW-336,
    DW-405, DW-407, DW-426, DW-436, DW-439).
  • Never rewind a sweep migration over a HEAD that moved past the task baseline or over
    unrelated dirt: recovery resets pause instead, dirt is parked under
    refs/attempt-preserve-dirty/*, and an escalated restart that finds a newer ledger
    re-pauses (sweep-migration-restore-diverged) (DW-427, DW-428, DW-429, DW-430,
    DW-435).
  • Pause a sweep migration before dispatch when its tracked ledger differs from the
    committed one (migrate-ledger-dirty: commit it, then resume), and finish one with
    nothing to convert as DONE with no session (DW-437, DW-440).
  • Make the exact-path ledger publication robust: hold index.lock across the target
    compare and reset, read past git replace refs, refuse a live exec-bit mismatch
    (chmod the ledger to match), bind attempt-owned spec normalization to one inode, and
    prune or name leftover candidate worktrees on failure (DW-319, DW-323, DW-324,
    DW-325, DW-326, DW-327, DW-328, DW-329, DW-330, DW-331, DW-398, DW-399, DW-400,
    DW-402, DW-403, DW-404).
  • Skip the candidate cleanup's git worktree remove --force / prune when
    <git-common-dir>/worktrees/ holds a symlinked or junction entry, which git would
    empty the target of; remove the link, then prune (DW-494).
  • Report undelivered nudges as failed (nudge-send-failed) instead of sent, in the
    generic and opencode-http adapters; heartbeat stall_nudges_sent counts delivered
    nudges only (DW-449, DW-503).
  • Stop recording a failed session probe as a vanished session: only a window listing
    that proves the session gone writes session-vanished; otherwise
    session-probe-failed (DW-459).
  • Distinguish confirmed missing tmux-family sessions from failed window listings (#525).
  • Journal observation faults instead of folding them into a healthy-looking empty
    answer. Generic adapter: liveness-probe-failed, parked-probe-failed,
    log-evidence-failed, result-json-refused, usage-sample-failed,
    transcript-scan-failed, spec-readback-failed, env-fault-scan-failed,
    spec-reconcile-skipped-status, and a liveness_unknown flag on over-budget and kill
    verdicts. opencode-http: usage-sample-failed, usage-capture-failed,
    sse-stream-failed, sse-frame-dropped. Verdicts are unchanged (DW-382, DW-447,
    DW-448, DW-450, DW-451, DW-452, DW-453, DW-454, DW-455, DW-456, DW-457, DW-460,
    DW-461, DW-462).
  • Warn instead of reading as "nothing there" when a mux session listing, pane or option
    read, or backend probe fails (mux and validate report mux.backend-probe), and
    when the removal guard cannot list sessions (DW-458, DW-463, DW-464, DW-466).
  • Report an unreadable runs dir, run dir, legacy registry or orphan sweep instead of "no
    runs" / "nothing orphaned": list, status, clean, cleanup and the TUI say so,
    with additive --json keys listing_fault, sessions.legacy_unverified,
    worktree_faults and state_dir_fault. An unreadable engine.pid reads as liveness
    unknown, and resume warns about an unreadable config baseline (DW-465, DW-467,
    DW-468, DW-469, DW-470, DW-471).
  • Surface TUI read faults: a stale run header (⚠ state stale),
    decisions unreadable, agent unreadable, and notes for unreadable ATTENTION or
    journal files (DW-472, DW-473, DW-474, DW-475).
  • Shape every notice through gates.notify/gates.notice_line: control characters
    become visible escapes, multi-line faults and pause reasons fold onto one line, a
    4000-character cap applies, and lone surrogates never raise. state.json, --json
    and the journal keep the raw text (DW-13, DW-332, DW-417, DW-419, DW-491, DW-492).
  • Resolve artifact paths from BMAD's central _bmad/config.toml, falling back to
    _bmad/bmm/config.yaml only for missing keys, and refuse malformed values; sweep
    triage reads the orchestrator's resolved ledger (#154, #769).
  • Read untracked paths verbatim so rollback snapshots, cleanup and failed-unit diffs
    handle non-ASCII and space-edged filenames (#783).
  • Run a declarative post_story hook from the repo root after worktree teardown, and
    count plugin-hook-error in diagnose (#779).
  • Refuse init when .bmad-loop, its policy.toml or .gitignore resolves outside
    the project (#771).
  • Parse plugin manifests in validate (plugins.manifests) without importing plugin
    code (#765).
  • Scope the sweep skill's open_ids to the session's triage universe, so --only /
    --min-severity no longer burns a retry (#824).
  • Show a sweep run's effective max_bundles, repeat, max_cycles and selector in
    text status (#815).
  • Diagnose non-completed sweep triage and migration sessions: which of result.json
    and the hook events arrived (#752).
  • Prove ownership of an untagged control window before the TUI attaches to or kills it,
    so two projects using the same --run-id no longer target each other's
    orchestrator (#531).
  • Preserve inherited model, effort and extra_args when a stage names an alias of
    the base client; key the run --dry-run preview on adapter kind.
  • Honour launch_args before serve for opencode-http wrapper profiles
    (npx -y opencode-ai), with adapter.launch-args-unservable in validate; report a
    deeply nested pattern entry as a ProfileError (DW-373, DW-374).
  • Fit TUI dialogs to narrow and short terminals (down to 39×9): long titles and paths
    truncate with …, and button rows wrap or scroll instead of going off-screen (DW-358,
    DW-359, DW-414, DW-415).
  • Decode unity plugin child output as UTF-8 with replacement, so a cp932 Windows
    codepage no longer raises UnicodeDecodeError (DW-365).
  • On Windows, stop a DW bundle pausing at baseline capture when the artifacts root
    reports no inode (artifact-observation-unpinned) (DW-444).
  • Alias commit and snapshot refs in diagnose dumps (DW-318, DW-337, DW-435).
  • Correct docs: why resume tolerates a dirty tree while run/sweep refuse one, and
    gates.on_escalation is reserved (only pause, no effect) (DW-360, DW-376).

Security

  • Pin the run dir, each worktree mount, and every writer below them to the identity
    recorded when the orchestrator minted them (run_dir_identity,
    worktree_identity in state.json): verify-stream and integration-snapshot writes,
    spec repair/reset/reconcile/adoption, park records and their rollback, re-arm/replan
    writes and ledger restores now refuse a root, mount or .bmad-loop/ subdirectory
    swapped for a link instead of writing outside the repository. Older state records its
    roots at the first resume (root-identity-recorded). Parent directories above the
    worktree, and operator-chosen roots, may still be links (DW-338, DW-423, DW-445,
    DW-446, DW-486, DW-497, DW-498, DW-500).
  • Harden the exact-path candidate worktree: check it out without running smudge filters
    or fsmonitor, and pin its root, .git gitfile, admin dir and commondir so a swap
    before a later git call is refused instead of steering it into another repository;
    on Windows, refuse junctions when creating its parents (DW-401, DW-420, DW-425,
    DW-442, DW-493, DW-495).
  • Create missing artifact directories through no-follow opens pinned to the accepted
    root, and on Windows refuse a junction at or below the artifacts directory (DW-421,
    DW-422).
  • Pass -- before notify-send positionals and shape the Unity dialog probe's line, so
    an untrusted window title cannot forge ATTENTION lines (DW-418).