refactor(build): route on intent gaps instead of forks

A fork covered two different things: a question about what the user wants,
and a design choice the user would never notice. Only the first needs the
human. Name it an intent gap, define it as something the request does not
say and the user would notice in the result, and tell the model to decide
and record everything else itself. Forbid writing an intent gap into the
frozen block as an assumption.
This commit is contained in:
Alex Verkhovsky
2026-08-27 20:14:29 -06:00
parent 47544c5b9e
commit 095da05573
4 changed files with 13 additions and 12 deletions
+6 -5
View File
@@ -80,11 +80,12 @@ expensive kind of mistake to find later.
### 4. Approve a Plan When Asked
After investigation, `bmad-build` routes to the smallest safe path. It reports
three facts about the settled design: judgment calls a reviewer could answer
differently (forks), irreversible actions, and footprint. A design clean on all
three takes the light path — a minimal spec and implementation in the same
session, reviewed afterwards. Anything flagged gets a full written plan first,
with each fork recorded as an open question you answer before approval.
three facts about the settled design: intent gaps (things you did not say that you
would notice in the result), irreversible actions, and footprint. A design
clean on all three takes the light path — a minimal spec and implementation in
the same session, reviewed afterwards. Anything flagged gets a full written
plan first, with each intent gap recorded as an open question you answer
before approval.
Approve the plan when it describes the right thing to build. Push back if it
does not — fixing the plan is cheaper than fixing the code.
@@ -45,8 +45,8 @@ context: [] # optional: `{project-root}/`-prefixed paths to project-wide standar
## Open Questions
<!-- One entry per unresolved fork: a judgment call where a defensible alternative exists and
neither the intent nor codebase convention decides it. State the choice, the defensible
<!-- One entry per intent gap: something the request does not say, the code cannot settle,
and the user would notice in the result. Choices the user would not notice are yours. State the choice, the defensible
options, and each option's consequence. The spec cannot leave `draft` while any entry
remains: when the human answers, record the decision inside <frozen-after-approval> and
delete the entry. When no entries remain, DELETE THIS ENTIRE SECTION. -->
@@ -13,14 +13,14 @@
Do not ask the human during investigation. When something is unclear, look in the repository, planning artifacts, or history first. Keep looking until you know, or until those sources have nothing more to say. Leave any remaining choice for the next step.
3. Decide the path. You already have a plan. Write down three facts about it — as it is now, not as a guess:
- **Forks** — choices where someone could reasonably pick a different option, and neither the request nor the existing code picks for you. Test: for each "I chose X over Y", could a reasonable reviewer say "actually, Y"?
- **Intent gaps** — things the request does not say, the code cannot settle, and the user would notice in the result. Only the human can answer these. Choices the user would not notice are yours: decide and record them in the spec.
- **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 forks, 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: '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]]`.
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 Fork you listed, add one `## Open Questions` entry: the choice, the options, and what each option means. If `preserved_intent` is non-empty, replace the `<frozen-after-approval>` block with it before writing. Write the result to `{spec_file}`.
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}`.
5. Self-review against READY FOR DEVELOPMENT standard. For anything important that's missing: if the repository can tell you, go look and fix the spec; if a human has to decide, add an `## Open Questions` entry. Do not invent the answer.
6. Resolve the gates before the checkpoint. Two things must be settled, in whatever order the conversation makes natural; combine them in one message when both apply.
- **Token count** (see SCOPE STANDARD). If the spec exceeds 1600 tokens, show the count and give the user a choice:
@@ -31,7 +31,7 @@
summary: <one sentence naming the deferred goal>
evidence: <why this was split from the current spec>
```
- **Open Questions.** Present every entry as a numbered question with its options and what each option means, and HALT for the human's answers. Write each answer into the `<frozen-after-approval>` block as a decision and delete the entry. An answer may expose a new fork — add it and ask again. Until every entry is gone, nothing inside `<frozen-after-approval>` may assume an answer: leave the dependent boundary, matrix row, or example out and fill it in from the decision. When the last entry is gone, delete the section.
- **Open Questions.** Present every entry as a numbered question with its options and what each option means, and HALT for the human's answers. Write each answer into the `<frozen-after-approval>` block as a decision and delete the entry. An answer may expose a new intent gap — add it and ask again. When the last entry is gone, delete the section.
### CHECKPOINT 1
@@ -18,7 +18,7 @@ Follow `[[bmad-snapshot:sync-sprint-status.md]]` with `target_status` = `in-prog
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.
**Escalation ramp.** If implementation surfaces a fact the route gate did not see — a new fork (a judgment call a reasonable reviewer could answer differently), 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 fork), set `route: 'dispatch'` and `status: 'draft'`. Return to `[[bmad-snapshot:step-02-plan.md]]` and resume at its Drain Open Questions instruction.
**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).
### Review