← Knowledge

Spec-Driven Development for AI Coding Agents

A technical lesson on preserving intent, constraints, and verification when AI makes software implementation cheap.

Spec-driven development for AI coding agents is a software-development method in which durable specifications, constraints, and verification evidence guide delegated implementation. It treats generated code as a replaceable artifact and preserves product intent in documents and tests that can be inspected across agent sessions.

The method addresses a bottleneck created by fast code generation. When implementation becomes cheap, teams can produce plausible patches faster than they can establish whether those patches preserve intended behavior. The scarce work moves from typing code to defining the desired state, grounding the work in the actual repository, mapping claims to evidence, and recording what happened.

This lesson presents one practical model:

specification surface + repository grounding + verification map + execution receipts = delegable software work

The model is a synthesis of emerging research and production practice. Its direction is useful, but several supporting studies are recent preprints or self-reported production accounts rather than settled evidence.

Definition and scope

A useful specification is an operational state object rather than a wish list. It records:

  • what exists now;
  • what should change;
  • what must not change;
  • which files, interfaces, and user workflows are in scope;
  • which non-goals prevent scope expansion;
  • which constraints are hard invariants;
  • which tests, evaluations, or observations would demonstrate success.

This is related to test-driven development, but it covers a larger surface. Test-driven development places executable expectations before implementation. Spec-driven development keeps enough of the product model explicit that work can be delegated, resumed, reviewed, and corrected across multiple agent contexts.

The important distinction is between a prompt and an artifact. A prompt is an instruction delivered during one interaction. A specification is durable state that later agents and reviewers can inspect.

Four layers

Specification surface

The specification surface is the set of documents that define what a system claims to support. Depending on the project, it can include product intent, workflows, constraints, non-goals, invariants, interfaces, acceptance criteria, risks, and decision records.

GitHub Spec Kit separates these concerns into a constitution, specification, implementation plan, task list, and convergence pass. The exact file structure is less important than the separation of responsibilities:

  • the constitution records standing constraints;
  • the specification records desired behavior;
  • the plan records implementation strategy;
  • tasks record executable decomposition;
  • implementation changes code;
  • convergence repairs disagreement among those artifacts.

The specification needs enough precision to constrain work without freezing every implementation detail. A cathedral of Markdown is still a cathedral, even when the clergy are agents.

Repository grounding

A specification must refer to the repository that actually exists. Before planning, an agent should inspect current files, interfaces, dependencies, tests, documentation, and recent changes. After each major phase, it should validate that its proposed paths and assumptions remain true.

This prevents context blindness: the tendency of an agent to invent nearby-looking APIs, file paths, or architectural patterns when it has not inspected the relevant source. “Remember to check the repository” is weak procedural advice. A read-only discovery phase followed by explicit file and interface checks is auditable evidence.

Verification map

A verification map connects each intended behavior or protected invariant to evidence. The map can include:

  • unit and integration tests;
  • type and lint checks;
  • API contract tests;
  • browser or interface smoke tests;
  • evaluations;
  • migration dry-runs;
  • runtime logs and traces;
  • manual acceptance criteria when no automated test is suitable.

Tests are therefore evidence of intent, not a complete substitute for intent. A supplied test can specify an interface, edge case, or expected output while leaving product-level constraints unstated.

The distinction between fail-to-pass and pass-to-pass tests is particularly useful. Fail-to-pass tests show that the reported defect was repaired. Pass-to-pass tests protect neighboring behavior that worked before the patch. A fix that passes its new test while breaking unrelated behavior is still a regression, merely one with excellent paperwork.

Execution receipts

Long-running agent work needs receipts that connect the specification to the resulting artifact. A completion receipt should identify:

  • the specification version used;
  • the tasks attempted;
  • the files changed;
  • the acceptance criteria addressed;
  • the checks that ran, failed, or were skipped;
  • assumptions that changed during implementation;
  • unresolved follow-up work.

Receipts make incomplete or interrupted work resumable. They also reveal drift: if implementation forced a design change, the specification should change with it rather than leaving the next agent to begin from a false description of the system.

Evidence from coding-agent research

The empirical literature is young and uneven, but several results support the general direction.

The test-driven code-generation study found that providing public tests improved model performance over problem statements alone on function-level benchmarks, and that remediation loops produced further gains. Private evaluation tests still mattered because passing only the supplied tests can reward overfitting.

SWE-bench moved coding-agent evaluation from isolated functions to repository-level issue resolution. Its fail-to-pass tests made real bug repair measurable. Later work has emphasized that issue-specific success does not establish the absence of collateral damage, which is why regression-oriented pass-to-pass checks remain important.

The TDAD preprint reports that verbose instructions to “do test-driven development” were less useful than supplying a test-impact map tied to the changed files. In its reported experiments, graph-grounded test context reduced regressions substantially. The sample and model sizes limit generalization, but the mechanism is plausible: exact context is more useful than procedural scolding.

Bun's Rust rewrite

Bun's July 2026 account of rewriting Bun in Rust is a production case study in process control around large-scale agentic implementation. Bun reported a rewrite from roughly 535,000 lines of Zig over eleven days, with thousands of commits and dozens of concurrent agents.

The relevant details are the control surfaces around the generation:

  • PORTING.md and LIFETIMES.tsv recorded durable migration intent;
  • the first pass was deliberately mechanical rather than an opportunistic redesign;
  • implementer and adversarial reviewer agents operated in separate contexts;
  • compiler errors, linker errors, stack traces, and failing tests became explicit work queues;
  • workflows were revised when agents used destructive Git commands or stubbed code merely to make compilation pass;
  • resource isolation eventually required operating-system controls rather than polite prompts;
  • the merge required green cross-platform continuous integration without deleting or skipping tests.

The result is better understood as agent-workflow governance than as raw model capability. Generation supplied volume. Specifications, role separation, execution feedback, tests, and resource controls made the volume reviewable.

Application to persistent agent runtimes

In a persistent runtime such as Letta Code, spec-driven development can be implemented as a compact loop:

  1. Spec intake: record intent, constraints, non-goals, acceptance criteria, and ownership.
  2. Grounding: inspect the real repository, interfaces, tests, documentation, and recent history.
  3. Task compilation: convert the spec into bounded tasks with expected files and checks.
  4. Implementation: change code within the task boundary.
  5. Impact analysis: map changed files and interfaces to tests and evaluations at risk.
  6. Proof run: execute the required checks and capture their results.
  7. Receipt: report the spec version, changes, evidence, failures, skips, and follow-up.
  8. Convergence: update the specification when implementation reveals that an assumption was wrong.

The review target then changes. Reviewers inspect the relationship among intent, invariants, and evidence before treating line-by-line code reading as the only trustworthy validation mechanism.

Failure modes

Paperwork without force

A specification without acceptance criteria, ownership, verification, or update discipline does not constrain implementation. It records aspiration after the fact.

Premature precision

An over-specified document can freeze the wrong abstraction. Good specifications distinguish hard invariants from provisional implementation choices.

Implementer-authored proof

When one agent writes both the implementation and its only tests, the tests can reproduce the same misunderstanding as the code. Independent review, external checks, and protected regression suites reduce this correlated failure.

Stale specification drift

When code changes and the specification does not, future work starts from an inaccurate state model. Updating the spec is part of completion, not a later documentation chore.

Bottleneck preservation

If every generated change still requires the same maintainer to manually bless every line, a new document layer has not changed the review architecture. The purpose is to move human judgment toward product intent, invariants, and proof coverage.

Practical rules

  1. Write the invariant before broad implementation begins.
  2. Keep product intent, implementation strategy, and local tasks in separate artifacts.
  3. Make every acceptance criterion evidential, or state why it cannot be.
  4. Ground plans in repository evidence before proposing architecture.
  5. Prefer exact test and interface context over generic procedural reminders.
  6. Track neighboring regressions as well as issue-specific fixes.
  7. Update the specification when evidence disproves an assumption.
  8. Review the proof surface before treating generated code volume as progress.

Evidence limits

This lesson combines peer-reviewed work, active software documentation, recent preprints, and self-reported production results. The architecture is more mature than the empirical literature. Large effects reported in small or recent studies should be treated as hypotheses for local evaluation rather than universal performance guarantees.

Sources

Sources

  1. GitHub Spec Kit methodology
  2. SWE-bench
  3. Bun: Rewriting Bun in Rust
  4. Test-Driven Development for Code Generation
  5. TDAD: Test-Driven Agentic Development

Connections

Related

Linked here

Suggest a correction ↗