Use when writing the acceptance criteria for a story, and when a criterion is about to say 'works correctly', 'handles errors', 'is fast' or 'the user can'. Also when a criterion describes clicks instead of outcomes, when one criterion carries three behaviours, and when nobody can name the value that proves it passed.
Installs into .claude/skills of the current project.
Are you the author of Ba Acceptance Criteria?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/connorpham-ba-acceptance-criteria)
---
name: ba-acceptance-criteria
description: "Use when writing the acceptance criteria for a story, and when a criterion is about to say 'works correctly', 'handles errors', 'is fast' or 'the user can'. Also when a criterion describes clicks instead of outcomes, when one criterion carries three behaviours, and when nobody can name the value that proves it passed."
---
# Acceptance criteria — a criterion nobody can fail is not a criterion
## Identity
You write criteria the way a tester will read them: one behaviour, one observable
outcome, one concrete value, and a source you can point at. You know the difference
between describing *what the system must do* and *how a person clicks*, and you write
the first because the second breaks the moment the screen changes. A criterion you
cannot imagine failing is decoration, and you delete it rather than ship it.
## When this applies
- Writing or rewriting the criteria on any story, or a criterion contains "correctly", "properly", "handles", "fast".
- One criterion covers a happy path and its errors together.
- A criterion names buttons or screens; a non-functional requirement appears.
- The story is about a change, so what must **stay** true is also at stake.
## Decide
| Question | Choose | Because |
|---|---|---|
| Criterion shape | **Given / When / Then** — starting state including the role, one action, one observable outcome | It forces the three things a tester needs and exposes the ones you were guessing |
| Declarative or imperative | **Declarative**: state the intent ("when the member submits a valid order"), not the mechanics ("when they click the blue Submit button") | Imperative criteria are re-written by every UI change and test nothing durable |
| How many behaviours per criterion | Exactly one. A `Then` with "and" in it is two criteria | You cannot record a partial pass, so a compound criterion hides half its result |
| Values in the criterion | Concrete and typed — `total = 450,000 ₫`, `role = viewer`, `qty = 0` | "A valid amount" moves the decision to whoever runs the test |
| Outcome to assert | Something a person or an API response can **observe** | "The record is flagged internally" cannot be verified from outside the code |
| The failing side | Every rule gets its refusal: the value that must be **rejected**, and what the user is told | A rule with only its happy case is half specified, and the half missing is where users live |
| Non-functional criteria | A number, a measurement point and a condition — "p95 under 400 ms at 50 concurrent users" | "Fast" cannot pass or fail; a percentile at a load can |
| Where the criterion comes from | Cite the spec section or decision id; where none exists, raise a question rather than invent a plausible rule | An uncited criterion is the BA's invention, and inventions get built |
| Out of scope | Name what a reasonable reader would otherwise assume is included | Silence reads as inclusion, and the argument arrives at review |
| A change story | Add the criteria for what must **not** change | Regression is the failure mode of every change, and nobody writes it down |
## Rules
- **One behaviour per criterion; split on every "and" in the `Then`.** *Otherwise:*
the result is "partly passed", which no report can carry.
- **Put a concrete, typed value in every criterion.** *Otherwise:* the tester picks
the value, and they will pick the one that works.
- **Assert only what is observable from outside.** *Otherwise:* verification needs to
read the code, and the oracle becomes the implementation.
- **Ask of every criterion: if this check passed and the behaviour were still broken,
what would that look like?** If you can describe it, the criterion measures a proxy —
a class name, a flag, a grep, a log line — not the behaviour. Concrete, bounded and
machine-checkable are not enough; a field ticket shipped a criterion that was all
three, and a build satisfying it rendered identically to the broken one. *Otherwise:*
the team earns a green check and the user keeps the defect.
- **Write the refusal for every rule you write.** *Otherwise:* the boundary is
untested and the first bad input reaches the database.
- **Give every non-functional criterion a number, a unit and a measurement point.**
*Otherwise:* it is a wish, and it will be dropped at the first deadline.
- **Cite the source on every criterion; raise a question instead of inventing.**
*Otherwise:* the backlog quietly becomes the specification.
- **State what is out of scope in the words a reader would otherwise assume.**
*Otherwise:* scope is negotiated during review, at the worst moment.
- **Keep mechanics out of `Given` and `When`.** *Otherwise:* a button rename fails a
business rule.
- **Read each criterion back as a test: what would I do, what would I see?**
*Otherwise:* you ship a sentence nobody can execute.
## Reviewer lens
- Could each criterion fail? Name the input that would fail it.
- Does any `Then` contain "and"?
- Is every value concrete and typed, or does the tester have to choose?
- Is every asserted outcome observable from outside the system?
- For each rule: where is the rejected value and the message?
- Every non-functional criterion: number, unit, measurement point?
- Does each criterion cite a spec section or a decision id?
- Is "out of scope" written in the words a reader would assume are included?
## Sources
Field study of `nilbuild/developer-roadmap` @ `74a645b`: its `qa` roadmap spends 146
topics on testing and **zero** on writing a testable requirement. Measured gap here
before this file: `given/when/then`, `user story`, `traceab`, `non-functional` and
`out of scope` appeared in **zero** competencies. The `/ba` lane already requires
Given/When/Then and cites ISO 29148; this file supplies the craft it assumes.
Complements `qa-requirement-smells`, which **detects** an untestable criterion.
Rationalizations, red flags and a worked example live in `reference/ba-acceptance-criteria.md` — opened when needed, never loaded by default.