practitioner guide

How to Write a Spec for an AI Coding Agent

Your AI coding agent isn't dumb. It fills silence with a plausible guess. This is the practical guide to removing the silence — the seven sections of a build-ready spec, a copy-paste template you can lift today, and acceptance criteria that don't drift.

SparkVibeAI·2026-06-20·12 min read

You ask Claude Code to "add login." Ten minutes later you have a working-looking auth flow. It also stores passwords in plaintext, has no rate limiting, and uses a session model that contradicts the one in the file next to it. The agent didn't fail. You handed it a one-line prompt with a dozen unstated decisions inside it, and it made every one of those decisions for you — confidently, and out of your sight.

This is the single most common failure in AI-assisted development, and it has nothing to do with the model. It has to do with your spec. This guide is about writing one the agent can't misread.

Why your AI coding agent keeps guessing

An AI coding agent is a completion engine pointed at a codebase. Give it a goal and it produces the most probable implementation of that goal. The problem is that "add login" has thousands of probable implementations, and they differ on exactly the things you care about: which auth method, where the secrets live, what happens on the fifth failed attempt, whether the email field is case-sensitive.

You had answers to all of those in your head. You just didn't write them down. So the agent did what completion engines do with missing information — it filled the gap with whatever was most statistically plausible given the surrounding text. That's the guess. It isn't the agent being careless; it's the agent being asked to read your mind and politely making something up when it can't.

The job of a spec is to remove the silence. Every place where your spec is silent is a place the agent will guess, and you won't find out which guess it made until you're reading the diff — or worse, until production tells you. A good spec is not a longer prompt. It is a prompt with the guesses pre-empted.

the core idea

Wherever your spec is silent, the agent is not. It fills the gap with a plausible guess. Writing a spec is the discipline of finding the silence before the agent does.

The seven sections of a build-ready spec

A spec the agent can build from — what we call a build-ready spec — has a predictable shape. Seven sections, each closing a category of guess. You can write them in any order, but skip one and you've left a door open.

  1. Goal & context. One paragraph: what this feature does, who it's for, and how it fits the system it's landing in. The agent uses this to resolve ambiguity everywhere else — if a later decision is unclear, this is what it reasons from. Vague goal, vague everything.
  2. Scope & non-goals. What's in, and — just as important — what's deliberately out. Non-goals are the section everyone skips and the one that does the most work. "This does not handle SSO" stops the agent from helpfully building SSO and dragging in three dependencies you never wanted.
  3. Constraints. The non-negotiables: security (never log secrets, hash with bcrypt), performance (p95 under 200ms), stack (Postgres, not a new datastore), compliance (no PII in logs). If a constraint lives only in your head, the agent will violate it the moment it's convenient.
  4. Data model & interfaces. The tables, fields, types, and the shape of every endpoint or function the feature exposes. This is where you stop the agent from inventing a schema that fights the rest of your app. Name the fields. State the types. Show the request and response.
  5. Acceptance criteria. The definition of done, written so a machine could check each one. This is the heart of the spec and gets its own section below. If you write nothing else well, write these.
  6. File/structure hints. Where the code should go: which directory, which existing module to extend, what to name things. The agent will respect your conventions if you state them and reinvent them if you don't.
  7. Out-of-scope & known risks. The things you know are hard, the assumptions you're making, the edge cases you're knowingly deferring. Naming a risk converts a silent guess into a documented decision the reviewer can see.

A copy-paste spec template

Here is a complete template you can lift into a SPEC.md and fill in. It's shown populated with a real "add authentication" feature so the shape is concrete rather than abstract. Replace the example content with yours; keep the seven headings.

# SPEC: Email + password authentication

## 1. Goal & context
Let returning users sign in with an email and password so the app can
gate per-user data. This is the first auth in the system; later SSO
will build on the same user table. Single-region web app, ~5k users.

## 2. Scope & non-goals
In scope: sign up, sign in, sign out, password reset by email link.
Non-goals: SSO / OAuth providers, MFA, "remember me" devices,
admin user management. These are separate, later specs.

## 3. Constraints
- Security: bcrypt (cost factor 12) for password hashing. Never log
  passwords, reset tokens, or session IDs.
- Stack: existing Postgres DB; no new datastore. Node/Express API.
- Sessions: stateless JWT, 24h expiry, signed with KMS-held key.
- Rate limit: max 5 failed sign-ins per email per 15 min, then lock
  the email for 15 min.
- Compliance: no PII (email included) in application logs.

## 4. Data model & interfaces
Table `users`:
  id            uuid  primary key
  email         text  unique, stored lowercased
  password_hash text  not null
  failed_count  int   default 0
  locked_until  timestamptz  null
  created_at    timestamptz  default now()

Endpoints:
  POST /auth/signup   { email, password } -> 201 { token } | 409
  POST /auth/signin   { email, password } -> 200 { token } | 401 | 423
  POST /auth/signout  (auth)              -> 204
  POST /auth/reset    { email }           -> 202 (always, no leak)

## 5. Acceptance criteria
- GIVEN a new email and an 8+ char password
  WHEN POST /auth/signup
  THEN a users row is created with a bcrypt hash and a 200/201 with a JWT.
- GIVEN an email that already exists
  WHEN POST /auth/signup
  THEN respond 409 and create no row.
- GIVEN a valid email + password
  WHEN POST /auth/signin
  THEN respond 200 with a JWT whose exp is 24h out.
- GIVEN 5 prior failed sign-ins in 15 min for an email
  WHEN POST /auth/signin (6th attempt)
  THEN respond 423 and do not check the password.
- GIVEN any signin/signup
  WHEN the request is logged
  THEN the log line contains no password, token, or raw email.

## 6. File / structure hints
- Route handlers in `api/routes/auth.js` (extend existing router).
- Hashing + token logic in `api/lib/auth.js` (new).
- DB migration in `migrations/` following the existing numbered format.
- Reuse `api/lib/mailer.js` for the reset email.

## 7. Out-of-scope & known risks
- Reset-token storage/expiry is in scope but token rotation is not.
- Assumes SMTP is already configured (mailer.js exists).
- Risk: clock skew on JWT exp across instances — accept for now.

That spec is a few hundred words. It is also nearly impossible to misread. Every decision the agent would otherwise guess at — hashing algorithm, lockout behavior, log redaction, file placement — is stated. Hand this to Cursor or Codex and the build converges instead of drifting.

Writing acceptance criteria that don't drift

Acceptance criteria are where most specs quietly fail. They get written as wishes — "auth should be secure," "the API should be fast" — which are untestable and therefore unenforceable. An untestable criterion is decoration. The agent can claim to satisfy it and you can't prove otherwise.

The fix is given-when-then: a precondition, a trigger, an observable outcome. Each one describes exactly one behavior, and each one is machine-checkable — you could write the test from it without asking a follow-up question.

Drifty (bad)Machine-checkable (good)
Login should be secure.GIVEN a sign-in request, WHEN it's logged, THEN the log contains no password or token.
Handle bad logins gracefully.GIVEN a wrong password, WHEN POST /auth/signin, THEN respond 401 and increment failed_count.
Rate limit the endpoint.GIVEN 5 fails in 15 min, WHEN a 6th sign-in arrives, THEN respond 423 without checking the password.
Emails should work.GIVEN "User@X.com" at signup, WHEN stored, THEN the email column holds "user@x.com".

Three rules keep criteria honest. One behavior each — if a criterion has an "and" in the outcome, split it. Observable outcomes only — "responds 423," not "handles it properly." No adjectives in the THEN — "secure," "fast," and "graceful" are how criteria drift. State the number, the status code, the exact stored value. This is also exactly what makes a spec scorable: a deterministic checker can ask "is this criterion machine-checkable?" and get a yes or no, which is the first thing SparkVibe's structural validator measures.

Tool-specific notes

The spec is the same regardless of which agent builds it — that's the point of writing it down. But each tool has a preferred way to put the spec in front of the agent. Keep these neutral and practical.

Across all four, the lesson is identical: the agent builds what's in its context, and a referenced spec file beats a long prompt that scrolls out of view. The format is portable markdown precisely so you write it once and hand it to whichever builder you prefer — SparkVibe sits upstream of all of them and produces that file.

new — skip the copy-paste

You can hand the spec to your agent without leaving it. SparkVibe's MCP server lets Claude Code, Cursor, and Codex commission a spec, poll it to a score, pull the body straight into context, then push_to_github — no copy-paste from a browser. Same portable markdown, delivered in the loop. Included on Pro and up; see the setup.

The mistakes that waste an agent's context

An agent has a finite context window, and a bad spec spends it on noise. The recurring offenders:

Every one of these is the same failure in a different costume: the spec said something the agent couldn't act on precisely, so the agent guessed precisely instead.

How to know your spec is actually good

Here's the uncomfortable part. You cannot eyeball spec quality. A spec that reads beautifully — confident, well-formatted, thorough-looking — can be plausible boilerplate that encodes none of your real intent, and it fails exactly like a vague one does: you find out by trying to build it. The seven sections and the given-when-then discipline get you most of the way, but "looks complete" and "is build-ready" are different claims, and the gap between them is invisible to the naked eye.

That's why scoring belongs before the build, not after. SparkVibe runs every spec through three independent judges — an LLM rubric across eight dimensions, a deterministic structural validator that checks whether your acceptance criteria are actually machine-checkable and your constraints actually covered, and a three-persona builder panel that scopes the build and measures whether they converge. When judges that work differently agree, you have real signal. When they disagree, the disagreement points at the exact section that's overstating itself. That's how you know — not by reading it again.

FAQ

What's the difference between a PRD and a spec for an AI agent?

A PRD describes a product for humans — the why, the user value, the success metrics — and deliberately leaves implementation open. A spec for an AI coding agent describes a build for a machine: scope, constraints, a data model, and machine-checkable acceptance criteria. A PRD answers "should we build this and why." A spec answers "exactly what gets built, and how do we know it's done." The agent needs the spec; the PRD is upstream context that may inform it.

How long should a spec for an AI coding agent be?

As long as it takes to remove the silence, and no longer. For a single feature that usually lands between 300 and 800 words plus the acceptance criteria. The wrong question is word count; the right question is whether every decision the agent must make is either stated or explicitly delegated. A short spec that closes every ambiguity beats a long one padded with boilerplate the model would have written for any prompt.

Should I write one big spec or several small ones?

Prefer several small ones — micro-specs. An agent works against a finite context window, and one spec per shippable unit of behavior keeps it focused, makes each spec independently reviewable, and lets you regenerate one feature without disturbing the others. Reserve a single larger spec for genuinely cross-cutting concerns like the shared data model or an auth model several features depend on.

Don't hand-write the template every time.

SparkVibeAI turns a one-line intent into the whole build-ready spec — all seven sections, given-when-then criteria included — then scores it with three independent judges before you ever hand it to your agent. The template, automated and pressure-tested.

keep reading
what-is-spec-driven-development.md
What Is Spec-Driven Development? (And the Question Every Guide Skips)
vibe-coding-vs-spec-driven-development.md
Vibe Coding vs. Spec-Driven Development: Surviving the 6-Month Wall