[BUG] bmad-build resume and parallel runs lack attributable per-run state

Author: jpiquotCreated Sep 8, 2026Updated Sep 8, 2026

Description

bmad-build has no efficient, attributable resume path for an existing in-progress story. Resuming by story identifier can:

  1. recompile epic context before resolving the existing spec;
  2. launch an implementation subagent even when every implementation task is already checked;
  3. preserve the story's original baseline_commit indefinitely; and
  4. make the parent agent generate and read the complete baseline-to-worktree diff.

On a long-lived or dirty brownfield branch, unrelated planning changes and repository history can therefore dominate the cost of resuming a story. The workflow spends most of its time rebuilding context, rediscovering already-completed work, and processing an unrelated diff rather than performing the remaining story work.

The same design also prevents safe parallel story builds in one checkout. Concurrent sessions share the worktree, generated context files, sprint-status file, temporary review assumptions, and eventual commit surface without a run identity or ownership boundary. They can therefore review, stage, or commit one another's changes even when the stories are logically independent.

This is related to #1188 and #2760, but distinct: those issues cover repeated validation sessions and duplicate review layers. This report concerns the cost and attribution of a single bmad-build resume run and the missing isolation contract for concurrent story runs.

Steps to reproduce

  1. Create a story spec with bmad-build, leaving it at status: in-progress after its implementation tasks have been checked.
  2. Continue normal repository development so that HEAD advances beyond the spec's original baseline_commit.
  3. Modify any unrelated file under the configured planning-artifacts directory so it is newer than epic-N-context.md.
  4. Optionally begin with unrelated dirty worktree changes, then explicitly allow the workflow to continue.
  5. Resume with /bmad-build N.M rather than passing the exact spec path.
  6. Observe that the workflow may:
    • invalidate and synchronously regenerate the epic context;
    • dispatch implementation again despite completed task checkboxes;
    • generate a diff from the preserved original baseline;
    • read that complete diff into the parent context before verification.
  7. Start a second bmad-build for a different story in the same checkout.
  8. Observe that neither run has a durable per-run changed-path set, ownership claim, review artifact namespace, or commit boundary that excludes the other run's concurrent changes.

Expected behavior

  • A story identifier should resolve an existing active spec before expensive context compilation.
  • A resumed story whose implementation tasks are already complete should take a bounded conformance/verification path and dispatch implementation only when drift or incomplete work is demonstrated.
  • Review should remain attributable to the story or current run rather than including every later commit and unrelated worktree change since the original baseline.
  • Attribution must work in the current checkout and must not require creating a Git worktree.
  • Independent stories with disjoint change surfaces should be able to build concurrently without reviewing, staging, committing, or rewriting one another's files.
  • When two stories claim the same file or shared artifact, the workflow should detect the conflict and serialize or halt that bounded operation instead of serializing every story from start to finish.
  • Large diff bodies should remain file-backed for reviewers instead of being loaded wholesale into the parent agent's context.
  • The workflow should retain phase timings so performance regressions can be diagnosed without platform-specific telemetry.

Actual behavior

The current upstream workflow contains the following behavior:

  • skills/bmad-build/step-01-clarify-and-route.md considers epic-N-context.md valid only when no file anywhere in the planning-artifacts directory is newer.
  • The same step launches compile-epic-context.md synchronously when that broad timestamp condition fails.
  • skills/bmad-build/step-03-implement.md preserves an existing baseline_commit, always executes implementation_handoff, then requires the parent agent to generate and read all changes since that baseline.
  • skills/bmad-build/step-04-review.md regenerates the same baseline-scoped review artifact for reviewer subagents.
  • The default skills/bmad-build/customize.toml gives the implementation subagent the complete spec with no resumed-story fast-path contract.
  • The workflow has no run identifier, per-run ownership manifest, or locking/merge protocol for shared BMAD artifacts such as compiled epic context and sprint status.

Anonymized measured evidence

One real resumed-story initial turn was reconstructed from local model-usage telemetry and the workflow's retained checkpoint:

Phase Calls Wall time Model time Input tokens
Route and inspect 8 1:30 1:26 235,247
Recompile epic context 20 3:15 3:14 1,429,296
Reconcile context and dispatch 7 1:54 1:10 401,754
Implementation subagent 49 7:12 6:51 6,023,764
Parent audit and verification 13 4:00 3:17 1,880,365
Total 97 17:54 15:58 9,970,426

Additional observations:

  • The two sequential subagents consumed 63.1% of model time.
  • Fifty-four requests carried at least 100,000 input tokens and consumed 67.4% of model time.
  • Summed time-to-first-token was 8:16.
  • The implementation handoff discovered that the recorded implementation tasks were already complete.
  • The required baseline-to-worktree artifact exceeded 100 MB and 1.7 million lines because it included unrelated history and worktree state.
  • No external compilation or deployment stage ran during the measured turn; the delay was workflow/context overhead.

The same telemetry cohort shows that this is not an isolated slow run. The measured turn was below the median duration of recent first-turn build sessions, suggesting a systemic performance problem rather than one anomalous model response.

Constraint: Git worktrees are not a universal isolation mechanism

The solution should not assume that every project can create a detached or secondary Git worktree. Examples where that may be unavailable, unsafe, or incompatible with the real verification environment include:

  • repositories with nested Git submodules or submodule layouts that cannot be initialized or updated independently in another worktree;
  • Microsoft Dynamics 365 ERP projects written in X++, where Visual Studio, the AOS, package metadata, generated resources, and deployment/build tooling can be bound to a fixed PackagesLocalDirectory layout rather than an arbitrary Git worktree;
  • enterprise build environments with fixed absolute paths, registered SDK/tool locations, shared package stores, or IDE state tied to the primary checkout;
  • repositories where policy permits only the existing checkout or where an additional worktree would not reproduce the connected runtime environment.

An alternate worktree can remain an optional implementation when it is supported, but the core resume and attribution design needs a same-worktree option based on immutable Git objects, run-start manifests/preimages, a temporary index, or another non-destructive snapshot mechanism.

Proposed solution

1. Resolve active specs before compiling context

When the invocation is a story identifier such as N.M, search active implementation artifacts for an exact matching story spec first. If exactly one match exists, route immediately by its frontmatter status. Compile epic context only when planning a new spec or when the resumed operation explicitly requires refreshed planning context.

2. Add a resumed-story preflight

Before implementation_handoff:

  • detect whether all execution tasks are checked;
  • check for unresolved Open Questions or review-loop work;
  • perform a bounded conformance check against the spec's Code Map and current changed paths.

If no implementation drift is found, skip the implementation subagent and continue directly to verification/review. Checked boxes alone should not be trusted, but they should trigger a cheap preflight rather than an unconditional full implementation pass.

3. Separate story provenance from the current review anchor

Keep baseline_commit as immutable story provenance, but add a separate, resumable review anchor, for example last_verified_commit or a run-start snapshot/manifest.

  • Initial implementation can still review from baseline_commit.
  • After a successful task audit, record the verified anchor.
  • A later resume reviews only changes since that anchor plus changes attributable to the current run.
  • If attribution cannot be established safely in a dirty worktree, halt or require explicit changed paths instead of silently reviewing the ancient baseline.
  • Do not require a secondary Git worktree. Support a same-checkout implementation, such as a run-start hash manifest with saved preimages for already-dirty paths or a temporary Git index that never mutates the real index. A worktree may be used only as an optional optimization.

The parent agent should inspect changed-path statistics and task-owned hunks. Full diff content should stay in the temporary file and be consumed by context-free reviewers by path.

4. Add per-run identity and parallel-safe ownership

Assign every bmad-build invocation a unique run ID and keep all transient state in a run-specific namespace:

  • timing events, diff files, snapshots, and reviewer inputs;
  • the spec path and the exact paths the run is allowed to modify;
  • pre-run hashes/preimages for those paths;
  • the candidate commit and review anchor;
  • child-agent IDs and completion state.

Before modifying a path, compare it with the run's preimage and check whether another active build has claimed it. Disjoint path sets may proceed concurrently. An overlapping claim should stop with a clear conflict rather than overwrite or absorb another story's work.

Shared BMAD files need narrow critical sections rather than a global build lock:

  • update sprint-status.yaml with an atomic read/merge/write guarded by a short file lock;
  • publish epic-N-context.md atomically, with same-epic regeneration deduplicated;
  • stage and commit only the current run's attributable files;
  • never use a broad worktree commit merely because the repository is dirty.

External environments may also require locks. For example, a fixed package store, compiler host, or connected runtime can be serialized per resource while planning, source editing, static checks, and review continue concurrently for unrelated stories.

5. Make epic-context caching dependency-aware

Replace the directory-wide newest-mtime test with a manifest/digest of the planning inputs used for that epic. Regenerate only when one of those inputs changes. Use generated-section markers or a merge contract so regeneration does not overwrite explicitly maintained additions.

6. Add portable phase timing

Record start/end timestamps for:

  • routing/context resolution;
  • epic-context compilation;
  • planning;
  • implementation handoff;
  • task audit and verification;
  • review and repair loops.

Store a compact timing table in the spec or a sidecar artifact. Platform model/token telemetry can enrich this when available, but should not be required.

Suggested acceptance criteria

  • Given an existing in-progress spec with all implementation tasks checked, when it is resumed by story identifier and no drift is found, then no implementation subagent is launched.
  • Given an unrelated planning-artifact change, when an epic's actual input manifest is unchanged, then its cached context is reused.
  • Given a resumed story with an old original baseline, when review starts, then the review artifact contains only attributable changes since the latest verified anchor/current run.
  • Given a repository where Git worktrees are unavailable or incompatible with the build/runtime environment, when a story is resumed, then attribution and review still work from the primary checkout without mutating its real Git index or unrelated files.
  • Given two concurrent builds whose declared and observed changed paths are disjoint, when both reach implementation, review, and commit, then each run includes only its own files and neither run blocks for the full lifetime of the other.
  • Given two concurrent builds that attempt to modify the same path, when the second write is about to occur, then the workflow reports the owning run and stops or waits rather than overwriting the first run's change.
  • Given concurrent updates to sprint-status.yaml or the same epic-context artifact, when each run publishes its state, then a bounded lock and atomic merge preserve both updates without serializing unrelated implementation work.
  • Given a shared external build or connected-runtime resource, when concurrent stories require it, then only that resource-bound phase is serialized and the retained timing report identifies the wait separately from execution time.
  • Given a large review diff, when the parent performs task verification, then the full diff body is not loaded into the parent context.
  • Given any build route, when it completes or halts, then the retained artifact reports elapsed time per workflow phase.

Environment

  • Module: BMad Method (BMM) - Core Framework
  • BMad version: 6.12.0
  • AI IDE: Copilot CLI / GitHub Copilot
  • Operating system: Windows
  • Models in measured run: one primary model and two configured subagent models

Existing-issue search

  • #1188 discusses general token waste and multi-session validation gaps.
  • #2760 discusses duplicate review layers between bmad-build and bmad-code-review.
  • Neither issue covers resumed-spec routing, directory-wide epic-context invalidation, unconditional implementation dispatch, ancient-baseline diff attribution, or safe concurrent story builds without mandatory Git worktrees.

Source: bmad-code-org/BMAD-METHOD