Chips, players and services cannot build on a specification that changes several times a day. The core, the part meant to stop moving, is now named, pinned and held: the envelope (WAI1, WAI2), the manifest, capability dispatch, the model pin, receipts and the determinism tiers, with the floor capabilities, record formats and conformance suites they need. Everything else is a profile with its own version. - wai/CORE.md names the core: spans of SPEC.md and of five extensions between <!-- wai-core:begin/end <id> --> markers (determinism-tiers §2, prior-carriage §2, jwp-receipts' delivery receipts, claim envelope, binary form, verifier, relay rules and key discovery, energy-measurement §2.7, conformance-results); the mandatory floor, video floor, recommended set and wai.prior.set; the envelopes' magics, the key log and module records and conform-result/1; and the suites that hold them. Each core-to-profile reference was read and is informative or binds only an implementation of that profile; what still resists the split is listed there. - wai/core/lock.json pins each item by SHA-256; wai/core/profiles.json gives each of thirteen profiles its version (0.1), its extensions and the core items it builds on; wai/CORE-ERRATA.md is the errata list. - scripts/check-wai-core-freeze.py fails a change to a core item unless it is an erratum row with the item's new digest or the core version is raised and the lock rewritten; against the commit a change starts from, it also fails a lock rewritten without a bump and an erratum edited. It runs in the repository checks after its self-test, which plants each fault and each change that must pass. - SPEC.md, wai/README.md, VERSIONING.md, CONTRIBUTING.md and the repository's README say the core is a 1.0 candidate and profiles are versioned separately. Not a release: 1.0 is declared by the maintainers. Co-Authored-By: Claude Opus 5.5 <[email protected]>
13 KiB
Contributing to WAI
WAI is a specification (SPEC.md and its extensions/)
with a reference implementation in Rust (wai-rs/, with
wai-quantum/ and the browser crate wai-web/). Both
are pre-1.0 drafts: substantive changes are welcome.
The canonical repository is the forge at
https://git.transaction.science/Transaction-Science/open-standards. Send changes
there; mirrors of it are read-only.
To the specification
A change is in scope if it:
- names a real ambiguity in the text (point at the section and the case that breaks);
- adds a missing structural requirement (byte layout, conformance rule, dispatch behaviour) needed to make another implementation falsifiable;
- registers a capability with a byte-precise payload format and conformance vectors, not a placeholder (see Registering a capability, below).
A change is out of scope if it adds optional fields with no testable conformance effect. §8 says what a revision may change: a registered capability's payload format and role are fixed once registered, and a later edition of a standard that changes its bitstream takes a new capability string.
The core (CORE.md, a 1.0 candidate) is the text between
<!-- wai-core:begin <id> --> and <!-- wai-core:end <id> --> markers, the core
capabilities' registry entries and the pinned corpora. A change there is either an
erratum, a correction that changes no meaning, added as a row of
CORE-ERRATA.md with the item's new digest
(python3 scripts/check-wai-core-freeze.py --item <id> prints it), or a change of
meaning, which raises the core version in core/lock.json. A
change outside the core is a profile's (core/profiles.json), and
a new extension names the profile it belongs to there. scripts/check-wai-core-freeze.py
fails anything else.
Registering a capability
REGISTRY.md states the procedure: add the string to the registry
table in wai-rs/src/codecs/, register it in SPEC §5 under the same heading, run
wai registry export, and link it from the codec glossary or list it there as
having no entry. The tests hold SPEC, the registry and the exported files to the
same set of strings.
To the reference implementation
Building
The default codecs feature links system codec libraries (libjxl, libavif,
libopus, libdav1d, liblzma, libflac); without them the build stops in a codec
crate's build script. Everything else builds without them, and CI builds that way:
cd wai-rs
cargo test --lib --no-default-features --features world,audio_scene,haptic,splat4d,score,provenance,neural_int,film
cargo run --no-default-features --bin wai -- capabilities
Name the features a change touches explicitly; cargo test with the defaults is
not what CI runs.
Tests that CI must see
- A test file gated on a feature (
#![cfg(feature = "...")]) compiles to an empty binary, which reports success, in any build without that feature. Declare it inwai-rs/Cargo.tomlwith a[[test]]stanza andrequired-features, and add--test <name>with the right--featuresto a step in.github/workflows/, or to a verifier step's own file (below), or tag the file for the fast integer kernels' steps (below). A test no workflow names runs nowhere. - A test that reads inputs from outside the tree skips without them. CI sets
WAI_REQUIRE_SOTA_VECTORS=1where it provides them, and a missing input then fails. - Falsify every new test once: change the code it guards, show it fails, restore the code through git, and show it passes again.
Vectors
Committed conformance vectors and pinned hashes change only through their
generator (the wai_*_vectors gen binaries and the export tools in
tools/), never by hand. A test whose comment says its hex must never
be edited to make it pass means exactly that.
Corpus content: where it comes from
Corpus content is generated in this repository, by a generator from a seed or a formula, or it is an input whose source and licence are recorded and whose licence permits redistribution. An image, a recording or footage that was not made here and has no such licence is not committed, as content or as training data.
Every corpus states its provenance: each *-conformance/README.md in a line that
begins **Provenance.**, and wai-rs/tests/vectors/README.md in a row for each
vector directory. The line names the generator and its input, and for an input not
generated here, its source and licence. A word such as "synthetic" is not a
provenance line. wai-rs/tests/corpus_provenance.rs requires the line in every
corpus, and tools/wai_audio_provenance_check.py, which CI runs, fails when a
committed audio vector correlates with a reference recording the host has.
A corpus can also keep a licence record for each vector, in a licences.json beside
its README: the vector's licence, as an SPDX identifier from a list of licences that
permit redistribution; whether its content is generated here or a third-party input;
the generator and its input, or the source; and, for a vector whose model was trained,
the same of its training data. neural-conformance keeps one.
scripts/check-wai-corpus-licences.py, run on every change, fails on a vector of such a
corpus that has no record, and on a record whose licence is outside that list or that
leaves out what its content needs.
Independent implementations
Conformance is defined by the committed corpora (*-conformance/) and the
criteria of SPEC §7, not by agreement with this implementation's own tests. A
second implementation in another language is validated against the same corpora;
the OER receipt kernel in C (oer-conformance/settle/) is one.
Changes that add files rather than append to shared lines
Many changes are open here at once, and a line two of them edit is a conflict that holds one of them back. So a change adds files of its own where the tree lets it (its changelog entry, and its lists in the conformance workflow), and in a file that stays shared it inserts beside the entry it builds on:
- Its changelog entry is a new file,
changes/<yyyy-mm-dd>-<slug>.md, holding the entry as the changelog does (changes/README.md). It does not edit the head ofCHANGELOG.md. The repository checks require a change underwai/to add or edit an entry file directly inchanges/(a release, which renders the files and removes them, passes), or to carry the trailer lineChangelog: none, exactly, in the trailer block of one of its commit messages. A pull request opened before the check reached main passes as it is; one opened since that adds its entry to the head ofCHANGELOG.mdfails. - A step of the independent verifiers in
wai-conformance.ymlis a new file,.github/wai-conformance/verifiers/<name>.sh: the step's shell body, run inwai/, under a header of# key: valuelines.# step:names it and# order:places it;# features:brings its features intoWAI_FEATURESand its lists into the features-alone job;# paths:brings the extension text it reads into the paths the workflow's gate runs on (the trigger takes all ofextensions/; a path under none of the trigger's paths still needs a line there, whichchecknames);# bins:and# release-features:cover the rest, and every--testit runs with"$WAI_FEATURES"joins the job's first build. A corpus's independent verifier is an# independent:line of its step's file (its name, its area, and the command line its README gives), never a line of the body: one step runs every one of them aswai conform --independent, whose tablewai-rs/build.rsreads from those lines. A step that has only verifiers to run is a file with that header and no body.scripts/wai-conformance-lists.pystates every key;python3 scripts/wai-conformance-lists.py dry-runprints every list and step the workflow runs, andrun --only <name>runs one step. - A cargo-class corpus is a new file of
.github/wai-conformance/corpora/, naming its tool and directory. A test of the fast integer kernels carries the first line// wai-conformance: int-kernels(withint-kernels-amdwhen the AVX-512 host runs it too, orint-kernels-releasealone when only the release step does), and the steps take its required features fromCargo.toml. - A file every change still shares takes a new entry beside the entry it builds on,
never at the end, and is not re-sorted.
wai-rs/Cargo.toml's[features],[[bin]]and[[test]]tables,wai-rs/src/lib.rs'smodlines and the tables ofwai-rs/src/codecs/registry.rsare grouped by topic, not sorted: a feature goes after the feature it depends on, a target after its sibling's, a module after the module it extends.registry/capabilities.json, the registry table ofREGISTRY.mdandvocab/v2.*are generated fromregistry.rsin sorted order, so a new row lands at its sorted place: never edit them by hand, and on a conflict in one take either side and runwai registry exportagain. .gitattributes. A corpus whose bytes must never be converted gives its own directory a.gitattributes(* -text), aspin-conformance/andlive-conformance/scene/do (a verifier that lists its directory's files must admit that one, asscene_verify.pydoes), and adds no line towai/.gitattributes. A pattern only that file can hold goes beside the line it is most like, with its comment, never at the end.- The corpus table of
live-conformance/README.md, and the list of verifier commands under it, take a new row beside the row of the extension it builds on or extends (a delivery record beside its delivery sibling, a live extension beside its live sibling), never at the end. The table is written by hand: its rows name sections, schemes and generators that no file of the corpora states.
Public text
The specification, the extensions, READMEs, research notes, code comments and the CHANGELOG state what WAI does, plainly:
- name no company, vendor, customer or commercial product. Standards, standards bodies, protocols, papers and open-source projects may be named, factually;
- make no comparisons with other products or approaches;
- give no counts that drift (crate counts, test counts, line counts).
The cross-simulator corpus (quantum-cross-simulator/) runs the engine beside
open-source simulators and publishes what each one gave, as data: every check
with its measured value and tolerance, and each engine's timings and memory on
named hosts. Its page names those projects only in lines marked allow-name:,
and states no ranking: no prose on which engine is faster, smaller or better.
Three checks enforce this. scripts/check-no-vanity-metrics.sh (opt-out marker
allow-vanity: <reason>) and scripts/check-comparative-prose.sh (opt-out
allow-phrase: <reason>, for an identifier or a quotation) run on every change.
scripts/check-public-names.sh holds the names of the maintainers' products
that their public site names in the script itself, since they are public
already, and checks them on every change (--maintainer-products-only). Its
other patterns are a private list (the WAI_NAME_DENYLIST secret in CI, given
only to the run on a push to main, so a pull request is checked against that
list when it lands). A functional identifier — a capability string, a model-hub
id a tool defaults to, a repository URL, an OS API name, a security advisory
id — is kept and its line marked allow-name: <reason>. Put the marker in a
comment of the file's own syntax (<!-- ... -->, // ..., # ...); the reason
must contain a letter or a digit. JSON has no comments, so a JSON file carries
no such identifier: the codec glossary titles a format by its own name, FourCC,
file extension or designation, and never by a company, a product or a person
(glossary/SCHEMA.md). The checks fail closed: a pattern
that does not compile, or any grep error, fails them, and
scripts/test-public-text-checks.sh proves that on every run.
Dated research records are not rewritten: a correction is a dated annotation after the original text, and a name is redacted in place with a dated marker.
Licence
The specification and the reference implementation are Apache-2.0. By
contributing, you agree your contribution is licensed under the same terms. Each
payload and companion capability carries a licence class (royalty-free,
patent-pool, proprietary, non-commercial) for implementing it, recorded in the
registry; derivations carry the class codecs::derivation gives them; records
carry none. Companion implementations outside this tree keep their own licences.
No CLA. The licence is the agreement.