UI Guidelines

Core principles for building conversation-native Tool UIs. This document will be tuned over time as we iterate and learn.

Fundamentals

Every Tool UI follows these 5 rules:

One Intent per Surface

Each instance does one primary job. A table that filters, exports, and schedules is three surfaces, not one.

Inline, Not Destination

Tool UIs live inside messages. No fullscreen takeovers, no mini-apps. They support the conversation, not replace it.

Schema-First & Addressable

Components render from JSON with stable IDs (surface + key children) enabling references like "the second row" or "the card above". Include asOf when freshness matters.

Assistant-Anchored

The assistant narrates every Tool UI before, during, and after interaction. The UI never "speaks" silently.

Temporal & Lifecycle Aware

Surfaces move through explicit phases and show honest timestamps. Canonical lifecycle: invocation → output-pending → interactive → committing → receipt → errored.

Roles

Every Tool UI declares its primary role:

  • Information: Display data (e.g., DataTable, MediaCard)
  • Decision: Capture consequential choices that require commitment and produce receipts (e.g., approve/reject prompts, send/cancel dialogs)
  • Control: Adjust parameters that modify behavior without commitment. Safe, iterative, no side effects (e.g., filters, sort, date ranges)
  • State: Expose internal tool/assistant activity (observability)
  • Composite: Primary + secondary (e.g., DataTable with actions = "information with control")

Lifecycle

Tool UIs follow predictable phases:

invocation → output-pending → interactive → committing → receipt → errored

Temporal overlays: fresh | active | stale | historical | superseded.

Significant changes or time gaps: create a new surface and mark the old one superseded rather than silently updating stale data.

Required Invariants

Core Contract

  • Single outcome & declared role
  • Fully serializable from schema (JSON-safe, reconstructable)
  • Stable IDs for surface and key children

Conversation Requirements

  • Every important action maps to a canonical sentence
  • Side effects produce durable receipts (status, summary, identifiers, timestamp)
  • Temporal honesty (show asOf and staleness/supersession behavior)

Quality Floor

  • Keyboard navigable, screen-reader friendly, AA contrast
  • No layout thrash during streaming (skeleton matches final layout; no container resize)
  • Errors as first-class surfaces, not toasts

Conversation Coherence

Every UI action has a natural language equivalent:

Example 1: Approving an action

  • UI Action: Click "Approve"
  • Canonical Sentence: "Approve PR #42"
  • User: "yes, approve it"
  • Assistant: "Approved PR #42."

Example 2: Selecting items

  • UI Action: Select rows 2-4
  • Canonical Sentence: "Select items 2 through 4"
  • User: "those three"
  • Assistant: "Selected 3 items."

This enables the Triadic Loop: User ⇄ Assistant ⇄ Tool UI, where the assistant interprets all interactions.

Anti-Patterns

Don't build these:

  • Forms in chat: Multi-field forms pretending to be Tool UIs
  • Hidden mutations: Side effects without receipts
  • Kitchen sinks: Dashboards crammed into messages
  • Mini-apps: Navigation flows inside Tool UIs