> ## Documentation Index
> Fetch the complete documentation index at: https://sandtable.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> sandtable is an early-alpha, open-source CLI (TypeScript, MIT): https://github.com/sandtable-ai/sandtable. Run it as `npx sandtable-ai <command>`, never with the bare name: npm's `sandtable` is an unrelated package. If a flag here disagrees with `npx sandtable-ai <command> --help`, trust the help output.
> Grading is deterministic: pass/fail comes from the world's end state, invariants and the tool-call log, never from an LLM judge.
> To draft a domain for a user, read /guides/init and /reference/spec, then run `npx sandtable-ai validate`, `lint` and `review` before any model-backed run. Keep all example data fictional.
> In Claude Code, the sandtable plugin teaches these steps as skills: `/plugin marketplace add sandtable-ai/sandtable`, then `/plugin install sandtable@sandtable`.
> Every page is available as Markdown by appending .md to its URL. /llms.txt lists every page, /llms-full.txt contains the whole site, and https://sandtable.dev/mcp is a search MCP server for these docs.

# Domains: a simulated copy of one customer's world

> A domain is a folder of plain files for one customer workflow: the records, API, rules and conversations that sandtable uses to rehearse an agent.

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.

```text theme={null}
examples/freight/
├── sandtable.yaml     # world, tools, invariants, personas; points at the files below
├── policy.md          # the SOP the agent must follow
├── tasks.yaml         # the rehearsed conversations
└── source/            # what the customer handed over (OpenAPI spec, transcripts)
```

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 and policy**
>
> `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.

> **world**
>
> 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.
>
> ```yaml theme={null}
> world:
>   tables:
>     carriers:
>       key: mc_number
>       rows:
>         - { mc_number: "100202", name: Northline Haulers, insurance_status: expired, equipment: dry_van }
>     bookings:
>       key: booking_id
> ```

> **tools**
>
> 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.
>
> ```yaml theme={null}
> - name: book_load
>   description: Book a load for a vetted carrier at the agreed all-in rate in USD.
>   params:
>     load_id: { type: string }
>     mc_number: { type: string }
>     rate: { type: number }
>   checks:
>     - { table: loads, key: $load_id, where: { status: available }, error: "Load ${load_id} is not available" }
>   ops:
>     - create: bookings
>       values: { load_id: $load_id, mc_number: $mc_number, rate: $rate, status: confirmed }
>     - update: loads
>       key: $load_id
>       set: { status: booked }
> ```
>
> 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.

> **invariants**
>
> 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](/concepts/invariants).

> **personas and tasks**
>
> 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](/guides/write-tasks).

## 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](/reference/spec), or draft one with [`sandtable init`](/guides/init) from an OpenAPI spec, an SOP and call transcripts, then review it. Either way, run these after every edit:

```bash theme={null}
npx sandtable-ai validate ./my-domain   # schema and cross-references
npx sandtable-ai lint ./my-domain       # every task ready, solvable, non-vacuous and graded
```

The bundled example, [`examples/freight`](https://github.com/sandtable-ai/sandtable/tree/main/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`](https://github.com/sandtable-ai/sandtable/tree/main/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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.