# Configuration files (drive, machine, cell)

> Field reference for rt-core machine, drive and cell configuration, with units, allowed ranges and defaults, plus what rtctl compile writes, the three identity digests, and the warnings and refusals the compiler returns.

URL: https://advancedmetalresearch.com/docs/reference/configuration
Section: RosieOS docs / Reference
Last updated: 2026-10-10

rt-core reads one compiled configuration. You write it as JSON in `rt-core/config/`, and [`rtctl compile`](/docs/reference/rtctl#compile) turns it into an immutable directory named after its digest. This page lists every field of the machine, drive and cell layers. Robot descriptions have their own page: [Robot description files](https://advancedmetalresearch.com/docs/reference/robot-description).

Compile a shipped simulation machine and print its digest directory:

```bash
cd rt-core
make control
build/rtctl compile --config config/machines/simulation/simulation-program.json --out build/config
# /…/rt-core/build/config/<configuration_sha256>
```

How the layers fit together is explained in [Cells, machines and positioners](https://advancedmetalresearch.com/docs/concepts/cells-and-positioners).

## Templates

`rt-core/config/templates/` holds one annotated template per layer: `machine.json`, `drive.json`, `cell.json` and `robot.json`. Every field has a `<field>_doc` sibling that states its unit, allowed values and default. The tables below are drawn from those templates and the compiler.

The templates are class definitions, not deployable configs. They show mutually exclusive branches side by side (flat axes and robot-bound axes, for example), and `<required>` marks a value the example cannot choose. To use one, pick a branch, remove the `_doc` siblings and fill the values.

Tests in `rt-core/tools/rtctl` and `rt-core/config/cells` check the templates against the compiler's readers and compile their simulation branches. None of them touches hardware.

### Provenance siblings

Numeric robot facts and safety exceptions must say where they came from. Add a `<field>_source` ("cited: …") or `<field>_unverified` ("unverified: …") string beside the field. Missing provenance is a compile error. Free-text reasons (for example `collision_watchdog.reason`) must be non-empty.

## What compile writes

`rtctl compile --out DIR` writes `DIR/<configuration_sha256>/`:

| File | Contents |
|---|---|
| `argv.json` | The core's command line, from the compiled configuration. |
| `axes.conf` | Per-axis native profiles. |
| `configuration.identity` | The canonical text the configuration digest is computed over. |
| `configuration.base.identity` | Only for a machine with an `io` block: the identity before cell I/O was bound. The final digest covers that base digest plus the `io` block and its terminal profile. |
| `machine.identity`, `deployment.identity` | Canonical texts for the machine and deployment digests. |
| `coordinate.identity.json` | Per-axis coordinate identities and their SHA-256. |
| `robot.json` | The robot binding, when the machine pins a robot description. |
| `resources.json`, `resources/<sha256>` | The robot description files (and cell calibration) as content-addressed resources. `rt-control` serves them through Describe and `GET /v1/resources/<sha256>`. |

`rt-control` loads this directory through `--compiled-config` or `ROSIE_RT_COMPILED_CONFIG`.

### Three digests

The compiler produces three SHA-256 digests. Describe reports all three.

| Digest | Covers | Changes when |
|---|---|---|
| `configuration_sha256` | The original machine JSON and the resolved drive configs (and, for a robot-bound machine, the pinned description) | Any byte of meaning in the machine or its drives changes. This is the pin in cell configs, `rt-control --configuration-sha256` and every `acquire` binding. |
| `machine_sha256` | Axis list and order, names, slave positions, units, scaling, gearing, sign, wrap; drive PDO, SDO, DC, Home, absolute, brake and coordinate blocks; limits, tolerances, jog durations, motor cap; cycle and lateness timing; bus bring-up policies | Anything that changes motion or drive behaviour. |
| `deployment_sha256` | Runtime CPU and priority, host, NIC, sockets, pair id, service users, backend and other non-motion metadata | Only where and how the core runs. |

Canonical identity text uses sorted keys, one `key=value` per line, `%.17g` doubles and decimal integers.

## Machine config

A machine config binds drives, and optionally a robot description, to bus positions, timing, limits, I/O and host policy. The shipped ones are in `config/machines/`, `config/machines/bench/` and `config/machines/simulation/`.

A machine uses one of two axis forms:

- **Flat axes** declare their own scaling and limits. Simulation fixtures and bare-motor benches use them.
- **Robot-bound axes** name a `robot_joint` in a pinned robot description. They inherit travel, velocity, acceleration, gearing and Home policy from the description, and must not redeclare them.

config/machines/simulation/simulation-rosie1400.json (excerpt):

```json
{
  "schema_version": 1,
  "backend": "simulation",
  "cycle_ns": 1000000,
  "robot": {
    "robot_description": {
      "path": "robot_description/robots/rosie_1400_v3",
      "sha256": "sha256:<identity>",
      "source": "cited: …"
    }
  },
  "axes": [
    { "robot_joint": "J1", "slave_position": 0,
      "limits": { "max_target_lead": 0.02, "following_error": 0.04, "following_error_timeout_ms": 100,
                  "completion_tolerance": 0.0002, "completion_timeout_ms": 500 } }
  ],
  "max_cycle_lateness_ns": 20000000,
  "runtime": { "cpu": 2, "priority": 90 }
}
```

> [!NOTE] On the simulated bus, `simulation-rosie1400.json` takes the real drive profile from the Rosie 1400 definition, and Home is refused on it, so nothing arms or jogs. For a simulated cell that homes, use the dev-stack machine described in [Run everything in simulation](https://advancedmetalresearch.com/docs/guides/run-in-simulation).

### Top level

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `schema_version` | integer | — | `1` | Required |
| `backend` | string | — | `simulation` (synthetic drives) or `live` (hardware) | Live admission. `rtctl run --backend live`, `control-env` and `inventory` require `live`. |
| `cycle_ns` | integer | ns | 250000 to 10000000, divisible by every nonzero drive DC quantum | Required. Shipped machines use 1000000 (1 kHz). |
| `max_cycle_lateness_ns` | integer | ns | 1 to 249999999 | Required |
| `axes` | array | — | 1 to 16 ordered axes | Required |
| `drive_speed_limit_motor_rpm` | number | motor rpm | > 0; at most 6000 on the supported servo motor frame | 3000 for flat fixtures. Forbidden on robot-bound machines: their cap derives from the URDF velocity. |
| `max_motor_rpm` | number | legacy wire rpm | > 0; exclusive with `drive_speed_limit_motor_rpm` | Omitted. Deprecated: compile warns. |
| `runtime` | object | — | See [runtime](https://advancedmetalresearch.com/docs/reference/configuration#machine-runtime) | Required |
| `robot` | object | — | See [robot](https://advancedmetalresearch.com/docs/reference/configuration#machine-robot) | Absent for flat axes |
| `control` | object | — | See [control](https://advancedmetalresearch.com/docs/reference/configuration#machine-control) | `lan` profile |
| `bus` | object | — | See [bus](https://advancedmetalresearch.com/docs/reference/configuration#machine-bus) | Unknown slaves refused |
| `bus_bringup` | object | — | See [bus_bringup](https://advancedmetalresearch.com/docs/reference/configuration#machine-bus-bringup) | Defaults below |
| `io` | object | — | See [io](https://advancedmetalresearch.com/docs/reference/configuration#machine-io) | Absent |

### `axes[]`, flat form

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `name` | token | — | ASCII letters, digits, `_`; unique | Required |
| `slave_position` | integer | EtherCAT position | 0 to 65535, unique across axes, I/O and unused slaves | Required |
| `profile` | string | — | A drive config basename in `config/drives/`, without `.json` | Required |
| `type` | string | — | `rotary` or `linear` | Required |
| `wire_counts_per_rev` | integer | counts per wire revolution | 1 to 2147483647 | Required |
| `motor_encoder_counts_per_rev` | integer | counts per motor revolution | 1 to 2147483647 | Required |
| `wire_revs_per_axis_rev` | number | wire revolutions per axis revolution | > 0; `single_turn_absolute` requires 1 | Required |
| `gear_ratio.numerator`, `.denominator` | integer | ratio | 1 to 2147483647 each | 1 for flat simulation |
| `lead_m_per_rev` | number | m per revolution | > 0 for linear axes | 0 for rotary |
| `sign` | integer | — | −1 or +1 | Required |
| `coordinate_evidence_mode` | string | — | `multi_turn` or `single_turn_absolute` | Required on flat axes. A generic simulation drive uses `multi_turn`. |
| `position_tracking_mode` | string | — | Identity token | `continuous` in simulation; required otherwise |
| `require_home` | boolean | — | `true` requires the drive's `native_home` | Required on flat axes |
| `max_acceleration` | number | rad/s² or m/s² | > 0 | Flat fixtures only |
| `limits` | object | — | See [limits](https://advancedmetalresearch.com/docs/reference/configuration#machine-limits) | Required |
| `startup` | object | — | Per-axis drive startup overrides, constrained by the drive's `startup_schema` | Drive `startup_defaults` |
| `jog`, `brake`, `collision_watchdog`, `diagnostics` | object | — | See below | Omitted |

### `axes[]`, robot-bound form

| Field | Type | Description |
|---|---|---|
| `robot_joint` | token | Exactly one joint name from the pinned robot definition. No joint twice. |
| `slave_position` | integer | EtherCAT position, as above. |
| `limits` | object | Only the tracking fields: `max_target_lead`, `following_error`, `following_error_timeout_ms`, `completion_tolerance`, `completion_timeout_ms`. |
| `startup`, `jog`, `brake`, `collision_watchdog`, `diagnostics` | object | As for flat axes. |

A robot-bound axis must omit `limits.min`, `limits.max`, `limits.max_velocity`, `limits.jog_acceleration`, `max_acceleration` and `coordinate_evidence_mode`. It inherits them:

- travel and maximum velocity from `robot.urdf`
- trajectory and jog acceleration from `config.json` → `planning.joints.<joint>.acceleration_rad_s2` (required)
- the per-axis drive speed cap, derived from the URDF velocity through the definition's gearing and encoder scale
- Home policy and coordinate evidence mode from the definition

Every joint in the definition needs an axis, unless you list it in `robot.absent_joints`.

### `limits`

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `min`, `max` | number | rad (rotary) or m (linear) | Finite, `min` < `max` | Required on flat axes; inherited on robot-bound axes |
| `max_velocity` | number | rad/s or m/s | > 0 | Flat fixtures only |
| `jog_acceleration` | number | rad/s² or m/s² | > 0 | Flat fixtures only |
| `max_target_lead` | number | rad or m | > 0 | Required |
| `following_error` | number | rad or m | > 0 | Required |
| `following_error_timeout_ms` | integer | ms | 1 to 1000 | Required |
| `completion_tolerance` | number | rad or m | > 0 | Required |
| `completion_timeout_ms` | integer | ms | 1 to 4294967295 | Required |

### `jog`

The ramp when jog input stops. Also settable per drive; the axis value wins.

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `arrest_ns` | integer | ns | 1 to 65535 × `cycle_ns` | 200000000 (200 ms) |
| `quick_stop_ns` | integer | ns | 1 to 65535 × `cycle_ns` | 300000000 (300 ms) |

### `brake` and `brake_override`

Brake fields are taken from the drive config first, then overridden per axis.

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `brake.present` | boolean | — | `false` cannot keep `gravity_axis` or `released_signal` | `false` |
| `brake.gravity_axis` | boolean | — | `true` only with `present` | `false` |
| `brake.release_delay_ms` | integer | ms | 0 to 4294967295 | 100 |
| `brake.hold_delay_ms` | integer | ms | 0 to 4294967295 | 100 |
| `brake.hold_displacement_tolerance_counts` | integer | counts | 0 to 2147483647 | 1 |
| `brake.released_signal.semantic` | string | — | An existing TX PDO semantic containing the bit | Required with `released_signal` |
| `brake.released_signal.bit` | integer | bit | 0 to 31 for mapped brake feedback | Required with `released_signal` |
| `brake_override.present` | boolean | — | `false` only | Required with `brake_override` |
| `brake_override.reason` | string | — | Non-empty, with a validated bench hold policy | Required with `brake_override` |

`brake_override` exists for benches whose brake wiring is not yet verified. It disables brake handling on that axis and must say why.

### `collision_watchdog`

Faults the axis when torque or following error stays above a bound. It detects an impact after it happens; it does not avoid one. Also settable per drive.

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `torque_abs_max_raw` | integer | raw drive torque units | 1 to 1000 | Required when enabled |
| `following_error_counts_max` | integer | counts | 1 to 2147483647 | Required when enabled |
| `sustained_cycles` | integer | cycles | 2 to 1000 | Required when enabled |
| `disabled` | boolean | — | `true` needs a `reason` and no thresholds; `false` needs thresholds and a torque PDO | `false` |
| `reason` | string | — | Non-empty | Required when disabled |

Both branches need `_source` and `_unverified` siblings. An axis with no watchdog at all compiles with a warning: `axis <name>: collision_watchdog missing; disabled`.

### `diagnostics`

| Field | Type | Allowed | Default |
|---|---|---|---|
| `external_enable_input.bit_index` | integer | 0 to the mapped digital-input width minus 1, at most 31 | Required with the block |
| `external_enable_input.polarity` | string | `active_high` or `active_low` | Required with the block |

This lets rt-core report the state of an external enable, such as an auxiliary contact on the cell's stop chain. It is a diagnostic only. It never gates motion and is not a safety function. See the [safety model](https://advancedmetalresearch.com/docs/get-started/safety-model#hardware-e-stop).

### `runtime`

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `cpu` | integer | logical CPU | 0 to 1023 | Required |
| `priority` | integer | SCHED_FIFO priority | 1 to 99 | Required |
| `telemetry.retention_ms` | integer | ms | 100 to 3600000, within a 2 GiB ring ceiling | 100000 |
| `telemetry.dump_count` | integer | files | 1 to 100 | 20 |
| `telemetry.dump_bytes` | integer | bytes | 529680 or more | 536870912 |
| `telemetry.directory` | string | absolute path | Non-root, no NUL | The runtime chooses |

The core writes telemetry dumps (a `.bin` file and its `.json` index) to `telemetry.directory`. Convert one with [`rtctl telemetry dump-to-jsonl`](/docs/reference/rtctl#telemetry).

### `robot`

| Field | Type | Description |
|---|---|---|
| `robot_description.path` | path | Repository-relative `robot_description/robots/<model_id>`. The manifest must register `rtcore_definition.json`. |
| `robot_description.sha256` | `sha256:<64 hex>` | The description identity, as `go run ./cmd/identity` prints it. Compile refuses a mismatch with `robot_description_mismatch`. |
| `robot_description.source` | string | Provenance. |
| `absent_joints` | string[] | Definition joints this machine has no axis for. Default empty. |
| `bench_measurement_profile` | string | Restricted to one bare-motor bench measurement profile. Omit otherwise. |
| `machine_planning_calibration.path` | path | Component-relative `config/...` path to this machine's `machine_planning_calibration.json`. Every joint it corrects must exist and its frames must name known links. |
| `machine_planning_calibration.sha256` | 64 hex | SHA-256 of the file's exact bytes. |
| `machine_planning_calibration.source` | string | Provenance: who measured it, how and when. |

Omit `machine_planning_calibration` and the cell serves the empty calibration document for its model.

### `control`

The per-cell lease and jog-age ceilings. See [Control authority](https://advancedmetalresearch.com/docs/concepts/control-authority).

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `link_profile` | string | — | `lan` or `internet` | `lan` |
| `max_grant_lease_ns` | integer | ns | 1000000 to 10000000000, whole milliseconds | 500000000 on `lan`, 3000000000 on `internet` |
| `max_jog_input_age_ns` | integer | ns | 1 to 2000000000 | 250000000 on `lan`, 750000000 on `internet` |

> [!WARNING] A longer lease or jog age delays the unattended stop after a client or link failure by the same amount. Keep the `lan` defaults unless you have measured a reason not to. The `internet` values are marked unverified in the code.

### `bus`

| Field | Type | Allowed | Default |
|---|---|---|---|
| `unknown_slaves` | string | `refuse`, or `hold` (needs `unknown_slaves_note`) | `refuse` |
| `unknown_slaves_note` | string | Why extra slaves may stay on the bus | — |
| `hold_on_robot_acknowledged` | boolean | `true` plus `hold_on_robot_note` for a robot machine using `hold` | `false` |
| `unused_slaves[]` | array | 0 to 256 entries: `slave_position`, `vendor_id`, `product_code`, `revision`, `note` | Empty |

`hold` leaves unknown slaves in PREOP without outputs. It is meant for test benches; an assembled robot cell must use `refuse`.

### `bus_bringup`

| Field | Type | Unit | Default |
|---|---|---|---|
| `startup_passive_ms` | integer | ms | 0 |
| `explicit_pdo_config` | boolean | — | `false`. `true` is incompatible with fixed drive PDO presets. |
| `disable_output_watchdog` | boolean | — | `false`. Use `true` only with a declared machine failure policy. |
| `no_dc` | boolean | — | `false`. `true` disables distributed-clock setup. |
| `wait_before_safeop_ms` | integer | ms | 250 |
| `preop_safeop_timeout_ms` | integer | ms | 5000 |
| `safeop_op_timeout_ms` | integer | ms | 5000 |

### `io`

Cell I/O through a digital I/O terminal. See [Process I/O and sensing](https://advancedmetalresearch.com/docs/concepts/process-io-and-sensing).

| Field | Type | Unit | Allowed | Default |
|---|---|---|---|---|
| `profile` | string | — | A terminal drive config in `config/drives/` | Required |
| `slave_position` | integer | EtherCAT position | 0 to 65535, unique | Required |
| `torch_qualified` | boolean | — | `false` only | Required |
| `inputs[]` | array | — | 0 to 8 unique bits | Required |
| `inputs[].name` | token | — | Unique | Required |
| `inputs[].bit` | integer | bit | 0 to 7 | Required |
| `inputs[].polarity` | string | — | `active_high` or `active_low` | Required |
| `inputs[].class` | string | — | `fast` or `supervisory` | Required |
| `outputs[]` | array | — | 0 to 8 unique bits | Required |
| `outputs[].name`, `.bit` | token, integer | —, bit | As for inputs | Required |
| `outputs[].safe_state` | boolean | — | `false` (OFF) only | Required |
| `outputs[].expiry_ns` | integer | ns | 1 to 1000000000 | Required |
| `outputs[].class` | string | — | `process` or `torch`. Torch outputs are always refused at runtime. | Required |
| `outputs[].readback_bit` | integer | bit | 0 to 7, unique per output | Required |
| `outputs[].readback_polarity` | string | — | `active_high` or `active_low` | Required |

`config/cell-io/simulation.json` is a complete simulation machine with an `io` block, not a terminal descriptor.

## Drive config

A drive config describes one drive or I/O terminal model: its EtherCAT identity, PDO layout, scaling semantics, startup parameters, Home transaction and protective defaults. A machine axis selects one with `profile`; a robot definition with `drive_profile`. Names resolve only in `config/drives/`. The template is `config/templates/drive.json`.

| Field | Type | Description |
|---|---|---|
| `schema_version` | integer | `1`. |
| `id`, `label` | string | Metadata only. `profile` selects the file, not `id`. |
| `simulation_only` | boolean | `true` requires `backend: simulation`. Default `false`. |
| `ethercat.vendor_id`, `.product_code`, `.revision_no` | integer | Expected slave identity. |
| `ethercat.rx_pdo`, `.tx_pdo`, `.rx_sync`, `.tx_sync` | integer | PDO assignment object indices and sync managers. |
| `ethercat.dc_quantum_ns` | integer | ns. A nonzero quantum must divide `cycle_ns`. |
| `ethercat.dc_assign_activate` | integer | DC activation bit mask. |
| `ethercat.rx_layout[]`, `.tx_layout[]` | array | Mapped PDO entries: `semantic`, `index`, `subindex`, `bits`. At most 32 entries in total. RX must map `cw`, `target_pos` and `mode`; TX must map `sw`, `pos` and `mode_disp`. |
| `ethercat.restore_assignment_on_exit` | boolean | Default `false`. |
| `home_truth_sign` | −1 \| +1 | Direction of the drive's Home reference. |
| `native_home` | object | The Home transaction: `steady_state_mode` and `commissioning_mode` (CiA402 mode numbers), `truth_source`, and an ordered `transaction[]` of `set_mode`, `restore_mode`, `write_sdo`, `wait_sdo`, `write_sdo_wrap_fraction`, `controlword_sequence`, `wait_statusword`, `refresh_truth` and `release_service_override` steps. |
| `feedback_counts_wrap`, `command_counts_wrap` | boolean | Whether position feedback and commands wrap. Linear axes require `false`. |
| `startup_schema`, `startup_defaults` | object | Which startup parameters the drive accepts, their types and ranges, and the default written at startup. A machine axis overrides them under `startup`. |
| `absolute_feedback[]`, `absolute_pair_field` | array, string | Absolute encoder readbacks used for coordinate evidence. |
| `coordinate_evidence` | object | Policy for accepting absolute position evidence. |
| `position_semantics.drive_native_ratio_enabled` | boolean | `true` when the drive scales to the output shaft; `false` when software scales from the motor shaft. |
| `jog`, `brake`, `collision_watchdog` | object | Defaults for the axis blocks above. |
| `max_acceleration` | number | Synthetic fixtures only. Never applies to a robot-bound axis. |

The PDO semantics RX accepts are `cw`, `target_pos`, `target_vel`, `target_torque`, `mode`, `tp_func` and `max_profile_vel`. TX semantics include `sw`, `pos`, `mode_disp`, `err`, `manufacturer_err`, `velocity_actual`, `following_error`, `torque`, `di` and the drive's extended diagnostics.

> [!NOTE] The shipped drive configs carry values measured on specific drive firmware. Treat them as the source of those values, and do not copy them into other documents.

## Cell config

A cell config binds one deployed cell to a machine config and its compiled digest. The template is `config/templates/cell.json`.

config/templates/cell.json (without _doc fields):

```json
{
  "name": "simulation-cell",
  "nodes": [
    {
      "runtime": "rt-core",
      "rt_core": {
        "machine_config": "rt-core/config/machines/simulation/simulation.json",
        "configuration_sha256": "<64 hex from rtctl compile>",
        "pair_id": "simulation-cell",
        "pair_revision": 1,
        "remote_listen": "127.0.0.1:8443"
      }
    }
  ]
}
```

| Field | Type | Allowed | Default |
|---|---|---|---|
| `name` | string | Display text. Ignored by the validator. | Omitted |
| `nodes[]` | array | Ordered nodes | Required |
| `nodes[].runtime` | string | `rt-core` for a native node | — |
| `nodes[].rt_core.machine_config` | path | Repository-relative path to an existing machine config, with no traversal or escaping symlink | Required |
| `nodes[].rt_core.configuration_sha256` | 64 lowercase hex | The exact `rtctl compile` digest | Optional to the reader. Pin it on every deployed cell. |
| `nodes[].rt_core.pair_id` | token | Letters, digits, `_`, `.`, `-` | Required |
| `nodes[].rt_core.pair_revision` | integer | Positive uint64 | Required |
| `nodes[].rt_core.remote_listen` | host:port | IP or DNS host, port 1 to 65535 | Required |

The validator in Go package `rosieos/rt-core/config/cells` recompiles every node's machine config and refuses a mismatched digest, axis count, pair or listener:

```bash
cd rt-core
go test -count=1 ./config/cells -run TestRepositoryCompatibility
```

Cell configs in `config/cells/` also carry deployment fields for the deploy tooling. Those fields select and pin a configuration but never enter its digest.

## Compile warnings

Warnings go to stderr, prefixed `warning:`. The compile still succeeds.

| Warning | Meaning |
|---|---|
| `axis <name>: collision_watchdog missing; disabled` | The axis has no collision watchdog. |
| `max_motor_rpm is accepted for one release …` | Migrate to `drive_speed_limit_motor_rpm` (motor rpm). |
| `legacy flat axes are accepted for one release …` | Migrate to a robot definition and `robot_joint` axes. |
| `axis <name> motor speed cap <n> rpm exceeds … rated 3000 rpm …` | The axis speed cap is above the servo motor's rated speed but within its 6000 rpm maximum. Above the maximum, compile refuses. |

## Robot compile refusals

When a machine pins a robot description, compile refuses with `<reason>: <detail>`:

| Reason | Cause |
|---|---|
| `robot_description_unavailable` | The description directory or its files cannot be read. |
| `robot_description_mismatch` | The pinned identity differs from the files, the definition's `robot_id` differs from the directory, or the directory has a `robot.urdf` its manifest does not register. The detail gives both hashes. |
| `robot_definition_unavailable` | The manifest does not register `rtcore_definition.json`. |
| `robot_definition_field` | An unexpected field, or a restricted field used where it is not allowed. |
| `robot_duplicate_field` | A JSON key appears twice. |
| `robot_reference_path` | A path escapes the repository root or is not a valid relative path. |
| `robot_hash_invalid` | A pinned hash is not lowercase SHA-256. |
| `robot_hash_mismatch` | A pinned file hashes to something else. |
| `robot_joint_mapping` | An axis names an unknown, duplicate or absent `robot_joint`. |
| `robot_joint_missing` | A definition joint has no axis. Declare it in `robot.absent_joints`. |
| `robot_urdf_limits_required` | An axis or definition tries to redeclare a limit, velocity or gearing it must inherit. |
| `robot_limit_widened` | A requested value exceeds the model's bound. |
| `robot_acceleration_required` | `config.json` has no `planning.joints.<joint>.acceleration_rad_s2` for a mapped axis. |
| `robot_bench_inheritance` | A bench machine redeclares a field it must inherit from the robot definition. |
| `machine_planning_calibration_invalid` | The pinned calibration fails its rules, or the description has no geometry to calibrate. |

Other compile errors print a plain message and exit 1. Nothing is written until the whole configuration is valid.

## Sources

Written from these files in the RosieOS repository (https://github.com/advanced-metal-research/RosieOS):

- `rt-core/config/templates/machine.json:1-321`
- `rt-core/config/templates/drive.json:1-629`
- `rt-core/config/templates/cell.json:1-24`
- `rt-core/config/templates/robot.json`
- `rt-core/config/machines/simulation/simulation-rosie1400.json`
- `rt-core/config/machines/simulation/simulation-program.json`
- `rt-core/tools/rtctl/command.go:16-138`
- `rt-core/tools/rtctl/compiler.go:171-200,265-280,650-745,988-1045`
- `rt-core/tools/rtctl/compiled_resources.go:130-154`
- `rt-core/tools/rtctl/collision_watchdog.go:9-45`
- `rt-core/tools/rtctl/robot_definition.go:66-160,200-230,313-420,500-735`
- `rt-core/tools/rtctl/render.go:170`
- `rt-core/config/cells/compatibility.go`
- `rt-core/cmd/rt-control/main.go:24-39`
- `motion-server/v1/local-rt-core.sh:17-28`
