skills/stories/SKILL.md
Use when generating or updating user story YAML files for UI testing — browses a site with agent-browser, discovers flows, creates structured stories using semantic locators (schema v2) with diff-aware updates, negative testing, source-aware contracts, journey awareness, and self-validation. Keywords - stories, generate, create, user journey, persona, QA, testing, semantic-locators.
npx skillsauth add thomasholknielsen/claude-tweaks claude-tweaks:storiesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Interaction style: Present decisions as numbered options so the user can reply with just a number. For multi-item decisions, present a table with recommended actions and offer "apply all / override." Never present more than one batch decision table per message — resolve each before showing the next. End skills with a Next Actions block (context-specific numbered options with one recommended), not a navigation menu.
Browse a website, understand its structure and flows, and generate user story YAML files for UI testing. Stories describe journeys for any persona — customers, admins, developers, operators.
/claude-tweaks:init → /claude-tweaks:capture → /claude-tweaks:challenge → /superpowers:brainstorming → /claude-tweaks:specify → /claude-tweaks:build → [ /claude-tweaks:stories ] → /claude-tweaks:test → /claude-tweaks:review → /claude-tweaks:wrap-up
^^^^ YOU ARE HERE ^^^^
(conditional — only when UI files change)
/claude-tweaks:build when UI files changed (lifecycle recommendation)Parse $ARGUMENTS to extract:
dev-url-detection.md in skills/_shared/.persona=<name> — the type of user to generate stories for (e.g. "customer", "admin", "developer"). If not specified, infer appropriate personas from the site's structure.dir=<path> — directory to write stories to. Default: stories/focus=<area> — specific area or flow to focus on (e.g. "checkout", "settings", "onboarding")refine=true|false — validate sample stories and self-correct. Default: true.negative=true|false — generate failure-path negative stories. Default: true.journey=<name> — scope generation to pages covered by the named journey. When set, only generates stories for pages documented in that journey file (docs/journeys/{name}.md).Keyword detection rules (applied to $ARGUMENTS):
refine=false → REFINE = false (default is true)negative=false → NEGATIVE = false (default is true)dir=<path> → OUTPUT_DIR = <path>persona=<name> → PERSONA = <name>focus=<area> → FOCUS = <area>journey=<name> → JOURNEY_FILTER = <name>If no URL is provided in $ARGUMENTS, run the dev URL detection procedure from dev-url-detection.md in skills/_shared/. This probes common ports, checks project configuration, and resolves APP_URL automatically. If no server can be detected or started, stop and ask the user for a URL.
Auto-detected behavior:
{OUTPUT_DIR}/*.yaml or {OUTPUT_DIR}/*.yml files already exist, automatically enter diff-aware mode. No keyword needed — the skill detects existing stories and only generates new or changed ones.schema_version: 2 at the top, the v1 detection / regeneration UX (see below) runs before update-mode comparison.All generated stories use schema_version: 2. Locators are semantic only — no CSS, no XPath:
schema_version: 2
stories:
- id: checkout-happy-path
description: Complete a purchase from cart to confirmation
journey: checkout
auth: { vault: "default-user" } # optional — see Auth Vault section
steps:
- action: click
locator: { role: button, name: "Add to cart" }
- action: fill
locator: { testid: "email-input" }
value: "[email protected]"
- action: assert_visible
locator: { text: "Order confirmed", exact: true }
Locators are always semantic (one of: ARIA role, data-testid, visible text, form label, input placeholder) — never CSS, XPath, or @eN snapshot refs. At runtime, locators resolve to session-scoped @eN refs via agent-browser --session <name> find <type> <args>; refs are NEVER stored in the YAML — they regenerate each snapshot. See agent-browser-reference.md in the /claude-tweaks:browse skill directory for the full operation vocabulary.
See locator types and preference order in story-examples.md.
Stories that require login reference an Auth Vault entry via the story-level auth: { vault: "<name>" } field. The vault stores credentials encrypted, locally. The LLM never sees passwords.
Setup (user runs once):
agent-browser auth set <vault-name> <username> <password>
In the story runtime, after open and before the first action:
agent-browser --session <story-id> auth use <vault-name>
This replaces the cookie-injection path used in earlier versions and removes the need for auth.yml profile files in new projects. Stories generated against an existing project that already has {OUTPUT_DIR}/auth.yml continue to work — Step 2's auth resolution detects the legacy file and offers to migrate vault entries from it.
Gather pre-existing information before browsing.
{OUTPUT_DIR}/*.yaml AND {OUTPUT_DIR}/*.yml (both extensions — projects may use either).schema_version: 2. If any file lacks the marker, stop here and read migration.md in this skill's directory for the full migration procedure (auto-mode staging, interactive prompt, per-story diff, regeneration rules). Resolve the v1 prompt BEFORE doing any further processing. Do NOT silently parse legacy v1 files — they use CSS selectors which schema v2 forbids.stories array from each file.
b. Build an EXISTING_STORIES map: { storyId -> { url, locators[], steps[], sourceFile } } — extract all semantic locators from each story's steps.
c. Build an EXISTING_URLS set: all unique url values across existing stories.
d. Log: "Update mode: found {N} existing stories across {M} files in {OUTPUT_DIR}."Use the Glob tool to check whether any docs/journeys/*.md files exist.
journey-ingest.md in this skill's directory for the full ingest procedure (discover, parse, build JOURNEY_MAP + JOURNEY_URL_INDEX, cross-reference with existing stories). Populates JOURNEY_MAP and JOURNEY_URL_INDEX for Step 2; populates JOURNEY_LINK_SUGGESTIONS in update mode.Identify and read component source files to extract behavioral contracts that are not visible from the rendered DOM alone — input constraints, validation schemas, state transitions, conditional rendering, error handling, and API patterns. Produces a per-page SourceContract that feeds into Step 3 design.
For the full procedure (component-file identification, journey-seeded source files, behavioral-signal extraction, graceful degradation), read source-aware-design.md in this skill's directory. For framework-specific extraction patterns (Zod/yup/Joi schemas, JSX heuristics, React introspection) and the SourceContract schema, read source-analysis.md in this skill's directory.
Parallel execution (custom): Stories explores N pages in parallel sub-processes, one agent-browser session per page (
agent-browser --session <name> batch ...). Sessions are processes, not Task agents — each runs concurrently in its own process. Use a single batch per session to bundleopen + snapshot + screenshot. Pages that share state (login → dashboard → settings) must run sequentially within a single session.
mkdir -p via the Bash tool — -p creates parent directories and silently no-ops if the directory already exists; plain mkdir does NOT create parents on macOS/Linux)./claude-tweaks:browse skill to open the site. The concrete command is agent-browser --session <session-name> open <url>. Choose <session-name> per the kebab-case convention (e.g., stories-explore, stories-checkout). Use agent-browser batch --session <name> to bundle open + initial snapshot + reconnaissance screenshot in one process invocation.agent-browser --session <name> snapshot -i -c to understand the page structure. The snapshot returns elements with role, accessible name, text, label, placeholder, and data-testid — these are the only attributes used to build v2 locators.story-examples.md. Do NOT capture CSS classes, IDs, or XPath — schema v2 forbids them.When JOURNEY_MAP is non-empty (Step 1.1 found journeys):
For each page visited during exploration, check JOURNEY_URL_INDEX:
Page matches a journey step URL: This page is already documented. Browsing is enrichment-only:
action, should_feel, and should_understand fields for that contextPage does NOT match any journey step URL: Full discovery (existing behavior). This page has no journey context and gets standard exploration.
Journey-guided navigation: When a journey has step URLs that the standard exploration (items 4-5) would not naturally visit, add those URLs to the exploration queue. This ensures all journey-documented pages get enrichment passes. Journey enrichment pages are counted separately from the 5-8 page discovery cap — they are verification, not discovery.
If no discovered page requires authentication and no legacy {OUTPUT_DIR}/auth.yml exists, skip this section entirely.
If at least one auth-gated page was found OR a legacy auth.yml is present, read auth-resolution.md in this skill's directory for the full procedure (vault listing, vault-name convention, interactive/auto branches, legacy auth.yml migration trigger).
For each page visited during exploration, map its URL to the source files that render it. The mapping produces a SOURCE_FILES array per URL (relative paths, empty [] if none identified) that feeds into Step 3 design.
For the full procedure (route-path extraction, framework detection for Next.js App/Pages Router and SvelteKit/Remix, local-import discovery) and the gotchas (route groups, dynamic routes, monorepo subdirectories), read source-aware-design.md in this skill's directory.
id — a stable kebab-case identifier (e.g. front-page-loads, checkout-flow-completes)description — a human-readable description of the journey (also accepted as name for back-compat)url — the starting URL (the qa-agent auto-navigates here, so no "Navigate to X" step needed)tags — categorize by type: navigation, core, form, error-handling, smoke, criticalpriority — high for happy-path core flows, medium for secondary flows, low for edge casessource_files — array of relative file paths from the URL-to-Source-File Mapping (Step 2). Populate from the SOURCE_FILES data collected during exploration for the story's URL. Use an empty array [] if no mapping was found.auth — (optional) { vault: "<vault-name>" } for stories that require login. See Auth Vault section.steps — structured step array with action, semantic locator, optional value, and optional verify per the v2 schemaWhen a page has a non-empty SourceContract (from Step 1.5), use its signals to generate deeper stories — boundary-value stories for input constraints, state-transition stories, error-path stories, and conditional-rendering coverage. When the SourceContract is empty, generate stories from DOM exploration data only.
For the full per-signal generation rules (input boundaries, state transitions, error paths, conditional rendering), read source-aware-design.md in this skill's directory.
When JOURNEY_MAP is non-empty (Step 1.1 found journeys), use journey data to inform story design for pages with journey context:
a. Map journey steps to stories. Each journey becomes a candidate for one or more stories. Prefer creating one story per journey that walks the full flow (entry point → success state) rather than one story per step — journeys are meant to be walked end-to-end. For journeys with many steps (6+), split into logical segments (e.g., "signup flow" and "first-project flow" from a longer onboarding journey).
b. Set the journey: field. Stories derived from a journey MUST include journey: {journey-name} in the YAML output. This field references docs/journeys/{journey-name}.md and enables coverage tracking and filtered test execution.
c. Inherit source files. When a story has journey: set, its source_files starts with the journey's files: frontmatter array, extended by component-level files from source analysis (Step 1.5) and URL-to-Source-File Mapping (Step 2). De-duplicate the merged list.
d. Use journey step descriptions for assertions. Transform journey step expectations into concrete verify assertions:
should_feel: "fast and effortless" → verify assertions about page responsiveness, absence of loading spinners after action, minimal click countshould_understand: "their profile is saved" → verify assertions for success feedback (toast, status message, redirect to expected page)red_flags: "form clears on error" → verify that form state is preserved after validation errore. Preserve journey step ordering. Story steps should follow the journey step order. If a journey has steps 1 through N, the generated story should walk them in that sequence.
f. Still generate negative stories. Journey-aware pages still get negative story generation (when NEGATIVE=true). The journey's red_flags field provides additional negative scenarios beyond the standard form validation / 404 / auth negatives.
When a page has NO journey context (its URL does not appear in JOURNEY_URL_INDEX), generate stories from DOM exploration + source analysis only — the same behavior as before journey integration.
For the full staleness procedure (locator-resolution checks via agent-browser find, git diff source-file checks, behavioral-contract comparison against re-extracted SourceContract) and the warning templates, read source-aware-design.md in this skill's directory.
For each page discovered during exploration that has interactive elements, generate failure-path stories:
Form validation negatives (for pages with forms):
<script>alert(1)</script>, '; DROP TABLE users; --, " onmouseover="alert(1)".notanemail in email fields, abc in numeric fields).Navigation negatives:
{URL}/this-page-does-not-exist-404).Interaction negatives:
Auth negatives (if auth-gated pages were discovered):
Search negatives (if search functionality exists):
Negative story conventions:
neg- (e.g. neg-empty-form-submit, neg-404-handling, neg-search-injection).negative in addition to other relevant tags (e.g. [negative, form], [negative, error-handling]).medium by default. Security-related negatives (injection, XSS) get priority: high.For stories that share common prerequisites:
setup blockrequires labels to indicate prerequisites (e.g. requires: [auth])Auth Vault references (v2): When the story requires authentication, set the story-level auth: { vault: "<vault-name>" } field. This causes the runtime to invoke agent-browser --session <story-id> auth use <vault-name> after open and before the first action. The vault must already be set via agent-browser auth set <vault-name> <username> <password> (one-time, by user). Do NOT inline credentials in story YAML — the LLM never sees passwords.
When the project's auth came from a legacy auth.yml and the user opted not to migrate (Step 2 Auth Resolution), continue to use the legacy setup.auth: <profile> reference. Do not invent vault names that do not exist.
Identify depends_on relationships between stories:
depends_on: add-to-cart on story BStory categories to consider:
If PERSONA was specified, focus stories on that persona. If not, generate stories for the most obvious personas the site serves.
schema_version: 2 at the top. Use this exact format:schema_version: 2
# Optional file-level blocks
setup:
viewport: "1440x900"
# Auth Vault reference (v2 — vault stores credentials encrypted, locally):
auth: { vault: "default-user" }
# Or use the file-level `setup.steps` for non-auth setup actions:
steps:
- action: navigate
target: "https://example.com/setup-page"
teardown:
steps:
- action: click
locator: { role: button, name: "Log out" }
# Story array
stories:
- id: story-kebab-id
description: "Descriptive name of the journey"
url: "https://example.com/starting-page"
journey: profile-settings # optional — refs docs/journeys/profile-settings.md
auth: { vault: "default-user" } # optional — overrides file-level setup.auth
tags: [core, smoke]
priority: high
source_files: # relative paths to source files that render this page
- app/(dashboard)/starting-page/page.tsx
- app/(dashboard)/starting-page/components/hero.tsx
steps:
- action: assert_visible
locator: { role: heading, name: "Welcome" }
- action: click
locator: { role: button, name: "Add to cart" }
verify: "Cart shows 1 item"
- action: fill
locator: { testid: "email-input" }
value: "[email protected]"
verify: "Email field accepts the value"
- action: assert_visible
locator: { text: "Order confirmed", exact: true }
Locators in locator: must be semantic. CSS selectors, XPath, and snapshot refs (@eN) are forbidden in YAML. See locator types and preference order in story-examples.md.
setup and teardown blocks unless exploration found they need updating.stories array. If no file exists for this grouping, create a new file.When NEGATIVE is true:
stories array.{OUTPUT_DIR}/{site-name}-{persona}-negative.yaml.neg-) are included in the diff comparison — they get the same stale-selector treatment as positive stories.File naming: {OUTPUT_DIR}/{site-name}-{persona-or-area}.yaml
stories/myapp-customer.yaml, stories/myapp-admin.yaml, stories/myapp-customer-negative.yamlClose all browser sessions when done writing (Step 5 re-opens fresh sessions for refinement).
After writing, look at the YAML with fresh eyes. Fix issues inline — this is the cheap pass before Step 5's DOM-validation pass.
role, label, text, placeholder) or testid. CSS selectors, XPath, class names, IDs, and @eN snapshot refs are forbidden in schema v2 — brittle structural paths like div > div:nth-child(3) > button get caught here before they break in CI./dashboard and text:Welcome is visible" is actionable.If REFINE=true, Step 5 will catch DOM-level issues this pass missed. If REFINE=false, this is your only check — fix anything you find.
If refine=false was passed in $ARGUMENTS, skip this step entirely and proceed to Step 6.
Otherwise read refine.md in this skill's directory for the full Refine procedure: 5a Quick Validation Pass (sample selection, locator resolution, trace-on-failure), 5b Self-Correction Round (regenerate failed stories from a fresh snapshot, one round max), and 5c Persistent Failure Handling (REFINEMENT_WARNING comment + needs-review tag).
Report what was generated:
Source analysis additions:
Update mode additions:
Negative additions:
/claude-tweaks:test qa tag=negative"Refine additions:
needs-review) and a list of trace paths for inspection: agent-browser trace view <path>Journey coverage (when journeys exist):
When JOURNEY_MAP is non-empty, build the coverage report per coverage-report.md in this skill's directory — produce the Journey Coverage table, the Orphaned Stories list, and (in update mode) the JOURNEY_LINK_SUGGESTIONS auto/interactive flow as a separate batch decision.
| Action | Detail | Ref |
|--------|--------|-----|
| {Generated/Updated/Deleted} | {story description} — {yaml file} | — |
One row per story file written, updated, or deleted. Omit unchanged files.
When invoked by a parent skill (e.g., /claude-tweaks:flow), omit this block — the parent owns the handoff. When invoked directly by a user, emit 2-4 numbered options based on context — include the smoke option only when at least one story is tagged smoke; include the affected option only when update mode regenerated stories; include the journey option only when a journey was the dominant story source. One option marked (Recommended).
/claude-tweaks:test qa — validate all {N} stories against the running app (Recommended)/claude-tweaks:test qa tag=smoke — quick pass on {N} smoke stories first (when smoke stories exist)/claude-tweaks:test qa affected — validate only changed stories (when update mode regenerated stories)/claude-tweaks:test qa journey={name} — validate {N} stories for the {name} journey (when journeys exist)This skill is a component skill — invoked by /claude-tweaks:flow (auto-triggered between build and test when UI files change, unless no-stories). Parent invocation is signaled by $PIPELINE_RUN_DIR being set (set by /flow, /build, or other pipeline orchestrators). Direct invocations may pass --source <parent> as an explicit fallback. When invoked by a parent, omit the ## Next Actions block — the parent owns the handoff. When invoked directly by a user, render Next Actions as shown above.
For complete YAML examples covering DOM-only stories, source-aware stories (with boundary-value and error-path coverage), and journey-aware stories, read story-examples.md in this skill's directory.
id (kebab-case, stable across regeneration) and a description@eN snapshot refs. Refs are session-scoped and resolved at runtime via agent-browser find. See locator types and preference order in story-examples.md.url field handles initial navigation — do NOT include "Navigate to URL" as a first stepsmoke for quick health checks, critical for business-critical flowspriority: high for happy-path core flows, medium for secondary paths, low for edge casesdepends_on sparingly — only when story B literally cannot run without story A succeeding firstauth: { vault: "<name>" } referencing a vault the user has set via agent-browser auth set. The LLM never sees credentials.neg-, tag with negative, and assert on graceful failure behaviorsource_files on every story — use the URL-to-Source-File Mapping from Step 2. If no source files can be identified, use an empty array []. The field must always be present.agent-browser --session <name> trace save traces/<session>/<timestamp>.zip before closing the session and include the path in the failure recorddocs/journeys/, always ingest them before browsing — journey data bootstraps story design and prevents redundant discoveryjourney: on stories derived from a journey file — this enables coverage tracking and filtered test execution via /test qa journey={name}journey: set, seed source_files from the journey's files: frontmatter before extending with component-level files from source analysisschema_version: 2 at the top — required for v2 compatibility checks| Pattern | Why It Fails |
|---------|-------------|
| Generating stories without browsing the site first | Stories must be grounded in actual page structure and the live accessibility-tree snapshot |
| Using CSS selectors, XPath, IDs, or class names in v2 schema | Schema v2 is semantic-only — see locator types in story-examples.md. CSS is brittle and forbidden. |
| Storing @eN snapshot refs in story YAML | Refs are session-scoped and regenerate every snapshot — they are runtime-only. Always store the semantic locator and resolve to a ref at execution time via agent-browser find. |
| Skipping the v1 detection prompt and silently parsing legacy files | v1 stories use CSS selectors that schema v2 forbids — silent parsing produces broken stories. Always run the v1 detection / regeneration UX when schema_version: 2 is missing. |
| Inlining credentials in story YAML | Use auth: { vault: "<name>" } referencing an Auth Vault entry. The LLM never sees passwords. Inline credentials risk accidental commits. |
| Deleting existing stories in update mode | Existing stories may cover flows the exploration didn't re-encounter |
| Vague verify assertions ("page looks right") | QA agents need concrete, testable assertions |
| Including "Navigate to URL" as a first step | The url field handles initial navigation automatically |
| Skipping form-page negative coverage when NEGATIVE=true | When negative generation is enabled, forms with user input should always get validation-failure stories — they catch real security and UX issues. (Choosing negative=false is an explicit opt-out, not an anti-pattern.) |
| Running more than one refinement correction round | Diminishing returns and high token cost — cap at one round |
| Closing a session on step failure without capturing trace first | Failure records without a trace path are not actionable — always run agent-browser --session <name> trace save before close on failure |
| Blocking story generation when source analysis fails | Source analysis enhances stories but must never be a hard gate — degrade gracefully to DOM-only |
| Following imports beyond 3 levels of depth | Signal-to-noise ratio drops and analysis time increases — use what you have at the depth limit |
| Generating source-aware stories for non-user-triggerable conditionals | Conditionals based on server config or feature flags cannot be exercised through the browser |
| Browsing from scratch when journey files document the same pages | Journey files already contain URLs, personas, steps, and success states — ingest them in Step 1.1 and enrich with locators and assertions rather than rediscovering everything |
| Auto-linking existing stories to journeys without user confirmation | Journey link suggestions are recommendations presented in Step 6 — the user decides whether to add journey: fields to existing stories |
| Skill | Relationship |
|-------|-------------|
| /claude-tweaks:browse | /stories speaks /browse's operation vocabulary (open, snapshot, find, click, fill, screenshot, trace, close) and follows /browse's session-naming, screenshot-path, and trace-path conventions. The concrete agent-browser syntax lives in agent-browser-reference.md in the /browse directory. |
| /claude-tweaks:visual-review | /visual-review can share a session with /stories when run back-to-back against the same URL. /visual-review's annotated screenshots reference the same accessibility-tree refs that /stories' locators resolve to. |
| /claude-tweaks:test | /test qa and /test all validate the stories that /stories generates via the qa-agent. /test qa journey={name} filters to stories for a specific journey. Failed stories surface trace paths captured during /stories refinement. Both skills consume dev-url-detection.md from skills/_shared/ for auto-detection. |
| /claude-tweaks:review | /review gates on /test passing (which includes QA when stories exist). /review checks journey-to-story coverage in its code review — uncovered journey steps and orphaned stories are surfaced as informational findings. |
| /claude-tweaks:journeys | /journeys creates journey files (docs/journeys/*.md) that /stories ingests in Step 1.1 for journey-aware story generation. Stories reference their source journey via the journey: field. |
| /claude-tweaks:build | Runs BEFORE /stories — recommends /stories when UI files change. /build may create journey files via /journeys that /stories then ingests. |
| /claude-tweaks:flow | Auto-triggers /stories between build and test when UI files change (unless no-stories). Both consume dev-url-detection.md from skills/_shared/ for URL resolution. |
| /claude-tweaks:init | Detects agent-browser availability during setup; without it /stories' browse-exploration and refinement steps cannot run (degrades gracefully — generates from journey/source-analysis data only). |
| qa-agent (agents/qa-agent.md) | Runtime executor for /stories' YAML — opens an agent-browser session per story, uses Auth Vault for auth: { vault: ... } references, and captures trace-on-failure. /stories' refinement step prefigures this same execution path. |
| /claude-tweaks:help | /help recommends /stories when UI files change and no stories exist; /stories' Next Actions block routes back to /test qa which /help surfaces as next-up |
| _shared/auto-mode-contract.md | Single source of truth for auto-mode behavior — read before adding any auto-mode handling |
development
Use when conducting in-depth web research — multi-source synthesis, citation-audited reports with 4 runtime modes from quick (~2-5 min) to ultradeep (~20-45 min, multi-persona red-team). Keywords - research, deep research, web research, sources, citations, literature review.
development
Use when a lifecycle skill (/test, /review, /build, /flow, /visual-review, /specify) needs to invoke Impeccable design-quality commands. Wrapper that encapsulates "when, how, and whether to invoke Impeccable" so caller skills don't have to know.
tools
Use when you want to know which version of the claude-tweaks plugin is installed.
testing
Use when /claude-tweaks:review passes and you need to capture learnings, clean up specs/plans, update skills, and decide next steps. The lifecycle closure step.