| name | actionable-acceptance-criteria |
| description | Transform vague acceptance criteria into concrete, machine-verifiable checks. Required for the planner agent when writing task ACs. |
Actionable Acceptance Criteria
Turn each acceptance criterion into something a reviewer can verify without reading the implementation or asking the author "did you do it?".
The Rule
Every AC must be one of these forms:
| Form | Example |
|---|
| Command that exits 0 | cargo test --test e2e -- test_auth_flow passes |
| Command whose output contains X | curl -s localhost:8080/health returns {"status":"ok"} |
| File exists with content | src/models/user.rs exports a User struct with fields id: Uuid, email: String |
| Grep assertion | grep -r 'pub fn create_user' src/ matches exactly one file |
| Behavioral assertion | Clicking "Submit" with empty email shows "Email is required" inline (not an alert) |
| Negative assertion | cargo clippy -- -D warnings produces no output |
What Fails the Test
These are NOT actionable acceptance criteria:
- "Code is clean and well-structured" (subjective)
- "Error handling is implemented" (what errors? what handling?)
- "Tests are written" (what tests? what do they assert?)
- "Feature works correctly" (what does "correctly" mean?)
- "Follow existing patterns" (which patterns? for what?)
- "Performance is acceptable" (what threshold? measured how?)
How to Apply
For each AC the planner writes, run it through this checklist:
- Can a reviewer verify this without reading the code? If no, rewrite.
- Is there exactly one way to interpret it? If no, add specifics.
- Can it be checked with a command or a visual inspection? If no, decompose into sub-criteria that can.
- Does it test behavior or existence? Prefer behavior ("endpoint returns 200") over existence ("file exists"). Existence is OK when the task is purely structural (new module, new config file).
Granularity
Each task should have 2-5 ACs. If you have more than 5, the task is too big; split it. If you have exactly 1, it's probably too vague; decompose it.
Example
Bad:
acceptance_criteria:
- Authentication works
- Tests pass
Good:
acceptance_criteria:
- POST /api/auth/login with valid credentials returns 200 and a JSON body containing `token` (string) and `expires_at` (ISO 8601)
- POST /api/auth/login with invalid credentials returns 401 and `{"error":"invalid_credentials"}`
- cargo test --test auth -- test_login_valid exits 0
- cargo test --test auth -- test_login_invalid exits 0