# Understanding the data model (schemas)

Every record is a JSON object, but JSON alone doesn't tell you the field types,
which fields are required, the allowed enum values, or what a nested
`{ "name": "Site 1" }` actually points at. The CLI surfaces that contract in two
ways.

## 1. Schema sidecars in the snapshot

On `import`, every entity folder gets schema sidecars next to its records:

| File | What it is |
| ---- | ---------- |
| `_meta.json` | routing — api, microservice/endpoint, (scheduler) scope + dataTemplate |
| `_schema.json` | **distilled, agent-friendly contract** (read this) |
| `_schema.source.json` | the **raw authoritative schema** (the same model that drives the dynamic UI) |

So an agent editing `do/Asset/Filler 1.rdata` can read `do/Asset/_schema.json`
in the same folder to know exactly what's valid.

### Distilled `_schema.json` shape

```json
{
  "api": "do",
  "entity": "Asset",
  "endpoint": "assets",
  "microservice": "core",
  "naturalKey": "name",
  "fields": [
    { "name": "name", "type": "string", "required": true, "unique": true },
    { "name": "type", "type": "enum", "required": true,
      "enum": ["AREA", "LINE", "EQUIPMENT"] },
    { "name": "displayOrder", "type": "number", "required": false }
  ],
  "relationships": [
    { "name": "assetClass", "kind": "many-to-one", "references": "AssetClass",
      "keyField": "name", "required": true },
    { "name": "parent", "kind": "many-to-one", "references": "Asset",
      "keyField": "name" }
  ]
}
```

- `naturalKey` is the field used as the filename and for cross-environment
  matching in diff/deploy (usually `name`).
- A relationship means the field appears in the record as
  `{ "<keyField>": "<value>" }` (e.g. `"assetClass": { "name": "Palletisers" }`)
  and resolves to a record of `references`.

## 2. The `schema` command

```bash
tllt schema                      # list every known entity
tllt schema Asset                # full contract for one entity (live, DO)
tllt schema Equipment            # scheduler entity (live, under scheduler/)
tllt --json schema Asset         # machine-readable (same shape as _schema.json)
```

## Sources & reliability

Schemas are fetched **live** on import, so they're always current (the same model
the dynamic UI uses). The fetch is best-effort: if a schema can't be retrieved,
the import still proceeds — just without that entity's `_schema.json` sidecar.

### Scheduler: the schema is authoritative too

The CLI drives the Scheduler's **normalized REST API**, whose write shapes match
the published schema — so `tllt schema <Entity>` is authoritative
for scheduler entities just like DO. Nested references are stored by natural key
(e.g. `"changeoverSet": { "name": "Bottling" }`) and resolved to the target's id
on deploy. A few API quirks (number fields emitted as strings; unscoped leaf
collections) are normalised by the CLI — see [`scheduler.md`](scheduler.md).
