# The operation catalogue Every operation of the WAI toolchain, once. [`ops.json`](ops.json) is written by `wai ops export` from the table in `wai-rs/src/catalogue.rs`, and CI fails when the two differ (`.github/workflows/wai-catalogue.yml`). Do not edit it by hand: change the table, then ```bash cd wai/wai-rs && cargo run -q --no-default-features --bin wai -- ops export --out ../catalogue/ops.json ``` ## An operation An operation is named `noun.verb`: lowercase ASCII, a singular noun, and a verb from naming rule N3's closed list (create, list, show, update, delete, check, verify, run, build, export, import, start, stop, join, leave, grant, invite, revoke, rotate, tail, search, test, load, encode, decode, inspect, wrap, unwrap, fetch, generate, promote, rollback, lock, upgrade, setup, guide, stream, upscale, tonemap), never a near-synonym (`get`, `describe` and `info` are `show`; `keygen`, `init` and `new` are `create`; `up` is `start`, as in `wai dev`). A command a person already types keeps its spelling as the operation's command line: `wai hello` is `toolchain.test`, `wai stage refine` is `stage.encode`. The list as approved with WAI toolchain v1 ended at `stream`. Two verbs joined it after the wai 0.4.0 release, with `media.upscale` and `media.tonemap`: `upscale` and `tonemap`. Each names an exact derivation, an output any verifier recomputes bit for bit from the input and the parameters its record names (extensions/exact-upscale.md, extensions/exact-tonemap.md), and no approved verb names that: `encode` and `decode` run a codec, and `generate` makes media from a description. A verb joins the list the same way: in `VERBS` (wai-rs/src/catalogue.rs), this paragraph and the list above, and the doors' check (`scripts/check-wai-ops-doors.py`), which a test of the catalogue holds to `VERBS`. An entry (format `wai.ops/1`) carries: | Member | Meaning | |---|---| | `id` | `noun.verb` | | `summary` | One line, for help, docs and the search | | `cli`, `aliases` | The command line today, and other spellings; every operation also answers to `wai ` | | `usage` | The synopsis | | `placement` | `local`, `client` (on this machine, against a service it names), `service` or `both` | | `effect` | `reads`, `runs`, `writes-project`, `writes-target` or `destructive` | | `dry_run` | Whether it takes `--dry-run`: every operation whose effect is not `reads` | | `confirm` | When it waits for `--yes`: `never`, `when-replacing-an-unnamed-file`, `when-replacing-a-file`, `when-changing-the-target-or-replacing-a-file` or `always` | | `route` | Its HTTP method and path, for an operation that runs on a service | | `toolset` | `core`, `dev`, `app`, `quantum` or `maintainer` | | `stability` | `stable`, `preview` or `internal` | | `input`, `output`, `stream` | JSON Schemas: its arguments (each with the flag or position that gives it), the `data` of its `--json` envelope, and each line of a streaming operation | | `since`, `deprecated` | The toolchain version that first carries it; its replacement, once renamed | | `receipt` | Whether it writes a signed receipt or record that a `verify` operation checks | | `needs` | The build features it needs (`all` or `any` of them) | | `names` | Its name in each door | ## One name in every door | Door | `envelope.inspect` | Held by | |---|---|---| | CLI | `wai inspect`, `wai envelope inspect` | the `wai` binary's tests (`tests/cli_ops.rs`) | | MCP | `envelope_inspect` | `scripts/check-wai-ops-doors.py` | | HTTP API | `operationId: envelope.inspect` | `scripts/check-wai-ops-doors.py` | | C | `wai_envelope_inspect` | `scripts/check-wai-ops-doors.py` | | Rust | `wai::envelope::inspect` | | | JavaScript, Swift, Kotlin | `wai.envelope.inspect` (Swift and Kotlin: `WaiEnvelope.inspect`) | `scripts/check-wai-ops-doors.py` (Swift, Kotlin) | A door that does not carry an operation yet is reported as such by the check; a door that names one differently fails it. In the C header and the native sinks, a name is an operation's when its noun is a catalogue noun and its verb an operation verb; the codec, envelope and sink functions that are not operations are library API. ## Agent rules Every command of the catalogue keeps them. | Exit | Meaning | |---|---| | 0 | Done; every check held | | 1 | Ran, and a check failed | | 2 | Usage error or invalid input | | 3 | Authentication or permission | | 4 | Confirmation required; nothing changed | | 5 | Not found (file, target, room, operation, or a build feature) | | 6 | Target unavailable; safe to retry | | 7 | Internal error | With `--json`, stdout holds one envelope and logs stay on stderr; without it the output is text, on a terminal or not. One module holds these rules for every command (`wai-rs/src/bin/wai_cli/dx/envelope.rs`). ```json { "wai": "envelope/1", "op": "envelope.wrap", "ok": false, "exit": 4, "error": { "code": "confirmation-required", "message": "out.wai exists, and the command line did not name it as the output (-o); nothing was changed", "hint": "read the proposed changes, then run the confirming command", "retryable": false }, "proposed": [ { "replace": "out.wai" } ], "confirm": "wai wrap wai.text.zstd p.bin --yes" } ``` A success carries `data`: the command's own JSON where it has one, `{"text": ...}` otherwise. A failure carries its `exit`; a check that failed (exit 1) carries both `error` and `data`. A dry run adds `"dry_run": true` and the `plan` of what it would write, and of each request it would send to a service it names (a fleet service), none of them sent. `--yes` is never implied; off a terminal, or with `--no-input`, a change that waits for it exits 4. `--confirm` is a deprecated spelling of `--yes` that does the same; the export lists the agent flags (`agent_flags`) with it as a deprecated alias. `wai` sends nothing off your machine unless you name a remote service.