acceptance-criteria-extractor
Reads a user story, PRD section, or feature spec and emits well-formed acceptance criteria as Given/When/Then steps in Gherkin (Feature/Scenario file format) or as a numbered plain-text list. Identifies missing-precondition gaps and proposes Background blocks for shared context. Use after a story is testability-confirmed and before implementation begins.
Install with skills.sh (any agent)
npx skills add testland/qa --skill acceptance-criteria-extractoracceptance-criteria-extractor
Overview
This skill takes natural-language story text and emits acceptance criteria (ISTQB (opens in new window)) in two interchangeable shapes:
When to use
If the input is a non-functional requirement (perf / a11y / security / i18n), use non-functional-requirement-extractor instead - those have a different shape (thresholds, not Given/When/Then).
Step 1 - Identify the actor and the trigger
Read the input and tag each sentence as a Given (system context / actor state), When (the triggering event), or Then (the observable outcome, e.g. a data-testid="profile-saved-toast" appears) per Gherkin (gherkin-ref (opens in new window)). Sentences describing steady state are Givens; sentences with action verbs ("clicks", "submits", "navigates") are Whens; sentences asserting outcomes are Thens.
Step 2 - Choose Scenario vs. Scenario Outline
Per gherkin-ref (opens in new window), a Scenario Outline is the template form for running the same scenario with multiple data sets, expanded via the Examples: block:
Scenario Outline: User logs in with various credentials
Given a user with status "<status>"
When they submit valid credentials
Then login returns "<http_status>"
Examples:
| status | http_status |
| active | 200 |
| suspended | 401 |
| not_verified| 403 |Use Scenario Outline whenever the underlying logic is identical and only the data varies - typically for boundary checks, role variants, or status-code matrices.
Step 3 - Factor out Background
If multiple scenarios share preconditions, extract them to a Background block. Per gherkin-ref (opens in new window), "only one Background per Feature":
Feature: Profile settings
Background:
Given a logged-in user with email confirmed
And the user is on the /profile/settings page
Scenario: Update email
When they change the email field to "new@example.com"
And they click "Save profile"
Then the email field shows "new@example.com"
And a [data-testid="profile-saved-toast"] is visibleDon't over-extract - Background is for truly shared state (auth, seed data, navigation). Scenario-specific setup stays in the Given.
Step 4 - Validate observability of every Then
Every Then must be observable from outside the system. Reject Thens like:
Replace with:
Step 5 - Emit the artifact
Gherkin output (default)
Feature: <story summary>
As a <persona>
I want <capability>
So that <value>
Background:
Given <shared precondition>
Scenario: <case name>
Given <state>
When <event>
Then <observable outcome>
And <additional outcome>
Scenario Outline: <data-driven case>
Given <state with <param>>
When <event>
Then <outcome with <expected>>
Examples:
| param | expected |
| ... | ... |Plain-list output
When the project prefers prose:
## Acceptance Criteria
1. **AC-1:** Given <state>, when <event>, then <outcome>.
2. **AC-2:** Given <state>, when <event>, then <outcome>.
3. **AC-3:** ...Plain-list AC numbers (AC-1, AC-2) become commit-message references in the implementation PR (feat: AC-3 - show toast on save).
Step 6 - Flag implicit Givens
The agent never extracts what isn't there. If the story implies a precondition the author left unstated, emit a flag rather than fabricating:
## ⚠ Implicit precondition flagged
The story says "the user updates their profile" without specifying
the user's auth state. Two reasonable readings:
- **Reading A:** Logged-in user (most likely).
- **Reading B:** This includes profile updates by an admin on behalf
of another user.
Which reading should the Background reflect? (Answer determines
whether we need a second Scenario for the admin path.)Flag-and-ask is the load-bearing pattern. Silently picking Reading A produces a test suite that misses the admin path; flagging it forces the author to make the call.
Output format
## Acceptance criteria for `<story-id>`
**Output format:** gherkin | plain-list
**File path:** `<features/<feature-slug>.feature>` (Gherkin) | inline (plain)
**Scenarios produced:** N
**Implicit-precondition flags:** M
### <Gherkin or plain-list output>
### Implicit-precondition flags
<list of flagged questions for the author, if any>
### Coverage notes
- All scenarios pass the Observable / Decidable / Bounded testability heuristics.
- Per-Then assertion targets are concrete (`data-testid` / status code / DOM state).
- Examples table covers <N> data variants for the Outline scenario.Examples
Three worked examples (simple story, PRD with implicit preconditions, Scenario Outline opportunity) are in references/examples.md.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Generating multiple scenarios with copy-pasted Given | Background extraction missed; suite becomes brittle. | Extract shared Givens to one Background block. |
| Then with a verb but no observable target ("Then save") | Not testable - can't assert "save" without observing the side effect. | Emit data-testid / response status / DOM state. |
| One Scenario per data variant when a Scenario Outline fits | Test code duplication; Examples table is missed. | Scenario Outline + Examples. |
| Fabricating preconditions the story didn't state | Tests pass for the wrong reason; author never confirmed the assumption. | Flag-and-ask. |
References
acceptance-criteria-extractor - worked examples
View source (opens in new window)acceptance-criteria-extractor - worked examples
Three end-to-end examples of story text turned into acceptance criteria. The core procedure (Steps 1-6), the output templates, and the anti-pattern catalog live in SKILL.md (opens in new window).
Example 1: simple story -> 2 scenarios
Input (Linear story):
"Title: Email update on profile page
As a logged-in user, I want to update my email address from
/profile/settings. After update, the email field shows the new value and I see a confirmation toast."
Output:
Feature: Email update on profile page
As a logged-in user
I want to update my email address from /profile/settings
So that my account contact information stays current
Background:
Given a logged-in user with email confirmed
And the user is on the /profile/settings page
Scenario: Update email to a new valid address
When the user changes the email field to "new@example.com"
And the user clicks "Save profile"
Then the email field shows "new@example.com"
And a [data-testid="profile-saved-toast"] is visible
Scenario Outline: Email update validation rejects invalid input
When the user changes the email field to "<invalid_email>"
And the user clicks "Save profile"
Then the email field shows the previous value
And a [data-testid="email-validation-error"] is visible
Examples:
| invalid_email |
| not-an-email |
| missing@ |
| @example.com |
| spaces in@email.com |Example 2: PRD with implicit preconditions
Input (PRD excerpt):
"We will allow promo codes at checkout. Codes apply 20% off the subtotal."
Output:
## Implicit-precondition flags (3)
1. **Code source.** Where do codes come from - user-entered? auto-applied? Both?
2. **Eligibility.** Are codes user-tier specific (e.g. only new users)?
Time-limited? Per-user limited?
3. **Subtotal definition.** Subtotal *before* tax / shipping, or *after*?
Without these answers, the test suite cannot be written.The agent refuses to fabricate the missing pieces - instead returns a question list to the PRD author.
Example 3: Scenario Outline opportunity
Input:
"Login should work for active accounts (returning 200) but reject suspended (401), unverified (403), and non-existent (404) accounts."
Output:
Feature: Login authorization
Scenario Outline: Login response per account status
Given an account with status "<status>"
When the user submits valid credentials
Then the response status is <http_status>
And the response body contains "<error_code>"
Examples:
| status | http_status | error_code |
| active | 200 | |
| suspended | 401 | ACCOUNT_SUSPENDED |
| not_verified | 403 | EMAIL_NOT_VERIFIED |
| not_found | 404 | ACCOUNT_NOT_FOUND |Related skills
data-contract-extractor
Reads a data-product spec (data PRD, dataset README, lineage doc) and emits a structured data contract - schema (columns + types + nullability + PII flags), freshness SLA, volume bounds, distribution invariants, and ownership. The contract is consumable by data-quality tools such as dbt tests, Great Expectations, or Soda checks as their assertion baseline. Use when scoping a new data product or formalizing assertions on an existing one.
gherkin-scenario-coverage-map
Parses new Gherkin scenarios produced by acceptance-criteria-extractor, fingerprints each scenario by its Given/When/Then step sequence, diffs the fingerprints against existing step-definition usage in the live suite, and emits a coverage/duplicate map showing which new scenarios are already covered, which overlap partially, and which are genuine gaps. Use when acceptance criteria have been converted to .feature files and before any new automated tests are authored.
non-functional-requirement-extractor
Reads a PRD, design doc, or product brief and pulls out the non-functional requirements (performance, accessibility, security, internationalization, reliability, observability) as concrete, threshold-bound, testable assertions. Maps every NFR to its measurement source (Lighthouse, axe, OWASP ASVS, WCAG criterion, etc.) so the test suite knows what to assert against. Use after acceptance-criteria-extractor handles functional requirements.
spec-testability-heuristics
Judges whether a written requirement can be tested at all, by scoring each claim against three heuristics: Observable (a state or output visible from outside the system), Decidable (a deterministic pass/fail against a named oracle), and Bounded (stated inputs, states, and users). Supplies untestable-to-testable rewrite pairs per heuristic, severity assignment, a BLOCK / REVIEW / OK verdict rule, and a findings table that pairs every flagged sentence with a concrete replacement. Scope is testability only: it does not write the tests, rank risk, or judge whether the requirement is correct or complete. Use when a user story, acceptance criterion, PRD section, API contract, or PR description is about to be handed to implementation and someone needs to know which sentences cannot be verified as written.
stride-threat-modeling
Enumerates security threats against a feature specification or design using Microsoft's six STRIDE categories (spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege), each paired with the security property it violates. Covers the asset-and-trust-boundary walk that produces threat rows, the threat-row output schema, a likelihood x impact triage rule labelled plainly as practitioner convention rather than standard, a worked example, and an anti-pattern catalog. Enumerates threats against a design; it does not scan code, run a penetration test, or audit control compliance. Use when a PRD section, user story, design doc, or architecture sketch touching authentication, user data, payments, file uploads, or an external integration is about to enter implementation and no threat model exists for it yet.