What a published annotation row contains

One product ships: scene_report.declared.v1, produced by the reference annotator. A run names its annotator by import path, so any domain can ship its own; this page describes the row shape they all publish into.

Two halves, produced separately

A row’s values carries exactly two keys, and they come from different places.

key

written by

verified

output

the provider, verbatim

no

claims

Annotator.build_structured_output, before the request was sent

exact by construction

The provider is asked for prose and returns prose. Every value under claims is projected from the evidence this process already holds, in this process, before any request goes out — so no number on that side was ever authored by a model, and none can be. Nothing joins the two: no citation links a sentence to a claim.

For the reference annotator, output is {"expert_summary": [string, ...]} and claims is a SceneClaimsV1: scene quantities, receiver acquisition settings, a signals list, an optional interactions list, and the names of anything the evidence could not resolve.

The prose is not checked

No automated check asserts that the prose reflects the claims beside it, and none is planned. There is no reliable way to check a text description: a model can restate a value in a different unit, name a different quantity, invert a relation, or invent a property, and nothing mechanical over the text sees any of it. A caption may therefore contradict the claims in its own row.

Every row says so, in provenance.prose_verification, which is always not_performed. Failed rows carry it too, so a reader never has to infer verification status from an absence.

What this buys is that the machine-readable half is exact for every row however badly the prose reads, and that a contradiction, when it happens, sits in the open next to the truth rather than alone in a corpus. Verification of prose, where it is wanted, is ad hoc analysis with a stronger model, outside this pipeline — and it is cheap, because the judge needs only the row.

What is rejected

A reply that is not valid JSON, or that does not match the response schema the annotator itself declared, fails that record’s row. Core holds no table of reply shapes: the only authority on a reply is the annotator that asked for it.

A record whose annotator raises RecordNotAnnotatable publishes SKIPPED and is never sent to a provider — a quiet epoch is a fact about the corpus, not an outage. Any other preparation failure publishes FAILED. The set stays dense either way.

Naming

A product is <annotation_type>.<variant>.v<N>. Core checks that shape and nothing else: it holds no closed list of annotation types, so a sensing domain producing an occupancy_report names its own output without editing anything shared. The vN is the contract — the evidence consumed and the reply asked for. Prompt wording carries its own revision, declared in the Markdown file’s front matter and on the class that claims it.

annotation_type is derived from template_id and is not authored separately.

Versioning and provenance

An annotation set is identified by (annotation_set, publication_id) and is published atomically. A new run appends an overlay and never rewrites generation bytes; publishing different content under an existing publication id collides rather than overwriting.

Each row records the annotator’s identity, import path, origin and installed distribution; the prompt id and revision; the backend, provider and resolved model id; token usage; the evidence model that produced it; the projection the run configured; and prose_verification.

The projection fields are named configured_* deliberately. Core hands build_evidence the record itself, so whether those settings bounded the request is the annotator’s doing and not something core can attest to.

Hosted model text is intentionally not expected to be byte-identical across calls.

See also