Decision — verb classes
Status: accepted 2026-10-11. Amends AGENTS.md §5 (Bus verbs are wire contracts).
Amended 2026-10-20 by commands take parameters and return results.
Registry schema 4 adds a trailing schema field to every verb and family:
none for no body or an empty object, pending through 2026-11-30 inclusive
in UTC, or a Rust serde request type deriving JsonSchema. Type paths match
^[a-z][a-z0-9_]*(::[A-Za-z][A-Za-z0-9_]*)+$, such as
command::ExecuteRequest. The gate accepts schema 2, 3 or 4 for fixtures;
the repository registry must be 4. It refuses schema below version 4,
missing or malformed body schemas at version 4, and expired pending
placeholders, naming each expired verb or family. --today YYYY-MM-DD
overrides the UTC date for tests.
The decision
-
The verb registry moves to schema 2. Every entry in
docs/docs/spec/bus/verbs.conf.mixgains aclass, one of:class Meaning Examples intentChanges what an application or service holds, or what another caller would see, at someone's request <app>.open,<app>.execute,<app>.refresh,settings.apply,releases.notes(it fetches when nothing is cached),<app>.quit,<app>.windowqueryChanges nothing another caller could observe. The one allowance: it may write the artefact it was asked for and returns, such as a screenshot <app>.ping,.info,.state,.commands,.ui.tree,.ui.capture,.window.state,settings.get,ced.waitinputSynthetic low-level input, standing in for a person's hands <app>.ui.click,.ui.pointer,.ui.scroll,.ui.key,.ui.typesystemSession plumbing. The single-instance activation verb <app>.showissystemeven when something calls it directly<app>.show -
The class describes the verb, not one call. A verb that both reads and changes state is
intent. -
When in doubt, it is
intent. Dropping a real action is worse than keeping a read. -
A verb an application describes as
read_onlyis aquery. Each application's registry test checks this. The reverse need not hold:.ui.capturewrites a file, so it is not read-only, but it is still a query. -
The registry gate (
tools/verb_registry.mix) refuses:- a schema other than 2;
- an entry that is not in the canonical one-line form;
- an unknown or missing field;
- a bad name, owner, status or version;
- a duplicate;
- a class outside the four.
Why
Tools that watch the Bus need to know what a verb is for:
- a recorder keeps intent and drops reads;
- a live traffic view colours them differently;
- an agent asked "what changed?" looks only at intent.
Without a declared class, each tool guesses from verb names, and the guesses drift apart. The registry already declares every verb's owner, version and status, so its purpose belongs there too.
Consequences
- Every existing entry is classified in one change, together with the gate.
- A new verb is registered with its class from its first commit.
- Classes are about purpose, not replay. A recorder still needs each owner's
replay rules (rec and play), for example
that
ced.waitis a deliberate ordering point.