Why a pack asks before it acts, how an intake.yaml defines its questions field by field, how answers get a status, and a complete design read with real output. Includes how to write your own.
An intake is the list of questions a pack needs answered before it starts working. Without one, an agent that gets a thin request fills the gaps with guesses and you find out in the deliverable. With one, the agent asks a few questions at a time, in your language, records each answer with its status, and produces a brief that becomes the contract for everything the pack does next. Read this page if you use a pack that ships an intake (in 0.23.0, the Design pack), or if you want to give your own pack one.
Ask first, then swarmThe intake questions, the budget of 26 and the approval step, waves of sub-agents, and a tally that says simulated units are not people.Transcript
Before a pack acts, it asks. The intake puts a few questions at a time and marks the must-haves. Where you have no answer, it proposes a default, or records the question as unknown with its risk. The answers become a brief, and nothing in it is invented. Next, the swarm. By default, a run may use 26 sub-agents before the assistant must ask you. Ask for more, and the run waits, marked needs approval, until a person says yes. Once approved, sub-agents work in waves of ten by default, each reading one brief and writing one answer. The runtime validates every answer file and counts only the valid ones. This tally is demo data, and it opens with its disclosure: simulated units are not people. Read the rates as an upper bound, and confirm them with real people.
A pack acts on what it knows. Designing a screen for "a dashboard" without knowing who uses it, on which device and for what job produces a generic template that fits nobody. The cost of the missing answer is not visible until the work is done. An intake moves that cost to the front: each question says whether the pack can start without it, what it proposes if you do not care, and what goes wrong if you do not know.
Three files take part. The intake (intake.yaml) ships with the pack. The answers file is JSON and is written only by bmad-plus intake record. The brief is Markdown and is written only by bmad-plus intake render. Your assistant does the asking; the command never asks anything and never calls a model.
You let the pack’s proposed default stand. It is a proposal, not a fact you gave.
--default id
Answer (proposed default, not confirmed by the user):
unknown
You do not know. The question’s risk is copied with the answer, so it travels into the deliverable.
--set id=UNKNOWN
Answer: unknown, then the risk, and a line under "Open questions"
The pack is ready when every must-have question is either answered or marked unknown with its risk. Being "ready" does not mean the risks are gone: the brief lists them under "Open questions", and a deliverable built on an unknown must-have is expected to repeat the risk in its opening paragraph. A value you chose yourself is recorded with --set even when it equals the default; --default is only for "I accept what you propose".
The validator is strict: an unknown field, a wrong type or a broken rule is an error, never silently ignored. The same validator runs when you call bmad-plus intake check and when BMAD+ itself is built.
Top-level and section fields
Field
Required
Rule
schema_version
yes
Must be 1.
id
yes
A lowercase slug starting with a letter (letters, digits, hyphens). Names the intake; the answers file refers to it.
pack
yes
A lowercase slug: the id of the pack the intake belongs to.
title
yes
Non-empty text. Becomes the first line of the brief.
language
no
user (ask in the user’s language, the default) or a tag such as fr or en-GB.
max_per_message
no
Integer from 1 to 20; 6 when absent. The most questions the assistant asks in one message.
sections
yes
Non-empty list of sections. Each has an id (slug, unique), a title and a non-empty questions list.
Question fields
Field
Required
Rule
id
yes
snake_case, starting with a letter, unique across the whole file (not only its section).
ask
yes
The question, as the assistant will put it. Write it as you would ask a person.
kind
yes
text, choice, number, boolean or list.
choices
for choice
At least two distinct non-empty values. Refused on any other kind.
min, max
no
Bounds for a number. Refused on any other kind; min may not exceed max.
default
no
A value valid for the kind. Forbidden on a must-have question: the user answers it.
must_have
no
true when the pack cannot start without an answer or an explicit "unknown".
risk_if_unknown
for must-have
What goes wrong if the answer stays unknown. Required when must_have is true; allowed otherwise.
feeds
no
List of artifact anchors such as brief.md#audience. Informative: it tells a reader which part of the work the answer feeds.
Kinds and how a value is typed on the command line
Kind
Accepted value
Limits
text
Any non-empty text; line breaks and tabs allowed, other control characters refused.
At most 4000 characters.
choice
Exactly one of the listed choices.
Anything else is refused with the list of valid values.
number
A finite number, within min and max when set.
Out of range is refused.
boolean
true, yes, y or 1 for true; false, no, n or 0 for false.
Anything else is refused.
list
Items separated by commas on the command line: --set "highlights=faster install, new swarm command".
The command lists which questions to ask next: must-haves first, then the others in file order, at most max_per_message. It exits 0 when ready and 2 when a must-have is still open.
In your project terminal
npx bmad-plus intake plan --intake FILE --answers FILE
02
Ask
The assistant asks those questions in one message, in your language, with the choices or bounds, and proposes the default where there is one. It does not pad the message and does not ask what is already recorded.
03
Record
Each answer is checked against the question’s kind and bounds and refused if it does not fit (nothing is written). UNKNOWN records the risk; --default records the proposal. A question can be set only once per call.
Plan again. When it says ready, stop asking, even if optional questions remain: the assistant names the open ones and moves on unless you want to continue.
05
Render the brief
The brief lists every section and every answer with its status, then the open questions with their risks. It is the input of the pack’s next step.
A complete example: the Design pack’s design read#
The only intake shipped in 0.23.0 belongs to the Design pack. Atelier, its agent, runs it before any design work (/design read). It has 13 questions in 5 sections, and 3 of them are must-haves: the surface, the audience and the job the page must do. The three dials (variance, motion, density) and the stack carry no default on purpose: a dial you leave open is set by Atelier from the surface, and the brief records that it was inferred.
Here is a real run of the 0.23.0 CLI on this intake (a dashboard for accountants, answers typed in French). Validity first, then the plan with its exit code 2, then three answers, then the brief.
Design read: check, plan, record, render
$ npx bmad-plus intake check .agents/skills/pack-design/intake.yaml.agents/skills/pack-design/intake.yaml: valid intake design-read (design), 13 questions, 3 must-have.$ npx bmad-plus intake plan --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.jsondesign-read (design): not ready — 0 answered, 0 defaulted, 0 unknown, 13 not asked yet.Must-haves still open: surface_kind, audience, jobAsk next (at most 6 per message): - surface_kind [choice] Which surface is this? A marketing landing page, a dashboard, a data table, a form, an auth or onboarding flow, a mobile screen, an email, a chat or AI surface, a checkout, a settings page, or something else? (must-have; choices: landing, dashboard, table, form, auth, onboarding, mobile, email, ai-chat, checkout, settings, other) - audience [text] Who uses it, and in what situation (device, time pressure, expertise, frequency)? (must-have) - job [text] What is the one job this page must get done, and how will you know it did? (must-have)# Three more optional questions follow in the real output (brand_assets, design_system, aesthetic_family). The exit code is 2: not ready.$ npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set surface_kind=dashboard --set "audience=Comptables en cabinet, sur ordinateur, en fin de mois" --set "job=Repérer les factures en retard et relancer en un clic"Recorded surface_kind, audience, job in _bmad-output/design/intake.json.design-read (design): ready — 3 answered, 0 defaulted, 0 unknown, 10 not asked yet.$ npx bmad-plus intake render --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --out _bmad-output/design/brief.mdBrief written to _bmad-output/design/brief.md (3022 bytes); the intake is ready.$ head -n 3 _bmad-output/design/brief.md# Design read<!-- owned by bmad-plus intake design-read; written by bmad-plus intake render, do not edit --># The "Ask next" list that record prints after each batch is left out here. The last command shows the first three lines of the brief.
npx bmad-plus intake check .agents/skills/pack-design/intake.yaml
npx bmad-plus intake plan --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json
npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set surface_kind=dashboard --set "audience=Comptables en cabinet, sur ordinateur, en fin de mois" --set "job=Repérer les factures en retard et relancer en un clic"
npx bmad-plus intake render --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --out _bmad-output/design/brief.md
head -n 3 _bmad-output/design/brief.md
Output captured from a real run. Source: bmad-plus 0.23.0, 2026-10-19
The second transcript continues with a different scenario (a landing page for a coffee bar) and shows the other two statuses: a default accepted with --default, a value chosen with --set, and a dial marked unknown. Note that the plan stays "ready" with an unknown dial because it is not a must-have, and that the brief that follows reads the status of each answer.
Design read: defaults, unknown, and the brief
$ npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set surface_kind=landing --set "audience=Urban professionals who buy coffee most days; on a phone, on the way to work, a few seconds of attention" --set "job=Get a first-time visitor to open a loyalty card; success is the sign-up button being tapped"Recorded surface_kind, audience, job in _bmad-output/design/intake.json.design-read (design): ready — 3 answered, 0 defaulted, 0 unknown, 10 not asked yet.$ npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set brand_assets=partial --default design_system --default aesthetic_family --set motion=3 --set variance=UNKNOWNRecorded brand_assets, motion, variance, design_system, aesthetic_family in _bmad-output/design/intake.json.design-read (design): ready — 5 answered, 2 defaulted, 1 unknown, 5 not asked yet.Unknown: variance$ npx bmad-plus intake render --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --out _bmad-output/design/brief.mdBrief written to _bmad-output/design/brief.md (3043 bytes); the intake is ready.# The "Ask next" lists printed by record are left out.
npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set surface_kind=landing --set "audience=Urban professionals who buy coffee most days; on a phone, on the way to work, a few seconds of attention" --set "job=Get a first-time visitor to open a loyalty card; success is the sign-up button being tapped"
npx bmad-plus intake record --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --set brand_assets=partial --default design_system --default aesthetic_family --set motion=3 --set variance=UNKNOWN
npx bmad-plus intake render --intake .agents/skills/pack-design/intake.yaml --answers _bmad-output/design/intake.json --out _bmad-output/design/brief.md
Output captured from a real run. Source: bmad-plus 0.23.0, 2026-10-19
Part of the rendered brief (_bmad-output/design/brief.md)
# Design read
<!-- owned by bmad-plus intake design-read; written by bmad-plus intake render, do not edit -->
Intake `design-read` of pack `design` — 5 answered, 2 defaulted, 1 unknown, 5 not asked yet. Ready.
## Who and why
### audience (must-have)
Who uses it, and in what situation (device, time pressure, expertise, frequency)?
Answer:
Urban professionals who buy coffee most days; on a phone, on the way to work, a few seconds of attention
Feeds: brief.md#audience
### design_system
Is a design system or component library already in use?
Answer (proposed default, not confirmed by the user):
none
Feeds: brief.md#design-system
### variance
Layout variance, from 1 (symmetric and safe) to 10 (asymmetric and bold)? Leave it open and Atelier sets it from the surface.
Answer: unknown
Feeds: brief.md#dials
## Open questions
- variance: unknown
- density: not asked yet
- stack: not asked yet
- accessibility_target: not asked yet
- languages: not asked yet
- required_states: not asked yet
Atelier then builds from this brief. The rules it follows come from the Design pack: an answer keeps its status through every deliverable (a default or an inference is flagged where it was used, an unknown must-have travels with its risk), and a dial left open is set from the surface and recorded as inferred.
Use with the Design pack installed, or with any pack that ships an intake, before the first deliverable.
In your AI assistant
Run the design intake before you build anything. Use bmad-plus intake plan to see what is open, ask me at most six questions at a time in my language with must-haves first, propose the defaults, record what I do not know with its risk, then render the brief and show it to me before you start.
What you should get The assistant asks the first batch (surface, audience, job and up to three more), records your answers through intake record, repeats until plan says ready, and renders _bmad-output/design/brief.md. You see which answers are yours, which are proposed defaults and which are unknown with their risks before any design work starts.
Any file in your project can be an intake: bmad-plus intake check|plan|record|render take plain project-relative paths. The example below is a made-up pack called releasekit. It has two must-haves with a risk each, one default that is honest (the audience is both unless told otherwise), a bounded number and a free list.
intake/release-notes.yaml
schema_version: 1
id: release-notes
pack: releasekit
title: Release notes brief
language: user
max_per_message: 4
sections:
- id: release
title: The release
questions:
- id: version
ask: Which version is being released?
kind: text
must_have: true
feeds: [brief.md#release]
risk_if_unknown: The notes cannot say what changed in which version.
- id: audience
ask: Who reads the notes?
kind: choice
choices: [developers, end-users, both]
default: both
feeds: [brief.md#audience]
- id: content
title: What goes in
questions:
- id: breaking_changes
ask: Is anything breaking for existing users?
kind: boolean
must_have: true
risk_if_unknown: A breaking change ships without a migration note.
- id: highlights
ask: What are the highlights, one per item?
kind: list
- id: max_words
ask: How long may the notes be?
kind: number
min: 50
max: 2000
default: 300
Validate first. The file below is deliberately broken (a must-have with a default and no risk, a choice with a single value); the validator reports all three problems at once. The terminal then validates the sound file and runs the loop on it.
intake/broken.yaml
schema_version: 1
id: broken-demo
pack: releasekit
title: A deliberately broken intake
sections:
- id: release
title: The release
questions:
- id: version
ask: Which version is being released?
kind: text
must_have: true
default: "1.0.0"
- id: audience
ask: Who reads the notes?
kind: choice
choices: [everyone]
Validate, plan, record, refuse, render
$ npx bmad-plus intake check intake/broken.yamlintake/broken.yaml: not a valid intake - sections[0].questions[0]: a must-have question needs "risk_if_unknown" - sections[0].questions[0]: a must-have question takes no "default"; the user answers it - sections[0].questions[1]: a choice needs at least two distinct non-empty "choices"# Exit code 1.$ npx bmad-plus intake check intake/release-notes.yamlintake/release-notes.yaml: valid intake release-notes (releasekit), 5 questions, 2 must-have.$ npx bmad-plus intake plan --intake intake/release-notes.yaml --answers notes/answers.jsonrelease-notes (releasekit): not ready — 0 answered, 0 defaulted, 0 unknown, 5 not asked yet.Must-haves still open: version, breaking_changesAsk next (at most 4 per message): - version [text] Which version is being released? (must-have) - breaking_changes [boolean] Is anything breaking for existing users? (must-have) - audience [choice] Who reads the notes? (choices: developers, end-users, both; default: "both") - highlights [list] What are the highlights, one per item?$ npx bmad-plus intake record --intake intake/release-notes.yaml --answers notes/answers.json --set version=UNKNOWN --set breaking_changes=yes --default audience --set "highlights=faster install, new swarm command"Recorded version, breaking_changes, highlights, audience in notes/answers.json.release-notes (releasekit): ready — 2 answered, 1 defaulted, 1 unknown, 1 not asked yet.Ask next (at most 4 per message): - max_words [number] How long may the notes be? (default: 300)Unknown: version — risk: The notes cannot say what changed in which version.$ npx bmad-plus intake record --intake intake/release-notes.yaml --answers notes/answers.json --set max_words=40Intake: max_words: value must be at least 50$ npx bmad-plus intake record --intake intake/release-notes.yaml --answers notes/answers.json --set max_words=250Recorded max_words in notes/answers.json.release-notes (releasekit): ready — 3 answered, 1 defaulted, 1 unknown, 0 not asked yet.Unknown: version — risk: The notes cannot say what changed in which version.$ npx bmad-plus intake render --intake intake/release-notes.yaml --answers notes/answers.json --out notes/brief.mdBrief written to notes/brief.md (921 bytes); the intake is ready.
Output captured from a real run. Source: bmad-plus 0.23.0, 2026-10-19
The brief it wrote (notes/brief.md)
# Release notes brief
<!-- owned by bmad-plus intake release-notes; written by bmad-plus intake render, do not edit -->
Intake `release-notes` of pack `releasekit` — 3 answered, 1 defaulted, 1 unknown, 0 not asked yet. Ready.
## The release
### version (must-have)
Which version is being released?
Answer: unknown — risk: The notes cannot say what changed in which version.
Feeds: brief.md#release
### audience
Who reads the notes?
Answer (proposed default, not confirmed by the user):
both
Feeds: brief.md#audience
## What goes in
### breaking_changes (must-have)
Is anything breaking for existing users?
Answer:
yes
### highlights
What are the highlights, one per item?
Answer:
faster install
new swarm command
### max_words
How long may the notes be?
Answer:
250
## Open questions
- version (must-have): unknown — risk: The notes cannot say what changed in which version.
Make a question a must-have only if the pack really cannot do useful work without it. The risk sentence is the test: if you cannot say what goes wrong, it is not a must-have.
Give a default only when it is honest, that is when most people would pick it and a wrong guess is cheap. A default is shown in the brief as a proposal, so it never passes for the user’s own word.
Prefer a choice to free text when the answer drives a decision in the pack: the value can be tested, the brief stays comparable from one run to the next.
Bound numbers with min and max: a refused value is caught at record, not discovered in the deliverable.
Keep max_per_message low (3 to 6). A long form is answered badly and skimmed.
Use feeds to say which artifact each answer shapes. It costs nothing and lets a reviewer trace a decision back to a question.
Write ask in the plain language you would use with a client. The assistant translates it for the user when language is user; do not write several languages in one file.
In a project, put the file anywhere and pass it with --intake; the assistant needs to be told where it is (a line in your adapter or in the skill that drives the work). To make it part of a pack of the BMAD+ source repository, add intake: intake.yaml to the pack in registry.yaml (the path is relative to the pack directory): the build then validates it, the installer copies it and bmad-plus doctor requires it. The registry is a source-level file; it is not installed in your project. See the registry page.
Draft and validate an intake for your own workflow#
Use when a recurring job in your team always starts with the same missing information.
In your AI assistant
Write an intake file at intake/release-notes.yaml for the questions we always forget before writing release notes: the version, whether anything is breaking, who reads the notes, the highlights and a length limit. Make only the version and the breaking-change question must-haves, each with the risk if unknown. Give a default only where it is honest. Then run bmad-plus intake check on it and fix every problem it reports before showing me the file.
What you should get A YAML file that intake check reports as valid, with the number of questions and must-haves printed on one line. If the first draft breaks a rule, the validator lists every problem at once (for example a must-have that carries a default), and the file is fixed before you see it.
Answers are written only through record, the brief only through render. Do not edit either by hand.
render overwrites a brief only if the file starts with the ownership marker of the same intake (line 2). It never replaces a brief it did not write. record only overwrites an answers file of the same intake.
Neither command writes under .git/, _bmad/, .bmad-plus/ or node_modules/, nor through a symlink.
What you typed is printed as an indented block under Answer:, so a value can never become a heading or a list item of the brief.
If the intake file changes after answers were recorded, plan reports the mismatch. record --reset starts the answers file again, empty, for the same intake id; it never resets a file that belongs to another intake.
The pack must not start its pipeline or a swarm while a must-have is neither answered nor marked unknown. The commands do not check this themselves: it is an instruction in the autopilot and Nexus skills, which tell the assistant to run the intake of each pack involved first, in Discovery.
The runtime asks nothing, invents nothing and calls no model. A refused value changes nothing on disk.