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.mix gains a class, one of:

    classMeaningExamples
    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>.window
    queryChanges 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.wait
    inputSynthetic low-level input, standing in for a person's hands<app>.ui.click, .ui.pointer, .ui.scroll, .ui.key, .ui.type
    systemSession plumbing. The single-instance activation verb <app>.show is system even 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_only is a query. Each application's registry test checks this. The reverse need not hold: .ui.capture writes 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.wait is a deliberate ordering point.

This page as Markdown · This page describes the current main branch of MixOS.