Tagging for your audience
The same run JSON can serve every audience — engineers get steps and traces, product owners get capabilities, designers get storyboards, support gets a behavior catalog. What routes a scenario to the right audience is nothing more than tags your tests already support:
story.init(testInfo, { tags: ['audience:stakeholder', 'capability:checkout', 'storyboard'],});Tags cost one line, live next to the behavior they describe, and can never go stale independently of the test. This guide names a small vocabulary and shows how to surface it as persona views on your Astro site.
The vocabulary
Section titled “The vocabulary”None of these are special-cased by the framework — they are conventions the views filter and group on. Adopt the ones you need; skip the rest.
| Tag | Meaning | Who filters on it |
|---|---|---|
audience:stakeholder |
Readable by non-engineers: outcome-first title, no internals | Product, leadership |
capability:<name> |
The business capability this verifies (capability:checkout) |
Product — group by capability, not by file |
journey:<id>:<n> |
Position n in an ordered walkthrough — becomes a page at /journeys/<id> |
Product, design, support |
storyboard |
Carries per-step screenshots — renders as a visual filmstrip | Design |
state:<name> |
A UI state the product can be in — appears on the /states thumbnail grid |
Design — state catalog |
viewport:<name> |
Layout variant (viewport:mobile) — shown side by side within its state |
Design — responsive review |
support |
Answers “when a customer does X, what should happen?” | Customer success |
known-issue |
A deliberate limitation, documented honestly (often with story.but) |
Support, QA |
criticality:<level> |
Business criticality (criticality:revenue) |
QA, leadership — risk weighting |
Two existing options complement the tags: ticket links a scenario to the
requirement it verifies (PM coverage questions), and story.link({ label: 'Figma — Checkout v3', url }) attaches design references. Links pointing at a
design tool (Figma, Zeplin, Sketch) — or any link whose label starts with
“Design” — also surface as a Design strip at the top of the scenario’s
story page and of every journey it belongs to, so designers land on the
mockup next to the proof.
Persona views
Section titled “Persona views”The views config in executable-stories-astro mounts one filtered,
re-grouped index per audience — same collection, different lens:
export default defineExecutableStories({ source: 'reports/raw-run.json', views: [ { base: '/for/product', label: 'Product', description: 'What the product verifiably does, by capability.', include: { tags: ['audience:stakeholder'] }, groupBy: 'tag', }, { base: '/for/design', description: 'Visual walkthroughs and UI states, straight from the tests.', include: { tags: ['storyboard'] }, }, { base: '/for/support', description: 'When a customer does X, what should happen?', include: { tags: ['support'] }, }, { base: '/for/qa', description: 'Everything, including planned and known-issue scenarios.', groupBy: 'status', }, ],});Each view gets a page at its base, a sidebar group (“Audiences”) via
storiesSidebar(config), and the same interactive index the main /stories
page uses — search, filters, storyboard filmstrips, failure detail. A view
whose filters match nothing renders a getting-started note naming the tags it
expects, so an empty lens explains itself.
Views never hide failures: a red scenario in a stakeholder view is a feature, not a leak. Honest living docs are the point.
Journeys
Section titled “Journeys”Real user flows span several tests (isolation, parallelism, flake
containment), but stakeholders want one walkthrough. The journey:<id>:<n>
tag composes them:
test('Browse the catalog', async ({ page }, testInfo) => { story.init(testInfo, { tags: ['journey:guest-checkout:1'] }); // ...});
test('Guest checkout walkthrough', async ({ page }, testInfo) => { story.init(testInfo, { tags: ['journey:guest-checkout:2'] }); // ...});Each journey id becomes a page at /journeys/<id> — the member scenarios in
order, rendered as full cards with their storyboards, under one aggregate
status (failed if any member failed; passed only when all passed). The
order suffix is optional; untagged order falls back to source order. Embed a
journey in prose with <StoryJourney id="guest-checkout" />.
Because journeys are a tag convention, they work in every adapter today —
Playwright journeys get filmstrips, and scenarios that capture
state snapshots
show each chapter’s final state card, so a data-only journey still ends every
chapter with what the world looked like. State diffs never cross scenario
boundaries: each chapter tells its own before-and-after. Support teams can
paste /journeys/<id> links straight into tickets.
If your CI runs the CLI with --history-file, point the site at the same
store and journey pages add a run-history badge — “7/10 recent runs passed ·
flaky” — aggregated from the member scenarios (a journey fails a run when any
member failed it):
export default defineExecutableStories({ source: 'reports/raw-run.json', historyFile: 'reports/history.json',});The state catalog
Section titled “The state catalog”state:<name> tags feed /states: a thumbnail grid of every state the
product verifiably has, each card a scenario’s first screenshot — or, for
non-UI scenarios, a data-card thumbnail from its first
state snapshot —
linking to its story page. Tag viewport variants (viewport:mobile,
viewport:desktop) and they sit side by side within their state — same
state, two layouts, compared at a glance. Designers browse what shipped, not
what a hand-maintained inventory claims.
The tag and the doc verb are one concept at two granularities: state:<name>
names a state the product can be in; story.state() shows the data that
proves it.
For auditors: the traceability CSV
Section titled “For auditors: the traceability CSV”QA and compliance get the same truth as a spreadsheet:
executable-stories format reports/raw-run.json --format traceability-csvOne row per requirement-scenario pair (ticket, requirement status, scenario,
evidence grade, source, covered code), plus a row per untraced scenario — the
coverage gaps listed explicitly, not hidden. The evidence_grade column is
the same weak → strong grading the Evidence Review applies (screenshot, OTEL
trace, mutation score, failing-first verification), so the spreadsheet says
not just “passed” but how credible the proof is. It is a flat projection of
the traceability-matrix format, so the two can never disagree.
Environment drift
Section titled “Environment drift”When the site combines two or more sources (a staging run and a production
run, or one run per repo in a docs hub), a /drift page appears: every
scenario’s status per source side by side, mismatches floated to the top.
Behavior verified in one environment but failing — or missing — in another is
exactly the gap a per-environment report hides. Force it on or off with
injectDrift, move it with driftBase.
Leadership
Section titled “Leadership”No separate dashboard is needed: a /for/leadership view grouped by
capability:* tags is the capability map, and the shipped
<HealthDashboard /> and <Trajectory /> components drop into any authored
page for run health and trend.
Rules of thumb
Section titled “Rules of thumb”- Tag behaviors, not tests.
capability:refundsdescribes what the scenario proves. Tags likesloworflakybelong to CI, not personas. - Group by the audience’s mental model. Product thinks in capabilities
(
groupBy: 'tag'), QA in statuses, engineers in features/files. - Start with two views.
/for/productand one more. Add lenses when someone asks for them, not before. - Outcome-first titles do half the work. “Refund lands within 5 days” reads in every view; “test_refund_worker_retry” reads in none.