Skip to content
VERASPEC
Repository
Errata ledgerstable

§10 Owner review (23 August 2026)

The standard's owner reviewed the full branch and issued rulings. Every row below was verified at source before it was ruled on; none is a re-litigation. Three columns matter here:

  • Classification is the change class, as everywhere else in this ledger.
  • Disposition names the ruling and the artifact or track that carries it. VAL*, P*, F* and G* are the review's own ruling identifiers for the validator, producer, federation and governance tracks.
  • Status is Closed where the remediation is in this branch's standards artifacts, Ruled — <track> where the ruling is made and the remediation belongs to another track's files, and Deferred (planned) where the review deliberately logged a deliverable rather than producing it.

Where a row supersedes an earlier disposition, it says so and names the finding.

10.1 Verified conditions in the implementation

IDTitleSourceClassificationDispositionStatus
REV-01validator.py's rule runner swallows rule exceptions into an empty result, so an internal failure can report ok: true — fail-openaudit/ownerimplementation-onlyVAL1 — a raising rule emits VER001 validator-internal-error (error, stage 0); the report gains status: conformant | nonconformant | indeterminate; any VER001indeterminate; ok = (status == "conformant")Ruled — validator track
REV-02cli.py's --ignore filter drops ignored codes and recomputes ok, so a CLI flag redefines the standards resultaudit/ownerimplementation-onlyVAL2ok and status are ALWAYS unfiltered; the report gains ok_after_policy_exceptions and ignored_codes; the CLI exit code follows the policy-filtered result (that is the flag's purpose) and the text output prints bothRuled — validator track
REV-03api.py writes uploads to Path(tmp) / upload.filename — filename traversal — and its path-mode ingest walks arbitrary server paths unauthenticatedaudit/ownerimplementation-only (security)F1 — server-generated names (uuid + sanitized suffix); path-mode gated behind VERD_API_ALLOW_PATHS and resolved strictly beneath an allowlisted root; optional bearer auth; streaming per-file and file-count limits; clamped limit paramsRuled — federation track
REV-04jetstream.py logs a handler exception and saves the cursor anyway — permanent silent event loss; delete commits ignored; update commits defeated by an early (producer, pixel_hash) dedupaudit/ownerimplementation-onlyF3 — cursor advances only on success or a successful dead-letter write; deletes remove the matching points and write a tombstone; updates re-process instead of dedup-skipping. Supersedes WIRE-03/WIRE-04's documented — not patched and WIRE-05's DeferredRuled — federation track
REV-05The 1.1 draft's bundle path pattern accepted ..\evil, C:\evil and a\..\evil — it constrained a segment's shape and never its alphabetaudit/owneradditive-1.1 (draft defect)fixed in this branch$defs/portablePath: printable portable charset per segment, / only, no empty/./.. segment, no leading /, plus a complement-class not so a trailing newline cannot slip past $. Draft §4.2, ADR-0007 revision 3Closed
REV-06The 1.1 draft's size limits were incoherent: a 16 MiB decoded-inline-raw cap is unreachable inside a 10 MiB serialized cap (base64 ≈ 21.3 MiB). The same incoherence sat in the validator's VER104 defaultsaudit/owneradditive-1.1 (draft defect)fixed in this branch — draft §7's three-quantity model: serialized record ≤ 10 MiB, decoded inline raw ≤ 6 MiB, decoded single vector ≤ 6 MiB, each individually reachable inside the one that contains it; referenced bytes governed separately. VAL5 aligns VER104Closed (standards half); Ruled — validator track (VER104)
REV-07tests/test_cpnp_determinism.py::test_broken_icc_profile_does_not_kill_ingest protects the fail-open ICC behaviour annex E8 forbidsaudit/ownerimplementation-onlyP1 — ICC fails closed per E8: a present-but-unusable profile raises CpnpError, no icc_error fallback, an absent profile still assumes sRGB; the test is replaced with fail-closed assertionsRuled — producer track
REV-08The validator wheel ships only py.typed — no schemas — so an installed wheel depends on the repository layoutaudit/ownerprocessVAL4 — the 1.0.0 and 1.0.1 schema JSONs become build-synced package data with a sync-check test; loader order VER_STANDARDS_ROOT → repo-tree walk → packaged resourcesRuled — validator track
REV-09recordio.py and atp.py hardcode fp16 regardless of the declared dtypeaudit/ownerimplementation-onlyP5 — dtype-aware encode and decode, byte length asserted against dim × sizeof(dtype) before write. Supersedes REC-07's and WIRE-11's documented — not patchedRuled — producer track
REV-10Records are stored at records/<pixel_hash>.json; record_id is uuid5(content:pixel) with no producer scope; the index point id collapses record variants; rkey is pixel_hash[:13]audit/ownerimplementation-onlyP3 + F4verd/identity.py with a producer DID; record_id = uuid5(ns, producer_did | content_hash | pixel_hash); storage keyed by record_id with an atomic pixel_hash → [record_id] sidecar index; rkey = the record_id UUID hex. Applies annex E27 in code, which DOCTRINE-PIXHASH-HALF-APPLIED said was only half-appliedRuled — producer / federation tracks
REV-11hashes_only publishes the embedding unless --no-vector is passed separately, so the mode's name promises more than it deliversaudit/ownerimplementation-onlyP8 + F4privacy becomes public | public_vector | hashes_only, where hashes_only omits vector AND thumb; contradictory CLI combinations are rejected. Matches draft §14.5's normative definitionRuled — producer / federation tracks
REV-12qd.ensure_collection checks dimension only and ignores space_id; queries never filter space_id; must_tags is an OR; the migration script deletes the live collection and restores only locally-held vectorsaudit/ownerimplementation-onlyF2 + F5 — collections bound to a space and refused on space_id mismatch; every query filters space_id; must_tags becomes a true AND with any_tags beside it; the migration script is dry-run by default, never deletes, and verifies before cutover. Supersedes EMB-02, QD-01, QD-02, MIG-01MIG-04Ruled — federation track
REV-13Raw-segment capture via Pillow's info dict is not byte-exact under annex E11, and the producer claims L1 anywayaudit/ownerimplementation-onlyP2 — stop emitting Pillow-derived raw[] as byte-exact evidence; records claim L0; availability still reported where detection is honest, with availability_basis naming the method. L1 returns only with a container-level segment extractor. Supersedes REC-01/IMPL-CONFORMANCE-SELF-DECLARED's partial fixRuled — producer track
REV-14Emitted space descriptors carry preprocessing: {"processor", "note"} and no reference block, so they make no conformance claim (annex E13)audit/ownerimplementation-onlyP7 — real preprocessing fields extracted from the processor where available; preprocessing.declared = false plus a logged conformance warning where extraction fails; no fabricated reference block — its absence is honest and VER404 is correct to warnRuled — producer track
REV-15recordio.py writes records non-atomicallyaudit/ownerimplementation-onlyP6 — temp file plus os.replace throughout recordio, recordio_segments and space_cache; append_provenance becomes read-modify-replace. Supersedes REC-12's documented — not patchedRuled — producer track

10.2 Standards-track rulings

IDTitleSourceClassificationDispositionStatus
REV-16ADR-0004 opens a value space with no governed registry behind it, does not bind conformance to a snapshot, does not say which tokens a consumer must understand, cannot distinguish an Asset event from a Record event, and lets sign be claimed without a signatureaudit/owneradditive-1.1ADR-0004 Accepted — conditionally, C1–C5. C4/C5 encoded (chain target, uri, signsignature); C2 encoded as provenance.registry; C3 stated as draft §2.3's three-tier table; C1 is a blocking deliverableClosed (draft); conditions blocking
REV-17ADR-0005's parent reference could be satisfied by identifiers that resolve to nothing (a bare record_id, annex E16) or to a group (pixel_hash, annex E27); recipe_uri needed no digest; the recipe document had no defined formataudit/owneradditive-1.1ADR-0005 Accepted — model adopted, shape revised. At least one globally resolvable identifier (record_uri, or {producer_id, record_id}); content_hash SHOULD-with-warning; pixel_hash as {value, cpnp_version}; recipe_urirecipe_sha256; the recipe envelope specified (draft §3.4); eight semantic fixtures requiredClosed (draft); fixtures blocking
REV-18ADR-0006 was the only open decision on the standards track, and it blocked 1.1 ratificationaudit/owneropen-ADR → additive-1.1ADR-0006 Accepted — revised Option A, version-gated. A producer_performed redaction REQUIRES a commitment; upstream_withheld names the never-held case in both the ledger and the availability enum; salted_sha256commitment_sha256 + commitment_alg with the ver-redaction-commitment/1 construction; the "proves a value existed" overclaim is retired and the salt-disclosure protocol is declared out of scope. 1.0.x unchanged; VER1002 stays a warning thereClosed
REV-19ADR-0007 redefined a published field's preimage — weights_sha256 — which silently changes the meaning of values already in circulationaudit/owneradditive-1.1ADR-0007 Accepted — principle adopted, construction revised. weights_sha256 keeps annex E29's reading forever; the 1.1 pin is model.bundle.manifest_sha256 with the construction intrinsic to bundle_digest_alg; the decorative canonicalization member is dropped; model.artifacts[] and checkpoint_urirevision are addedClosed (draft)
REV-20ADR-0008's binary rules — metric, normalization, carriage, packing — were prose the schema did not carry, so a binary space with metric: "cosine" and an inline float array was validaudit/owneradditive-1.1ADR-0008 Accepted as written, and the binary rules are encoded: binaryhamming, normalization: "none", no inline vector, and tolerance_hamming_max in place of the withdrawn ±1-expansion cosine readingClosed (draft)
REV-21The 1.1 draft suppressed encodable rules and left seven objects open in order to preserve a schema-superset property, so the standard's own machine-checkable requirements went unenforcedaudit/owneradditive-1.1S1 + S2 — the superset goal is withdrawn. The seven objects are closed (draft §15.2); every JSON-Schema-encodable rule is encoded; the migration is enumerated with real failure pointers (draft §12.1); VERSIONING.md §2.2 replaces the superset requirement with an enumerated-migration requirementClosed
REV-22The corrected 1.0 reading is fragmented across a frozen specification, an errata annex, this ledger, a compatibility matrix and a draft, so no single document states what VER 1.0 currently requiresaudit/ownerprocessA consolidated corrected 1.0 reading is a planned P2 deliverable. It is logged, not produced on this branch: assembling one is a substantial editorial act with its own review, and shipping a half-consolidated reading beside five authoritative sources would add a sixth. The precedence rule stands meanwhile — the annex governs 1.0.x, this draft governs 1.1Deferred (planned)
REV-23VER 1.0 was published as a standard with no reference corpus, no conformance runner, no second implementation and no governance process — the condition that produced the 189-finding auditaudit/ownerprocessS8VERSIONING.md §3.4 states a six-condition publication bar for every release from 1.1 onward. 1.0.0 and 1.0.1 predate it and are not held to it retroactively — they were preservation acts, and unfreezing them to meet a later bar would destroy the property the freeze provides. 1.1 is explicitly held to it, and five of its checklist rows are that barClosed
REV-24The repository has no LICENSE, no security policy, no contribution guide, no code owners and no CIaudit/ownerprocessG1–G4 — Apache-2.0 at the root and in the validator package; SECURITY.md with a reporting contact, a supported-versions table and the credential-rotation note; CONTRIBUTING.md covering the gates and the standards-track rules (frozen directories, code-registry stability, the ADR process, fixture authoring); a CODEOWNERS stub; a pinned four-job CI workflowRuled — governance track
REV-25The 1.1 draft made error_detail — free human-readable text — REQUIRED and load-bearing for c2pa.status: "unverifiable", putting a machine obligation on a string written for peopleaudit/owneradditive-1.1S6 — a machine-readable reason is REQUIRED for unverifiable and forbidden otherwise, over a six-token enum plus an <authority>/x-… escape; error_detail becomes optional and non-normative in every stateClosed (draft)
REV-26Integrity-relevant URIs in the 1.1 draft could be carried without the digest that makes fetching them safeaudit/owneradditive-1.1S4descriptor_uridescriptor_sha256, recipe_urirecipe_sha256, set_uriset_manifest_sha256 (unconditionally: a reference block is a tolerance claim), checkpoint_urirevision; all schema-encoded. Draft §15.3 also states the two URIs deliberately left uncoupled, and whyClosed (draft)
REV-27A binary space had no native tolerance: conformance was expressed as a cosine floor over the ±1 expansion of the packed bitsaudit/owneradditive-1.1S5tolerance_hamming_max is REQUIRED for a binary space's reference, and the cosine tolerances MUST NOT appear on it. The ±1-expansion reading is withdrawn as a conformance basisClosed (draft)
REV-28The 1.1 draft's wire position was to open every lexicon object with a MUST-ignore-unknown rule, which would let an unreviewed member sit beside pixelSha256 on a public firehoseaudit/owneradditive-1.1 (wire profile)S7 — the hybrid: a stable collection NSID across 1.x; verVersion and cpnpVersion REQUIRED on the wire as the negotiation signal; identity- and security-critical structures closed; extensibility through exactly one namespaced ext map that consumers MUST ignore when unrecognised; a new NSID only for a breaking wire major. Draft §14.1Closed (draft)

10.3 Raised after the review

Rows here are not owner-review dispositions: they were raised after 23 August, while building the 1.1 deliverables, and they continue the REV-nn numbering because the namespace is one sequence. Status carries §1's ledger-wide meaning, so Open here means a decision is required and has not been made.

IDTitleSourceClassificationDispositionStatus
REV-29Annex E7 requires every logical-screen pixel that frame 0's rectangle does not cover to be RGBA (0,0,0,0) entering §4 step 5, and therefore opaque white in the Canonical Buffer. verd.cpnp honours this where the GIF declares a transparency index; where it declares none, the uncovered region decodes to the colour of palette index 0 instead. Found during decoder-corpus construction (25 August 2026) and reproduced against services/verd/src/verd/cpnp.pydecoder corpusimplementation-onlydocumented — not patched; owner ruling requested before decoder-corpus release 2. The divergence is recorded in standards/decoder-corpus/1/PROFILE.md §3.2 and §6, and corpus release 1 deliberately carries no digest for the divergent case: a reference corpus must not publish an expected digest for behaviour a normative errata item forbids, because doing so would freeze the divergence into a conformance target. The transparency-index case is pinned, by corpus/gif-frame0-subrectangle.gif. Either outcome — an implementation fix, or an errata amendment — is a ruling this branch did not take, and release 2 cannot carry a digest for the case in either direction until it is takenOpen
REV-30The 1.1 draft opens an other key in metadata.availability (§10.3) and the 1.1 schema's raw[].segment enum carries other, but the profile validator's family universe was still the five 1.0 keys, so every availability-versus-carriage rule was blind to the sixth family: a 1.1 Record declaring availability.other: "redacted" while still preserving the other raw segment validated conformant — the exact annotated leak VER1005 exists to reject. VER1001's pairing arms and VER901's basis requirement were equally blind. Found by the 30–31 August repository audit (adversarially verified, reproduced empirically)repository auditimplementation-onlyfixed at source, version-gated. levels.py gains FAMILIES_1_1/FAMILY_SEGMENT_1_1 (six families, E21 mapping extended identity-spelled for other); metadata_rules.py selects the universe by schema version, so VER1005, both VER1001 pairings and VER901 now see other under --schema 1.1-draft while every 1.0.x result stays byte-identical (the L1 completeness gate VER303 deliberately keeps counting five). Four regression tests pin the leak, the safe pattern, the unledgered case and the basis requirementClosed
REV-31Every availability-versus-carriage rule keyed on metadata.availability alone, so a producer that redacted a field but forgot or mis-stated the family's state slipped VER1001 and VER1005 both; a pointerless producer_performed entry whose stand-in object was absent skipped VER1009 entirely; and --registry verdicts were computed against whatever snapshot the operator supplied, never checked against the snapshot the Record names (ADR-0004 C2)repository auditimplementation-onlyfixed at source, version-gated (31 August). Entry-level arms of VER1001/VER1005/VER1010 via the conservative PREFIX_FAMILY map; VER1009's retention arm; VER1208 registry-snapshot-mismatch with membership left unverified on a digest disagreement. All 1.1-draft-gated; 1.0.x byte-identicalClosed
REV-32Nothing connected metadata.redactions[] to embeddings[]: a Record could redact a caption with a proper §5.3 commitment while carrying a contextual embedding computed from that caption — a vector that inverts to the text — beside source.text_sha256, an unsalted, undomained digest of the withheld value that functions as the confirmation oracle §5.3's framed salted construction exists to prevent. Fused vectors carry the same leak one recipe hop awayrepository auditdraft-normative gapADR-0009, drafted-in-1.1 §5.4 (normative MUST: derived artifacts are withdrawn with the value), VER1011 for the exact-field map (dc:title/dc:description/Iptc4xmpExt:AltTextAccessibility → contextual source classes), §5.5 privacy considerations (informative), §15.4 rows recording what no validator can reach. Fixture invalid/ver1011-… pins the coexistence defectClosed (draft)
REV-33The wire profile's ext rules were all consumer-side (MUST-ignore, MUST-preserve, MUST-NOT-index) with no producer-side prohibition — an open smuggling channel for content the declared privacy mode withholds; tags is REQUIRED on every wire record including hashes_only, whose definition is "the identity tier only"; and a retraction reason was free text on a permanent public log with no rule against restating the sensitive content it retractsrepository auditdraft-normative gapADR-0009, drafted-in-1.1 §14.5/§14.6: a record MUST NOT carry in ext content its privacy mode withholds (salt prohibition included); a hashes_only record's tags MUST be empty; a retraction reason MUST NOT carry personal data or retracted content. Mirrored into both draft lexicons and the wire READMEClosed (draft)
REV-34§5.2/E15's omission remedy and §10.1's index-contiguity requirement contradicted each other for a multi-segment family: omitting the one segment carrying the redacted value leaves a legitimately gapped family that VER902 must reject, so the only conformant execution of the carve-out was un-writable for exactly the families (Extended XMP) most likely to need itrepository auditdraft-normative defectdrafted-in-1.1 §5.2: omission is family-granular — a redaction that requires omitting any segment of a family omits every segment of it, because partially omitted families re-assemble to bytes the producer never held; §10.1's contiguity rule therefore never meets a legitimately gapped familyClosed (draft)
REV-35The wire trust-tier vocabulary (signed_verified/signed_unverified/unsigned) had no name for a signature that was checked and FAILED — the most security-relevant outcome it can observe — so a failed verification either read as merely unchecked or as no claim at allrepository auditdraft-normative gapADR-0009, drafted-in-1.1 §14.7: fourth tier signed_invalid; MUST NOT be served as verified under any filter; SHOULD be retained rather than retried into unsignedClosed (draft)
REV-36ADR-0006 part 2 and the migration guide tell a producer that lost the salt to restate the entry as state: "upstream_withheld", but draft §5.2 defines that state as "the Producer never received the value" — a producer that HELD the value and lost the salt would be making a false statement of provenance to escape an impossible commitment. The honest alternative ADR-0006 also names (the Record stays at 1.0) has no such costrepository auditdraft-normative tensiondocumented — owner ruling requested. Options: (a) bless the restatement by widening §5.2's definition to "cannot commit" (weakens the state's meaning for every honest use); (b) require stay-at-1.0 for lost-salt entries and supersede ADR-0006's restatement sentence (ADR-0010 material); (c) a third state. The migration guide now presents stay-at-1.0 as the primary lost-salt path pending the rulingOpen