Skip to content

Spec-driven development with AI agents: our issue contract

For CTOs and engineersHarness and checks · Updated

Filip PapranecFounder, Digitaliko

The spec is an issue. The gate checks it was met. Illustration: a GitHub issue with acceptance criteria passing a readiness check and a gate on its way to a pull request.The spec is an issue. The gate checks it was met. Illustration: a GitHub issue with acceptance criteria passing a readiness check and a gate on its way to a pull request.

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
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:

TypeThe issue givesOutput that must exist
codewhat is wrong or wanted, acceptancea PR that closes the issue, a test that fails before and passes after, the repo's checks green
researchthe question, scope, outputfindings plus a claims file: every claim with a source URL and a verbatim quote from that page
qaURL or flow, scenariosa report with PASS or FAIL per scenario, plus screenshots
docwhat, for whom, outputthe document, with working links
replythe message and its contexta 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.

The spec is a GitHub issue. A read-only READY check compares it with the code, then the agent builds, then a gate checks the output for the task type: code needs a PR with a test that fails before and passes after, research needs every claim with a source and a verbatim quote, QA needs a PASS or FAIL report with screenshots, a document needs working links.The spec is a GitHub issue. A read-only READY check compares it with the code, then the agent builds, then a gate checks the output for the task type: code needs a PR with a test that fails before and passes after, research needs every claim with a source and a verbatim quote, QA needs a PASS or FAIL report with screenshots, a document needs working links.

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
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?

  1. One outcome

    Action verb and concrete object in the title. One finished result, one PR.

  2. Done as yes or no

    Acceptance criteria a reviewer can answer with yes or no.

  3. Name the proof

    Which test proves it, or which person has to look at it.

  4. Name the output

    PR, report or document, and where it will be.

  5. 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.

What the rest of the agent's setup looks like, from rules to hooks, is in What is an agent harness? Agent = model + harness. When the built-in planning mode is enough instead of a spec is covered in Claude Code plan mode.

Is your issue ready for an agent?

Want a spec-to-PR loop in your team?

We build this contract and the checks behind it right in a team's repository as part of AI-native SDLC for development teams. Short of hands rather than process? See a senior developer for your team. Since 2023 we have delivered 15+ production projects.

We get back to you within two business days, and together we pick what to fix first.

If you'd rather talk first, book a 15-minute meeting with Filip.

What did "done" mean in your team's last issue, and who checked it?

In short

  • Spec-driven development: what to build and how you will know it is done, before the agent writes code.
  • Our spec is a GitHub issue: one outcome, a task type, acceptance criteria and the output.
  • A read-only check compares the issue with the code before the work starts.
  • A gate checks the output exists: a red-then-green test, quoted sources, a QA report.
  • A check that cannot fail is not a check.

Frequently asked questions

What is spec-driven development?

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.

ChatGPTClaudePerplexity
Engineering

Other articles

Harness and checks

Playwright MCP: let the AI agent prove it works

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.

Harness and checks

LLM as a judge: a reviewer that tries to refute the work

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.

Harness and checks

Claude Code review in CI: what AI code review misses

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.

Time to stop planning and start solving.

Contact

  • Digitaliko s.r.o.
    Šípková 377/17
    958 03 Malé Uherce, Slovakia
  • Get in touch
    info@digitaliko.com
    +421 950 595 506
    WhatsApp