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]>
9.4 KiB
The WAI toolchain: versions, stability and deprecations
This page says what anyone building on the wai command line can rely on: which parts are public,
what a version number promises about each, and how a change is announced before it happens. It
covers the wai binary, the operation catalogue and the doors the catalogue names. WAI Quantum's
packages have their own page, wai-quantum/VERSIONING.md.
The deprecated items, each with the version that deprecated it and its replacement, are listed in DEPRECATIONS.md.
One version
wai, the standard's text and the reference implementation are released together, under one
version: the wai crate's (wai-rs/Cargo.toml). The newest release heading of
CHANGELOG.md names it, the catalogue's export carries it, and a test holds the three
equal, so a version can be read from the binary and never has to be read from a page:
wai --version --json
| Member | What it says |
|---|---|
toolchain |
This binary's version |
catalogue |
The export's format, its operations, and the SHA-256 of wai ops export, which is catalogue/ops.json byte for byte |
envelope |
The version of the JSON envelope --json prints |
registry |
The registered capabilities, and the SHA-256 of registry/capabilities.json |
specification |
The release of the standard this binary implements, and the envelope formats it reads |
features |
The build features the catalogue names that this binary was built with, which decide the operations it holds |
What is public
| Surface | What is public | What holds it in CI |
|---|---|---|
| The operation catalogue | Each operation's id, its command line and declared aliases, its flags and arguments, its effect, when it waits for confirmation, its stability and the schema of its --json data: catalogue/ops.json, which wai ops schema <id> prints one entry of |
wai ops export --check, and the catalogue's rules (wai-catalogue.yml); wai breaking, category api (surface catalogue, wai-breaking.yml) |
| The agent rules | The exit codes, the envelope envelope/1, its error.code strings, --json, --dry-run, --yes and --no-input |
wai-rs/tests/cli_ops.rs |
| The MCP door | Each tool's name, the operation of the catalogue it is, and its input schema: catalogue/mcp.json, which the binary's tests hold to the tools wai mcp serves |
wai-rs/tests/cli_mcp.rs and the binary's tests, in wai-conformance.yml; scripts/check-wai-ops-doors.py; wai breaking, category api (surface catalogue) |
| The project file | wai.json, as schema/wai-json-1.schema.json and, for version 2 (version 1 and breaking), schema/wai-json-2.schema.json state it |
The reader's tests, which hold the reader to both |
| The lock and the build record | wai.lock and wai-build.json, as schema/wai-lock-1.schema.json and schema/wai-build-1.schema.json state them, and the lock's bytes in project-conformance/ |
wai-rs/tests/cli_project.rs and the second reader, project_verify.py, in wai-conformance.yml |
| The C ABI, the Rust API of the library crates, the JavaScript, Swift and Kotlin APIs and the HTTP services' OpenAPI documents | wai.h, what the crates document, the browser packages' bindings (wai-web, wai-live-web), the native sinks' public declarations (wai-sink-swift, wai-sink-android), and each document's operations |
wai breaking, category api (wai-breaking.yml) |
| The wire and the signed records | The registry's capabilities and wire identifiers, the conformance corpora, the signing domains and the published JSON Schemas | wai breaking, categories wire and record, and the corpora's generators and verifiers |
What is not public: the text a command prints without --json, which is for people and may change
in any release; an operation marked internal, which is never published; and the keystore's own
files (wai.key/1), which only wai key reads and writes.
An operation's stability says how far its entry is promised. A stable operation changes only
with a deprecation window. A preview operation is published and works, and its shape may still
change: a change to one is in the changelog, and the window below is not owed.
What a version number promises
The toolchain follows Semantic Versioning 2.0. Every release so far is 0.y.z, for which that promises nothing, so this page does: the first non-zero component is the major.
- A change of y (0.4.0 to 0.5.0) may break a public surface. Every break is in the changelog with what to do instead, and the deprecation rule below holds for a stable operation.
- A change of z adds or fixes, and breaks nothing a public surface promised.
The wire format is the exception the standard states. Its core (CORE.md: the envelope, the manifest, dispatch, the model pin, receipts and the determinism tiers) is a core 1.0 candidate, which changes only by an erratum or a raised core version, under a check that runs on every change. Everything else is a profile, versioned on its own in core/profiles.json, and may change, with each change recorded in the changelog.
Deprecation
A renamed command, flag, route or tool stays as an alias for at least two minor releases and six months, and says it is deprecated when used. Removing it after that is a break, in the changelog with what to do instead.
| Where it is used | How a deprecated item says so |
|---|---|
| A flag on the command line | It still works, and prints one line on standard error: wai: deprecated: --confirm, use --yes instead |
| An operation's id or spelling | Its catalogue entry's deprecated member (since, use_instead) |
| Help and the catalogue's export | wai --help and each operation's help name it, and agent_flags lists it with deprecated |
| The list | DEPRECATIONS.md, from the release that deprecates it |
A test holds DEPRECATIONS.md to the catalogue: every flag and operation the catalogue marks deprecated is listed there, and every item listed there is marked in the catalogue or has its removal named.
How a break is acknowledged
wai breaking --against <git-ref> reports what the working tree breaks, by category, and exits 1 on
a break the changelog does not acknowledge. A change acknowledges one with a **Breaking:** line
naming the finding's id in backticks, in its entry under changes/. CI runs it on
every pull request that touches WAI, and on every push to main that does.
The catalogue is read as this page promises it:
- A change to a stable operation, its flags, its
--jsonoutput or its MCP tool is a break. - A change to a preview operation is no break, and is owed a line: the change's entry names the finding's id in backticks, with or without the word "breaking". Without one the run fails.
- A stable name the catalogue can keep under its old spelling (an operation's id, kept as a
deprecated operation; a flag, kept as a deprecated alias;
--confirm) renamed with the old one kept, deprecated, is no break. Removing the old one is a break a line acknowledges only once its window has closed: two minor releases after the release that deprecated it, read from the export's version, and six months after that release's tag (wai-v<version>). Removing it before then, or removing a stable name that was never deprecated, is a break no line acknowledges, and the run says so: keep the old name, deprecated, until its window closes.
A project's breaking changes
wai breaking runs on any project, in its directory (one with wai.json) or with
--project <dir>, against the project at a git ref (--against <ref>) or as it was published
(--baseline <dir>: the project's directory, or its package alone, as served). It compares:
- The package
wai buildwrote underdist/, as the sinks that play it are configured for it (categorywire): a class of the packaging policy that is gone, or whose playlists or stage view are gone, or whose objects need a capability they did not, is a break for the sinks set up with that class; so is a floor codec the classical presentation no longer offers, and a MoQ track a subscriber named that is gone or changed. A class that now takes the floor alone is no break: its sinks still play. A run with no package to compare with on either side stops (exit 5); it does not pass what it did not compare. - The API the project names under
breaking.apiinwai.json: JavaScript and TypeScript modules, Swift and Kotlin sources, OpenAPI documents and JSON Schemas, each read by its kind.
A project accepts a break in wai.json, with the reason it is accepted, which the report gives
beside the finding; an entry with no reason is refused with the file. breaking is a member of the
project file's version 2, which says so, so that a wai that reads version 1 alone refuses the file
as a later version:
{ "$schema": "https://wai.transaction.science/schema/wai.json/2", "wai": 2,
"breaking": { "api": ["sdk/index.d.ts"],
"allow": [{ "id": "api:js:sdk/index.d.ts:play", "reason": "renamed start in 2.0; call start" }] } }