An AI coding agent doesn't know what you meant — it knows what you typed, and it fills every gap with the most plausible-looking completion. This page teaches the difference between a prompt and a spec: the four ingredients that turn a hope into something checkable, why five small steps beat one big one, and the checklist pattern that lets an agent work through a real feature without you watching every keystroke.
An agent generates the next token that best continues everything it has seen — your prompt, the code it can read, and its training. Given a checkable target, that process converges on a checkable answer. Given "make the dashboard better," there is no target to converge on, only a direction. The agent still has to produce something, so it produces the most statistically plausible reading of "better dashboard": probably more whitespace, probably a card layout, probably a color it picked. It is not guessing lazily — it is doing exactly what it was asked, which was to guess.
Three things go wrong at once, and they compound:
None of this is a model failing to understand you. It understood "make it better" perfectly — that phrase simply does not encode a decidable outcome. The fix is not a smarter agent; it's a prompt that does encode one. That's what a spec is.
This is a simplified but accurate mechanism, not a claim about any specific model or vendor. Coding agents are trained to produce output that scores well against human preference and, where available, automated checks (tests, linters, compiler errors). When a prompt supplies a checkable target — a failing test, a stated acceptance criterion — "scores well" and "is actually correct" point at the same thing, and the model has a signal to aim at. When a prompt supplies no checkable target, "scores well" collapses to "looks like a good answer to a person skimming it," which is a much weaker property than "does what you needed." The gap between those two is exactly the gap a spec closes.
A spec is not more words. It is four specific ingredients layered onto the same request, each one closing off a different way for the agent to guess wrong:
Try it below. The prompt on the left never changes — it's the same four words a person actually types. Add ingredients on the right and watch the simulated agent's outcome narrow from a plausible guess to something you could actually sign off on.
Notice what each ingredient removes, not just what it adds. The goal removes ambiguity about the target. Constraints remove ambiguity about approach. Acceptance criteria remove ambiguity about what "done" means. Non-goals remove scope the agent would otherwise add on its own initiative. Steps — the fifth ingredient, covered next — remove ambiguity about order, which turns out to matter as much as the other four.
A spec can be perfectly written and still fail in execution if it asks for the whole feature in one motion. The unit that actually matters is the step: an imperative verb plus a check. "Add a login endpoint. Check: a correct password returns 200 and a wrong one returns 401." Not "build the auth system" — that's a goal wearing a step's clothes, still unbounded, still unverifiable until the very end.
The payoff of small steps isn't tidiness. It's where a failure lands. When a five-step feature breaks on step 3, steps 1 and 2 are already committed, already correct, and already out of the blast radius — see how git makes committed work durable. Only step 3 needs attention, and its diff is small enough to read in one pass. When a one-step feature breaks, everything is suspect at once: the migration, the endpoints, the middleware, and the UI are one undifferentiated block of uncommitted change, and nothing in the history tells you which part is wrong.
A step is sized correctly when its check can pass or fail on its own, independent of later steps. That's what makes it committable: a commit that passes its own check is safe to leave in history even if step 4 later turns out to need rework. This is the same property that makes git bisect useful for human-written history — a tight sequence of independently-correct commits localizes a bug to one of them by binary search. Steps sized for an agent should have the same property on purpose, not by accident.
Once a feature needs more than two or three steps, write them down as a checklist before the agent starts — a short PRD (product requirements doc), just markdown. The agent works through it top to bottom: read the next unchecked box, implement it, run its check, commit, tick the box, move to the next one. The whole spec, plus which steps are already done, lives in one file the agent can re-read at any point — including a fresh session that has no memory of the last one.
## Add email/password auth Goal: returning users log in and reach their saved dashboard. Constraints: existing users table, no new dependency, 14-day sessions. Non-goals: social login, password reset, 2FA — later specs. - [x] 1. users migration + password_hash column. Check: migrates and rolls back clean on a scratch db. - [x] 2. POST /signup — hash + store. Check: new email returns 201; duplicate returns 409. - [ ] 3. POST /login — verify + session cookie. Check: correct password → 200 + Set-Cookie; wrong → 401. - [ ] 4. Session middleware guarding /dashboard. Check: no cookie → redirect; valid cookie → loads. - [ ] 5. POST /logout — clears session. Check: cookie cleared; /dashboard redirects again.
Two boxes are already checked in the example — that's the point of the pattern, not an accident. Each checked box is a real commit that already passed its own check; an agent resuming this file (or a person reviewing it) can trust everything above the first unchecked box without re-verifying it. Bigger features are just longer versions of this same list — see running several of these checklists across agents without them colliding.
Keep the goal, constraints, and non-goals at the top of the file rather than repeating them per step. They apply to every step equally, and a single shared statement is easier to keep consistent than five slightly different restatements that drift apart as the checklist grows. The per-step lines should be narrow by comparison — an imperative verb and a check, nothing more. A step that needs a paragraph to describe is usually two steps that haven't been split yet.
A spec doesn't remove the human from the loop; it tells the human exactly where to stand. Three places are worth actually looking, in this order:
The other half of the gate happens before any code is written: let the agent ask clarifying questions against the spec, and answer them, before it starts. A well-formed spec makes most questions unnecessary by construction — that's what acceptance criteria are for — but the ones that surface are usually the exact ambiguity that would otherwise have been silently guessed. "Should the 401 on wrong password happen after 1 attempt or should there be lockout?" is a five-second answer now and a rebuilt endpoint later.
Where you place the gate matters as much as that it exists. A gate at the end of the whole feature only tells you something went wrong, not where — you're back to reading an undifferentiated diff, the same failure mode small steps exist to avoid. A gate after every step keeps each review small enough to actually do carefully instead of skimming, and it catches a wrong assumption after one step's worth of wasted work instead of five.
Without acceptance criteria, "done" defaults to the only signal an agent actually has: it produced output and stopped. That's a claim about effort, not correctness — the code changed, which is not the same statement as the feature working.
Push this all the way down to the step level, not just the feature level. A five-step PRD where only the final step has a check still leaves four steps where "done" means "I changed the code" — exactly the failure mode small steps were supposed to close. Every step needs its own check, or it isn't a step yet; it's an unverified hope with a number in front of it.
The test itself has to earn its place, too. A test that only asserts the endpoint returns a status code, without asserting which one, will pass whether login works or not — it satisfies "tests pass" while proving nothing about the acceptance criterion it's supposed to check. Definition of done is only as strong as the weakest check standing behind it, which is why the review gate in the previous section includes reading the tests, not just running them.
A spec written for today's agent doesn't expire when the task finishes. Committed to the repo, it becomes a record of what was actually asked for, what was explicitly excluded, and how "done" was defined — read by the next agent session that touches the same code, whether that's tomorrow or six months from now with a different model entirely. That session doesn't start from the vague memory of a Slack message; it starts from a checklist with acceptance criteria already written down, the same way it would read whatever standing context file your agent loads at the start of a session.
This is also why non-goals matter beyond the immediate task: a future agent reading "no password reset in this spec" understands that the omission was a decision, not an oversight, and won't quietly "fix" it by adding scope nobody asked for. A spec without non-goals looks, to a later reader, exactly like a spec that forgot something. A repo with a habit of small, checked, spec-driven steps ends up with a history that reads like documentation — because it effectively is: each commit message and its originating spec together answer "why is this code like this?" without anyone having written a separate wiki page. Compare that to what a coding agent actually does with each instruction it receives — the loop only produces that kind of trail when the instruction it started from was checkable in the first place.
TermRoam's execute workflow is built around this exact pattern: you write a spec as a PRD, a validator machine-checks that every step has an imperative verb and a verifiable acceptance criterion before anything is allowed to run, and a resident agent loop works the checklist step by step — implement, check, commit, push, tick the box, repeat — so the definition of done on this page is enforced structurally rather than trusted on faith.