The contracts

What is settled before the code is.

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.

SpecWhat it fixesLines it bindsState
tool-surface.mdHow every call behaves: handles, post-conditions, errors, self-description, conventionsPW1–PW6written
project-config.mdWhat a project declares, and how a value is resolvedPW5to come
measurements.mdThe closed vocabulary a comparison may returnPW8–PW11written
acceptance-spec.mdWhat “correct” means, as a file a search can aim atPW12, PW13, PW15written
geometry.mdA shape as data rather than as a programPW30–PW34to come
provenance.mdWhat is recorded beside an artefact, and the cache keyPW6, PW14, PW17to come

The four written ones live in docs/specs/. They are ordinary documents — nothing governs them, and they are edited directly, unlike the roadmap.

Two format rules, so nobody has to decide twice

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.

An acceptance spec, in full

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.

mascot.accept.toml
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.1

What an acceptance spec deliberately cannot say

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

Deliberately not specced yet

  • The clip and curve format. It depends on decisions the skeleton line makes, and a format written before them would be guessing. Its own roadmap section holds the requirements until then.
  • The paid-service lock and ledger. One file written by one task, whose fields are already pinned on that line. It becomes a spec here if a second consumer appears.
  • The tool list. Which calls exist is implementation, and it follows from the tool surface rather than needing a document of its own.