Skip to main content
A domain is everything sandtable needs to rehearse an agent for one customer workflow: the records the agent can read and change, the API it calls, the rules it must never break, and the conversations to rehearse. It is a directory of plain files, meant to live in the customer’s repo (or yours) and change with it.
Any top-level value in sandtable.yaml that is a relative path to a .md, .yaml or .yml file is inlined, so policy: ./policy.md and tasks: ./tasks.yaml keep long sections in their own files. A path to a missing file is a validation error.

The parts

description says who the agent works for. Both the agent and the simulated user see it. policy is the customer’s operating procedure. The Claude reference agent uses it as its system prompt, and every agent receives it as part of the default system prompt (your own agent may ignore that and use its own prompt). The simulated user never sees the policy.
Tables of records, each with a key field and seed rows. Every trial starts from a fresh copy, so trials are independent and can run in parallel. Keep seed rows fictional, even when they are modelled on real cases.
A tool is what the agent sees (a name, a description written for a model, and typed params) plus what it does to the world: checks the real API would enforce, then ops that run atomically.
Model the API’s behaviour, not its implementation. A tool needs to change the world the way the real call would, closely enough that the end state can be graded.
Rules that hold in every conversation, checked before every tool call and on every agent message. One violation fails the trial. See Invariants vs expectations.
A persona describes how a caller behaves (impatient, evasive, friendly). A task is one rehearsed conversation: a hidden goal for the simulated user, an optional persona and script, and the expected outcome. See Write tasks that discriminate.

One domain per workflow

A domain models one workflow for one customer: “carrier sales at this brokerage”, not “logistics”. Two customers with different rules get two domains, even if their APIs look alike. That is the point: the rules that differ between customers are the ones an agent gets wrong.

How a domain gets written

You can write one by hand from the spec, or draft one with sandtable init from an OpenAPI spec, an SOP and call transcripts, then review it. Either way, run these after every edit:
The bundled example, examples/freight, is a fictional freight brokerage’s carrier-sales desk with 8 tables, 9 tools, 18 invariants and 50 tasks. A second one, examples/clinic, is a clinic’s phone receptionist with 25 tasks and 12 invariants, covering identity checks, double booking, insurance coverage, the cancellation window and patient privacy. Reading it is the fastest way to learn the format.