From qa-shift-left
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-shift-left:acceptance-criteria-extractorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
ISTQB defines **acceptance criteria** as "the criteria that a work
ISTQB defines acceptance criteria as "the criteria that a work product must satisfy to be accepted by the stakeholders" (after IREB Glossary; ISTQB Glossary V4.7.1) (istqb-ac).
This skill takes natural-language story text and emits acceptance criteria in two interchangeable shapes:
spec-to-suite-orchestrator.testability-reviewer's
Observable / Decidable / Bounded heuristics.If the input is a non-functional requirement (perf / a11y /
security / i18n), use nfr-extractor
instead - those have a different shape (thresholds, not Given/When/Then).
A scenario has three structural pieces per Gherkin (gherkin-ref):
Save profile").data-testid="profile-saved-toast" appears").Read the input and tag each sentence as a Given / When / Then candidate. Sentences that describe steady state are Givens; sentences with verbs of action ("clicks", "submits", "navigates") are Whens; sentences asserting outcomes are Thens.
Per gherkin-ref, 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.
If multiple scenarios share preconditions, extract them to a Background block. Per gherkin-ref, "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 "[email protected]"
And they click "Save profile"
Then the email field shows "[email protected]"
And a [data-testid="profile-saved-toast"] is visible
Don't over-extract - Background is for truly shared state (auth, seed data, navigation). Scenario-specific setup stays in the Given.
Per testability-reviewer, every
Then must be observable from outside the system. Reject Thens like:
Replace with:
data-testid="confirmed-icon" is visible AND has color
#3CB371."Content-Security-Policy
header is present."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 |
| ... | ... |
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).
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.
## 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 testability-reviewer's Observable / Decidable / Bounded heuristics.
- Per-Then assertion targets are concrete (`data-testid` / status code / DOM state).
- Examples table covers <N> data variants for the Outline scenario.
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 "[email protected]"
And the user clicks "Save profile"
Then the email field shows "[email protected]"
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 [email protected] |
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.
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 |
| 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. |
testability-reviewer - the
upstream review that should pass before this skill is invoked.nfr-extractor - sibling for
non-functional requirements (different shape).spec-to-suite-orchestrator - downstream agent that takes plain-list AC and chains to
test-case generation.npx claudepluginhub testland/qa --plugin qa-shift-leftSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.