{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://getzero.dev/contracts/zero.a2ui.v1.schema.json",
  "title": "ZERO a2ui.v1 · the agent-stream delta grammar",
  "description": "The published contract for ZERO's agent stream: intent in, governed generative UI out. An agent emits an ordered sequence of frames — a2ui deltas (append/replace/remove) over a closed 16-kind node vocabulary — that a pure reducer folds into a tree and renders to ZERO primitives. No free-form HTML, no open node set: an unknown kind renders a visible error, never a silent fallback. The stream is honest by construction — every stream declares an evidence_class (live/replay/simulation/unavailable) and rides the five data-beats (see → decide → authorize → act → prove). Consequential intents halt the stream at the GATE sentinel, which pairs with an approvalGate node and holds until the operator rules; a replay holds no authority and renders its refusal as the receipt. Both surfaces build against this one contract: the Shell v3 composer driver that emits frames and the a2ui runtime (window.ZeroA2UI) that folds and renders them.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema",
    "stream_id",
    "evidence_class",
    "frames"
  ],
  "properties": {
    "schema": { "const": "zero.a2ui.v1" },
    "stream_id": {
      "type": "string",
      "pattern": "^a2ui_[A-Za-z0-9_-]+$",
      "description": "Stable identifier for one intent-to-receipt stream."
    },
    "created_at": { "type": "string", "format": "date-time" },
    "intent": {
      "type": "object",
      "additionalProperties": false,
      "description": "The intent that opened the stream. The door speaks a closed verb set; every verb carries a consequence tier T0..T3 (reversible/context/consequential/irreversible).",
      "properties": {
        "natural_language": { "type": "string", "minLength": 1 },
        "verb": { "type": "string", "minLength": 1 },
        "tier": { "$ref": "#/$defs/consequenceTier" }
      }
    },
    "evidence_class": { "$ref": "#/$defs/evidenceClass" },
    "beat": {
      "$ref": "#/$defs/dataBeat",
      "description": "The terminal data-beat the stream settles on. A completed stream settles on prove; a refused one still settles on prove (the refusal is the proof)."
    },
    "frames": {
      "type": "array",
      "minItems": 1,
      "description": "The ordered wire sequence: a2ui deltas interleaved with GATE sentinels. Frames are pure data — driver-local imperatives (beat changes, scroll) are not frames.",
      "items": { "$ref": "#/$defs/frame" }
    },
    "tree": {
      "$ref": "#/$defs/tree",
      "description": "Optional: the folded result of applying every non-sentinel frame in order. Provided for consumers that want the final render target without running the reducer."
    }
  },
  "$defs": {
    "dataBeat": {
      "enum": ["see", "decide", "authorize", "act", "prove"],
      "description": "The five-beat loop the surface walks: see (ambient perception), decide (intent forming), authorize (consequence gated), act (execution), prove (receipt rendered)."
    },
    "evidenceClass": {
      "enum": ["live", "replay", "simulation", "unavailable"],
      "description": "The honesty declaration for the whole stream. live = real runtime authority; replay = a recorded fixture (no authority); simulation = a modelled what-if; unavailable = the source could not be read (say so, never fake it)."
    },
    "consequenceTier": {
      "enum": ["T0", "T1", "T2", "T3"],
      "description": "T0 reversible read · T1 context change · T2 consequential (gate + signature) · T3 irreversible (operator-only, typed confirm)."
    },
    "nodeKind": {
      "enum": [
        "stack",
        "row",
        "card",
        "sectionTitle",
        "text",
        "mark",
        "chip",
        "hash",
        "receipt",
        "statCell",
        "button",
        "approvalGate",
        "toolCallCard",
        "asyncTask",
        "planSurface",
        "streamingCursor"
      ],
      "description": "The 16 closed a2ui kinds — layout (stack, row, card, sectionTitle) · content (text, mark, chip, hash, receipt, statCell) · action (button, approvalGate, toolCallCard, asyncTask, planSurface, streamingCursor). This set is closed: an unknown kind is a visible error, never a silent fallback."
    },
    "tone": {
      "enum": ["bone", "green", "red", "amber", "magenta", "blue"],
      "description": "Semantic tone token. bone is neutral; the rest are monosemic status colors paired with a glyph, never color alone."
    },
    "markState": {
      "enum": [
        "observe",
        "observing",
        "plan",
        "planning",
        "act",
        "acting",
        "proof",
        "complete",
        "refuse",
        "refused",
        "idle"
      ]
    },
    "approvalState": { "enum": ["waiting", "approved", "refused"] },
    "toolState": { "enum": ["pending", "ok", "error"] },
    "asyncState": { "enum": ["queued", "running", "done"] },
    "cursorState": { "enum": ["streaming", "idle"] },
    "planStepState": { "enum": ["pending", "active", "done"] },
    "nodeId": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "receiptRow": {
      "type": "object",
      "additionalProperties": false,
      "required": ["k", "v"],
      "properties": {
        "k": { "type": "string", "minLength": 1 },
        "v": { "type": "string" },
        "tone": { "$ref": "#/$defs/tone" }
      }
    },
    "planStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["label"],
      "properties": {
        "label": { "type": "string", "minLength": 1 },
        "state": { "$ref": "#/$defs/planStepState" }
      }
    },
    "node": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind"],
      "description": "One typed node in the flat adjacency list. props are kind-specific; children (ids) are meaningful only for the container kinds stack, row, card.",
      "properties": {
        "id": { "$ref": "#/$defs/nodeId" },
        "kind": { "$ref": "#/$defs/nodeKind" },
        "props": { "type": "object" },
        "children": {
          "type": "array",
          "items": { "$ref": "#/$defs/nodeId" }
        }
      },
      "allOf": [
        {
          "if": { "properties": { "kind": { "const": "sectionTitle" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": { "props": { "required": ["text"], "properties": { "text": { "type": "string" } } } }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "text" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": { "props": { "required": ["value"], "properties": { "value": { "type": "string" } } } }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "statCell" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["label", "value"],
                "properties": {
                  "label": { "type": "string" },
                  "value": { "type": "string" },
                  "tone": { "$ref": "#/$defs/tone" }
                }
              }
            }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "mark" } }, "required": ["kind"] },
          "then": {
            "properties": { "props": { "properties": { "state": { "$ref": "#/$defs/markState" } } } }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "streamingCursor" } }, "required": ["kind"] },
          "then": {
            "properties": { "props": { "properties": { "state": { "$ref": "#/$defs/cursorState" } } } }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "receipt" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["rows"],
                "properties": {
                  "kind": { "type": "string" },
                  "hash": { "type": "string" },
                  "rows": { "type": "array", "items": { "$ref": "#/$defs/receiptRow" } }
                }
              }
            }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "approvalGate" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["summary"],
                "properties": {
                  "summary": { "type": "string", "minLength": 1 },
                  "state": { "$ref": "#/$defs/approvalState" },
                  "hash": { "type": "string" },
                  "eta": { "type": "string" }
                }
              }
            }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "toolCallCard" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["tool"],
                "properties": {
                  "tool": { "type": "string", "minLength": 1 },
                  "server": { "type": "string" },
                  "state": { "$ref": "#/$defs/toolState" },
                  "args": { "type": ["object", "string"] },
                  "result": { "type": "string" }
                }
              }
            }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "asyncTask" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["name"],
                "properties": {
                  "name": { "type": "string", "minLength": 1 },
                  "state": { "$ref": "#/$defs/asyncState" },
                  "progress": { "type": "number", "minimum": 0, "maximum": 1 },
                  "eta": { "type": "string" },
                  "caption": { "type": "string" }
                }
              }
            }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "planSurface" } }, "required": ["kind"] },
          "then": {
            "required": ["props"],
            "properties": {
              "props": {
                "required": ["steps"],
                "properties": {
                  "caption": { "type": "string" },
                  "steps": { "type": "array", "items": { "$ref": "#/$defs/planStep" } }
                }
              }
            }
          }
        }
      ]
    },
    "tree": {
      "type": "object",
      "additionalProperties": false,
      "required": ["schema", "root", "nodes"],
      "description": "The folded render target. Note the tree carries the runtime schema tag a2ui.v1 (the reducer's internal vocabulary version), distinct from this contract's zero.a2ui.v1 envelope tag.",
      "properties": {
        "schema": { "const": "a2ui.v1" },
        "root": { "$ref": "#/$defs/nodeId" },
        "nodes": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/node" } }
      }
    },
    "appendDelta": {
      "type": "object",
      "additionalProperties": false,
      "required": ["op", "node"],
      "description": "Append a node and link it under parent (defaults to root) at index (defaults to end). Re-appending an existing id replaces the node in place.",
      "properties": {
        "op": { "const": "append" },
        "node": { "$ref": "#/$defs/node" },
        "parent": { "$ref": "#/$defs/nodeId" },
        "index": { "type": "integer", "minimum": 0 }
      }
    },
    "replaceDelta": {
      "type": "object",
      "additionalProperties": false,
      "required": ["op", "node"],
      "description": "Replace the node with the matching id in place. Children are inherited from the prior node unless the replacement supplies its own.",
      "properties": {
        "op": { "const": "replace" },
        "node": { "$ref": "#/$defs/node" }
      }
    },
    "removeDelta": {
      "type": "object",
      "additionalProperties": false,
      "required": ["op", "id"],
      "description": "Remove the node with this id and unlink it from every parent's children.",
      "properties": {
        "op": { "const": "remove" },
        "id": { "$ref": "#/$defs/nodeId" }
      }
    },
    "gateSentinel": {
      "const": "GATE",
      "description": "The approval sentinel. Not a node — a stream-control token that halts the pump. It always follows an approvalGate node in the waiting state and holds the stream until the operator rules: approve continues the stream to execution and a receipt; refuse ends it with the refusal as the receipt. On a replay-class stream the gate holds no authority and refuses with its reason."
    },
    "frame": {
      "description": "One wire frame: an a2ui delta or the GATE sentinel.",
      "oneOf": [
        { "$ref": "#/$defs/appendDelta" },
        { "$ref": "#/$defs/replaceDelta" },
        { "$ref": "#/$defs/removeDelta" },
        { "$ref": "#/$defs/gateSentinel" }
      ]
    }
  },
  "examples": [
    {
      "schema": "zero.a2ui.v1",
      "stream_id": "a2ui_read_status_r8841",
      "created_at": "2026-07-06T00:00:00Z",
      "intent": { "natural_language": "status", "verb": "status", "tier": "T0" },
      "evidence_class": "replay",
      "beat": "prove",
      "frames": [
        { "op": "append", "node": { "id": "title", "kind": "sectionTitle", "props": { "text": "status · the fixture answers" } }, "parent": "root" },
        { "op": "append", "node": { "id": "row", "kind": "row", "children": [] }, "parent": "root" },
        { "op": "append", "node": { "id": "s1", "kind": "statCell", "props": { "label": "equity · replay", "value": "$48,210.66", "tone": "bone" } }, "parent": "row" },
        { "op": "append", "node": { "id": "s2", "kind": "statCell", "props": { "label": "lenses live", "value": "2 of 4", "tone": "bone" } }, "parent": "row" },
        { "op": "append", "node": { "id": "s3", "kind": "statCell", "props": { "label": "refused · session", "value": "97.0%", "tone": "green" } }, "parent": "row" },
        { "op": "append", "node": { "id": "note", "kind": "text", "props": { "value": "Read verbs are T0 — no signature, no receipt owed. Source and age attached: journal · 00:00 → now UTC · replay." } }, "parent": "root" }
      ],
      "tree": {
        "schema": "a2ui.v1",
        "root": "root",
        "nodes": [
          { "id": "root", "kind": "stack", "children": ["title", "row", "note"] },
          { "id": "title", "kind": "sectionTitle", "props": { "text": "status · the fixture answers" } },
          { "id": "row", "kind": "row", "children": ["s1", "s2", "s3"] },
          { "id": "s1", "kind": "statCell", "props": { "label": "equity · replay", "value": "$48,210.66", "tone": "bone" } },
          { "id": "s2", "kind": "statCell", "props": { "label": "lenses live", "value": "2 of 4", "tone": "bone" } },
          { "id": "s3", "kind": "statCell", "props": { "label": "refused · session", "value": "97.0%", "tone": "green" } },
          { "id": "note", "kind": "text", "props": { "value": "Read verbs are T0 — no signature, no receipt owed. Source and age attached: journal · 00:00 → now UTC · replay." } }
        ]
      }
    },
    {
      "schema": "zero.a2ui.v1",
      "stream_id": "a2ui_drain_lens_momentum_r9012",
      "created_at": "2026-07-06T00:00:00Z",
      "intent": { "natural_language": "drain lens_momentum", "verb": "drain", "tier": "T2" },
      "evidence_class": "replay",
      "beat": "prove",
      "frames": [
        { "op": "append", "node": { "id": "title", "kind": "sectionTitle", "props": { "text": "drain · lens_momentum · the recorded loop" } }, "parent": "root" },
        { "op": "append", "node": { "id": "plan", "kind": "planSurface", "props": { "caption": "plan · replayed against the recording", "steps": [ { "label": "read the lens · lens_momentum", "state": "active" }, { "label": "simulate — what will change", "state": "pending" }, { "label": "collect authorization · T2", "state": "pending" }, { "label": "execute · render the receipt", "state": "pending" } ] } }, "parent": "root" },
        { "op": "append", "node": { "id": "cur", "kind": "streamingCursor", "props": { "state": "streaming" } }, "parent": "root" },
        { "op": "append", "node": { "id": "tool", "kind": "toolCallCard", "props": { "tool": "lens.status", "server": "engine · replay", "state": "ok", "args": { "lens": "lens_momentum" }, "result": "locked · capital $18,420.10 · 3 positions · age 1s" } }, "parent": "root", "index": 2 },
        { "op": "replace", "node": { "id": "plan", "kind": "planSurface", "props": { "caption": "plan · replayed against the recording", "steps": [ { "label": "read the lens · lens_momentum", "state": "done" }, { "label": "simulate — what will change", "state": "active" }, { "label": "collect authorization · T2", "state": "pending" }, { "label": "execute · render the receipt", "state": "pending" } ] } } },
        { "op": "append", "node": { "id": "sim", "kind": "row", "children": [] }, "parent": "root", "index": 3 },
        { "op": "append", "node": { "id": "m1", "kind": "statCell", "props": { "label": "positions close", "value": "3", "tone": "bone" } }, "parent": "sim" },
        { "op": "append", "node": { "id": "m2", "kind": "statCell", "props": { "label": "to reserve", "value": "$18,420.10", "tone": "amber" } }, "parent": "sim" },
        { "op": "append", "node": { "id": "m3", "kind": "statCell", "props": { "label": "est. slippage", "value": "0.04%", "tone": "bone" } }, "parent": "sim" },
        { "op": "append", "node": { "id": "simnote", "kind": "text", "props": { "value": "What you see is what you sign — the change renders before the signature (LAW IV · SimPreview)." } }, "parent": "root", "index": 4 },
        { "op": "replace", "node": { "id": "plan", "kind": "planSurface", "props": { "caption": "plan · replayed against the recording", "steps": [ { "label": "read the lens · lens_momentum", "state": "done" }, { "label": "simulate — what will change", "state": "done" }, { "label": "collect authorization · T2", "state": "active" }, { "label": "execute · render the receipt", "state": "pending" } ] } } },
        { "op": "remove", "id": "cur" },
        { "op": "append", "node": { "id": "gate", "kind": "approvalGate", "props": { "summary": "drain lens_momentum — 3 positions close, $18,420.10 returns to reserve · T2 · replay", "state": "waiting", "hash": "r-9012·draft" } }, "parent": "root" },
        "GATE",
        { "op": "replace", "node": { "id": "gate", "kind": "approvalGate", "props": { "summary": "drain lens_momentum — approved by the operator · T2 · replay", "state": "approved", "hash": "r-9012·signed" } } },
        { "op": "replace", "node": { "id": "plan", "kind": "planSurface", "props": { "caption": "plan · replayed against the recording", "steps": [ { "label": "read the lens · lens_momentum", "state": "done" }, { "label": "simulate — what will change", "state": "done" }, { "label": "collect authorization · T2", "state": "done" }, { "label": "execute · render the receipt", "state": "active" } ] } } },
        { "op": "append", "node": { "id": "task", "kind": "asyncTask", "props": { "name": "drain · lens_momentum", "state": "running", "progress": 0.25, "eta": "4s", "caption": "closing 3 positions · replay" } }, "parent": "root" },
        { "op": "replace", "node": { "id": "task", "kind": "asyncTask", "props": { "name": "drain · lens_momentum", "state": "running", "progress": 0.7, "eta": "2s", "caption": "returning capital to reserve · replay" } } },
        { "op": "replace", "node": { "id": "task", "kind": "asyncTask", "props": { "name": "drain · lens_momentum", "state": "done", "progress": 1, "caption": "recorded run complete" } } },
        { "op": "replace", "node": { "id": "plan", "kind": "planSurface", "props": { "caption": "plan · replayed against the recording", "steps": [ { "label": "read the lens · lens_momentum", "state": "done" }, { "label": "simulate — what will change", "state": "done" }, { "label": "collect authorization · T2", "state": "done" }, { "label": "execute · render the receipt", "state": "done" } ] } } },
        { "op": "append", "node": { "id": "rc", "kind": "receipt", "props": { "kind": "drain · replay", "hash": "r-9012", "rows": [ { "k": "lens", "v": "lens_momentum" }, { "k": "positions closed", "v": "3" }, { "k": "returned", "v": "$18,420.10" }, { "k": "gates", "v": "2/2 · pass", "tone": "green" }, { "k": "window", "v": "00:00 → now UTC" } ] } }, "parent": "root" }
      ]
    },
    {
      "schema": "zero.a2ui.v1",
      "stream_id": "a2ui_halt_refused_r_refusal",
      "created_at": "2026-07-06T00:00:00Z",
      "intent": { "natural_language": "halt", "verb": "halt", "tier": "T3" },
      "evidence_class": "replay",
      "beat": "prove",
      "frames": [
        { "op": "append", "node": { "id": "title", "kind": "sectionTitle", "props": { "text": "halt · refused · the reason renders" } }, "parent": "root" },
        { "op": "append", "node": { "id": "mk", "kind": "mark", "props": { "state": "refused" } }, "parent": "root" },
        { "op": "append", "node": { "id": "why", "kind": "text", "props": { "value": "Halt is operator-only (T3, typed confirm) and this surface is a replay — it holds no authority over live lenses. The refusal is the receipt." } }, "parent": "root" },
        { "op": "append", "node": { "id": "rc", "kind": "receipt", "props": { "kind": "refusal · replay", "rows": [ { "k": "verb", "v": "halt" }, { "k": "tier", "v": "T3 · irreversible" }, { "k": "reason", "v": "replay holds no authority", "tone": "red" } ] } }, "parent": "root" }
      ]
    }
  ]
}
