§2 The VER registry, and the provenance action value space
2.1 The problem
VER 1.0 closes seventeen value spaces as JSON Schema enums — eighteen enum keywords, of which two declare dtype identically on the space descriptor and on the embedding — while 1.0 §5 uses registry language (“Registered perceptual algorithms in 1.0…”). There is no registry. Every new perceptual hash, chain action, source class, acquisition method, metric, or segment family therefore requires a full specification revision, and implementers extend by convention instead — which is how interoperability dies. The measured consequence today: provenance.chain[].action has six values and cannot express publish, export, sign, or any geometric edit, so real pipelines either lie (transcode) or omit events.
2.2 Registry policy (DRAFT)
ADR-0004 is accepted conditionally. The conditions below are conditions of ratification, not of drafting: none of them is satisfied today, and §2.3 may not ship until all of them are.
The VER Registry is established as a companion document to the specification, maintained under a Specification Required policy: a new entry requires a stable, publicly available document defining the value's exact semantics, and review by the designated expert.
The registry covers four value spaces: provenance actions, perceptual algorithms, contextual source classes, and metadata segment families. Each entry carries: the token, a one-line definition, the specification reference, the VER version in which it was registered, and its status (
active|deprecated).Registry additions are minor-version events for the registry document and do not require a schema revision for value spaces the schema has opened.
Ratification conditions (ADR-0004). All four are blocking rows on RELEASE.md's checklist:
- The registry must exist before §2.3 ratifies. A governed registry means: a stable published URL; immutable, individually addressable snapshots; a digest per snapshot; append-only history; a written deprecation policy; named maintainers; and an appeals path for a refused registration. A registry with none of these is a wiki, and an open value space behind a wiki is an unconstrained string.
- Conformance binds to a registry snapshot, never to the live registry. A Record whose chain carries a bare action token that this specification does not itself define MUST declare
provenance.registry— the snapshotversionandsha256it was minted against, optionally with the snapshot'suri. A token is never reused for a different meaning, and a deprecation never invalidates history: a Record minted against snapshot N stays conformant when the token is deprecated in snapshot N+1, and a Consumer resolves the token against the snapshot the Record names. - Three tiers, and what a Consumer owes each. §2.3 states them.
signis registered, and is constrained — §2.3's rules onprovenance.signatureand ontarget.
2.3 Provenance actions become an open value space (ADR-0004)
Three mechanisms were considered: (a) bump the enum every minor version, (b) an open string plus a registry, (c) namespaced x- escape values. The draft selects (b), with a closed core set, and takes one element of (c): the namespaced form is how an unregistered value is spelled. (a) makes every vendor action a standards-track event; (c) applied to every extension produces a permanent two-tier vocabulary in which the interesting values are all second class.
provenance.chain[].actionis a string, not an enum. It MUST match:where
<authority>is a reverse-DNS label sequence ([a-z0-9]([a-z0-9-]*[a-z0-9])?joined by., at least two labels).A bare token MUST either be one of the sixteen this specification defines below, or an
activeentry of the registry snapshot the Record names inprovenance.registry. A value that is neither MUST be namespaced under an authority the Producer controls (com.example.pipeline/watermark).
Three tiers, and the Consumer's obligation to each.
| Tier | Which tokens | What a Consumer MUST do |
|---|---|---|
| Core (must-understand) | The six 1.0 values: decode, normalize, embed, transcode, redact, import | Understand them. Reasoning about reproducibility depends on them, and they keep their exact 1.0 meanings forever |
| Specification-registered | The ten added at 1.1: publish, export, compose, upscale, crop, resize, color_adjust, denoise, generate, sign | Understand them. They are defined by this document, so a 1.1 consumer resolves them without consulting anything |
| Registry-registered, and authority-namespaced | Every other bare token (resolved against the named snapshot), and every <authority>/<token> | MAY treat as opaque. MUST preserve it, MUST NOT reject the Record on that basis, and MUST NOT infer semantics from the token's spelling |
Old-consumer behaviour on a new bare token, stated because it is the failure mode this design has. A 1.1 consumer meeting a bare token outside the sixteen above is meeting a value registered after this specification shipped. It MUST treat that event as opaque custody — the same as an authority-namespaced token — and MUST NOT reject the Record. It MAY resolve the token against the snapshot named in provenance.registry; if it cannot fetch that snapshot, the event stays opaque and the Record stays conformant. A consumer that rejects on an unrecognized token has converted an extensible value space back into a closed enum, unilaterally.
The value space is therefore syntactically open and administratively governed: the schema admits both forms, and registry membership is a rule of the standard enforced by the profile, not by the schema. A Producer that invents watermark as a bare token with no registry snapshot behind it is non-conformant; the same Producer emitting com.example.pipeline/watermark is conformant, today, with no standards action required.
Core and specification-registered actions (sixteen entries). The six VER 1.0 values are registered unchanged; ten are added:
| Token | Registered | Definition |
|---|---|---|
decode | 1.0 | The Asset was decoded to a pixel buffer. |
normalize | 1.0 | CPNP-1 was executed, producing the Canonical Buffer. |
embed | 1.0 | One or more vectors were computed. |
transcode | 1.0 | The Asset was re-encoded into another container or codec without geometric or tonal change. |
redact | 1.0 | Metadata was removed or replaced under 1.0 §7.5. |
import | 1.0 | The Asset entered the Producer's custody. |
publish | 1.1 | The Record, or a projection of it, was made available to parties outside the Producer. |
export | 1.1 | A rendition of the Asset was written out for consumption elsewhere. |
compose | 1.1 | Two or more Assets were combined into this one. Requires a lineage block (§3). |
upscale | 1.1 | Resolution was increased, by interpolation or by a model. |
crop | 1.1 | A sub-rectangle was selected. |
resize | 1.1 | Dimensions were changed without cropping. |
color_adjust | 1.1 | Tonal or colour values were altered (grade, curve, white balance). |
denoise | 1.1 | A noise-reduction operator was applied. |
generate | 1.1 | Pixel content was produced by a model rather than captured or edited. |
sign | 1.1 | A detached signature was computed over the Record and recorded in provenance.signature (E17). The event's at is the signing time and its actor is the signer. |
2.3.1 Asset events and Record events (target)
An acquisition chain that mixes “what happened to the pixels” with “what happened to the document describing the pixels” is ambiguous exactly where provenance matters. 1.1 separates them:
provenance.chain[].targetis an optional value,asset|record. Absent meansasset— which is what every 1.0 chain event was, so no 1.0 event changes meaning.
signacts on the Record. Where asignevent statestarget, it MUST berecord(schema-encoded).publishacts on whichever of the two the Producer made available, and SHOULD state which.- Every other registered action in §2.3's table acts on the Asset.
provenance.acquisition.acquired_at, which the chronology rules below lean on, is defined here because 1.0 used it without defining it: it is the instant the Asset entered the Producer's custody, as an RFC 3339 timestamp with an explicit offset. Chain events MUST NOT precede it (VER1202) — a comparison that is only meaningful now that the quantity has a definition.
2.3.2 uri on a chain event
provenance.chain[].uriis an optional absolute URI naming where the event's output was placed: the publication URI for apublish, the rendition's location for anexport.
- A
publishevent SHOULD carryuri, or carry an identifier from which the publication URI is derivable. The 1.0 gap this closes is measured: the reference federation pipeline had no legal home for itsat://publication URI and put it in a search-index payload instead (REC-11,WIRE-30).uriis deliberately not digest-coupled (§15.3). It names a location, not a content-addressed artifact whose integrity this Record binds. A Consumer MUST NOT treat a chainurias evidence of anything but the Producer's claim that it published there.
2.3.3 sign requires the signature it describes
A Record whose chain carries a
signevent MUST carryprovenance.signature(schema-encoded). Asignevent without a signature asserts a signing that left no artifact, which is indistinguishable from a false claim.The converse is not required: a signed Record need not carry a
signevent, because signing may happen after assembly. The event is inside the signed payload — E17 removes onlyprovenance.signaturebefore canonicalization, not the chain — so a Producer that wants the signing time inside the signature writes the event first and signs afterwards.
sign is registered because signing is a first-class operation with a first-class home in the Record — provenance.signature — and, until now, no way to say that it happened. It is the one registered action that acts on the Record rather than on the Asset.
transcode MUST NOT be used for upscale, crop, resize, color_adjust, denoise, or generate. Recording a geometric or tonal edit as a transcode is non-conformant in 1.1; it was merely undetectable in 1.0.
Every action that changes pixels — compose, upscale, crop, resize, color_adjust, denoise, generate — produces a new Asset with a new content_hash and a new pixel_hash. Where the Producer holds the input, the resulting Record SHOULD carry a lineage block naming it.
2.4 The remaining three value spaces
identity.perceptual[].alg, embeddings[].source.class, and metadata.raw[].segment are registered but not yet opened in this draft: the schema keeps their enums closed. Opening them is gated on the registry document existing, because an open value space without a populated registry is just an unconstrained string. The intent is to open alg and segment in the same revision that publishes the registry; source.class additionally requires a grade assignment for each new class (E19) and MUST NOT be opened before the grade table is registry-backed.
The mechanism of §2.3 is designed to generalise to all three, and to the other value spaces §2.1 counts. It has not been applied to them here. Until it is, SCH-22's observation — that VER uses registry language with no registry behind it — is answered for one value space out of seventeen and open for the rest.
