bdd greenfield
Run the full orchestrated loop from an empty directory to an implemented requirement, with exactly two human gates: approving the spec wording, and approving the generated tests before they run. Everything else — scaffolding, validation, scenario authoring, step generation, test execution, phase tracking — is automated.
Usage: bdd greenfield [OPTIONS]
Flags
| Flag | Description |
|---|---|
--root <ROOT> | Project root. Defaults to .. |
--model <MODEL> | LLM model for the generation steps, this run only. |
The orchestrated flow
1. inspect / init scaffold if the root is empty (asks for language)
2. describe you describe what to build in plain words; the
model splits it into requirement proposals
3. pick + wizard you pick a proposal; every field arrives
pre-filled - Enter accepts, typing replaces
4. spec validate structure gate; findings loop back to rewording
5. spec refine wording gate; each finding comes with a "try:" fix
── HUMAN GATE 1 ── approve the requirement's wording
6. scenario + steps Gherkin scenario tagged @REQ-...; step definitions
── HUMAN GATE 2 ── approve the generated tests
7. test → RED the scenario fails honestly
8. implement Enter lets the model attempt the implementation;
a number, e.g. 5, buys that many hands-off attempts
9. test → GREEN loop back to 8 while failing
10. refactor optional; only offered on GREEN
11. mark implemented the requirement's status flips in the spec
The description-driven wizard
With a resolved model, drafting starts from a plain-words description instead of a blank title prompt:
Describe what to build in plain words (one or several requirements). Enter drafts manually instead:
sum numbers from a comma separated string, empty input means zero
Splitting the description into requirements with qwen3-coder-next:latest - working ...
The description holds 2 requirement(s):
1. Comma separated numbers are summed
2. Empty string returns zero
Which requirement first? [1-2, Enter for 1]:
2
Left for later runs: Comma separated numbers are summed. Draft them the same way afterwards.
Walking through REQ-001. Each prompt shows the proposal - Enter accepts it, or type your own wording.
REQ-001 title [Empty string returns zero] (Enter keeps it):
REQ-001 story (As a ..., I want ..., so that ...) [As a user, I want empty input to be 0 so that no input is a safe default.] (Enter keeps it):
REQ-001 criterion 1 [Given an empty string "", when add is called, then the result is 0] (Enter keeps it, '-' drops it):
REQ-001 criterion 2 (leave blank to finish the criteria):
The model must deliver each proposal complete — title, story, and at least one Given/When/Then criterion — or the proposal is dropped. Nothing is accepted silently: every field passes through your hands, and the validate + refine gates still run on whatever you accept.
Drafting falls back to the classic manual prompts whenever the description is left blank, no model is resolved, the model is unreachable, or its reply holds no complete requirement.
At each gate you can approve, decline (the run stops cleanly), or pause to resume later — the phase state and staged changes survive between invocations.
The implementation attempt
On a RED bar with a resolved model, Enter asks the model to make the failing tests pass, then reruns the suite and prints the counts:
RED: 2 tests, 1 failures, 1 errors.
- Req001Test.empty_string_returns_zero: TODO: assert - ...
Press Enter to let the model attempt the implementation and rerun the tests, enter a number to attempt up to that many times without asking again, or type stop to pause here:
Generating an implementation attempt - working ...
Updated src/main/StringCalculator.java (llm).
Updated src/test/java/Req001Test.java (llm).
Running the tests - working ...
GREEN: 2 tests, 0 failures, 0 errors.
Every working ... line is live on a terminal: the trailing dots
animate in light yellow — . .. ... and over again — while the
model call or test run is in flight, then the line settles. Piped
output prints the single static line.
The model receives the requirement, the full failing test details — stack traces and build output included — the project’s source files, every prior attempt on this requirement (which files it wrote, which failures it was addressing, and what the run after it actually reported), and the session language’s best practices — package naming for Java, snake_case modules for Rust, and so on — so a second attempt never starts blind or repeats an approach that already failed. It must reply with complete files: production code plus real bodies for the TODO placeholders in the generated tests and step definitions. Only paths already in the project (or the production file named after the spec’s project) are accepted; anything else in the reply is dropped. The rerun is the real validator — if the bar stays RED, press Enter for another attempt (the fresh failure details plus the attempt history go back to the model) or implement by hand and press Enter.
A number buys a hands-off stretch: answering 5 lets the model
attempt, rerun, and attempt again up to five times without asking in
between — each round announced as Attempt 2 of 5. — stopping early
the moment the bar turns GREEN. When the budget runs out on RED, the
prompt returns. Anything unreadable at the prompt behaves like
Enter: one attempt.
An unusable reply is narrated
(The model's reply held no usable file update. Implement by hand instead.) and the loop simply hands control back to you. Dead ends
like this — model failures, missing runtimes, hand-offs back to manual
work — print in red so they stand out from the loop’s narration. Without a
model, Enter just reruns the tests.
Typing stop pauses the run; a paused project continues with the
standalone bdd implement command, which runs the
same attempt from the persisted failure details:
bdd implement REQ-001 && bdd changes commit && bdd test
Rewording loop details
When validation or refinement finds problems, each finding is printed with a concrete suggestion:
REQ-001: the outcome is not concrete
try: end with the exact expected value, e.g. '..., then the result is 3'
With a model, the findings become its brief: the draft and the findings go to the model, and the re-prompts carry its corrected proposal — Enter accepts each fix. If the review rejects a wording again, the next model call also recounts every earlier wording and the findings each one produced, so the model never circles back to a wording the review already rejected:
Asking qwen3-coder-next:latest to address finding 1 of 1 - working ...
The model reworded the draft. Each prompt shows its proposal - Enter accepts it, or type your own wording.
With several findings, each one is its own model call — call 2 is briefed with the draft call 1 fixed, so the corrections accumulate one finding at a time instead of all at once.
On a color console the bracketed suggestion — the text Enter
will use — renders green; the destructive '-' drops it hint on
criterion prompts and dead-end messages render red; the animated dots
on working ... lines render light yellow, and the
Generating an implementation attempt announcement renders dark
green. On a real
terminal every answer is edited on a > line with full line editing:
the arrow keys move the cursor anywhere in the typed text, Home/End
jump, and the up arrow recalls earlier answers from this session.
If the model call fails or its rewording is unusable, the re-prompt falls back to the requirement id and your prior answer; Enter keeps it:
REQ-001 title [Two numbers separated by a comma are summed] (Enter keeps it):
REQ-001 criterion 1 [Given "1,2", when add is called, then the result is 3] (Enter keeps it, '-' drops it):
REQ-001 criterion 3 (leave blank to finish the criteria):
Reply
The final JSON reply summarizes where the run ended:
{
"requirement": "REQ-001",
"feature": "features/string_calculator.feature",
"phase": "GREEN",
"completed": true,
"nextStep": "REQ-001 is implemented. Draft the next requirement with 'bdd spec draft'."
}
completed is false when a gate was declined, a runtime was
missing, or the run was paused; nextStep always says how to
continue.
Requirements for a full run
- The language’s runtime must be present (
mvn,node,dotnet, orcargo) — the orchestrator refuses to fake a test run. - An LLM is optional: with no reachable Ollama model the generation
steps fall back to deterministic templates you edit yourself. The
model this CLI is developed and run against is
qwen3-coder-next:latest(ollama pull qwen3-coder-next:latest). Your mileage will vary with other models, especially those not trained for development work.
See also
- The workflow — the same rhythm, step by step.
bdd model— pick which model powers generation.