Matthew McBride← Perspectives

P004 · Technical series · Building AI Agent Experiences, part 1 of 3 · 14 September 2026 · 7 min read

One Intent, Four Contracts

An architecture for AI agents, interfaces, and governance

The architecture of an experience orchestration layer — how a single intent resolves into agent conduct, interface, outcome, and governance

CONTEXT · ROLE · CHANNEL · IDENTITYINTENTcancel_subscriptionResolveDETERMINISTICAX BINDINGHow the agent behavesexplicit_confirmHX BINDINGWhat the interface rendersdestructive_confirmOX CONTRACTWhat the person experiencesauditabilityLevel: traceGOVERNANCEWhat is allowed, and whyrule + suggested fixONE INTENT, FOUR CONTRACTS. THE INTERFACE IS ONLY ONE OF THEM.

Part 1 of a three-part technical series on experience orchestration. Part 2: Designing Agent Conduct. Part 3: The Runtime Beneath the Experience.

In February 2026 I set out to build the next design system and ended up building something that wasn't one. I called it MOTHER, and the last section of the manifesto I wrote for it said so plainly: this is not a design system. It is an experience orchestration protocol.

The reasoning was simple. A design system governs what an interface looks like. The systems we ship now don't just render — they act. An agent books, cancels, refunds, and escalates. No design system has an opinion about how that agent should ask for permission, what it should say when it isn't sure, or what the person should be able to see afterward. Those decisions get made anyway: in prompts, in glue code, in whatever the model happens to do that day. Designers are designing agent behavior without knowing it. Engineers are shaping the observable experience without a spec.

This series is the architecture I built to make those decisions explicit. The essays The Interface Is a Decision and The Observable Experience make the argument. These three articles are the machinery.

The unit of design is the intent

Component libraries made the component the unit of design. That works when a person drives every step. It stops working when a person states an outcome and a system decides the steps.

So the spine of the architecture is the intent: a named, registered outcome — upgrade_plan, cancel_subscription, query_balance — with a definition that everything else hangs from. An intent is not a screen and not a prompt. It's the stable thing that stays the same while the channel, the model, and the interface change underneath it.

Resolution takes a small, explicit input: which intent, the context it's happening in, who is asking, and the channel it arrived on. An optional identity selects whose voice, tone, and trust posture apply.

ts
interface ResolveIntentInput {
  intentId: string;                 // must exist in the registry
  context: Record<string, unknown>; // what is true right now
  role?: string;                    // who is asking
  channel?: "web" | "cli" | "slack" | "voice" | "api" | "headless";
  identityId?: string;              // whose voice, tone, and trust posture apply
}

One intent, four contracts

The central idea is that a single intent resolves, in one step, to four contracts. Only one of them is about the interface.

  • The AX binding says how the agent behaves: how it clarifies, when it needs permission, how it signals uncertainty, where it escalates, how it explains itself, and what it does when it can't proceed.
  • The HX binding says what the interface renders, if there is one: a pattern, its tokens, and the primitives it's allowed to use.
  • The OX contract says what the person must experience: a latency target, whether a trust signal is required, how transparent and auditable the action must be, how recovery works, and the emotional posture of the response.
  • Governance rules say what is allowed, why, and what to do when something isn't.

Here is an illustrative definition. It is the whole unit — nothing about this experience is specified anywhere else.

json
{
  "intentId": "cancel_subscription",
  "description": "Cancel a recurring subscription",
  "axBinding": {
    "clarificationStrategy": "confirm_before_act",
    "permissionModel": "explicit_confirm",
    "uncertaintyLanguage": "state_confidence",
    "escalationPath": {
      "triggerConditions": ["confidence_below_threshold"],
      "targetRoleOrChannel": "support",
      "handoffMessageTemplate": "Connecting you with someone who can help."
    },
    "explainabilityTemplate": "Your subscription will end on {periodEnd}.",
    "fallbackBehavior": "escalate"
  },
  "hxBinding": {
    "patternId": "destructive_confirm",
    "tokens": {},
    "primitives": ["Card", "Text", "Button"]
  },
  "oxContract": {
    "targetLatencyMs": 500,
    "trustSignalRequired": true,
    "transparencyLevel": "summary",
    "auditabilityLevel": "trace",
    "recoveryPattern": "retry",
    "emotionalPosture": "apologetic"
  },
  "governanceRuleIds": ["destructive_requires_confirmation"]
}

Read it as a designer and it's a complete brief: a destructive action, confirmed explicitly, explained in one sentence, traced for audit, delivered with an apologetic posture, handed to support if the agent loses confidence. Read it as an engineer and it's a schema that can be validated, versioned, diffed, and tested. That double reading is the point.

Five layers

The protocol is organized as five layers. Four of them are new relative to a design system; the one a design system already covers is the fourth.

LAYER 01IdentityTokens, voice, tone, posture, accessibility, trustLAYER 02IntentSchema, types, resolution, binding rulesLAYER 03AX · AgenticClarify, ask permission, escalate, explain, fall backLAYER 04HX · InterfaceTokens, primitives, components, patternsLAYER 05OX · ObservableLatency, transparency, trust, recovery — measuredTHE SPINEA DESIGN SYSTEMGOVERNS THIS LAYERORCHESTRATIONGOVERNS ALL FIVEWHEN SOFTWARE ACTS, BRAND IS CONDUCT — IDENTITY IS MORE THAN TOKENS
Fig. 1Five layers. A design system governs one of them. The intent layer is the spine the others hang from.

Identity is the layer most often misunderstood. Design systems encode identity as tokens — color, type, spacing. Here identity is surface plus behavior plus outcome posture: tokens, but also voice, tone, behavioral posture, accessibility thresholds, trust signals, and permission defaults. Two brands can ship identical components and still have very different identities, because one explains before it acts and the other asks for less confirmation. When software acts, brand is conduct.

Intent holds the schema, the registry, the resolution rules, and the binding rules. AX holds the agent's conduct contracts. HX holds the traditional surface: tokens, primitives, components, and patterns. OX holds the measurable outcome definitions that the runtime evaluates continuously.

Resolution is deterministic

Resolution is a pure function of its input. The same intent, context, role, channel, and identity always produce the same four contracts.

intentIdREQUIREDcontextWHEREroleWHOchannelHOW IT ARRIVEDidentityIdWHOSE VOICEResolutionengineTARGET UNDER 50 MSagentBehaviorBindingHOW IT BEHAVESuiPatternBindingWHAT IT RENDERSobservableOutcomeContractWHAT MUST BE TRUEgovernanceRulesWHAT IS ALLOWEDlineageWHERE IT CAME FROMSAME INPUT, SAME OUTPUT. AN UNKNOWN INTENT IS A 404, NOT AN IMPROVISATION.
Fig. 2Resolution. Five inputs, one deterministic step, four contracts and a lineage record out.

That was a deliberate constraint, and it's the one I'd defend hardest. There is no model in the resolution path. Determinism is what makes the experience testable in CI, auditable after the fact, and consistent across channels. It also makes the failure mode honest: an intent that isn't in the registry returns a 404. The system doesn't improvise a flow for an outcome nobody designed.

It also keeps resolution cheap enough to sit in the request path. The design target was under 50 milliseconds.

Every resolution can carry its provenance, so a support engineer, an auditor, or a future version of the system can answer the question where did this behavior come from?

json
{
  "agentBehaviorBinding": { "…": "…" },
  "uiPatternBinding": { "…": "…" },
  "observableOutcomeContract": { "…": "…" },
  "governanceRules": ["…"],
  "lineage": {
    "source": "file",
    "path": ".mother/intents.json",
    "loadedAt": 1707840000000,
    "resolvedAt": 1707840010000
  }
}

The interface is optional

One acceptance criterion shaped the whole architecture: the system must be able to operate without a visible interface. A Slack-only flow. A voice-only flow. A headless agent calling an API.

INTENTcancel_subscriptionWEBSLACKVOICEHEADLESSAX bindingHOW IT BEHAVESHX bindingWHAT IT RENDERSdestructive_confirmmessageNONENONEOX contractWHAT MUST BE TRUEGovernanceWHAT IS ALLOWEDTHE INTERFACE MAY DISAPPEAR. THE CONDUCT AND THE CONTRACT CANNOT.
Fig. 3One intent across four channels. The interface changes or disappears; the conduct, the outcome contract, and the rules do not.

This is where separating the contracts pays off. When cancel_subscription arrives by voice, there's no pattern to render — but the agent still confirms explicitly, still explains the end date, still escalates to support when it's unsure, and the action is still traced. The HX binding drops out. The AX binding, the OX contract, and the governance rules travel with the intent into every channel.

If your agent's behavior is defined inside your React components, it can't survive the move to voice. If it's defined on the intent, it can't not survive it.

Generation only through bindings

The most forward-looking part of the protocol was a single endpoint: compile an intent for generative use. Instead of letting a model generate interface freely, the runtime resolves the intent and hands the model a strict schema derived from the bindings — which primitives exist, which pattern applies, what's required.

UNCONSTRAINED GENERATIONModelANY COMPONENT · ANY FLOW · SOME INVENTEDINSTRUCTION HOPES THE MODEL COMPLIESGENERATION THROUGH BINDINGSModelCompile forgenerativeSCHEMA FROM BINDINGSREGISTRY ONLYButtonTextSTRUCTURE CHECKSGoverned interface<InventedWidget/>REJECTED
Fig. 4Unconstrained generation hopes the model complies. Generation through bindings makes the model compose only what the registry declares.
json
{
  "intentId": "upgrade_plan",
  "patternId": "checkout_confirm",
  "primitives": [
    { "name": "Button", "tokens": { "variant": "primary" } },
    { "name": "Text", "tokens": { "fontSize": 16 } }
  ],
  "generationSchema": {
    "type": "object",
    "properties": {
      "Button": { "type": "component", "description": "Use Button primitive" },
      "Text": { "type": "component", "description": "Use Text primitive" }
    },
    "required": ["Button", "Text"]
  }
}

The instruction to the model was blunt: resolve the intent through the tool, use only the returned binding, and don't invent components or flows. In the manifesto I described the protocol as the compiler for generative UI.

Look closely at that schema, though. "type": "component" isn't a JSON Schema type. Nothing could validate against it; it was a description the model was asked to respect. That is the honest weakness of this version, and the most useful lesson in it. Instruction hopes the model complies. Structure checks. A schema no validator can run is still a prompt — the control only exists when something outside the model rejects whatever isn't declared.

Governance that explains itself

Most governance in design systems is linting: a rule fires, a build fails, someone googles the rule name. Here every rule is linked to the intent it protects and the identity principle it serves, and it carries its own remedy.

ts
interface GovernanceRule {
  ruleId: string;
  explanation: string;             // why this rule exists, in plain language
  linkedIntent: string;            // which outcome it protects
  linkedIdentityPrinciple: string; // which brand or trust principle it serves
  suggestedFix: string;            // what to change
  autoApplyAvailable: boolean;     // whether the fix is safe to apply automatically
}

A rule that can't explain itself trains people to work around it. A rule that proposes its own fix, and says whether that fix is safe to apply without a human, turns governance from a gate into a collaborator. Part 3 covers how the runtime uses that flag.

What held up, and what didn't

Looking back at it from September 2026, four things held up well:

  • The intent as the unit of design. Everything that has worked since — in agents, in generative interfaces, in authority and governance — starts from a named outcome rather than a screen.
  • Separating conduct, surface, outcome, and rules. It's what lets an experience survive a change of channel or model.
  • Determinism in the resolution path. Keep the model out of the part that decides what's allowed.
  • Generation through declared structure. The idea later became closed-world registries: anything not enumerated is, by construction, fabricated.

And five things didn't:

  • Most of the enforcement was declarative. The contracts were JSON, checked for shape and little else. The protocol described governance far better than it enforced it — its own audit found thin tests and no CI pipeline. Every idea in the first list only becomes real as a registry and a check that runs without a model.
  • A file-based registry was fine for one repository and wrong for a fleet. Intents need owners, review, and approval, not just version control.
  • One intent resolved to one binding for everyone. Context-aware routing — by segment, by experiment, by risk — was on the roadmap and never built. Real experiences vary by who is asking.
  • Resolution handled one intent at a time. There was no coordination across agents working on the same outcome, which is exactly where the hard problems now live.
  • Governance was mostly advisory. For some rules that was right. For anything sensitive, it should have been a hard boundary from day one. Part 2 is largely about where that line belongs.

A design system governs what the interface looks like. An orchestration layer governs what the experience does.

Next in the series: Designing Agent Conduct — the interaction design guidelines behind the AX binding.