feat(build): shorten completion handoff (#2822)

Build now ends with a one or two sentence summary and a one-line offer
of next steps: create a PR, use bmad-walkthrough, or make another
change. The open_spec default is empty, so no editor opens unless
customized, and Build no longer appends a Suggested Review Order to the
spec; bmad-walkthrough generates a trail in conversation when the spec
has none.

Rename the in-session route to oneshot to match the step name, and
rewrite step-oneshot in plain English. Guard sprint-status updates at
the call sites so untracked work skips the sub-step, and shorten the
sync instruction while keeping its edge cases. Resume in-progress
oneshot specs on the oneshot route instead of dispatch, which expects a
Code Map and Tasks the oneshot spec does not have.

Drop the negative code -r renderer assertions, which only pinned the
config default; the sentinel override test still covers open_spec
substitution.

Rework the walkthrough doc to open with the comprehension order a
reader should follow and why a raw diff fails at it, frame "when to
use" around understanding a change and deciding whether to ship, move
the human-versus-agentic review note into an admonition, and drop the
Review Trail section.
This commit is contained in:
Alex Verkhovsky
2026-09-03 09:48:03 -06:00
committed by GitHub
parent 891c0abbc1
commit 33bdfb5132
12 changed files with 116 additions and 173 deletions
+6 -6
View File
@@ -108,16 +108,16 @@ review bot — see [Review a Change](review-a-change.md).
### 6. Review the Result
When it finishes, `bmad-build` shows you the completed change and its review
notes. This is the main checkpoint. For a guided walkthrough of the finished
work, see [Walk Through a Change](walk-through-a-change.md).
When it finishes, `bmad-build` gives you a short summary and offers the usual
next steps: create a PR, walk through the change, or make another change. For
a guided review of the finished work, see
[Walk Through a Change](walk-through-a-change.md).
- Skim the diff to confirm the change matches your intent
- Run the walkthrough or skim the diff to confirm the change matches your intent
- If something looks off, tell the agent what to fix — it can iterate in the
same session
Once you are satisfied, push the commit. It can offer to push and create a PR
for you.
Once you are satisfied, ask it to push the commit and create a PR for you.
:::caution[If Something Breaks]
If a pushed change causes unexpected issues, use `git revert HEAD` to undo the
+21 -26
View File
@@ -5,27 +5,32 @@ sidebar:
order: 3
---
`bmad-walkthrough` walks you through a finished change — from purpose and
context into details — so you can decide whether to approve, rework, or
discuss further. See [how a run works](#run-bmad-walkthrough).
The best way to look at any change is to start at why it happened, then look
at the core logic change (usually some controller or domain code), then go
into the implementation details, and finally — peripheral consequences.
This is human comprehension, not a substitute for the review `bmad-build`
already ran, or for [`bmad-code-review`](review-a-change.md).
A regular diff presents a change in a sort of alphabetical order. If there is
more than ~5-10 files changed, it's hard to keep track. As a result, you end
up approving something you did not fully understand.
`bmad-walkthrough` walks you through a change **in the order** that is right
for comprehension. See [how it works](#run-bmad-walkthrough).
:::note[Human review]
This skill is for human review. Agentic review is done during a
[`bmad-build`](build-a-change.md) run, or by
[`bmad-code-review`](review-a-change.md).
:::
## When to Use It
The primary handoff is from [`bmad-build`](build-a-change.md).
Implementation is done, the spec file is open with a review trail appended,
and you need to decide whether to ship. Say "walkthrough" and go.
Use it when you want to look at a change with your own eyes, make sense of
it, and decide whether it is worth shipping.
Build runs long with little supervision. Walkthrough is where you take back
the wheel. You could eyeball the diff, but once the change spans many files
you lose the thread, miss a connection, or approve something you did not
fully understand. A raw diff presents files in git order, which is almost
never the order that builds understanding.
It also works standalone:
Typical moments:
- **After one or more [`bmad-build`](build-a-change.md) runs** — you let an LLM
drive for as long as you dare let it drive, then take the wheel back.
- **Reviewing a PR** — especially one with more than a handful of files or
cross-cutting changes
- **Onboarding to a change** — when you need to understand what happened on
@@ -74,7 +79,7 @@ expectations for scope.
The change is organized by **concern** — cohesive design intents like "input
validation" or "API contract" — not by file. Each concern gets a short
explanation of *why* this approach was chosen, followed by clickable
explanation of _why_ this approach was chosen, followed by clickable
`path:line` stops that you can follow through the code.
This is the design judgment step. You evaluate whether the approach is right
@@ -138,16 +143,6 @@ The five steps are there to make sure you see the whole picture, but how
deep you go at each step — and what tools you bring in — is entirely up to
you.
## The Review Trail
The walkthrough step works best when it has a **Suggested Review Order**
a list of stops the spec author wrote to guide reviewers through the change.
When a spec includes this, the workflow uses it directly.
When no author-produced trail exists, the workflow generates one from the
diff and codebase context. A generated trail is lower quality than an
author-produced one, but far better than reading changes in file order.
## What It Is Not
`bmad-walkthrough` is not the review skill. It does not replace the review
+4 -7
View File
@@ -15,9 +15,7 @@ Use a macOS or Linux shell with Git, Node.js 20.12+ and `npx`,
[uv](https://docs.astral.sh/uv/getting-started/installation/), and a coding tool
supported by BMad. Complete [Build Your First Change](../start/build-your-first-change.md) before
continuing. The exact install and launch commands below are for Claude Code. If
you use another supported tool, you can run Build there instead. VS Code is
optional but useful. Build can open the finished work for you when VS Code's
`code` command is available.
you use another supported tool, you can run Build there instead.
:::
## 1. Check Out the Exact Django Version
@@ -109,8 +107,8 @@ Once approved, it builds and reviews the change, handles its findings, and
shows you the result. Keep this exercise about JSON output for `diffsettings`;
filtering, redaction, and CI behavior belong in the next exercise.
If `code` is available, Build opens the project and finished spec in VS
Code. The Suggested Review Order links lead you through the change.
Build ends with a short summary and offers the next steps. Continue with the
manual checks below before asking it to create a PR.
## 6. See It Work
@@ -133,8 +131,7 @@ Look through the JSON and compare it with the choices you made with Build.
## 7. You Built It
Congratulations, you've now added something useful to a complex open-source
codebase. If you use VS Code, you're probably looking at the finished change
there now.
codebase.
## 8. Write a Spec for the Larger Change
+6 -15
View File
@@ -31,19 +31,16 @@ persistent_facts = []
on_complete = ""
# How Build opens a completed spec for human review. The default deliberately
# opens the repository root first and the spec second in a reused VS Code
# window so relative review links resolve in the correct workspace. Override
# this with the application and equivalent behavior you prefer. Set it to an
# empty string to disable automatic opening and all related completion output.
# Optional instructions for opening a completed spec. The empty default leaves
# the spec closed. Override this with the application and behavior you prefer.
# Example requests for bmad-customize: "Open completed specs in Cursor and
# reuse the project window" or "Do not open completed specs automatically."
#
# Example `open_spec` override instructions for bmad-build.user.toml:
# {project-root} and {spec_file} are substituted at run time.
#
# VS Code and compatible GUI editors — shipped default uses `code`; Cursor,
# Windsurf, Antigravity, Zed, and similar editors can substitute their launcher.
# VS Code and compatible GUI editors — a custom override can use `code`; Cursor,
# Windsurf, Antigravity, Zed, and similar editors can use their own launcher.
# Preserve reuse-window behavior when the launcher supports it, and always pass
# the repository root first and the spec second:
# Run `code -r "{project-root}" "{spec_file}"`, root first.
@@ -61,13 +58,7 @@ on_complete = ""
# Emacs — reuse an Emacs server without waiting for the buffer to close:
# Run `cd "{project-root}" && emacsclient -n "{spec_file}"`.
#
# For every non-empty override, also tell Build to note in the completion summary
# that the spec was sent to the chosen editor and contains a Suggested Review
# Order, then include the default navigation tip shown below.
open_spec = """
Run `code -r "{project-root}" "{spec_file}"` — the repository root first so VS Code opens in the right context, then the spec file. Always double-quote both paths to handle spaces and special characters. If `code` is unavailable or the command fails, skip gracefully and tell the user the spec file path instead. In the completion summary, note that the spec was sent to VS Code and that it contains a Suggested Review Order, then add this navigation tip: "Ctrl+click (Cmd+click on macOS) the links in the Suggested Review Order to jump to each stop."
"""
open_spec = ""
# Handoff for the implementation subagent in step 03. The whole execution
# recipe — a subagent by default, but an override may run it any other way
@@ -138,7 +129,7 @@ Do not invoke any skill, and do not spawn subagents of your own — you are the
"""
# Review layers for the in-session (light) route.
# Review layers for the oneshot route.
[[workflow.oneshot_review_layers]]
id = "blind-hunter"
@@ -3,7 +3,7 @@ title: '{title}'
type: 'feature' # feature | bugfix | refactor | chore
created: '{date}'
status: 'draft' # draft | ready-for-dev | in-progress | in-review | done
route: '' # in-session | dispatch — set by step-02's route gate after design
route: '' # oneshot | dispatch — set by step-02's route gate after design
review_loop_iteration: 0 # incremented by step-04 before each review loopback
context: [] # optional: `{project-root}/`-prefixed paths to project-wide standards/docs the implementation agent should load. Keep short — only what isn't already distilled into the spec body.
---
@@ -26,7 +26,7 @@ context: [] # optional: `{project-root}/`-prefixed paths to project-wide standar
## Boundaries & Constraints
<!-- Two tiers: Always = invariant rules. Never = out of scope + forbidden approaches. -->
<!-- If step-02's route gate reported all facts clean (route: 'in-session'), DELETE THIS ENTIRE SECTION. -->
<!-- If step-02's route gate reported all facts clean (route: 'oneshot'), DELETE THIS ENTIRE SECTION. -->
**Always:** INVARIANT_RULES
@@ -56,7 +56,7 @@ context: [] # optional: `{project-root}/`-prefixed paths to project-wide standar
## Code Map
<!-- Agent-populated during planning. Annotated paths prevent blind codebase searching. -->
<!-- If step-02's route gate reported all facts clean (route: 'in-session'), DELETE THIS ENTIRE SECTION. -->
<!-- If step-02's route gate reported all facts clean (route: 'oneshot'), DELETE THIS ENTIRE SECTION. -->
- `FILE` -- ROLE_OR_RELEVANCE
- `FILE` -- ROLE_OR_RELEVANCE
@@ -66,7 +66,7 @@ context: [] # optional: `{project-root}/`-prefixed paths to project-wide standar
<!-- Tasks: backtick-quoted file path -- action -- rationale. Prefer one task per file; group tightly-coupled changes when splitting would be artificial. -->
<!-- If an I/O Matrix is present, include a task to unit-test its edge cases. -->
<!-- AC covers system-level behaviors not captured by the I/O Matrix. Do not duplicate I/O scenarios here. -->
<!-- If step-02's route gate reported all facts clean (route: 'in-session'), DELETE THIS ENTIRE SECTION. -->
<!-- If step-02's route gate reported all facts clean (route: 'oneshot'), DELETE THIS ENTIRE SECTION. -->
**Execution:**
- [ ] `FILE` -- ACTION -- RATIONALE
@@ -20,7 +20,7 @@ Before listing artifacts, resolve existing workflow state in this order. Skip th
Did the user pass a specific file path, spec name, or clear instruction this message?
- If the user explicitly supplied a spec folder and a story id, with no specific spec file path, set `spec_folder` and `story_id`. Read `{spec_folder}/stories.yaml`; if it is missing or fails to parse, HALT rather than falling back to `{{.implementation_artifacts}}`. Find the one entry whose string `id` exactly equals `story_id`; if none exists, HALT rather than falling back. Use that entry's `title` and `description` as the starting intent.
- Look for files matching `{spec_folder}/stories/{story_id}-*.md`. More than one match → HALT rather than choosing one. Exactly one match → set `spec_file` to that path and process it exactly as if the user had supplied that specific file path, including **Story-key resolution** and the existing status route below. No matches → derive a valid kebab-case slug from the entry's `title` (and `description` if needed), then set `spec_file` = `{spec_folder}/stories/{story_id}-{slug}.md` and proceed to INSTRUCTIONS.
- If it points to a file that matches the spec template (has `status` frontmatter with a recognized value: draft, ready-for-dev, in-progress, in-review, or done) → set `spec_file`. Before exiting, run **Story-key resolution** (below). Then **EARLY EXIT** to the appropriate step: `draft``[[bmad-snapshot:step-02-plan.md]]`, `ready-for-dev`/`in-progress``[[bmad-snapshot:step-03-implement.md]]`, `in-review``[[bmad-snapshot:step-04-review.md]]`. For `done`, ingest as context and proceed to INSTRUCTIONS — do not resume.
- If it points to a file that matches the spec template (has `status` frontmatter with a recognized value: draft, ready-for-dev, in-progress, in-review, or done) → set `spec_file`. Before exiting, run **Story-key resolution** (below). Then **EARLY EXIT** to the appropriate step: `draft``[[bmad-snapshot:step-02-plan.md]]`, `ready-for-dev`/`in-progress``[[bmad-snapshot:step-03-implement.md]]` (or `[[bmad-snapshot:step-oneshot.md]]` when `route` is `oneshot`), `in-review``[[bmad-snapshot:step-04-review.md]]`. For `done`, ingest as context and proceed to INSTRUCTIONS — do not resume.
- Anything else (intent files, external docs, plans, descriptions) → ingest it as starting intent and proceed to INSTRUCTIONS. Do not attempt to infer a workflow state from it.
2. Recent conversation
@@ -32,7 +32,7 @@ Before listing artifacts, resolve existing workflow state in this order. Skip th
- Resume one of the listed specs
- **New** — start new work
If `draft` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT**`[[bmad-snapshot:step-02-plan.md]]` (resume planning from the draft)
If `ready-for-dev` or `in-progress` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT**`[[bmad-snapshot:step-03-implement.md]]`
If `ready-for-dev` or `in-progress` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT**`[[bmad-snapshot:step-03-implement.md]]` (or `[[bmad-snapshot:step-oneshot.md]]` when `route` is `oneshot`)
If `in-review` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT**`[[bmad-snapshot:step-04-review.md]]`
If the user chooses **New**: proceed to INSTRUCTIONS
- Unformatted spec or intent file lacking `status` frontmatter? → Suggest treating its contents as the starting intent. Do NOT attempt to infer a state and resume it.
@@ -17,7 +17,7 @@
- **Irreversibles** — things you cannot undo: migrations, data deletion or mutation, external side effects, deploy or config triggers.
- **Footprint** — how big: files you will change, and anything new that other code will call or depend on.
If there are no intent gaps, nothing irreversible, and the change is small: read `[[bmad-snapshot:spec-template.md]]` fully and write `{spec_file}` with only the frontmatter, `## Intent` (inside its `<frozen-after-approval>` block), and `## Implementation Notes`. Delete every other section; the template says you may. Set `route: 'in-session'` and `status: 'in-progress'`, resolving `date` to the current system date. If `preserved_intent` is non-empty, use it as the frozen block. **EARLY EXIT**`[[bmad-snapshot:step-oneshot.md]]`.
If there are no intent gaps, nothing irreversible, and the change is small: read `[[bmad-snapshot:spec-template.md]]` fully and write `{spec_file}` with only the frontmatter, `## Intent` (inside its `<frozen-after-approval>` block), and `## Implementation Notes`. Delete every other section; the template says you may. Set `route: 'oneshot'` and `status: 'in-progress'`, resolving `date` to the current system date. If `preserved_intent` is non-empty, use it as the frozen block. **EARLY EXIT**`[[bmad-snapshot:step-oneshot.md]]`.
Otherwise write the full spec. Set `route: 'dispatch'` and continue.
4. Read `[[bmad-snapshot:spec-template.md]]` fully. Fill it out from the intent and investigation, resolving the template's `date` field to the current system date. Put the investigation into `## Code Map`: paths, symbols or lines, what to reuse, and what not to change. Implementation should work from the spec without being told the investigation again. For each intent gap, add one `## Open Questions` entry: the choice, the options, and what each option means. Never write an intent gap into the frozen block as an assumption. If `preserved_intent` is non-empty, replace the `<frozen-after-approval>` block with it before writing. Write the result to `{spec_file}`.
@@ -24,7 +24,7 @@ Capture `baseline_commit` (current HEAD, or `NO_VCS` if version control is unava
Change `{spec_file}` status to `in-progress` in the frontmatter before starting implementation.
Follow `[[bmad-snapshot:sync-sprint-status.md]]` with `target_status` = `in-progress`.
If `{story_key}` is not empty and `{{.implementation_artifacts}}/sprint-status.yaml` exists, read `[[bmad-snapshot:sync-sprint-status.md]]` with `{target_status}` = `in-progress`.
Execute the implementation handoff below: substitute the runtime placeholders (e.g. `{spec_file}`) into it, then follow it verbatim.
@@ -10,49 +10,11 @@
## INSTRUCTIONS
### Generate Suggested Review Order
Read `{baseline_commit}` from `{spec_file}` frontmatter and construct the diff of all changes since that commit.
Append the review order as a `## Suggested Review Order` section to `{spec_file}` **after the last existing section**. Do not modify the Code Map.
Build the trail as an ordered sequence of **stops** — clickable `path:line` references with brief framing — optimized for a human reviewer reading top-down to understand the change:
1. **Order by concern, not by file.** Group stops by the conceptual concern they address (e.g., "validation logic", "schema change", "UI binding"). A single file may appear under multiple concerns.
2. **Lead with the entry point** — the single highest-leverage file:line a reviewer should look at first to grasp the design intent.
3. **Inside each concern**, order stops from most important / architecturally interesting to supporting. Lightly bias toward higher-risk or boundary-crossing stops.
4. **End with peripherals** — tests, config, types, and other supporting changes come last.
5. **Every code reference is a clickable spec-file-relative link.** Compute each link target as a relative path from `{spec_file}`'s directory to the changed file. Format each stop as a markdown link: `[short-name:line](../../path/to/file.ts#L42)`. Use a `#L` line anchor. Use the file's basename (or shortest unambiguous suffix) plus line number as the link text. The relative path must be dynamically derived — never hardcode the depth.
6. **Each stop gets one ultra-concise line of framing** (≤15 words) — why this approach was chosen here and what it achieves in the context of the change. No paragraphs.
Format each stop as framing first, link on the next indented line:
```markdown
## Suggested Review Order
**{Concern name}**
- {one-line framing}
[`file.ts:42`](../../src/path/to/file.ts#L42)
- {one-line framing}
[`other.ts:17`](../../src/path/to/other.ts#L17)
**{Next concern}**
- {one-line framing}
[`file.ts:88`](../../src/path/to/file.ts#L88)
```
> The `../../` prefix above is illustrative — compute the actual relative path from `{spec_file}`'s directory to each target file.
When there is only one concern, omit the bold label — just list the stops directly.
### Mark Spec Done
Change `{spec_file}` status to `done` in the frontmatter.
Follow `[[bmad-snapshot:sync-sprint-status.md]]` with `target_status` = `review`.
If `{story_key}` is not empty and `{{.implementation_artifacts}}/sprint-status.yaml` exists, read `[[bmad-snapshot:sync-sprint-status.md]]` with `{target_status}` = `review`.
### Commit and Complete
@@ -62,14 +24,15 @@ If version control is available and the tree is dirty, create a local commit wit
### Display Summary
Display summary of your work to the user, including:
Display a very short completion summary — one or two sentences — including:
- What changed.
- The verification and review result, including whether anything was deferred.
- The commit hash, if one was created.
- Review findings breakdown: patches applied, items deferred, and the rejected count — reasons are recorded in the spec's `## Review Triage Log`.
Display file paths and `file:line` references in whatever form is clickable where you are presenting them (e.g. code citation in chat, CWD-relative path with no leading `/` in terminal). If unsure, use CWD-relative path.
Do not list changed files, repeat details from the spec, or narrate the process unless the user asks.
Offer to push and/or create a pull request.
Offer applicable next actions in one short line: when version control and a remote are available, create a pull request (and push first if needed); use `bmad-walkthrough`; or make another change.
Workflow complete.
+50 -38
View File
@@ -1,95 +1,107 @@
# Step One-Shot: Implement, Review, Present
Entered only from step-02's route gate: `{spec_file}` already exists with `route: 'in-session'`.
You reach this step from step 2, or from step 1 when resuming a spec whose `route` is `oneshot`. `{spec_file}` already exists.
## RULES
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
- NEVER auto-push.
- Content inside `<frozen-after-approval>` in `{spec_file}` is read-only. Do not modify.
- All review subagents must run at the same model capability as the current session.
- Run subagents synchronously: launch them together as blocking calls awaited in this turn — never backgrounded or detached, never ending the turn to await results.
- Speak in `{{.communication_language}}`. Write files in `{{.document_output_language}}`.
- Do not push to a remote unless the user asks.
- Do not edit anything inside `<frozen-after-approval>` in `{spec_file}`.
- Review subagents must use the same model level as this session.
- Start all review subagents in this turn and wait for all of them to finish. Do not run them in the background or end your turn before they return.
## INSTRUCTIONS
### Implement
Follow `[[bmad-snapshot:sync-sprint-status.md]]` with `target_status` = `in-progress`.
If `{story_key}` is not empty and `{{.implementation_artifacts}}/sprint-status.yaml` exists, read `[[bmad-snapshot:sync-sprint-status.md]]` with `{target_status}` = `in-progress`.
Implement directly from `{spec_file}` — its Intent is the source of truth. As you work, append to its `## Implementation Notes` section: decisions made, files touched, surprises encountered.
Build the change from `{spec_file}`. The Intent section is what you implement. As you work, add notes to `## Implementation Notes`: decisions you made, files you changed, surprises.
**Escalation ramp.** If implementation surfaces a fact the route gate did not see — an intent gap (something the request does not say and the user would notice in the result), an irreversible action, or footprint growth beyond the designed scope — stop editing. Record the trigger in `## Implementation Notes`, then upgrade `{spec_file}`: reinstate `## Code Map` (populated from your live context) and `## Open Questions` (one entry per intent gap), set `route: 'dispatch'` and `status: 'draft'`. Return to `[[bmad-snapshot:step-02-plan.md]]` and resume at its gate instruction (step 6).
**When to stop and replan.** Stop coding if you learn something step 2 did not account for:
- the request left out something the user would notice in the result
- you need to do something you cannot undo
- the change is growing beyond what was planned
Write what triggered the stop in `## Implementation Notes`. Then update `{spec_file}`: add back `## Code Map` (filled in from what you learned while implementing) and `## Open Questions` (one question per gap), set `route: 'dispatch'` and `status: 'draft'`. Go back to `[[bmad-snapshot:step-02-plan.md]]` step 6.
### Review
Announce skipped layers first, then launch every active layer before handling any layer's result. Try running all active layers simultaneously. After substituting runtime placeholders, when an instruction launches a reviewer subagent, launch that child with the prompt text; do not load the reviewer instruction file yourself. For any other customized instruction, execute it as written:
Say which review layers you are skipping, then start every active layer before reading any results. Run them at the same time when you can. Fill in runtime placeholders first. When a layer tells you to launch a reviewer subagent, launch it with that prompt text. Do not read the reviewer's instruction file yourself. For any other customized instruction, do what it says:
{workflow.oneshot_review_layers}
If a layer's instruction requires subagents and none are available, for each such layer write under `{{.implementation_artifacts}}` the exact child prompt from that layer's instruction after placeholder substitution (not a path-only pointer), then HALT. Ask the human to run each in a separate session and paste back the findings.
If a layer needs subagents and you cannot launch them, write the full prompt for each layer under `{{.implementation_artifacts}}` (with placeholders filled in, not just file paths). Stop and ask the user to run each prompt in a separate session and paste back the findings.
### Classify
Once every layer has reported — and not before — render a verdict on each finding, ahead of any deduplication or grouping. Disregard any severity a reviewing subagent assigned — they lack the context to grade.
Wait until every review layer has reported. Then judge each finding. Ignore severity labels from reviewers — you decide.
For each finding:
- **Verify the finding's claim.** At the cited file and line, does the bad outcome the reviewer describes actually occur? Read beyond the changed lines — follow callers, guards upstream, etc — until you can answer yes or no. A different finding about nearby code does not settle this one. Judge whether the problem is real, not whether the proposed fix is plausible. Code that loudly fails on a situation you never showed the program can reach is correct behavior, not a defect.
- **Render exactly one verdict** from what verification established — the verdict is the whole triage decision; there is no separate keep-or-dismiss.
- `high` (intolerable), `medium` (tolerable), `low` (cosmetic or negligible) — the bad outcome is real. Assign severity by how much it hurts end users or developers. For developer-only problems, name where it will cause trouble; a vague "this is messy" with no named harm is not a severity grade — use `false` or `maybe-false` instead. When the harm is real but you cannot tell how bad, pick the higher grade.
- `false` — you checked, and the bad outcome does not happen at the cited location. Write what disproves this specific claim. A true fact about nearby code that does not disprove the claim does not count.
- `maybe-false` — you could not tell whether the bad outcome happens. Write what you would need to check to find out. Use this only when the diff and surrounding code leave the question open; when they are enough to decide, pick `high`, `medium`, `low`, or `false`.
- Record every finding with its verdict and evidence; never drop one silently.
- **Check the claim.** Go to the cited file and line. Does the problem the reviewer describes actually happen? Read surrounding code and callers until you can say yes or no. A nearby issue does not answer this one. Judge whether the bug is real, not whether the suggested fix sounds good. Code that fails loudly on a state you have not shown the program can reach is correct, not a bug.
Reject `false` findings on their refutation.
- **Pick one verdict:**
- `high` (intolerable), `medium` (tolerable), or `low` (cosmetic or negligible) — the problem is real. Rate it by harm to users or developers. For developer-only issues, say where it will hurt. Vague complaints like "this is messy" are not `high`/`medium`/`low` — use `false` or `maybe-false`. When unsure how bad, pick the higher grade.
- `false` — you checked and the problem does not happen. Say what you found that disproves it.
- `maybe-false` — you could not tell. Say what you would need to check. Use this only when the code and diff are not enough to decide.
Reject `low` findings when it is unlikely that users or developers would meet the defect in everyday use (judged plainly — no proof needed) and the fix is more than a direct correction or deletion — adding guards, branches, parameters, or other complexity.
- Write down every finding with its verdict and evidence. Do not drop any.
All remaining findings continue to grouping.
Reject `false` findings.
Group the survivors by shared root cause — two findings belong in one entry only when the same defect produced both. Same location alone is not a shared root cause, and neither is a shared fix. An entry carries every member's verified bad outcome and the highest verdict among them (`high` > `medium` > `low` > `maybe-false`). A group that includes verified `high`, `medium`, or `low` members routes by its highest such verdict — not to defer just because a member is `maybe-false`. Route each entry in this order:
Reject `low` findings when users or developers would rarely hit the problem in normal use and the fix would add more than a simple correction or deletion.
Group what remains by root cause — two findings go together only if the same bug caused both. Same file or same fix is not enough. For each group, keep the worst verdict (`high` > `medium` > `low` > `maybe-false`). If a group has verified `high`, `medium`, or `low` members, route by the worst of those — not `defer` just because one member is `maybe-false`.
For each group:
- **patch** — This change caused or exposed the problem. The smallest fix is simple, adds no new public API, and does not guard code paths you did not show are reachable. Fix it now.
- **HALT** — Same as patch, but the smallest fix is not that simple. Stop and ask the user before continuing.
- **defer** — Everything else: old bugs not caused by this change, ideas for later, groups where every member is `maybe-false` and would be `medium` or `high` if true (record that severity marked unverified, and what would prove it; if it would only be `low`, reject it), or fixes that would edit CLAUDE.md, AGENTS.md, rules, or specs. Add one entry to `{{.implementation_artifacts}}/deferred-work.md`:
- **patch** — Patch every entry caused or exposed by this change that shows a defect that actually occurs, missing coverage for a specific case, or a broken gate or convention — not a state nothing reaches — and whose smallest fix is trivial, adds no public surface, and guards no state the finding did not demonstrate. Apply that smallest fix immediately.
- **HALT** — HALT on every entry caused or exposed by this change that shows the same evidence but whose smallest fix fails any of those conditions. Present it to the human for decision before proceeding.
- **defer** — Defer every other entry: pre-existing issues, improvement ideas, entries whose members are all `maybe-false` where the claim, if true, would be `medium` or `high` (record that severity marked unverified, plus what would settle it; if it would only be `low`, reject it with the same note), and any entry whose fix edits agent-context files (CLAUDE.md, AGENTS.md, rules, specs). Append one new entry to `{{.implementation_artifacts}}/deferred-work.md` using this format. Do not modify existing entries or look for duplicates.
```markdown
- source_spec: `{spec_file}`
summary: <one sentence>
evidence: <why this is real; for a maybe-false finding, what evidence would settle it>
evidence: <why this is real; for maybe-false, what would prove it>
```
Do not edit old entries or check for duplicates.
### Finalize Spec
Update `{spec_file}`:
1. **Frontmatter** — set `status: 'done'`.
2. **Suggested Review Order** — append after Intent. Build using the same convention as `[[bmad-snapshot:step-05-present.md]]` § "Generate Suggested Review Order" (spec-file-relative links, concern-based ordering, ultra-concise framing).
3. **Review Triage Log** — only when the review produced findings: add the section with one line per finding with its verdict and evidence — the refutation for `false`, what would settle it for `maybe-false`, why a rejected `low` was not worth fixing.
1. Set `status: 'done'` in the frontmatter.
2. If review found anything, add `## Review Triage Log` with one line per finding: verdict and evidence. For `false`, the disproof. For `maybe-false`, what would settle it. For rejected `low`, why it was not worth fixing.
Follow `[[bmad-snapshot:sync-sprint-status.md]]` with `target_status` = `review`.
If `{story_key}` is not empty and `{{.implementation_artifacts}}/sprint-status.yaml` exists, read `[[bmad-snapshot:sync-sprint-status.md]]` with `{target_status}` = `review`.
### Commit
If version control is available and the tree is dirty, create a local commit with a conventional message derived from the intent. If VCS is unavailable, skip.
If git is available and there are uncommitted changes, commit with a conventional message based on the Intent. If git is not available, skip.
### Present
{workflow.open_spec}
Display a summary in conversation output, including:
Give the user a short summary — one or two sentences:
- The commit hash (if one was created).
- List of files changed with one-line descriptions. Display file paths and `file:line` references in whatever form is clickable where you are presenting them (e.g. code citation in chat, CWD-relative path with no leading `/` in terminal). If unsure, use CWD-relative path. This differs from spec-file links which use spec-file-relative paths.
- Review findings breakdown: patches applied, items deferred, and the rejected count — reasons are recorded in the spec. If every finding was rejected, say so.
- What changed.
- Review result, including anything deferred.
- Commit hash, if you made one.
Offer to push and/or create a pull request.
Do not list files, repeat the spec, or walk through what you did unless asked.
HALT and wait for human input.
Offer next steps in one line: create a PR (push first if needed) when git and a remote exist; use `bmad-walkthrough`; or make another change.
Stop and wait for the user.
Workflow complete.
## On Complete
If anything appears below, follow it as the final terminal instruction before exiting; otherwise exit normally.
If anything appears below, do it before exiting. Otherwise exit.
{workflow.on_complete}
@@ -1,19 +1,5 @@
# Sync Sprint Status
Shared sub-step for updating `sprint-status.yaml` during build. Called from any route (dispatch, in-session, future routes) with a `target_status` parameter.
## Preconditions
Skip this entire file (return to caller) if ANY of:
- `story_key` is unset
- `{{.implementation_artifacts}}/sprint-status.yaml` does not exist on disk
## Instructions
1. Load the FULL `{{.implementation_artifacts}}/sprint-status.yaml` file.
2. Find the `development_status` entry matching `{story_key}`. If not found, warn the user once (`"{story_key} not found in sprint-status; skipping sprint sync"`) and return to caller.
3. **Idempotency check.** If `development_status[{story_key}]` is already at `target_status` or a later state (`review` is later than `in-progress`; `done` is later than both), return to caller — no write needed. Never regress a story's status.
4. Set `development_status[{story_key}]` to `{target_status}`.
5. **Epic lift (only when `target_status` = `in-progress`).** Derive the parent epic key as `epic-{N}` from the leading numeric segment of `{story_key}` (e.g., `3-2-digest-delivery``epic-3`). If that entry exists and is `backlog`, set it to `in-progress`. Leave it alone otherwise. Skip this sub-step entirely when `target_status` is not `in-progress`.
6. Refresh `last_updated` to the current date.
7. Save the file, preserving ALL comments and structure including STATUS DEFINITIONS and WORKFLOW NOTES.
Set `development_status[{story_key}]` to `{target_status}` in `{{.implementation_artifacts}}/sprint-status.yaml`.
If `{story_key}` is missing, warn once and stop.
If the story is already at `{target_status}` or later, stop.
When `{target_status}` is `in-progress`, set parent epic (e.g. `3-2-foo``epic-3`) from `backlog` to `in-progress` if present.
Update `last_updated`. Preserve comments and structure.
+10 -11
View File
@@ -467,12 +467,6 @@ async function main() {
const oneshot = fs.readFileSync(path.join(dir, 'step-oneshot.md'), 'utf8');
assert(oneshot.includes('#### Blind Hunter (`blind-hunter`)'), 'oneshot review layer block missing');
// The spec editor handoff must reach both terminal routes (#2652).
const present = fs.readFileSync(path.join(dir, 'step-05-present.md'), 'utf8');
assert(present.includes('code -r'), 'open_spec default missing from step-05-present.md');
assert(oneshot.includes('code -r'), 'open_spec default missing from step-oneshot.md');
assert(/^Offer to push\b/m.test(present), 'standalone "Offer to push" line was lost');
const artifacts = `${fs.realpathSync(build.project)}/implementation`;
assert(markdown.includes(`${artifacts}/sprint-status.yaml`), 'sprint-status path was not baked absolute');
assert(markdown.includes(`${artifacts}/deferred-work.md`), 'deferred-work path was not baked absolute');
@@ -489,16 +483,21 @@ async function main() {
);
});
test('empty open_spec override disables automatic opening', () => {
test('non-empty open_spec override reaches both terminal routes', () => {
const build = fixture({ skillName: 'bmad-build' });
fs.mkdirSync(path.join(build.bmad, 'custom'), { recursive: true });
fs.writeFileSync(path.join(build.bmad, 'custom', `${build.skillName}.user.toml`), '[workflow]\nopen_spec = ""\n', 'utf8');
fs.writeFileSync(
path.join(build.bmad, 'custom', `${build.skillName}.user.toml`),
'[workflow]\nopen_spec = "OPEN-SPEC-SENTINEL {project-root} {spec_file}"\n',
'utf8',
);
const dir = path.dirname(entry(run(build)));
for (const name of ['step-05-present.md', 'step-oneshot.md']) {
const rendered = fs.readFileSync(path.join(dir, name), 'utf8');
assert(!rendered.includes('code -r'), `open_spec default survived in ${name}`);
assert(!rendered.includes('spec was sent'), `opening summary survived in ${name}`);
assert(rendered.includes('Suggested Review Order'), `review trail generation disappeared from ${name}`);
assert(
rendered.includes('OPEN-SPEC-SENTINEL {project-root} {spec_file}'),
`open_spec override or its runtime placeholders missing from ${name}`,
);
}
});