On this page

← RCF

Concept

Acceptance criteria as the contract

Acceptance criteria define the behaviour code must satisfy. A single acceptance criterion pairs with a single test case that proves it.

Last updated

Acceptance criteria as the contract is RCF’s central rule: each criterion is written before the code and pins one-to-one to the test case that proves it, so the criterion, not the code, is what the agent’s output answers to.

Acceptance criteria connect the decisions above them to the code and tests below them. Their quality determines whether the chain can verify the intended behaviour.

What are acceptance criteria in AI development?

Acceptance criteria are short, verifiable statements of what a piece of software must do for a specific scenario. In AI-assisted development they do extra work, because the agent doing the coding doesn’t carry the team’s institutional memory and won’t infer intent from a hallway conversation. The AC is the contract the agent has to satisfy and the verification step can confirm, in that order. Everything that follows is mechanics.

What an AC actually is

A Given/When/Then sentence. Given a precondition, when an event happens, then an observable thing must be true. Three clauses. One specific behaviour.

Two things matter about that shape. First, it’s verifiable as written. The default is an automated test: if you can turn a Given/When/Then into a piece of test code, the criterion has a clean machine-checkable home, and most ACs land there. A few don’t. A k8s cluster following a naming convention, a deployed manifest matching a target shape, anything where the practical verification step is a human reviewer reading a real artefact against the rule. Those are real ACs too. The rule is verifiability, not test-automation. If the criterion can’t be verified at all, by a test or a human against an artefact, it isn’t one. It is not verifiable. Second, it’s observable. The then clause has to describe something a verification step can check from the outside: a response code, a stored value, a screen state, a naming pattern, a side effect on another system. If the only way to confirm the criterion is to read the source code, the criterion isn’t one. It is a code review note.

The one-to-one rule

Every acceptance criterion is verified by exactly one test case. Every test case verifies exactly one acceptance criterion. The mapping is one to one, by construction, and the test case carries the AC ID that names the criterion it proves. The test suite is the container the test cases live in: it hangs off a single user story and may hold cases for any or all of that story’s acceptance criteria. This one-to-one is a claim about test cases specifically. Code Nodes also reference acceptance criteria, through implementsAcIds, but that link is deliberately many-to-many and separate: several nodes can implement one criterion and one node can serve several, none of which disturbs the one-case-per-criterion rule.

The one-to-one rule makes test coverage attributable to individual criteria. It records which test case verifies which product behaviour.

A criterion without a case has no test evidence. Two cases for one criterion duplicate maintenance. A case covering two criteria makes it difficult to identify which criterion passed. One-to-one mapping removes that ambiguity.

A criterion is a contract

wish versus criterion · one to one

A criterion is a contract: wish versus criterion, and the one-to-one lock between a criterion and its test case. An infographic contrasting a vague wish with a real acceptance criterion, then showing the one-to-one rule. On the left, a card headed WISH holds the quoted line "handles errors gracefully and provides good user feedback", annotated: two developers write different tests, both pass, the product still breaks. A burnt-orange arrow labelled "split into" runs from the wish card to the right card. On the right, a signal-outlined card headed CRITERION holds one continuous Given-when-then sentence: Given a malformed upload, when the user submits, then the API returns a 422 validation error status code and an inline field error. The Then clause is tagged "observable". Below the contrast, a second section shows the one-to-one rule: a card labelled Acceptance criterion with id AC-204-01 sits on the left. On the right, a dashed container labelled "TS-204, suite for US-204" holds two test-case cards: a load-bearing one with id TC-204-01-01 pinned to AC-204-01, and a faded sibling with id TC-204-02-01 pinned to the story's other criterion AC-204-02. The AC card and the load-bearing TC card are joined by two arrows whose ids point at each other, one labelled "verified by" and one "points back". The strapline reads: one criterion, one test case, one answer. The closing caption reads: the trust sits with the contract, not the agent. A wish, not a criterion. "Handles errors gracefully and provides good user feedback." Two developers write different tests, both pass, the product still ships broken. WISH “handles errors gracefully and provides good user feedback” Two devs write different tests. Both pass. The product still breaks. A real acceptance criterion as one continuous Given-when-then sentence. One behaviour, one observable outcome: Given a malformed upload, when the user submits, then the API returns a 422 validation error status code and an inline field error. CRITERION Given a malformed upload when the user submits then the API returns a 422 validation error status code and an inline field error One behaviour. One observable outcome. Test suite TS-204 hangs off user story US-204 and holds the test cases that verify its acceptance criteria. TS-204 · suite for US-204 Acceptance criterion AC-204-01. Verified by exactly one test case, TC-204-01-01, sitting inside the suite on its user story. Criterion AC-204-01 Test case TC-204-01-01. Pins to exactly one acceptance criterion, AC-204-01. Lives inside test suite TS-204 on user story US-204. Test case TC-204-01-01 Sibling test case TC-204-02-01, drawn quieter. Pins to a different acceptance criterion on the same user story, AC-204-02. In the same suite as TC-204-01-01, which is what the suite container is showing. Test case TC-204-02-01
the trust sits with the contract, not the agent

What this rule isn’t saying about testing

The rule governs the AC-to-case mapping. It does not require one test runner or put every kind of testing into the chain.

Unit tests are out of the AC chain. They’re standard engineering hygiene, expected alongside the source code at the team’s normal coverage target. They prove individual functions behave the way the code says they should. They don’t prove an AC is met, and they aren’t meant to. Different work, different layer. RCF doesn’t replace unit tests; it sits above them.

The form of the test case doesn’t matter. If it tests the criterion, it is an RCF test case. A timing assertion in an HTTP test is fine. A jmeter run defined in code is fine. A playwright browser journey is fine. A contract test against a third-party API is fine. What matters is that the case proves the AC and that the case carries the AC ID so traceability can resolve it. The one-to-one rule is about that mapping, not about which runner you use.

This layer is the layer that does traceability work. It isn’t the totality of testing on the project. Unit tests, smoke tests, exploratory testing, ad-hoc performance probes, internal QA runs, all of these can and should exist alongside the AC-mapped suites. They do not need to be part of the chain.

Tests written against existing code can miss the intended criterion. The theatre risk page describes the safeguards: an AC schema, separate test-writing and code-writing contexts, and human approval at AC review.

Why this matters more with AI agents in the loop

A developer may organise three tests around one criterion without recording the mapping. The team can retain that relationship in memory when it does the work itself.

An agent needs the mapping explicitly. An instruction to cover the criteria can produce plausible tests without complete coverage. The one-to-one rule requires a test case that carries its criterion’s ID and a criterion reference back to that case, making gaps identifiable.

How do you write acceptance criteria for AI coding agents?

Make each criterion observable, unambiguous, and binary: a Given/When/Then sentence whose then clause a test can check from the outside, with pass and fail as the only outcomes. Write it before the agent writes the code, give it exactly one test case that proves it, and hold the agent’s output to it.

What a good AC looks like

A good AC is specific, observable, and self-contained. Specific means it describes one behaviour, not a family of related behaviours. Observable means the then clause is checkable without reading the source. Self-contained means the test can be written from the AC text alone, without having to reconstruct context from the wider story.

Here’s a workable one:

AC-042-02. Given a signed-in user, when they upload a file over 5 MB or in an unsupported format, then the upload is rejected with an HTTP 400 response, an error code of upload-too-large or upload-format, and no partial state is persisted in the photo store.

The example names three required elements. The precondition is named (signed-in user). The triggers are named (over 5 MB; unsupported format). The expectations are concrete and checkable (HTTP 400, specific error codes, no partial state). Writing a test against that AC is mechanical work.

What a bad AC looks like

A common failure is to describe a general quality without defining an observable result:

AC-042-bad. The upload feature handles errors gracefully and provides good user feedback.

This statement does not define observable behaviour. Different engineers can interpret it differently and write passing tests that leave the error flow broken. Split it into criteria that name each error condition and its expected response.

Where ACs come from

Write acceptance criteria with the user story, ideally by the product owner or whoever performs that role. They commit to required behaviour before implementation. Criteria added after the code only reconstruct what was built.

ACs can change as the team learns. If a criterion lacks a precondition, update it and commit the correction before continuing. The commit records a deliberate change to the requirement, rather than an unrecorded adjustment to match the implementation.

AI can help draft a thorough set of criteria from well-written requirements and stories, including edge cases the author may miss. The drafts still need human review and revision. A separate treatment of AI-assisted upstream work is planned.

ID schemes and references

AC IDs are scoped to the story. AC-042-01 is the first criterion on US-042. The string is opaque (never renumbered, never reused) and load-bearing (test cases point at it, Code Nodes reference it, work items mention it). On a project of any size, ACs are the thing you look up by ID most often.

IDs should be easy to say, type, search for in the repository and recognise in test output. UUIDs make these routine uses harder and reduce the likelihood that the team will maintain the chain.

What you get when ACs are doing their job

This mapping supports three routine checks.

You can answer “is this requirement satisfied” in a single query. Pull the requirement’s stories, pull their ACs, pull the suites, check they’re green. Done.

Trace a function’s tests back through the chain to identify the requirement it serves. The chain records why the function exists; the source explains how it works.

An agent’s output can be checked against criteria a human approved. This addresses the AI trust gap by using an agreed contract. The theatre risk page describes the safeguards that keep approval tied to that contract.