This tutorial takes one risky workflow in your AI product and gives it the eight artifacts that make its behavior governable: commitments, a semantic component, contracts for speech and action, an authority gate, compiled context, a renderer rule, an eval pack, and a replayable trace. Plan on a week: two days of decisions, three days of wiring.
You do not need a platform team, a vendor, or a rewrite. Every artifact is a small file in version control, and a runnable reference implementation exists so you can watch the whole loop work before you build your own. The examples below follow Corredor, the book's fictional remittance product; substitute your own workflow throughout.
One idea drives everything below: the product owns meaning; the model owns expression. Your model may write every sentence and draw every card, but what the product has promised, what it may do, what it treats as true, and how you would prove any of it later all live in artifacts you own, version, and test. When that boundary holds, a model upgrade is a configuration event. When it does not, every prompt is product logic nobody documented.
Why one workflow? Because abstraction costs something, the cost lands up front, and the payoff arrives at scale. Building the full architecture before one workflow has proven it is buying insurance against a fire that is not burning. Build the narrowest governed loop, watch it catch something, and let the evidence argue for the rest.
Two variables decide where to start: what a wrong sentence costs, and how many places it can appear. Look for the workflow where the answer to the first is "money, safety, rights, or compliance" and the answer to the second is "more than one channel or market." In Corredor it is the first-time transfer above the identity-verification threshold. In your product it might be a refund decision, a dosage explanation, a cancellation flow, a quote.
Write it down, one page, five headings:
# /experience-system/workflows/first_transfer.md
Intent: send_money_today (first transfer, above threshold)
Consequence: money movement, regulated disclosures, manual review
Channels: WhatsApp first, mobile app, support dashboard
Owners: product (flow), compliance (disclosures), eng (tools)
Failure modes: disclosure drift, softened requirements, premature
confirmation, invented policy, skipped education
Go deeperThe Adoption Threshold gives the full decision table and the maturity ladder, including when the honest answer is "a prompt file is fine."
Book a room with whoever owns risk for this workflow: legal, compliance, support, product. Leave with three lists. What the product must say before the user acts. What it must never imply, no matter how warm the phrasing. Which actions require approval before they run.
Be concrete to the point of discomfort. Not "be transparent about verification" but "say verification is required, disclose that it may delay the transfer, and never promise a completion time." The test for a commitment is that a validator could check it: a fact is something true about the world; a commitment binds the product and has an owner, a version, and a test.
Go deeperThe Core Model explains why facts and commitments are different things and why the middle layer needs its own owner.
A semantic component is a reusable behavior, not a prompt: it declares its intent, the facts it may not run without, the actions it may offer, the commitments it must honor, and the evals that test it. Pick the single behavior at the heart of your workflow. For Corredor it is explaining identity verification:
# /experience-system/components/explain_identity_verification.yaml
id: component.explain_identity_verification.v1
intent: explain
requiredFacts:
- verification_required_above_threshold
- transfer_may_be_delayed
- accepted_identity_documents
allowedActions:
- upload_identity_document
- contact_support
requiredCommitments:
- disclose_required_status
- disclose_delay_risk
contractId: contract.explain_identity_verification.v1
evalPackId: evalpack.explain_identity_verification.v1
The decision that matters: what happens when a required fact is missing because a policy service is down or stale? The component refuses and falls back to something safe, a fuller deterministic explanation or a human handoff. What it must never do is let the model improvise the missing fact.
Go deeperSemantic Components covers the vocabulary (Explain, Confirm, Repair, and the rest), component boundaries, and versioned rollout.
The contract turns step 2's lists into something enforceable. It names the verified facts in, the required claims, the forbidden claims, the output shape, and, critically, what happens when validation fails:
# /experience-system/contracts/conversation/explain_identity_verification.yaml
id: contract.explain_identity_verification.v1
facts:
required: [verification_required_above_threshold,
transfer_may_be_delayed, accepted_identity_documents]
commitments:
must: [say_verification_is_required, disclose_delay_risk,
offer_next_step]
mustNot: [imply_verification_is_optional,
promise_completion_time, invent_fee_or_policy]
outputShape:
maxWords: 90
validators:
- disclosure_required_status
- discloses_delay_risk
- no_completion_time_promise
degradeTemplate: >-
To send more than $500 we need to verify your identity. It is
required, and your transfer may be delayed while we complete it.
You can upload your ID here, or contact support for help.
Notice the model is being asked to express, not to decide. And decide the failure behavior now: there are only four choices when a validator fires: retry narrower, degrade to the approved template, block and hand to a human, or block the action while the conversation continues. The boring defaults are right: required disclosures degrade to templates; consequential actions block. A stiff but accurate sentence beats a fluent omission.
Go deeperContracts, Capabilities, and Authority covers contract boundaries and the full validator-failure decision.
Your workflow ends in something with a side effect: a transfer, a refund, a booking, a cancellation. That action gets a contract of its own, because the expensive failure mode is not what the product says but what it does:
# /experience-system/contracts/actions/initiate_transfer.yaml
id: action.initiate_transfer.v1
tool: transfer_api.create_transfer
sideEffect: creates_pending_money_transfer
requiredInputs: [senderId, recipientId, amount, quoteId]
preconditions:
- sender_authenticated
- recipient_confirmed
- quote_not_expired
- disclosure_acknowledged
humanApprovalWhen:
- amount_over_1000
reversibility:
cancel_before_status: submitted_to_partner
audit: [contract_version, tool_input_hash,
user_confirmation_event_id]
The Chevrolet lesson applies here: an assistant that can discuss a sale is one missing check away from appearing to make one. The preconditions are the difference between "the model said yes" and "the product committed."
Contracts define boundaries; authority defines who may cross them, and when. Without an authority gate, every tool wired into your system is implicitly available to every user, on every channel, at every amount. That is never what anyone intended, and it is exactly what the model will assume:
# /experience-system/authority/transfer_execution.yaml
id: authority.transfer_execution.v1
subject: user.sender
capability: transfer.initiate
allowedWhen:
- sender_authenticated
- recipient_confirmed
- quote_not_expired
- disclosure_acknowledged
humanApprovalWhen:
- amount_usd > 1000
replayEvidence: [authority_version, policy_decision_id,
user_confirmation_event_id]
Run this gate before anything renders, so the interface never offers an action the system already knows it cannot honor. In the reference implementation, the confirm button arrives disabled because authority said so, not because the model chose restraint.
The NYC MyCity bot told businesses to break the law because it had ingested a mountain of content with no layer that could say which facts were current, which were superseded, and which it had no authority to state. Your repair is a context compiler: instead of dumping everything into the model, gather candidate facts, check where each came from, resolve contradictions, and emit a bounded packet:
{
"context_packet_id": "ctx_4471",
"task_id": "task_transfer_4471",
"facts": [
{ "key": "verification_required", "value": true,
"source": "policy.identity.v12",
"authority": "compliance_approved", "freshness": "2026-07-01" },
{ "key": "transfer_literacy", "value": "first_time_sender",
"source": "memory.user.transfer_literacy", "confidence": 0.78 }
],
"excluded": [
{ "key": "old_delay_policy",
"reason": "superseded_by_policy.identity.v12" }
],
"safe_default": "render_required_disclosures"
}
Three rules keep it honest. Declare precedence: compliance-approved beats cached, newer beats older. Fail closed: when the compiler cannot decide, run with every required disclosure and no personalization. And give it a latency budget from day one: compile once per task, cache the packet, invalidate on source change. A compiler that takes eight hundred milliseconds per message will be quietly bypassed by the first engineer under deadline pressure.
Go deeperContext Compiler, Memory, and Knowledge adds memory write policies: purpose, retention, consent, and correction.
A disclosure that is technically present but folded below a detail section has not been disclosed. Renderer contracts are salience rules: what must be visible, in what order, before which affordance:
# /experience-system/contracts/renderers/whatsapp_text.yaml
id: renderer.whatsapp_text.v1
salience:
disclose_delay_risk: visible_before_confirm
fee_and_rate: visible_before_confirm
actions:
confirm_transfer: enabled_only_when_authorized
forbidden:
- hide_required_disclosure
Test these the way you test accessibility, because they are the same kind of rule: is the disclosure inside the first viewport, is it before the confirm control in reading order, is the disabled button actually disabled rather than styled gray? And define the fallback: when a generated layout fails its checks, render the plain, pre-approved version. It will be ugly. It will also be correct.
The eval pack travels with the component and gates its releases. Fixtures are realistic task states; graders are the validators from step 4; the release gate blocks on required failures:
# /experience-system/eval-packs/explain_identity_verification.yaml
id: evalpack.explain_identity_verification.v1
artifact: component.explain_identity_verification.v1
fixtures:
- name: first_time_sender_high_value
context: { amount_usd: 900, locale: es-MX,
transfer_literacy: first_time_sender }
- name: returning_sender_high_value
context: { amount_usd: 900, locale: es-MX,
transfer_literacy: understands_standard_transfer }
graders:
required:
- disclosure_required_status
- discloses_delay_risk
- no_completion_time_promise
releaseGate:
blockOnRequiredFailure: true
The unit of evaluation is the behavior the person experienced, not the model response. When a contract changes, its eval pack changes in the same review. When a model upgrade arrives, this pack is what decides whether it ships, which is what makes the model replaceable and the commitments not.
Go deeperEvaluation-Driven Experience Design covers online evaluators, human review, and turning production failures into fixtures.
When something goes wrong, the team that argues from screenshots loses a week; the team that replays the trace repairs the responsible artifact in an afternoon. One record per turn, connecting everything that decided it:
{
"trace_id": "trc_8f3a",
"task_id": "task_transfer_4471",
"intent": "send_money_today",
"context_packet_id": "ctx_4471",
"authority": { "id": "authority.transfer_execution.v1",
"allowed": false,
"failed": ["recipient_confirmed"] },
"policies": ["policy.identity.v12"],
"graph_id": "graph.first_transfer_confirmation.v1",
"planner": { "decision": "keep_full_graph", "reversible": true },
"contracts": ["contract.explain_identity_verification.v1"],
"renderer": "whatsapp_text",
"model": "provider/model/version",
"validators_failed": [],
"degraded": false,
"outcome": "verification_started"
}
Generic telemetry says the system ran. Domain evidence says whether the product kept its word: which disclosure was required, which policy version applied, which authority gate ran, what the person actually saw. Traces are product records, not debug logs: give them stable ids, retention rules, and privacy boundaries.
Before wiring your own product, watch the loop work. The book's reference implementation is the whole tutorial in about four hundred lines:
git clone https://github.com/kyle-c/corredor-control-plane
cd corredor-control-plane
npm install
npm run demo # happy path, a misbehaving model caught by
# validators, and bounded planner adaptation
npm run evals # the eval pack, with a real release gate
The demo's second scenario is the one to internalize: an "overly warm" expression engine promises same-day delivery, the validators catch it, and the output degrades to the approved template while the trace records what happened. That is your architecture working. The expression engine is a deterministic template on purpose: swap in any model and every gate holds identically.
Then wire your own artifacts into your stack. The runtime is not exotic: compile context, check authority and policy, run the contract, validate, render, trace. If your product already has a request pipeline, the control plane is middleware, not a rewrite.
Go deeperReference Implementation Patterns covers compiling prompts from artifacts and the patterns that should be non-negotiable.
Everything so far was construction. This step is the habit that justifies it. Weekly, thirty minutes:
When a model upgrade lands, the routine is anticlimactic by design: run the eval packs, read the diff in behavior, ship or hold. The upgrade is allowed to improve expression. It must pass the same commitments.
Go deeperGovernance, Ownership, and Auditability maps this rhythm onto NIST, ISO 42001, and EU AI Act expectations. Corredor With Scars shows a full incident and its trace-driven repair.
Four problems account for most stuck moments. The two-line fix for each.
The tutorial builds the narrowest governed loop. The book is the argument, the failure modes, and the judgment. The map:
| Tutorial step | Book chapter | What the book adds |
|---|---|---|
| 1. Workflow | The Adoption Threshold | The full decision table, maturity ladder, and sunset conditions |
| 2. Commitments | The Core Model | Why facts and commitments differ; the three layers and their owners |
| 3. Component | Semantic Components | The intent vocabulary; boundaries; versioned rollout |
| 4–6. Contracts & authority | Contracts, Capabilities, and Authority | Tool contracts as interfaces; the boundary table; the Chevrolet incident in full |
| 7. Context | Context, Memory, Knowledge | Memory write policies; the MyCity incident; compiler failure modes |
| 8. Renderer | Adaptive Rendering and Generative UI | Generative UI as a contract-bound renderer, not a rival |
| 9. Evals | Evaluation-Driven Experience Design | Online evaluators; outcome metrics beyond contract checks |
| 10. Traces | Runtime, Corredor With Scars | The bounded planner; a complete incident replay |
| 11. Run it | Reference Implementation Patterns | Compiled prompts; policy beside execution; the non-negotiables |
| 12. Measure | Governance, The Case Against the Framework | Regulatory mapping; the objections, answered honestly |
Read the book for the argument. Send the 13-slide brief to the stakeholder who needs convincing. And govern one workflow this month. The tutorial only works if you do.