Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 111 additions & 29 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ bmad-loop tui # …or drive everything from the dashboard
| `bmad-loop run` | Drive the dev → review → verify → commit loop. `--epic N`, `--story KEY`, `--max-stories N`, `--dry-run`. `--spec <folder>` forces **stories mode** (folder+id dispatch off `<folder>/stories.yaml`), overriding `[stories].source`; `--story` then filters by story id. |
| `bmad-loop sweep` | Triage + execute open `deferred-work.md` entries. `--no-prompt`, `--decisions-only`, `--max-bundles N`, `--repeat`, `--max-cycles N`, `--dry-run`. `--archive [--before DATE]` instead moves closed ledger entries to `deferred-work-archive.md`, leaving id-preserving stubs. |
| `bmad-loop resume <run-id>` | Continue a run paused at a gate, escalation, or interruption. The resume command rendezvouses with delete/archive on the run lifecycle lock; if cleanup removed the run while resume waited, resume reports it missing without recreating files or launching an engine. |
| `bmad-loop resolve <run-id>` | Resolve a CRITICAL escalation: open an interactive resolve agent to fix the frozen spec, then re-arm the story and resume. On an _intent gap_ the re-drive can resume review on the attempted change instead of re-implementing it. `--story KEY`, `--no-interactive`, `--restore-patch <path>` (intent-gap patch-restore), `--resume` / `--no-resume`, `--force` (proceed when engine liveness is unverifiable; a provably-live engine still blocks). |
| `bmad-loop resolve <run-id>` | Resolve a CRITICAL escalation: open an interactive resolve agent to fix the frozen spec, then re-arm the story and resume — the resume is held when the correction provably has not reached the tree the re-drive reads (see [Resolving a CRITICAL escalation](#how-a-story-flows)). On an _intent gap_ the re-drive can resume review on the attempted change instead of re-implementing it. `--story KEY`, `--no-interactive`, `--restore-patch <path>` (intent-gap patch-restore), `--resume` / `--no-resume`, `--force` (proceed when engine liveness is unverifiable; a provably-live engine still blocks). |
| `bmad-loop decisions` | Answer deferred-work decisions earlier sweeps left unanswered (skipped by `--no-prompt`, or an abandoned interactive sweep). Recorded so the next sweep acts on them without re-asking. `--list` shows them without answering; `--json` emits them as a stable machine-readable document — id, question, context, recommendation, and every option's key/label/effect/intent/resolution/bundle-name with a derived `recommended` flag. It implies the listing and never prompts, so a script can select an option by policy instead of scraping the text. |
| `bmad-loop confirm <story-key>` | Complete a story parked at `awaiting-operator` once you have carried out the external actions it owes (buy the domain, publish the DNS record). Acknowledges each action in turn, writes the spec's `## Operator Confirmation` audit section, advances spec and board to `done`, and commits the pair — nothing is re-driven. `--list` shows every parked story and what it owes; `--yes` skips the prompts; `--reverify` re-runs the project's `[verify]` commands first and blocks the confirmation if they fail; `--json` emits the parked set as a stable machine-readable document. Every write is checked and the spec is read back from disk, so a story is never declared done over a write that did not land; a confirmation interrupted before its board write is **finished** by re-running the command, with no second prompt and no second audit section. The index it reads is machine-local, so a park is confirmed on the machine that ran it. |
| `bmad-loop list` (`ls`) | List every run/sweep with its short ref, type, and status — the handle you pass to the commands below. `--json` emits a stable machine-readable document instead — one entry per run, oldest first (short ref, run id, type, started-at, liveness-aware status, paused stage); an empty runs dir yields a valid empty document. |
Expand Down Expand Up @@ -247,6 +247,8 @@ sprint-status.yaml: 1-2-account-mgmt: ready-for-dev

**Resolving a CRITICAL escalation:** the escalated story is parked in a terminal `escalated` phase — `resume` skips it. To un-stick it, run `bmad-loop resolve <run-id>` (or press `R` in the TUI). That opens an interactive **resolve agent** seeded with the escalation and the frozen spec; you converse with it to disambiguate the spec, it records the resolution, and on your confirmation the orchestrator re-arms the story (`escalated → pending`, spec status reset to `ready-for-dev`) and resumes — a clean rebuild against the corrected spec, then on through the rest of the sprint. Already fixed the spec yourself? `bmad-loop resolve <run-id> --no-interactive` skips straight to re-arm + resume.

**When the resume is held.** Under `[scm] isolation = "worktree"` the re-drive mounts a fresh worktree cut from the committed target branch, so a correction living only in your working tree never reaches it. Where the re-arm can _prove_ that — the committed spec does not carry the status the re-drive routes on, or, for a pre-planning sentinel, the ref it mounts from does not hold this checkout's `SPEC.md` / `stories.yaml` — the re-arm still stands but the resume stops there, `--resume` notwithstanding, and both surfaces name the branch to commit on. Commit the correction, then `bmad-loop resume <run-id>`. Every other re-arm warning stays advisory and resumes in the one gesture as before.

**Intent-gap patch-restore.** When review halted on an **intent gap** — the implementation was sound but read the spec differently than intended — `bmad-build-auto` saves the attempted change as a patch before reverting ([BMAD-METHOD#2564](https://github.com/bmad-code-org/BMAD-METHOD/issues/2564)). If the attempted reading was in fact correct, `resolve` re-arms the spec to `in-review` and re-applies that patch onto baseline after every reset, so the re-driven session resumes **review** on the restored diff instead of re-implementing from scratch. The interactive agent supplies the patch automatically via `resolution.json`; on the hand-driven path pass `bmad-loop resolve <run-id> --no-interactive --restore-patch <path>`. A patch that fails to apply escalates rather than running on a half-restored tree, and deferred-work `sweep` bundles get the same recovery.

## Deferred-work sweeps
Expand Down Expand Up @@ -525,7 +527,7 @@ Merge-back is always **serialized** — `max_parallel` is a validated knob clamp
<img src="docs/images/settings-scm.png" alt="The settings editor with the [scm] section expanded: isolation, branch_per, merge_strategy, the seed-adapter-configs switch, and the extra-worktree-seed-files field." width="880">
</p>

For a monorepo or any layout where the git root differs from the project dir, set an optional `repo_root` key in `_bmad/bmm/config.yaml` — it decouples where git/code work happens from where run state lives (defaults to the project dir). Your `[verify].commands` run there too — the code's root, not the BMAD project dir — while the orchestrator's own artifact reads stay project-rooted. It is **not compatible with `isolation = "worktree"`**: provisioning seeds a worktree from `repo_root` while the preflight probes `project`, so `validate` reports the pair and `run`/`sweep`/`resume` refuse to start. Use one or the other — plumbing both through provisioning is tracked as #443.
For a monorepo or any layout where the git root differs from the project dir, set an optional `repo_root` key in `_bmad/bmm/config.yaml` — it decouples where git/code work happens from where run state lives (defaults to the project dir). Your `[verify].commands` run there too — the code's root, not the BMAD project dir — while the orchestrator's own artifact reads stay project-rooted. It is **not compatible with `isolation = "worktree"`**: provisioning seeds a worktree from `repo_root` while the preflight probes `project`, so `validate` reports the pair and `run`, `sweep`, `resume` and `resolve` refuse to start — `resolve` before it opens the interactive agent, since its re-arm advances the attempt baseline against that same root. Use one or the other — plumbing both through provisioning is tracked as #443.

### Plugins

Expand Down
28 changes: 23 additions & 5 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,19 @@ Result` section. Every other spec keeps warn-and-continue, and the record says w
correction is already committed says nothing, and neither does one whose spec sits in an artifact
directory configured outside the project: those are shared across checkouts rather than rebased
onto each worktree, so the flip lands on the one file every re-drive reads. That record also HOLDS
the resume: both surfaces re-arm and resume in one gesture, which made its own "commit the
corrected spec before resuming" advice unactionable the moment it printed and then burned the
escalation on a session that halts blocked. They now stop after the re-arm — the story stays armed,
`bmad-loop resume <run-id>` picks it up once the fix is committed, and `--resume` does not override
it, since the record is written on proof rather than suspicion. The advisory warnings do not hold.
the resume: both surfaces re-arm and resume in one gesture, which made its own "before resuming"
advice unactionable the moment it printed and then burned the escalation on a session that halts
blocked. They now stop after the re-arm — the story stays armed, `bmad-loop resume <run-id>` picks
it up once the correction lands, and `--resume` does not override it, since the record is written
on proof rather than suspicion. Each surface names the HELD record's own remedy rather than one
hardcoded literal, because the holding records do not share one. Each remedy also names the
`status:` the re-drive routes on, so a correction that lands byte-correct but still terminal cannot
burn the escalation a second time — the sentinel remedy below renders that clause on the same
contract, and empty, since its leg has no spec status at all. This one asks you to commit, while
its in-place arm — isolation edited to `none` while the story was escalated, so the re-arm's writes
went into the escalated attempt's worktree and the re-drive now reads the main checkout — asks you
to correct the spec in the MAIN checkout, where committing is not the remedy and naming a branch
would be actively wrong. The advisory warnings do not hold.
A pre-planning **sentinel** gets the same treatment on its own artifacts. It is cleared by
deletion rather than a status flip, so there is no spec write to measure — but the correction that
stops it recurring is upstream (`SPEC.md` / `stories.yaml`, where the resolve skill sends the agent
Expand All @@ -127,6 +135,16 @@ Result` section. Every other spec keeps warn-and-continue, and the record says w
the re-drive mounts from does not already hold this checkout's copy of those two files, so a
correction already committed there resumes in one gesture, and an in-place re-drive never records
at all — it reads the main checkout, which is where the resolve session runs.
A **failed status flip** holds too, on exactly one of its three arms, which is why that one is
keyed on the record's flags rather than on its kind. `rearm-spec-flip-skipped` is journalled with
`refused = spec_path.is_file() and write_reaches_the_redrive`, so the arm that reaches the re-drive
without being refused proves the recorded spec path is not a readable file here — the re-drive
reads that same path and finds no spec there to route on, futile on the same proof as the two
above, and it reaches this arm from BOTH re-drive modes. Its remedy is to restore the recorded
path carrying the status the re-drive routes on, never to commit: the arm's own condition means
there is no corrected spec at that path to commit, and the path can be a shared artifact directory
outside the project that is not a repository at all. The refused arm raises rather than resuming, so there is no gesture left to hold, and the
remaining arm carries no imperative of its own — `rearm-spec-write-unreachable` holds that leg.
The whole re-arm is one **transaction**, and what it covers is stated narrowly: the SPEC's
BYTES, from the first spec write to `save_state`. That save is the commit point — until it
returns the run still calls the story escalated, so any fault escaping the window in between
Expand Down
Loading
Loading