rmb32 / barnspec
Capture user stories and Given/When/Then acceptance scenarios as a living Specification — story mapping for a Business Driven approach
Requires
- php: ^8.5
- ext-ds: ^2.0
- php-ds/php-ds: ^2.0
- rmb32/barn-paths: ^0.1@dev
- rmb32/barntree: ^0.1@dev
- rmb32/filesystem: ^1.0@dev
- rmb32/typed-input: ^2.0@dev
- symfony/uid: ^8.0
Requires (Dev)
- deptrac/deptrac: ^4.7
- infection/infection: ^0.35.4
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^12.0
- rector/rector: ^2.6
- rmb32/typed-json: ^1.0@dev
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Early, version 0.x. Not ready for real projects yet; the minor version is the breaking one.
Part of Barn.
Capture user stories and their Given/When/Then acceptance scenarios in plain language, in one place, and turn them into readable documents, a glossary, a visual story map, tracked work tickets and runnable tests.
You describe stories and scenarios once, one question at a time in the GUI, in the same "Complete the sentence…" style throughout — no code or framework knowledge needed. Until its Phase is released, anything can be reworded or deleted. Everything is written back as sentences that still read correctly, so anyone on the team can review it.
In a barn it is the Spec tab, beside
Barnark's architecture. It works without Barn too: barnspec init makes any project a barn.
The easiest way to use it is Barnspec GUI, in your browser: its story map, the map's history phase by phase, every capture flow and the Work board. The command line does the same from a terminal, and is the better fit for scripts, CI and AI agents.

Requirements
- PHP 8.5+
- Composer
The ds PHP extension is used when present but isn't required: the
php-ds/php-ds polyfill comes in as a dependency and satisfies it.
Installation
composer require --dev rmb32/barnspec rmb32/barnspec-cli rmb32/barnspec-gui
vendor/bin/barnspec gui # opens the GUI in your browser
rmb32/barnspec is the library; barnspec-cli provides
the vendor/bin/barnspec binary, and barnspec-gui
adds its gui command. Leave the GUI out where only the command line is needed. See the
quick start for shorter ways
to type it.
Quick start
From your project's root, run init if it does not yet have a .barn/ folder:
vendor/bin/barnspec init
vendor/bin/barnspec create-story --area-of-business="Commerce" --area-of-business-meaning="Buying and selling online" \
--role="Customer" --role-meaning="Someone who buys" --goal="update basket quantities" --reward="get exactly what they want"
vendor/bin/barnspec refine # add Given/When/Then Scenarios
vendor/bin/barnspec reword # change the wording, until its Phase is released
vendor/bin/barnspec set-example # give a parameter an example, at any time
vendor/bin/barnspec show # see what's been captured
refine is interactive on purpose — some questions (accepted or rejected?) are a
human judgement call. Everything else has a non-interactive form for scripts, CI and
AI agents (create-area-of-business, create-phase, restage-story, delete,
create-scenario --file=…, show --json). The quick start
walks through both side by side.
The lifecycle backbone
A Story can sit at one stage of the role's life with the business —
establish, adjust, transact, resolve or close, in that order. The
vocabulary is fixed across every area of business rather than defined per area
of business, so story maps across areas of business line up column for column. It is deliberately not CRUD:
transact and resolve have no mutation-verb analogue, which is the point —
the ordering principle is where in the lifespan a story sits, not which verb
it uses.
A stage is optional. A Story without one is Unassigned — a permanent home
for the stories that genuinely are not about a lifespan (reporting, compliance,
notification), not a staging area. Set one at capture time
(create-story --stage=…, or the skippable step in review) or afterwards
(restage-story, or the selector on a card in the GUI), until the Story's Phase
is released. After that the stage is locked like the rest of the Story, and
changes only through an elaboration.
Changing a released Story
Once a Story's Phase is released its Scenarios no longer change directly —
create-scenario and the editor refuse, and say what to run instead. A change
goes through an elaboration of the Story in the active Phase:
vendor/bin/barnspec elaborate-story --area-of-business=commerce --story=add-a-product-to-the-basket \
--goal="add any product to the basket" # optional new wording
vendor/bin/barnspec create-scenario --file=new-case.json # with "elaboration": true
vendor/bin/barnspec create-scenario --file=better-case.json # with "amends": "<scenario slug>"
vendor/bin/barnspec retire-scenario --area-of-business=commerce --story=add-a-product-to-the-basket \
--scenario=adds-a-unit-to-a-locked-basket
vendor/bin/barnspec withdraw-from-elaboration … # undo a pending change
refine offers a released Story once it is elaborated, and captures into the
elaboration. When the Phase is released the elaboration is merged into the
Story: its new and amended Scenarios hold from then on, retired ones stop
holding, and what was replaced is kept as history. Until then
scenario-bridge run-all --released skips the Scenarios being changed, so the
released Phases stay green while the new behaviour is built.
Was the reward delivered?
Scenarios prove a Story's goal. Its Impact records whether the reward
followed. Each Story gets one per Phase it shipped in, as dated, attributed
observations: evaluating, achieved, none or superseded.
vendor/bin/barnspec release-phase --area-of-business=commerce # asks about earlier Phases' rewards first
vendor/bin/barnspec record-impact --area-of-business=commerce --story=add-a-product-to-the-basket \
--state=achieved --observation="I filled my basket in one visit" --source=role --who="a regular customer"
A Phase is released only once it is proven: every Story in it green, each of
its Scenarios with its tickets done and a passing run recorded. An empty Phase
has nothing proven, so it can't be released. An elaboration that only rewords,
restages or retires needs no run. release-phase never refuses to
release because a reward is unanswered; it asks, and you can leave any answer
blank. achieved is settled only by the
role or someone speaking for them. Impact never changes a Story's colour.
Where your data lives
Barnspec keeps everything in the project's barn: a project whose root holds a .barn/ folder. Its
records are under .barn/barnspec/: each Area of Business is a Barntree data document in
areas-of-business/, beside scenarios/, work/ and refinements/. Commit the barn with your code.
Barnspec finds the barn from any folder inside it, and writes nothing anywhere else.
With no barn, Barnspec refuses to read or write. A tool built on Barnspec opens it with
new BarnspecApiFactory()->open($folder, $fileStore), or BarnspecReadOnlyApiFactory to only read;
both throw NoBarnFoundException, naming the folder, when there is no barn. To begin a spec, call
start($folder, $fileStore), which makes .barn/ in that folder if it has none. Inside another barn
it refuses with InsideAnotherBarn unless you pass insideAnotherBarn: true.
vendor/bin/wizard-reset (from Barnspec CLI) previews wiping it all so you can
start over; add --force to do it.
Render what you've captured
vendor/bin/barnspec write commerce --output-dir=docs/stories/commerce # one doc per Story
vendor/bin/barnspec glossary commerce --output=docs/glossary.md # ubiquitous-language glossary
vendor/bin/barnspec glossary --output-dir=docs/glossary # every area, plus a linked index.md
vendor/bin/barnspec history --output-dir=docs/history --stories-dir='../stories/{area-of-business}' # HTML history, phase by phase
vendor/bin/barnspec map --area-of-business=commerce # the story map, printed: every story once, as it reads now
Link scenarios to real work (Work)
Work tracks tickets against Scenarios and is the source of truth for their
status. Every work:* command names the area, the Story and then the
Scenario within it.
vendor/bin/barnspec work:create commerce basket-quantity-updates caps-at-the-stock-level --title="Cap quantity" --reference=SHOP-42
vendor/bin/barnspec work:list commerce basket-quantity-updates caps-at-the-stock-level
vendor/bin/barnspec work:status commerce <ticket-id> in-progress # to-do | in-progress | done
vendor/bin/barnspec work:delete commerce <ticket-id> # scrap a ticket opened by mistake
vendor/bin/scenario-bridge run commerce caps-at-the-stock-level # this is what turns it green
A Scenario's fulfilment colour is worked out live: yellow while any ticket
isn't done, otherwise green once it has passed a scenario-bridge run and
red until then, or after a run fails. A Scenario with no ticket and no run
has no colour. There is no command to confirm a Scenario by hand — a passing
run is the only thing that makes one green, and a later failing run takes it
back to red. work:list prints the last run beside
the colour, so a red Scenario says whether it has never been run or is
currently failing.
A recorded run never expires on a clock. Creating a new ticket on a green Scenario drops it back to yellow and discards the run: the evidence predates the new work, so the Scenario has to be run again once that ticket is done.
Tickets are disposable. Releasing a Phase deletes the done tickets in its area
of business, except those of a Scenario not run since, which they keep red.
work:delete deletes one at any time. The runs stay, so deleting a done ticket
changes no colour. Deleting an open one leaves the colour to the runs, and
opening it had already discarded the last one, so run the Scenario again.
Deleting a Scenario or a Story deletes its tickets and runs with it.
A Story's colour is then rolled up from its Scenarios rather than stored:
green when every Scenario is green, red when every Scenario is red or green
and at least one is red, yellow otherwise — including while one Scenario is
untouched and a sibling is already under way. A Story with no Scenario at all
has no colour, and work:create refuses it: run refine and capture one
concrete case first.
Turn scenarios into running tests
Two optional packages take your captured Scenarios to executable tests:
vendor/bin/port-generator generate commerce # write Arrange/Assert/Act/Authenticate stubs
# fill in the stubs with real application logic, then:
vendor/bin/scenario-bridge run commerce basket-quantity-updates
vendor/bin/scenario-bridge run-all commerce --summary --xml-report=build/report.xml
See Port Generator and
Scenario Bridge. Configure where stubs go in
.barn/barnspec/settings.json.
Related packages
- Barnspec CLI —
barnspec(wizard, write, glossary, map, history and work:* subcommands). - Barnspec GUI — browser front end.
- Port Generator (library +
port-generatorCLI) — generate Port stubs. - Scenario Runner · Scenario Runner CLI — execution engine and unit-test scaffolding.
- Scenario Bridge (library +
scenario-bridgeCLI) — run captured Scenarios. - Menu and Filesystem — shared plumbing.
More docs
Quick start · Philosophy · Contributing · Internals · History · Known issues
License
Proprietary. See LICENSE. Copyright (c) Roger Barnfather.
Unfinished authoring
SaveRefinement stores a labelled JSON authoring snapshot for an area of business without creating formal stories, scenarios or proof. GetRefinements retrieves those snapshots through either public API factory. Refinements are separate records under the configured Barnspec root; keep them with the project data when backing up. They do not change the scenario or release lifecycle.