A spec here is a contract more than one roadmap line depends on — a file format, a vocabulary, or a behaviour every tool obeys. It exists because a rationale section says why in 250 words, and a format needs to say what, at whatever length that takes.
Status: draft. There is no implementation. Each spec is settled enough to build against, and the first implementation that disagrees with one is evidence about the spec, not only about the code.
| Spec | What it fixes | Lines it binds | State |
|---|---|---|---|
tool-surface.md | How every call behaves: handles, post-conditions, errors, self-description, conventions | PW1–PW6 | written |
project-config.md | What a project declares, and how a value is resolved | PW5 | to come |
measurements.md | The closed vocabulary a comparison may return | PW8–PW11 | written |
acceptance-spec.md | What “correct” means, as a file a search can aim at | PW12, PW13, PW15 | written |
geometry.md | A shape as data rather than as a program | PW30–PW34 | to come |
provenance.md | What is recorded beside an artefact, and the cache key | PW6, PW14, PW17 | to come |
The four written ones live in docs/specs/. They are ordinary documents — nothing governs them, and they are edited directly, unlike the roadmap.
TOML for documents a person authors or reads — the project config, an acceptance spec, a geometry declaration. JSON for records a machine writes — provenance sidecars, job state, ledgers, cache entries. The split is about who edits the file, not about what is in it. A document may also arrive as JSON where a caller emits it programmatically; a record never arrives as TOML.
One file per asset, beside it or under a directory the project config names. This is the whole grammar: a list of named predicates, each naming a measure from the closed vocabulary and stating one bound, and a block per parameter the search is permitted to move.
asset = "mascot"
rung = "final" # the lowest preview rung a verdict may be taken at
[[predicate]]
id = "face-reads-cream"
measure = "delta_e"
region = [120, 80, 180, 140]
target = "#E8D5C4"
max = 2.0
[[predicate]]
id = "silhouette-holds"
measure = "silhouette_iou"
against = "docs/design/art/ui/mascot.png"
min = 0.97
[[predicate]]
id = "has-a-saturated-tail"
measure = "saturation_p99"
region = "subject"
min = 0.85
weight = 0.5
[search.light]
min = 0.5
max = 4.0
[search.form]
min = 1.0
max = 4.0
step = 0.1Anything no measure can compute. “Reads as cloth rather than paper” is a real criterion and not a predicate, and pretending otherwise by inventing a proxy for it is how a spec ends up satisfied by a render a person rejects. That case is expected rather than designed away — it is exactly what the contact sheet is for.