Fixture construction rules
These rules are what make a fixture a test vector rather than a sample. New fixtures MUST follow them.
- One defect per fixture. Every fixture is a complete, internally self-consistent record except for the single thing it is testing. A fixture that is wrong in two ways cannot tell you which rule fired.
expect_codesis an exact set, not a floor. Where a defect provably drags another code with it, both are listed andnotesexplains the coupling. The coupled cases in this corpus:
| fixture | also emits | why it cannot be isolated |
|---|---|---|
ver403-embedding-space-id-malformed | VER501 | a malformed space_id can never match a declared descriptor, because spaces[] entries are pattern-constrained by the schema |
ver705-no-canonical-visual-embedding | VER201 | VER705 is the profile's defense-in-depth restatement of the schema's contains constraint |
ver801-pdq-length, ver802-phash-length | VER201 | the 1.0.1 schema binds digest width to the algorithm; under --schema 1.0.0 the profile code stands alone |
ver1101-c2pa-raw-present-status-missing | VER1103 | one record exhibits both readings of the same C2PA inconsistency; the codes overlap by design |
ver1103-c2pa-state-inconsistent | VER307, VER308, VER1101 | VER1103's absence arm is scoped to L3 by annex E28, so the fixture must claim L3 — which drags in every other L3 gate the missing provenance.c2pa block fails |
ver306-provenance-missing, ver307-c2pa-status-missing | VER307, VER308 | an absent provenance block fails every L3 gate at once |
The 1.1-draft fixtures add one coupling with one cause. VER409, VER608, VER609, VER903, VER1006, VER1007, VER1207 and VER1605 each also emit VER201, because each is a profile rule the 1.1 schema also encodes — the VER705 precedent, applied eight more times. The profile restates them because it has to keep answering when the schema in play is relaxed, future or somebody else's, and because two of them (a bundle path handed to a filesystem, and which bytes are a vector) are questions a consumer cannot afford two answers to. ver409-bundle-path-illegal, ver1006 and ver1007 emit two VER201 issues rather than one; the set of codes is what the manifest states, and notes says so in each case.
- Every digest, byte string and length is computed, never invented. Anything a reader can recompute from the record MUST be correct:
metadata.raw[].sha256over exactly the carried bytes,vector_b64decoding todim × dtype_sizebytes (ceil(dim/8)forbinary),vector_ref.byte_length,image.source_bytes_ref.sha256equal toidentity.content_hash.value, and real signatures over the real RFC 8785 JCS payload.
- Values that cannot be recomputed are still real digests of a documented preimage. No fixture contains a hand-typed hex string. Where the true input is not carried by the record — model weights, a referenced vector blob, the asset bytes themselves — the value is
sha256("ver-conformance-corpus/1.0.1 <purpose>"), e.g.sha256("ver-conformance-corpus/1.0.1 source-bytes boardwalk-sunset.jpg")foridentity.content_hash. These stand in for values a real producer measures; per errata annex E26 they are illustrative and MUST NOT be cited as interoperability evidence. The perceptual digests are the leading 64 and 16 hex characters of such a digest: well formed, and deliberately not genuine PDQ/pHash outputs — the corpus tests structure, not perceptual similarity. The contextual embedding'ssource.text_sha256is the true sha256 ofSunset over a boardwalk, silhouetted pier in background., and the redaction proofs aresha256("ver-conformance-corpus/1.0.1 redaction-salt|<field>|<value>"). Documented preimages are dictionary-invertible by construction — everysalted_sha256/commitment in this corpus can be reversed from this README, which is acceptable for illustrative values (E26) and fatal for real ones. A real deployment's salt is never written anywhere a reader can reach (draft §5.3, §5.5); 1.1-draft fixtures use thever-conformance-corpus/1.1-draftprefix.
1.1-draft fixtures use their own prefix, sha256("ver-conformance-corpus/1.1-draft <purpose>"), so a value can never be mistaken for one of the 1.0.1 corpus's. The purposes in use are source-bytes <asset>, canonical-buffer <asset>, weights <checkpoint>, refset-manifest <set>, tokenizer <name> and icc-segment <asset>. Four constructions there are not stand-ins at all and recompute from the record itself: manifest_sha256, descriptor_sha256, recipe_sha256 and commitment_sha256 — see 1.1-draft fixtures.
- Metadata segment bytes are real container bytes. The EXIF segment is an APP1 payload (
Exif\0\0+ TIFF header + an Orientation entry), the XMP segment carries itshttp://ns.adobe.com/xap/1.0/\0identifier prefix, and the IPTC segment is a Photoshop IRB holding an 8BIM IPTC-NAA resource — the byte ranges errata annex E11 defines.
valid/l3-full.json is the strictest case, because it is the published 1.0.1 example: its icc entry is a complete JPEG APP2 ICC payload — ICC_PROFILE\0, the two sequence octets 01 01, and a real 588-octet sRGB profile — because an elision is not byte-exact preservation under E11 and E26 forbids shipping one at L1 or above. Elsewhere in the corpus the ICC and C2PA payloads are short illustrative fragments rather than a complete profile or a valid JUMBF manifest store; their digests are still the real digests of the bytes shown, and no fixture that carries one declares a level at which E26 would forbid it.
- Declare full space descriptors unless the fixture is about not doing so. Annex E13 enumerates seven
preprocessingmembers for avisualspace (resize,interpolation,antialias,crop,mean,std,channel_order), five for atextspace (tokenizer,tokenizer_sha256,max_length,truncation,casing) and none for afusedspace, whose geometry is fixed by itsrecipe(E20). Every space in everyvalid/fixture carries its full set, or VER405 would fire and the fixture would no longer be silent. - Name the file after the code:
<code-lowercase>-<slug>.json, matchingtarget_code. - Keep fixtures minimal and level-appropriate. Claim the lowest conformance level that still reaches the rule: an L3 claim drags in every L1 and L2 gate, and an unsigned L3 record always warns VER308. Signature carriage is not level-gated, so the stage-15 fixtures stay at L1. Where a rule is itself level-scoped — VER1103's absence arm is L3 only — the fixture must claim the level the rule needs and list the collateral.
- Re-run the corpus after any edit (see above). Editing a signed fixture invalidates its signature: re-sign it with the recipe below, or the VER1402/VER1403 expectations become meaningless.
valid/l3-full.jsontracks the published 1.0.1 example, so an edit to that example means regenerating the fixture from it and re-signing;valid/l3-full-1-1.jsontracksstandards/ver/1.1-draft/example-record-lineage.jsonthe same way and needs no signature, because the published 1.1 example carries none.
The corpus signing key
keys/corpus-signing-key.pub.pem is an Ed25519 public key whose private half is derived from the published seed sha256("ver-conformance-corpus/1.0.1 signing-key-seed"). It exists so the signature fixtures can be regenerated by anyone, and it carries no trust whatsoever: it MUST NOT appear in any trust store, and a record signed by it MUST NOT be treated as attributable.
Re-signing a fixture — Ed25519 is deterministic, so this reproduces the shipped value byte for byte when the record is unchanged:
valid/l3-full.json is the published 1.0.1 example record plus a detached JWS over the RFC 8785 JCS form of that record with provenance.signature removed (errata annex E17). With no key supplied the profile reports VER1403 — signature present, not verified — which is why the entry allows that one warning; with the corpus key it verifies and the record validates with zero issues.
