Lesson 1: The checkable spec
What you'll learn: how to define work so an AI agent, or future you, cannot quietly build the wrong feature. What you need: a text editor. No tools, no framework, no product. Time: ten minutes per feature. Cost: zero dollars.
The idea#
Before you ask an AI to build anything, write one page next to your code. Not a description of the work. A page the finished work can be graded against: a short list of what will be observably true when it ships, each line paired with the check that proves it, the files the work must not touch, and one command that has to pass.
Then build however you like, with whatever tools you like. When the build finishes, you grade the result against the page instead of against your memory.
That page is the smallest version of a spec, and it is the foundation this entire series builds on.
Why start here#
I have built with AI daily for thirteen months: fifteen repositories, four public sites, a few dozen scheduled jobs. The bottleneck was never producing software. It was remembering what I meant, because every new AI session starts with total amnesia about your project, and after enough projects, so do you. Nine months in I created a repository that holds no code at all, only written intent, and it now takes more commits than any code repository I own: 1,867 since January against 661 for my busiest code repo. The habit below is that repository reduced to its smallest useful unit.
The template#
Copy this into SPEC.md next to your code, one file per feature. The rule that makes it work: everything above Notes is binding, Notes is guidance. Keep the whole file under a page.
---
status: draft # draft -> building -> shipped
verify: npm test # one command that must exit 0
---
# <feature name>
## Done means
- [ ] <an observable state: a URL that loads, a file that
exists, a number that appears somewhere it did not>
check: <the command and its expected output, or the
page and what it must show>
## Do not touch
- <paths, files, or systems this work must leave alone,
named precisely enough to paste into a request>
## Notes (guidance, not binding)
<links, patterns worth copying, context the builder
might want>
One distinction before the parts, because your tools already read a similar file. CLAUDE.md and AGENTS.md are standing instructions: how to work in this repository, loaded into every session, kept deliberately short. This file is not that. It is per-feature intent, one page, and it retires when the feature ships. Feature intent pasted into the standing file becomes noise the day after, and both conventions warn that a bloated standing file gets ignored. Two files, two jobs: the standing file says how to work here, the spec says what must be true when this one piece of work is done.
Each part, and the mistake it prevents#
A done line is a state plus its check, fused. "Add a toggle component" always completes, whether or not the world changed. "The pricing page shows the annual toggle, check: open /pricing, toggle visible" can fail, and a line that can fail is a line that can be graded. Writing the check beside the claim closes the most common gap in software, where "done" quietly means "I stopped."
Do not touch is the section solo builders skip, and it costs more now than it ever has, because an AI agent has infinite enthusiasm. Left undirected it will refactor nearby code, upgrade a dependency, improve your error messages. Some of that is even good. All of it is drift you did not choose. Two path lines cost ten seconds, and because they are paths rather than prose, you paste them straight into the request and repeat them in review.
The verify command is where the honesty lives. One command that must exit clean, named before the build starts. If you cannot name one, you have learned the most useful fact available: you do not yet know what you want, and no amount of prompting will extract a target you have not set.
Notes is quarantine. Anything worth saying that is not a requirement goes below the line. If a sentence in Notes turns out to matter, it was a done line all along. Move it up and give it a check.
A worked example#
The spec for this lesson's own publication, exactly as I graded it:
---
status: shipped
verify: node scripts/check-blog-disclosure.mjs --staged
---
# lesson-1-post
## Done means
- [ ] the lesson page returns 200 on the live site
check: open the URL after deploy, template visible
in a code block
- [ ] the blog index lists it under the series title
check: open /blog, entry present
## Do not touch
- every other post in the blog content folder
- the blog index layout
## Notes (guidance, not binding)
The closing line points at lesson 2; keep it
forward-looking.
Small on purpose. The habit survives because it costs ten minutes, not because it produces a document anyone admires.
From the field#
Earlier this month a proposal of mine died because of its own check, written early. I wanted to simplify a scoring system I run by removing one of its inputs, and I had a page of numbers agreeing with me. The done line, written before those numbers existed, required the improvement to hold on data the analysis had never seen. It did not hold, and the change never shipped. The line was written by a version of me who did not yet want a particular answer. That is the entire mechanism: by the time results arrive you are a motivated reasoner, and the file is not.
Exercise#
- Pick the next feature you are about to ask an AI for.
- Write the file first. Cap it at ten minutes. If the page is hard to write, ask your AI to interview you about the feature and fill the template from your answers, then edit the binding lines yourself. The interview surfaces edge cases you had not considered; the editing keeps the intent yours.
- Paste the Do not touch lines into your request, then build the feature the way you normally would.
- Before you accept the work, grade the file line by line: tick each done line only after running its check, then run the verify command.
You're done when#
At least one line refuses to tick. There will be one; mine have ranged from a wrong chart default to a deploy that quietly did more than it was told. A failed check is not a failed build. It is the gap between what you meant and what you asked for, visible for the first time, and seeing it once will change how you write every request after.
Common mistakes#
- Writing activities instead of states in done lines: "refactor the handler" instead of "the endpoint returns the new shape."
- A check only you can evaluate, like "it feels faster." If it needs your taste, it is not a check yet.
- Skipping Do not touch because the feature seems too small. Small features are where agents wander most.
- Requirements hiding in Notes. If it must be true, it is a done line with a check.
- Writing the file after the build. The mechanism only works because the lines predate the temptation.
Next lesson#
Why every new AI session relitigates decisions you already settled, and the one-page record that stops it.
Keep reading
Watch the agent write
A polish agent drafts an essay against a pre-approved topic.
When your method repo and your product repo don't talk to each other
I built a method as a public repo and the product that runs it as two private ones, and none of them treated the others as a source of truth. The domain enum lived in four places. A persona drifted between its lens file and its API contract. Here is what that cost, and the one structural change that turned the whole class of bug into a failing test.
Context architecture beats documentation dumps
Dumping the whole corpus into an AI agent makes it worse, not better. The fix is architectural: each task loads a curated slice, not everything you have. Here is the method, and the same move at three different layers: specs, sensor data, and evaluation lenses.
SDD isn't about managing AI agents, it's about managing context
Spec-driven development reads like a methodology for controlling AI agents. It isn't. It's a methodology for managing context across stateless sessions. The spec is the persistent memory.
Spec-Driven Development and the Folder Architecture That Makes It Work
Why spec-driven development and structured folder architecture are the missing infrastructure for AI-assisted engineering: methodology, common mistakes, and where to start.
The schema is the product
SpecSelf looks like a set of features: coherence checks, persona rotation, review cadences, an audit trail. Every one of them was implied by ten frontmatter fields decided on day one. A life-OS is not a feature list. It is a schema decision, and the features are what fall out of making it correctly.
Follow the work
New tools and writing as they ship — pick a channel.