Without notes, state yesterday’s main idea and one unresolved question.
Write requirements and acceptance criteria
AI-Assisted Capstone: Build Without Vibe Coding
Objective
Turn the idea into testable behavior before implementation.
AI is an advanced instrument or junior collaborator: it expands throughput, but the engineer still defines requirements, verifies measurements, and signs off on safety.
- user stories
- acceptance criteria
- edge cases
- data sensitivity
Why this matters
Yesterday you wrote down who the app is for and the one loop it serves. That page is still a wish. Today you turn it into behaviour you can check — statements so specific that you, a stranger, or an AI agent can look at the running app and agree on whether each one passed. Ambiguity is the enemy here: every vague requirement becomes a decision someone makes silently at 11pm, and silent decisions are the ones that surprise you in the demo.
By the end of the hour you have a specification another developer could implement without guessing.
User stories: one behaviour each
A user story is one sentence describing one thing one user wants, in a fixed shape:
As a <user>, I want <to do this>, so that <this outcome>.
The shape is not decoration. The so that clause is a test: if you cannot finish it without
repeating the middle clause, the story has no purpose behind it and probably should not be built.
As Maya, I want to record a completed calibration for an instrument,
so that its calibration history is accurate without a spreadsheet.
One story, one behaviour. "I want to record and edit and delete calibrations" is three stories. Split them, because each will be built, tested, and demonstrated separately — and because a story you can only half-finish leaves the app half-broken.
Five to eight stories is the right size for a capstone. Fewer and you have not thought it through; more and you are writing next year's roadmap instead of this fortnight's project.
Acceptance criteria: the checkable part
An acceptance criterion is a specific observable behaviour with its conditions and its expected result. It is the strongest form a requirement can take, and it is what makes a story finishable — without it, "record a calibration" is done whenever you feel like saying so.
Write each one in three lines:
Given <the starting state>
When <the user does exactly this>
Then <this observable thing is true>
Compare. These are not requirements at all:
Make the form fast.
The app should be modern and intuitive.
Handle errors properly.
Use best practices.
None can be checked. "Fast" by whose stopwatch, "modern" by whose taste, "properly" meaning what? Each is a decision you have postponed while feeling like you made one. Now the same intentions, written as acceptance criteria:
Given an instrument exists with no calibrations
When Maya submits the form with date 2026-03-01 and technician "Maya R"
Then that calibration appears at the top of the instrument's history list
and the page shows "Calibration recorded"
Given the calibration form
When Maya submits it with the date field empty
Then the app does not save anything
and the message "Date is required" appears next to the date field
The disagreement test
Show a criterion to another person alongside the running app. If the two of you could look at the same screen and disagree about whether it passed, it is not an acceptance criterion yet. Every word that invites disagreement — fast, clean, nice, properly, seamless — must be replaced by something you can point at.
A criterion is a test specification
A datasheet line reading "fast switching" is worthless on the bench. What you can actually test is
"propagation delay ≤ 20 ns at 5 V, 25 °C, 15 pF load" — stated conditions, applied stimulus,
expected measured value. Given is the conditions, When is the stimulus, Then is the expected
reading. A requirement you cannot set up a measurement for is marketing copy.
Edge cases and failure cases
Your criteria so far describe the day going well. Most defects live in the other cases, and beginners skip them because they are boring — which is exactly why they survive to the demo.
Run every story through this list and write a criterion for any row that applies:
| Case | Ask |
|---|---|
| Empty | What if the field is blank, or the list has no rows yet? |
| Too long | What if the note is 5,000 characters? |
| Wrong type | What if the date is banana, or the quantity is -4? |
| Missing target | What if the instrument ID does not exist? |
| Duplicate | What if the same record is submitted twice? |
| Not allowed | What if this user should not be able to do it at all? |
| Offline | What if the server is unreachable when Submit is pressed? |
For each one, the criterion must say what the user sees, not only what the code does. "Returns 400" is half an answer; the other half is the message on the screen.
Fire doors
Nobody buys a building for its fire doors, and nobody notices them on a normal day. They are the part that decides how bad the worst day gets. Failure cases are fire doors: invisible when things go well, and the entire difference when they do not.
Data sensitivity
Before you design tables tomorrow, decide what each piece of data is. Go through every field your stories imply and label it:
- Public — fine for anyone to see. Instrument model, calibration interval.
- Internal — fine for your user, not for the world. Which instrument is overdue.
- Personal — identifies a human. Technician name, email. Storing it means you are responsible for it.
- Secret — must never appear in the database as plain text, in logs, or in a Git repository. Passwords (hashed only, from Day 51), API keys, tokens.
Then write two lines in your spec that you will check against later:
NEVER LOGGED: technician email, session tokens
NEVER RETURNED BY THE API: password hashes, internal notes field
Real data in a practice project
Do not seed your capstone with real colleagues' names, real customer records, or anything from your workplace you would not paste into a public repository. Invent the data. The safest sensitive data is the kind you never collected.
Walkthrough: one story, fully specified
Take the thin slice from Day 78 and write it out properly. Create docs/spec.md:
STORY 1
As Maya, I want to record a completed calibration for an instrument,
so that its calibration history is accurate without a spreadsheet.
ACCEPTANCE CRITERIA
1. Given an instrument exists
When Maya submits date 2026-03-01 and technician "Maya R"
Then the record appears at the top of that instrument's history
and the list count increases by one
2. Given the form
When Maya submits with the date empty
Then nothing is saved
and "Date is required" appears next to the date field
3. Given the form
When Maya submits a date in the future
Then nothing is saved
and "Date cannot be in the future" appears next to the date field
4. Given an instrument ID that does not exist
When a calibration is submitted for it
Then the API responds 404 and no row is written
DATA
- calibration date: internal, required, not in the future
- technician name: personal, required, max 80 characters
- notes: internal, optional, max 500 characters
DONE WHEN
All four criteria pass on a freshly seeded database, checked by hand.
Notice what happened: criterion 3 did not exist until the edge-case table was applied, and it is a real product decision — you just made it deliberately instead of discovering it in a bug report.
Reviewer mode — after your spec is written, not before
Today's AI mode is reviewer: you bring finished work and ask for defects. A useful review produces specific, actionable findings with evidence pointing at your text — never general praise and never a wholesale rewrite.
"Review this specification for ambiguous words, hidden decisions, and untestable acceptance criteria."
Take each finding one at a time and decide yourself whether it is right. Rewrite the criterion in your own words; do not paste the suggested wording. If the reviewer invents a requirement you did not want, that is a non-goal, not a defect.
Your turn
Write docs/spec.md for your own project.
- Write 5–8 user stories in the
As a … I want … so that …shape. Mark exactly one as SLICE 1 — the thin slice from yesterday. That is what you build on Day 82. - For each story, write 2–4 acceptance criteria in
Given / When / Thenform. - Run the edge-case table over every story. Add a criterion for each row that applies. Expect this to roughly double your criteria count; that is the point.
- Ban the vague words. Search your file for fast, modern, intuitive, properly, clean, robust, seamless, user-friendly. Replace each with something observable, or delete the line.
- Copy your non-goals from yesterday into the spec so it is one document.
- Add the data sensitivity block: label every field, then the
NEVER LOGGEDandNEVER RETURNED BY THE APIlines. - Add a DONE WHEN section: the exact conditions under which the whole project is finished.
- Run the reviewer prompt above, apply the findings you agree with, and commit:
docs: acceptance criteria for capstone.
You are done when
You can hand the spec to someone who has never heard of your project and they can build SLICE 1 without asking you a single question about behaviour. Any question they would have to ask is a criterion you still owe.
Common pitfalls
- Writing criteria that describe the implementation. "Then the
calibrationstable gets a row" is a design decision, not a behaviour. Say what the user can observe; you may still change the design tomorrow. - One giant story. If a story needs more than four criteria, it is two stories. Split it before it becomes a week you cannot finish.
- Skipping the failure cases because "it obviously errors". Obvious to you today is invisible to you on Day 86. Write the message text now.
- Treating the spec as unchangeable. It is a living document. Changing it deliberately, with the date noted, is engineering. Drifting away from it silently is not.
Verify it yourself
Open today's reference, OpenAI's Codex CLI documentation, and read the guidance on writing prompts and giving the agent context.
- Find what it recommends including so the agent knows when a task is complete. Compare that to
your
DONE WHENsection — is yours as specific? - Look for anything about verifying the agent's output. Which of your acceptance criteria could be
checked automatically, and which need your eyes? Mark each criterion
autoormanual.
Add both notes to the end of spec.md. On Day 82 an agent will implement SLICE 1 from these
criteria, and the ones marked manual are exactly the ones you must check yourself.
The hour
- 0–5 min Recall
- 5–20 min Learn
Read only the listed concept notes and official reference sections needed today.
- 20–48 min Build
Define 5–8 requirements, explicit non-goals, failure cases, and completion criteria.
- 48–55 min Explain and verify
Run the result, inspect evidence, and explain the data/control flow in your own words.
- 55–60 min Quiz and commit
Complete the quiz, record one lesson, and commit the verified change when applicable.
What to hand in
A specification another developer could implement without guessing core behavior.
Working with AI today
Provide existing work and ask for concrete defects, risks, missing tests, and unsupported assumptions—not praise.
Review this specification for ambiguous words, hidden decisions, and untestable acceptance criteria.
References
End-of-day quiz
Explain-back gate
Pass the quiz above to unlock completion.
Quiz + explain-back checks required.