The catalogue (wai/catalogue/ops.json) and the MCP door's tools (wai/catalogue/mcp.json, new, held to the tools' table by a test of the binary) are a surface of category api: each operation's id, command line, doors, effect, confirmation and stability; its flags and arguments; the schema of its --json data and streamed lines, read as a reader reads them; the envelope, the exit codes and the agent flags; each tool's name and input schema. A stable operation's change is a break. A preview operation's change is no break and is owed a changelog line that names it. An id, a flag or an agent flag renamed with the old name kept, deprecated, is no break; removing the old name is acknowledged only once its window (two minor releases and six months, from the export's version and the release tag's date) has closed, and a stable name removed without that is a break no line acknowledges. The JavaScript surface is the browser packages' #[wasm_bindgen] items by their JavaScript names (and any committed .d.ts); Swift and Kotlin are the native sinks' public declarations, each with its parameters, labels, defaults and result. In a project (a directory with wai.json, or --project), wai breaking compares the project with itself at a git ref or as published (--baseline: the project, or its package alone): the package wai build wrote, class by class as sinks are configured for it (the sink-class check), and the API named under breaking.api. With wai 0.5.0 released, wai.json's version 1 is published, so breaking (api, and allow: each accepted break with its reason) is version 2's (wai/schema/wai-json-2.schema.json): a file that names it says "wai": 2, a reader of version 1 alone refuses it as a later version, and wai create still writes version 1. An accepted break with no reason is refused with the file. A run with no package to compare with stops with exit 5. Planted faults, each caught: a stable operation renamed; a preview change unsaid; an accepted break with no reason; a version-1 file naming breaking; a sink class gone; a Swift label and a Kotlin required parameter; an MCP tool's required argument; a confirmation compared on a door that is new. Co-Authored-By: Claude Opus 5.5 <[email protected]>
9 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: until v1.0 freezes, it may change, and such a change is 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" }] } }