|
| 1 | +# Media Conformance Corpus (`moq-media-corpus/1`) |
| 2 | + |
| 3 | +A language-neutral corpus of media-format conformance vectors — LOC property |
| 4 | +blocks, LOC semantics, catalogs, BMFF structures — consumed by two |
| 5 | +implementations: this repository (TypeScript, via `runner/`) and an external |
| 6 | +LibMoQ (C) build, reached only through the `moq-media-probe/1` JSONL protocol — |
| 7 | +neither repository vendors the other. This is **private test infrastructure**: |
| 8 | +it lives outside `packages/` and is never published to npm. |
| 9 | + |
| 10 | +Every vector is schema-validated, and a vector is executed only against a seam |
| 11 | +that actually exists. A vector kind whose runner API does not exist yet is |
| 12 | +capability-marked rather than silently skipped — counted and reported, never run |
| 13 | +against a non-existent API. Nothing is marked pending today. |
| 14 | + |
| 15 | +## Layout |
| 16 | + |
| 17 | +``` |
| 18 | +conformance/media/ |
| 19 | + README.md # this file — governance |
| 20 | + MANIFEST.json # derived index (schema + per-domain counts + total) |
| 21 | + schema/ # JSON Schema for the manifest + scenario formats |
| 22 | + vectors/ |
| 23 | + properties/ # Layer-A property-block-decode (executable) |
| 24 | + loc/ # loc-properties (A+B) + loc-semantics (both executable) |
| 25 | + catalog/ # catalog-parse (executable via parseCatalogAuto) |
| 26 | + bmff/ # bmff-structure (executable via mp4-box utilities) |
| 27 | + runner/ # private TS workspace package: loader, validator, tests |
| 28 | +``` |
| 29 | + |
| 30 | +## Governance |
| 31 | + |
| 32 | +### Provenance (where the bytes came from) |
| 33 | + |
| 34 | +Every entry carries a mandatory `provenance` block with a `class`: |
| 35 | + |
| 36 | +- **`spec-derived`** — bytes hand-constructed to match a cited draft section |
| 37 | + (e.g. a canonical vi64 encoding). The only class that is *conformance* |
| 38 | + evidence, and only after independent review against the citation. |
| 39 | +- **`third-party`** — output of an external tool (ffmpeg, GPAC, moq-rs). Interop |
| 40 | + evidence. |
| 41 | +- **`implementation-generated`** — output of Playa or LibMoQ. *Regression pins |
| 42 | + only*, never conformance evidence. |
| 43 | + |
| 44 | +Provenance says where the bytes came from; `expectationBasis` (below) says what |
| 45 | +the expectation *claims*. A spec-derived vector can still be an `interpretation`. |
| 46 | + |
| 47 | +### `expectationBasis` (what the expectation claims) |
| 48 | + |
| 49 | +- **`normative`** — a MUST / registry entry with a precise citation. |
| 50 | +- **`interpretation`** — a documented Playa policy where the spec is ambiguous, |
| 51 | + silent, or its registry is incomplete (see policies below). |
| 52 | +- **`interop`** — matches observed third-party behavior. |
| 53 | +- **`regression`** — pins current behavior with no spec claim (e.g. the silent |
| 54 | + audio-level masking and duplicate last-wins the current parser exhibits). |
| 55 | + |
| 56 | +### Oracle independence (the load-bearing rule) |
| 57 | + |
| 58 | +For a `normative` or `interpretation` vector, the `expect` block is an |
| 59 | +**independently authored literal** — reasoned from the cited draft, never |
| 60 | +captured from the implementation under test. The authoring script then runs |
| 61 | +Playa *only* to populate `differential.playa`: absent (⇒ Playa matches the |
| 62 | +authored truth) or `diverges` + `currentBehavior` (⇒ it does not). This prevents |
| 63 | +an existing bug from being blessed as the normative answer. A `regression` |
| 64 | +vector is the sole exception — its `expect` legitimately IS the current |
| 65 | +implementation behavior, because that is what a regression pin means. |
| 66 | + |
| 67 | +The eighteen draft-18 divergence drivers (`loc/props-d18-*-diverges`) are the |
| 68 | +concrete payoff — every draft-18 value ≥ 64 in both directions (9 decode + 9 |
| 69 | +encode). They drove the vi64 wiring fix: the `-diverges` id suffix records that |
| 70 | +origin, and they now pass against production directly (no differential). |
| 71 | + |
| 72 | +### Canonical ordering / duplicate policy |
| 73 | + |
| 74 | +- **PropertyMap decode** preserves occurrence order and duplicates losslessly |
| 75 | + (dedup / last-wins is a Layer-B *semantic* policy, never a Layer-A behavior). |
| 76 | +- **Canonical encode** emits Key-Value-Pairs in **stable ascending-ID order**, |
| 77 | + preserving the relative order of duplicate IDs — matching the current |
| 78 | + encoders. (draft-18 §1.4.3.) |
| 79 | +- Trace event order is the **actual emission order** (never re-sorted); JSON key |
| 80 | + order within a record is cosmetic (structural comparison). |
| 81 | + |
| 82 | +### Documented policies (all `interpretation`) |
| 83 | + |
| 84 | +These are exercised by their vectors once the corresponding format support |
| 85 | +lands; recorded now so the corpus and its consumers share one reading. Each also becomes an upstream |
| 86 | +issue (to be filed). |
| 87 | + |
| 88 | +- **LOC02-P1** — LOC-02 id `0x06` is interpreted as TIMESTAMP per the IANA table |
| 89 | + (§6.1), which registers only TIMESTAMP; the §2.3.3.1 Audio Level prose |
| 90 | + assignment (also `0x06`) is treated as an erratum. No value-magnitude |
| 91 | + heuristics; Audio Level is unavailable in LOC-02; a session-scoped diagnostic |
| 92 | + is emitted; the raw id/value is always preserved. |
| 93 | +- **LOC02-P1b** — LOC-02 ids `0x04` (Video Frame Marking) and `0x0D` (Video |
| 94 | + Config) are accepted per their unambiguous prose assignments even though the |
| 95 | + §6.1 registry omits them (the registry resolves conflicts; it does not |
| 96 | + invalidate unambiguous definitions). One session-scoped diagnostic lists the |
| 97 | + prose-only properties accepted. |
| 98 | +- **LOC02-P3** — running LOC-02 semantics over transport-18 is an *interop |
| 99 | + profile*, not strict paired-draft conformance (LOC-02 normatively references |
| 100 | + transport-17). Nine-byte Timestamps are accepted. |
| 101 | +- **LOC-P2** — a zero Timescale (unspecified in every LOC draft) is a |
| 102 | + construction-time rejection (`invalid-timescale-zero`). |
| 103 | + |
| 104 | +### Stable ids + tombstones |
| 105 | + |
| 106 | +Ids are stable slugs (`domain/name`). They are never renamed. A retired vector |
| 107 | +is **tombstoned rather than deleted**: its manifest entry is replaced by |
| 108 | +`{ "id": "domain/name", "retired": { "reason": "…" } }`, which the schema |
| 109 | +accepts as an inert entry (no execution, no file). This keeps the id reserved |
| 110 | +(never reused) and the history diffable. Reviving a retired behavior uses a new |
| 111 | +id. |
| 112 | + |
| 113 | +### Canonical encoding |
| 114 | + |
| 115 | +- Every u64/i64-typed field is **always a decimal string** — never a JSON number |
| 116 | + (a JSON number above 2^53-1 silently loses precision). The loader rejects a |
| 117 | + JSON number in a wide-integer field. |
| 118 | +- Floats are prohibited. Bytes are lowercase hex; long blobs are |
| 119 | + `{sha256, byteLength}`. Strings compare as exact code points. |
| 120 | +- Error comparisons use `error.category`, never message text. |
| 121 | + |
| 122 | +### Read-only + regeneration |
| 123 | + |
| 124 | +The corpus is read-only by default. The runtime `GEN_CORPUS=1` regeneration gate |
| 125 | +may rewrite ONLY `implementation-generated` entries produced by this repo; |
| 126 | +`assertRegenerable` refuses spec-derived and third-party bytes. |
| 127 | + |
| 128 | +The corpus was authored by `runner/src/build-corpus.ts` (run manually with |
| 129 | +`AUTHOR_CORPUS=1`), which constructs spec-derived bytes deterministically and |
| 130 | +captures the exact current production behavior for executable vectors. It is |
| 131 | +byte-stable across runs. |
| 132 | + |
| 133 | +### Third-party fixture import (LibMoQ) |
| 134 | + |
| 135 | +The 14 LibMoQ MSF fixtures are imported byte-for-byte (5 executable MSF-00, |
| 136 | +9 forward-looking MSF-01/CMSF-01) with pinned provenance in |
| 137 | +`provenance/libmoq-msf-fixtures.json` (+ a human table in the sibling `.md`). |
| 138 | + |
| 139 | +Normal authoring is **hermetic** — it re-derives from the checked-in snapshot |
| 140 | +(`vectors/catalog/libmoq_*.json`), so a fresh clone or CI reproduces the corpus |
| 141 | +with no external checkout. The pinned per-fixture SHA-256 gates every byte, so a |
| 142 | +tampered snapshot fails authoring. |
| 143 | + |
| 144 | +``` |
| 145 | +# Hermetic re-author (default; no LibMoQ needed): |
| 146 | +AUTHOR_CORPUS=1 tsx conformance/media/runner/src/build-corpus.ts |
| 147 | +
|
| 148 | +# Deliberately RE-IMPORT from a LibMoQ worktree (opt-in). Verifies the worktree |
| 149 | +# HEAD is the pinned commit before copying, then re-gates every byte by SHA-256: |
| 150 | +LIBMOQ_REFRESH=1 LIBMOQ_ROOT=/path/to/libmoq \ |
| 151 | + AUTHOR_CORPUS=1 tsx conformance/media/runner/src/build-corpus.ts |
| 152 | +``` |
| 153 | + |
| 154 | +The pinned commit lives in `build-corpus.ts` (`LIBMOQ_COMMIT`) and the provenance |
| 155 | +file. Refresh runs `git rev-parse HEAD` via a direct exec (never a shell), so a |
| 156 | +`LIBMOQ_ROOT` containing spaces or metacharacters is handled literally. |
| 157 | + |
| 158 | +## Running |
| 159 | + |
| 160 | +``` |
| 161 | +pnpm test:corpus # the runner test suite (loader, validator, per-domain execution) |
| 162 | +pnpm test # includes the corpus lane |
| 163 | +``` |
0 commit comments