# 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](wai-quantum/VERSIONING.md). The deprecated items, each with the version that deprecated it and its replacement, are listed in [DEPRECATIONS.md](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](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: ```text 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](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](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](catalogue/ops.json), which `wai ops schema ` 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](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](schema/wai-json-1.schema.json) and, for version 2 (version 1 and `breaking`), [schema/wai-json-2.schema.json](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](schema/wai-lock-1.schema.json) and [schema/wai-build-1.schema.json](schema/wai-build-1.schema.json) state them, and the lock's bytes in [project-conformance/](project-conformance/README.md) | `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](https://semver.org). 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](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, a re-pin of the candidate 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](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](DEPRECATIONS.md), from the release that deprecates it | A test holds [DEPRECATIONS.md](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 ` 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/](changes/README.md). 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 `--json` output 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`). 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 `, against the project at a git ref (`--against `) or as it was published (`--baseline `: the project's directory, or its package alone, as served). It compares: - **The package** `wai build` wrote under `dist/`, as the sinks that play it are configured for it (category `wire`): 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.api` in `wai.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: ```json { "$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" }] } } ```