Spec-driven development means you write down what to build and how you will know it is done before an AI agent writes any code. GitHub's Spec Kit sums it up: "Define what to build and why, then turn the specification into a plan, tasks, and working code." We use a lighter version: the spec is a GitHub issue with a task type and acceptance criteria, and a machine gate checks that the promised output exists.
AI writes all of our code today. So our work moved into the spec and the checks around it. Below is our issue contract, the code that reads it, and what we learned when the spec was wrong.
When you build an agent, give it proper guardrails and a clear brief. Put it on one task and specify that task well.
Filip Papranec
Founder, Digitaliko
What is spec-driven development with AI agents?
The idea is old: agree what done means before you build. With AI agents it matters more, because an agent builds something either way. Without a spec it fills the gaps with guesses, and the guesses look finished.
Spec Kit runs it as commands in the agent's chat: project principles, specify, plan, tasks, implement. Other tools have their own flavor. They share one thing. The spec comes before the code, and it stays the reference for review.
Where context engineering fits: everything the agent sees is context, from rules files to tool output. The spec is the part of that context that says what done means. More context without it only makes a more confident wrong result.
Why a GitHub issue instead of a long spec document?
Our issues are a queue people read every morning. Our rule for them starts with three words: "Queue items, not specs." A long spec goes into a linked document. The issue is what one person or one agent picks up and finishes.
Long spec document
Written up front for a whole feature.
Drifts from the code after the first change.
Hard to check line by line.
Good for a big design decision.
What we use
Issue contract
One outcome, one owner, one PR.
Task type, acceptance, where the output goes.
Checked by code before and after the work.
Links a design doc when one is needed.
Fewer, bigger issues work better for agents too. Our rule is one finished outcome per issue and per pull request. A big one is still one PR, with one commit per layer, so a person can read it in build order.
What goes into our issue contract?
Three things are required: a title with an action verb and a concrete object, one priority label, and what to do. For simple work the title alone is enough. Sections like ## What, ## Acceptance and ## Output appear only when they carry content. An empty ## Acceptance: TBD is forbidden.
The task type decides what must exist at the end:
Type
The issue gives
Output that must exist
code
what is wrong or wanted, acceptance
a PR that closes the issue, a test that fails before and passes after, the repo's checks green
research
the question, scope, output
findings plus a claims file: every claim with a source URL and a verbatim quote from that page
qa
URL or flow, scenarios
a report with PASS or FAIL per scenario, plus screenshots
doc
what, for whom, output
the document, with working links
reply
the message and its context
a draft in the sender's language, never sent
Nobody adds a label for the type. Whoever picks up the issue infers it. A missing ## Output or ## Acceptance gets filled in as a stated assumption, never silently.
How does a machine check the spec?
Twice: before the work and after it.
Before the work: READY. A read-only model call reads the issue and the code and returns a typed answer. Shortened from our schema:
{
"ready": true,
"type": "code",
"done_when": ["criteria a reviewer can answer yes or no"],
"verifiable": "test",
"confirmation": ["the test that carries the proof"],
"premise": "verified against <file:line>",
"question": ""
}
premise saves us the most time. The check compares every claim the issue makes about today's behavior with the code, with file and line. If nothing of the issue still holds, it proposes closing it. verifiable says whether a test can prove the riskiest criterion, or only a person looking at it. If a product decision is missing, question asks one question with two options and a recommendation, and the work does not start.
After the work: the gate. Plain bash with exit codes, one branch per task type. For code it runs the repository's own typecheck, lint, tests and build, plus a red-proof: changed source needs a changed test, and that test must fail on the base branch. For research it opens every source URL and searches the page for the quote. A paraphrase fails. A model never grades this part.
What changed when we stopped sizing tasks?
We used to score tasks by size and route them by it. We replaced that with readiness: is the premise checked against the code, is the proof named, is anything left to decide.
The gates are the other half of the spec. The issue says what done means. The checks make sure nobody, person or agent, can call it done otherwise.
What we want is to build the gates the agent is allowed to pass through, and decide what we let it do.
Filip Papranec
Founder, Digitaliko
What went wrong with our specs?
The readiness check came late. At first the agent took the issue as written. We added the premise check against the code later. Today it is the first stage, and that is where we would start.
A wrong comment is not a wrong issue. An old plan in a comment made the check refute good issues. Now only a claim in the issue body counts. A refuted body goes to a person, who answers OK before any work starts.
A green gate that proved nothing. In one monorepo the red-proof ran tests without the right environment. Every "fails on the base branch" was a test that never started, not a real red. For a few days every red-proof there passed falsely. Now tests run through the repository's package manager, per package, on the pinned Node version.
The last one is the lesson we repeat most: a check that cannot fail is not a check. More on that in Red-green TDD with AI agents.
How do you write an issue an agent can finish?
01
One outcome
Action verb and concrete object in the title. One finished result, one PR.
02
Done as yes or no
Acceptance criteria a reviewer can answer with yes or no.
03
Name the proof
Which test proves it, or which person has to look at it.
04
Name the output
PR, report or document, and where it will be.
05
Check the premise
Before work starts, compare what the issue says with the code.
The last step is the one teams skip. It is also the cheapest: one read-only model call, before a line of code exists.
Writing down what to build, why, and how you will know it is done before anyone writes code, then turning that into a plan, tasks and code. With AI agents the spec is also the context the agent works from.
Do I need a long specification document?
No. For most work we use a GitHub issue: a title, a task type, acceptance criteria and where the output goes. A long spec lives in a linked document, not in the issue.
What is the difference between spec-driven development and context engineering?
Context engineering is everything the agent sees: rules, files, tools, history. The spec is the part of it that says what done means. Without a spec, more context only makes a more confident wrong result.
How do you check that the agent met the spec?
With code, not opinion. Before the work, a read-only check compares the issue with the code. After the work, a gate checks that the promised output exists: a PR with a test that fails before and passes after, sources with verbatim quotes, a QA report.
When is a spec not worth it?
For a one-line fix the title is the spec. Where only a person can judge the result, such as copy or visual feel, the spec says so and the work goes to that person.
Ask AI about this article
The AI also draws on our other articles and experience from real projects.
Playwright MCP gives an AI agent a real browser. How we use it so the agent checks its own change in the running app and hands back PASS or FAIL with screenshots, what it catches that unit tests miss, and when plain Playwright scripts are the better tool.
LLM as a judge means a language model grades another model's work against written criteria. How we run one on every pull request our AI agent opens: machine checks first, a fresh read-only session told to refute, a typed verdict, and the rules we added after the judge got it wrong.
How we run Claude Code review on every pull request with one reusable GitHub Action: what the AI reviewer is told to look for, how it stayed green and silent, what it misses, and why only a person approves.