Three documents, each with its own reader and its own pace. The value comes from four properties rather than from the file count, so if you'd rather keep one file, the properties are the part to copy.
| Document | Answers | Reader | Pace |
|---|---|---|---|
| ONTOLOGY.md | What exists, what each thing carries, how things relate, the rules, the interface | Implementer | The working spec, read constantly |
| CRITERIA.md | How you know it's correct, readable and good | Tester, tuner | Written with the rules; each row owns its tests |
| ARCHITECTURE_LOG.md | What changed, when, why, what it replaced, and the alternatives you turned down | Next maintainer | A dated entry in the same change |
Purpose goes at the top of ONTOLOGY as §0, so the tiebreaker sits on the document people actually open rather than in a file of its own.
One rule holds across both of the first two: describe the current state only. No "formerly", no "originally named", no before-and-after tuning notes. All of that lives in the log, along with the reasoning for each change and the alternatives you turned down. Otherwise a reader has to subtract the history to find out what the system does.
You can tell your decisions from the model's guesses. [D] means you decided it, and only you can mark something that way. [P] means the model proposed it and it's awaiting review. [T] means a number that's expected to move once something measures it.
Each log entry records what else you considered and why this won. Writing the alternatives down turns out to find more bugs than reviewing the decision does. A number that came from a model rather than a measurement says so.
Rules arrive with their test cases as Given → Expect → Instrument
rows, which makes writing the tests closer to transcription. If a criterion has no
instrument yet, it's marked not yet covered rather than left looking
covered.
Goals are G-n, decisions D-n, open questions
Q-n, criteria R-n. Code, tests, commits and docs all cite
them, and they're never reused or renumbered. A retired criterion leaves a tombstone
row behind so its number can't come back meaning something else.
Every entity and rule carries a Serves: line naming what it's there for:
a goal from §0, a named quality, a driver. You can read it in both directions. Something
that serves nothing is a candidate for removal, and a goal that nothing serves probably
isn't being met.
The second reading is a question code can't answer at all. The first is why dead code survives in most projects: with no stated purpose, you can't show that a thing is no longer needed, so nobody takes it out.
An automated rule row owns test/R-n/, and the directory is
the only link between them. Test files aren't listed in the criteria table, because two
lists drift apart. Each test asserts one rule, so a failure points at one place.
A small coverage test then runs with the suite. It fails when an
automated row has no tests, when a test/R-n/ directory exists
for a row that isn't automated, or when an instrument cell isn't one of the four allowed
forms.
That coverage test is 41 lines with no dependencies, and it's the piece I'd keep if I could only keep one.
Five entities and two rules don't need three files. For small CRUD work, write one
SPEC.md and borrow the four properties above as sections — they compress
into one file without losing much. The full set starts paying when the thing will be
tuned, when the numbers are uncertain, when more than one person or session touches it,
or when it'll outlive your memory of writing it.
That's the shape of it. The prompts cover how the cycle runs in practice: what order to write things in, and what to ask for at each step.