Skip to content

The machine contract

Agents and scripts do not read prose — they parse a contract. This reference defines that contract: the clean, stable output that --json, --quiet, and the MCP server emit, the structured error envelope agents branch on, and the hard rule that none of it ever carries emoji, celebration, or a warmth line.

The hard rule: no delight in the machine contract

TOVIO's human-facing output is deliberately warm — it celebrates a first landing and can name how to reverse a supported mutation retained in the local op-log. None of that may appear in machine output. This is a non-negotiable boundary, stated normatively in the Experience & Voice spec:

REQ-UX-011 Emoji, celebration, warmth lines, and decorative glyphs MUST appear only in interactive human (TTY) output. --json, --quiet, and the MCP/agent API output MUST contain none of it — no emoji, no celebration, no "warmth" line. This preserves the machine contract and is non-negotiable: a delight feature that leaks into --json is a compatibility bug.

The discipline is simple to state and absolute in effect:

Surface Audience May contain
Interactive TTY A human at a terminal Glyphs (✓ ✗ ! ⚠ 🔒 ⚡), color, one warmth line, a once-only milestone
--json A script or orchestrator Only structured JSON. No emoji, no warmth, no color.
--quiet A pipeline Only essential machine-relevant lines. No decoration.
MCP / agent API An agent Only parsed JSON objects and the error envelope. No prose to scrape.

Why so strict? Joy is for the human; the machine contract is sacred. An emoji that slips into --json can break a parser, shift a byte offset, or poison a downstream diff. So the rule is not "prefer plain output" — it is "the machine surfaces carry zero decoration, ever". Meaning is always carried by the words and the exit code, never by a glyph or a color.

A leak is a bug, not a nicety

If you ever see an emoji, a 🎉, or a "your first landing!" line in --json or an MCP response, that is a compatibility defect to report — not a feature. The same goes for any decoration in --quiet or non-TTY output.

The success contract

Machine output of a successful operation is a JSON object whose fields are stable within a protocol generation. The CLI's --json and the MCP tool results are two documented shapes that carry the same facts and follow the same add-only rules, so a parser written against either never meets decoration. A commit reports its stable change id and commit address on both. tovio commit --json:

{
  "change": "chg:a3f7b2",
  "commit": "blake3:91be07…",
  "files": 1,
  "next_change": "chg:k9v2p1"
}

The MCP commit tool result:

{
  "commit": "blake3:91be07…",
  "change_id": "chg:a3f7b2",
  "branch": "agent/cgm-sync",
  "verified": true
}

Note what is absent: there is no human-only guidance such as undo with \tovio undo``, and no glyph. The human terminal names that local reversal for a reversible mutation; the contract shows only the facts.

Success with follow-up

Some "successes" carry a follow-up the agent must act on — most notably a sync that reconciles a divergent lane. These are reported as success, never as an error. When a sync brings a divergent incoming tip on an unprotected lane, TOVIO joins the two tips into a two-parent commit on the lane — a clean merge, or one carrying first-class conflict objects — and reports it in a reconciled object, never a "diverged history" failure.

// tovio sync --json (the isolated-path and pin counts are omitted here)
{
  "remote": "origin",
  "received": 9,
  "sent": 3,
  "refs_merged": 2,
  "reconciled": {
    "lane": "main",
    "tip": "blake3:7c2e91…",
    "conflicts": 1,
    "conflict_paths": ["src/integrations/cgm/dexcom.ts"]
  }
}

An agent branches on reconciled (and reconciled.conflicts), not on a human-readable "3 conflicts stored — your work keeps moving" line. A clean reconciliation reports "conflicts": 0; when the pull found no divergence, reconciled is null. (That warm line is exactly what the human sees; the agent sees the object.)

The error envelope

Every failing call returns the same structured envelope — the Error Catalog §2.2 shape — so an agent branches on a stable code without parsing human text. On the CLI, --json wraps it in an error object:

{
  "error": {
    "code": "TVO-PERM-001",
    "area": "perm",
    "category": "authorization",
    "exit_class": "permission",
    "retryability": "after-user-action",
    "title": "Cannot read config/production/api-keys.env",
    "cause": "Read requires role=senior AND clearance=secrets; this agent has entity=agent, team=backend; missing clearance=secrets.",
    "remediation": [
      { "text": "A human must perform or re-authorize this read", "command": "tovio agent show cap_7r4qy9m2x8k3v6bd0n1p5h" }
    ],
    "context": {
      "path": "config/production/api-keys.env",
      "policy_id": "prod-secrets",
      "policy_requires": "role=senior & clearance=secrets",
      "your_attributes": ["entity=agent", "team=backend"],
      "missing": ["clearance=secrets"],
      "token_id": "cap_7r4qy9m2x8k3v6bd0n1p5h"
    },
    "exit_code": 13
  }
}

Over MCP the same engine envelope is passed through verbatim — code, title, cause, context, and exit_code, with no error wrapper — as the tool result's text, flagged isError. The Node SDK carries that identical envelope in a return value instead: every operation yields { ok: true, ... } or { ok: false, error }, and unwrap() throws the envelope as a typed error. The remediation list is defined by the catalog and rendered by the CLI; the engine marshal does not emit it, so an agent branches on code and treats remediation as optional.

Field Meaning
code The stable TVO-<AREA>-<NNN> identifier. Branch on this.
area, category, exit_class, retryability The catalog's closed classifications (CLI --json).
title A one-line human-readable summary (still plain — no glyphs).
cause What actually went wrong, in attribute terms the agent can reason about.
remediation An ordered list of { text, command } next steps (CLI --json; optional over MCP).
context Stage-appropriate structured fields (see leak-free ordering below).
exit_code The process exit code the CLI would return.

The codes an agent will see

These are the errors specific to the agent surface. The full catalog covers the rest.

Code Meaning Typical agent reaction
TVO-TOKEN-001 Path (or lane) outside scope Treat the path as not this agent's concern.
TVO-TOKEN-002 Token expired Request a fresh token and reconnect.
TVO-TOKEN-003 Token revoked Stop; the session is closed.
TVO-TOKEN-004 Operation not permitted / not exposed The op is outside the token (or never offered to agents).
TVO-TOKEN-005 Sub-token scope exceeds parent Delegate within the parent's scope.
TVO-CRYPTO-002 Token signature or delegation chain does not verify Stop; obtain a validly issued token.
TVO-PERM-001 Read denied by policy / clearance A human must read or re-authorize.
TVO-PERM-002 Write policy unsatisfied The push needs a satisfying proof.
TVO-SYNC-001 Relay unreachable (local work safe) Retry later; nothing was lost.
TVO-SYNC-002 Protocol version mismatch Negotiate a supported protocol.
TVO-CONFLICT-001 Conflict stored (success-with-follow-up) Not an error — resolve when ready.

Leak-free ordering is part of the contract

The error context reflects exactly what the enforcement stage was permitted to learn — and no more. This is a deliberate security property, not an inconsistency:

  • A path-scope rejection (TVO-TOKEN-001) carries the offending path (or branch) but never policy_requires. Policy was never read, so the response cannot disclose it.
  • An operation-allow-deny rejection (TVO-TOKEN-004) names the operation in op but carries no policy fields — it is a pre-policy stage.
  • A policy denial (TVO-PERM-001) — reached only when the path was in scope and the op allowed — identifies the policy that was evaluated (policy_id, plus secret_clearance on the clearance gate), because the policy was read.

The context objects above are what the engine emits over MCP and the SDK. The CLI's --json today sends an empty context for both TVO-TOKEN-* stages, so read the offending target from the envelope you get over MCP rather than expecting the CLI to repeat it.

// TVO-TOKEN-001 over MCP — out of scope: policy was never consulted, so no policy_requires
{
  "code": "TVO-TOKEN-001",
  "title": "Agent token is not scoped to this path",
  "cause": "The read targeted `config/production/api-keys.env`, outside the token's path_scope (minus denied_path_scope); it is rejected before any policy or envelope is consulted (§7 step 2).",
  "context": { "path": "config/production/api-keys.env" },
  "exit_code": 13
}

The asymmetry is the point: an out-of-scope request is told only that it is out of scope. An earlier stage must never leak what a later stage gates. An agent can rely on this — the presence or absence of policy_requires tells it which stage denied the call.

Stability guarantees

The contract is versioned with the protocol and is a stable compatibility surface:

  • Within a protocol generation, fields and codes are add-only — the server may add tools, optional inputs, and output fields, but never remove or repurpose one without a major-version bump.
  • Backward compatibility holds for one minor generation back (N-1): a newer server keeps every prior name and field working with unchanged meaning.
  • An error code never changes meaning without a major-version bump and the deprecation process.

These guarantees mirror across the CLI --json, --quiet, and MCP so the three surfaces stay in lockstep — see the MCP server.

Where this connects

  • The server that emits this contract: the MCP server.
  • The token whose denials populate these codes: capability tokens.
  • Wiring a client to branch on these codes: write an integration.
  • The human counterpart (where delight does live): TOVIO's interactive CLI output.

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure