{
  "$id": "https://botrail.github.io/botrail/schema/project.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "botrail project (.botrail)",
  "description": "A `.botrail` project: one robot cell as text — robots and their\nplacement, obstacles, frames, motions, sequences, signals, sensors,\ndevices, scenarios, toolpaths, the I/O map and the parts pinned to all\nof them. Every list is additive: a field a file does not carry is\nempty. `version` is 2.",
  "type": "object",
  "properties": {
    "allowed_contacts": {
      "description": "Process-contact exemptions, by names (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/AllowedContactMsg"
      }
    },
    "applicators": {
      "description": "Spray applicators by name (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/ApplicatorMsg"
      }
    },
    "brushes": {
      "description": "Brushes — named process settings toolpath strokes run with (absent\nin older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/Brush"
      }
    },
    "cameras": {
      "description": "Cameras (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/CameraMsg"
      }
    },
    "connection_plan": {
      "description": "Physical interface requirements and utility connections. These do\nnot execute the cell; the I/O assignment remains in `io`.",
      "$ref": "#/$defs/ConnectionPlan"
    },
    "devices": {
      "description": "Auxiliary devices (absent in older v2 files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/DeviceMsg"
      }
    },
    "flashes": {
      "description": "Weld-flash bindings (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/FlashMsg"
      }
    },
    "frames": {
      "description": "Named world frames (absent in older v2 files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/FrameMsg"
      }
    },
    "io": {
      "description": "The I/O map's assignment layer — controller nodes, point → channel\nbindings, declarations (absent in older files). The points\nthemselves are derived, never stored.",
      "$ref": "#/$defs/IoMap",
      "default": {}
    },
    "lidars": {
      "description": "LiDAR scanners (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/LidarMsg"
      }
    },
    "motions": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/MotionMsg"
      }
    },
    "obstacles": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ObstacleMsg"
      }
    },
    "parts": {
      "description": "Part identity pinned to residents and groups (absent in older\nfiles). The BOM itself is derived, never stored.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/PartEntry"
      }
    },
    "robots": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ProjectRobotMsg"
      }
    },
    "scenarios": {
      "description": "Scenarios — named initial-state deltas (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/ScenarioMsg"
      }
    },
    "sensors": {
      "description": "Pseudo-sensors (absent in older v2 files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/SensorMsg"
      }
    },
    "sequences": {
      "description": "PLC-style sequences (absent in older v2 files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/SequenceMsg"
      }
    },
    "signals": {
      "description": "Internal signal declarations (absent in older v2 files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/SignalDefMsg"
      }
    },
    "toolpaths": {
      "description": "Toolpaths — Cartesian process paths (absent in older files).",
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/ToolpathMsg"
      }
    },
    "version": {
      "description": "Project file format version (currently 2).",
      "type": "integer",
      "format": "uint32",
      "minimum": 0
    }
  },
  "required": [
    "version",
    "robots",
    "obstacles",
    "motions"
  ],
  "$defs": {
    "ActionMsg": {
      "description": "Robot-addressed actions carry `robot` — the instance name, or `None`\nfor the scene's sole robot (ambiguous, and rejected, with several).",
      "oneOf": [
        {
          "description": "Start a named motion (its owning robot drives); await it with the\n`done` condition.",
          "type": "object",
          "properties": {
            "motion": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "start_motion"
            }
          },
          "required": [
            "type",
            "motion"
          ]
        },
        {
          "description": "Start a toolpath (continuous Cartesian process path) on `robot`;\nawait with `done`.",
          "type": "object",
          "properties": {
            "robot": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "toolpath": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "start_toolpath"
            }
          },
          "required": [
            "type",
            "toolpath"
          ]
        },
        {
          "description": "Linearly ramp joints (gripper open/close); await with `done`.",
          "type": "object",
          "properties": {
            "duration": {
              "type": "number",
              "format": "double"
            },
            "robot": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "targets": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/RampTargetMsg"
              }
            },
            "type": {
              "type": "string",
              "const": "start_ramp"
            }
          },
          "required": [
            "type",
            "targets",
            "duration"
          ]
        },
        {
          "description": "Grasp an obstacle at its current relative pose (instantaneous).",
          "type": "object",
          "properties": {
            "link": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "object": {
              "type": "string"
            },
            "robot": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "touch_links": {
              "type": [
                "array",
                "null"
              ],
              "default": null,
              "items": {
                "type": "string"
              }
            },
            "type": {
              "type": "string",
              "const": "attach"
            }
          },
          "required": [
            "type",
            "object"
          ]
        },
        {
          "description": "Release an obstacle where it is (instantaneous; the carrier is\nlooked up from the attachment).",
          "type": "object",
          "properties": {
            "object": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "detach"
            }
          },
          "required": [
            "type",
            "object"
          ]
        },
        {
          "description": "Latch onto a moving part: taught poses ride its motion until the\ntrack is released (conveyor tracking).",
          "type": "object",
          "properties": {
            "link": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "object": {
              "type": "string"
            },
            "robot": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "type": {
              "type": "string",
              "const": "track"
            }
          },
          "required": [
            "type",
            "object"
          ]
        },
        {
          "description": "Stop following the tracked part (instantaneous).",
          "type": "object",
          "properties": {
            "robot": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "type": {
              "type": "string",
              "const": "untrack"
            }
          },
          "required": [
            "type"
          ]
        },
        {
          "description": "Write an internal signal.",
          "type": "object",
          "properties": {
            "signal": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "set"
            },
            "value": {
              "type": "boolean"
            }
          },
          "required": [
            "type",
            "signal",
            "value"
          ]
        },
        {
          "description": "Command an auxiliary device (output coil).",
          "type": "object",
          "properties": {
            "command": {
              "$ref": "#/$defs/DeviceCommandMsg"
            },
            "device": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "device"
            }
          },
          "required": [
            "type",
            "device",
            "command"
          ]
        }
      ]
    },
    "AerialMsg": {
      "description": "The aerial drive of a flying vehicle: present on the wire means the\nmachine flies its legs — z is free, each axis at its own rate.",
      "type": "object",
      "properties": {
        "climb_speed": {
          "type": "number",
          "format": "double"
        },
        "descent_speed": {
          "type": "number",
          "format": "double"
        },
        "fixed_yaw": {
          "description": "Hold this yaw over the whole flight; absent flies course-facing.",
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        }
      },
      "required": [
        "climb_speed",
        "descent_speed"
      ]
    },
    "AllowedContactMsg": {
      "description": "One [`crate::AllowedContact`] by names: robot instance, link, obstacle.",
      "type": "object",
      "properties": {
        "link": {
          "type": "string"
        },
        "obstacle": {
          "type": "string"
        },
        "robot": {
          "type": "string"
        }
      },
      "required": [
        "robot",
        "link",
        "obstacle"
      ]
    },
    "ApplicatorMsg": {
      "description": "One named [`crate::coat::Applicator`].",
      "type": "object",
      "properties": {
        "flow": {
          "description": "Paint delivered at the nozzle [m^3/s].",
          "type": "number",
          "format": "double"
        },
        "max_range": {
          "description": "Nothing lands past this axial distance [m].",
          "type": "number",
          "format": "double"
        },
        "name": {
          "type": "string"
        },
        "pattern": {
          "$ref": "#/$defs/Pattern"
        },
        "standoff": {
          "description": "Distance the pattern was measured at [m]. The whole model is a\nprojection of that plane, so this is the number the authored\nstandoff should match.",
          "type": "number",
          "format": "double"
        },
        "transfer_efficiency": {
          "description": "Fraction of `flow` that reaches the reference plane. The rest is\noverspray before the paint ever gets to the part.",
          "type": "number",
          "format": "double"
        }
      },
      "required": [
        "name",
        "standoff",
        "pattern",
        "flow",
        "transfer_efficiency",
        "max_range"
      ]
    },
    "Aspect": {
      "description": "The second facet a device or robot point carries when one name owns\nseveral wires: a device's numeric commands and a robot's handshake.\nRendered as `name.aspect`.",
      "oneOf": [
        {
          "description": "Indexed-transfer start (`Advance`).",
          "type": "string",
          "const": "index"
        },
        {
          "description": "Vehicle dispatch (`Goto`).",
          "type": "string",
          "const": "dispatch"
        },
        {
          "description": "Vehicle station select (`Goto`, word).",
          "type": "string",
          "const": "station"
        },
        {
          "description": "Axis position command (`MoveTo`, word).",
          "type": "string",
          "const": "position"
        },
        {
          "description": "Speed command (`SetSpeed`, analog).",
          "type": "string",
          "const": "speed"
        },
        {
          "description": "Robot start (motion / ramp / toolpath from another host).",
          "type": "string",
          "const": "start"
        },
        {
          "description": "Robot done / idle contact.",
          "type": "string",
          "const": "done"
        },
        {
          "description": "Robot program-number word.",
          "type": "string",
          "const": "program"
        }
      ]
    },
    "AttachmentMsg": {
      "description": "Attachment state of a grasped obstacle (see `ObstacleMsg::attached_to`).",
      "type": "object",
      "properties": {
        "grasp": {
          "description": "Fixed relative pose: `link ← object`.",
          "$ref": "#/$defs/PoseMsg"
        },
        "link": {
          "description": "Carrying link name.",
          "type": "string"
        },
        "robot": {
          "description": "Carrying robot instance name; `None` means the first robot.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "touch_links": {
          "description": "Links allowed to touch the object (carrying link, gripper fingers).",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "link",
        "grasp",
        "touch_links"
      ]
    },
    "Brush": {
      "description": "A process setting a stroke runs with — ABB's *brush*: which applicator,\nat what fraction of its flow, and how the trigger is timed around the\nstroke. Named on the scene, referenced from a toolpath's feed moves.\nAnalog process values live here, in the authoring, because signals are\nbool: the PLC enables the gun, the program picks the brush.",
      "type": "object",
      "properties": {
        "applicator": {
          "description": "A [`Scene::applicator`] name.",
          "type": "string"
        },
        "flow": {
          "description": "Multiplier on the applicator's flow. `1.0` is the flow it was\ncalibrated at; a primer at half flow is `0.5`.",
          "type": "number",
          "format": "double",
          "default": 1.0
        },
        "lag": {
          "description": "Seconds it keeps flowing after the stroke ends.",
          "type": "number",
          "format": "double",
          "default": 0.0
        },
        "lead": {
          "description": "Seconds the paint starts flowing *before* a stroke with this brush\nbegins — the programmed lead that compensates the fluid system's\ndelay, and coats the run-in. Zero is \"opens exactly at the\nstroke's first point\".",
          "type": "number",
          "format": "double",
          "default": 0.0
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "applicator"
      ]
    },
    "CameraMountMsg": {
      "description": "What a camera is bolted to.",
      "oneOf": [
        {
          "description": "A fixture; the pose is a world pose.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "world"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "description": "Rides a vehicle device.",
          "type": "object",
          "properties": {
            "device": {
              "type": "string"
            },
            "kind": {
              "type": "string",
              "const": "vehicle"
            }
          },
          "required": [
            "kind",
            "device"
          ]
        },
        {
          "description": "Bolted to a robot link (a wrist camera).",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "link"
            },
            "link": {
              "type": "string"
            },
            "robot": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "robot",
            "link"
          ]
        }
      ]
    },
    "CameraMsg": {
      "description": "A camera: a named viewpoint with pinhole optics. Presentation only —\nit publishes no signal (a vision sensor referencing it is the planned\nsignal path).",
      "type": "object",
      "properties": {
        "far": {
          "type": "number",
          "format": "double"
        },
        "fov_deg": {
          "description": "Horizontal field of view, degrees.",
          "type": "number",
          "format": "double"
        },
        "mount": {
          "$ref": "#/$defs/CameraMountMsg"
        },
        "name": {
          "type": "string"
        },
        "near": {
          "type": "number",
          "format": "double"
        },
        "pose": {
          "description": "Offset in the mount frame (world pose for a `world` mount).\n-Z is the view direction, +Y is image-up.",
          "$ref": "#/$defs/PoseMsg"
        },
        "resolution": {
          "description": "Image size in pixels (frustum aspect, export pixel size).",
          "type": "array",
          "items": {
            "type": "integer",
            "format": "uint32",
            "minimum": 0
          },
          "maxItems": 2,
          "minItems": 2
        }
      },
      "required": [
        "name",
        "mount",
        "pose",
        "fov_deg",
        "resolution",
        "near",
        "far"
      ]
    },
    "CatalogRef": {
      "description": "A catalog package reference: the id as resolved plus, when known, the\ndataset revision it was fetched at. `revision: None` is a hand-written\nreference that has not been pinned (a part named by its catalog id\nwithout going through `Robot.from_catalog`).",
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "revision": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id"
      ]
    },
    "ChannelKind": {
      "description": "The channel type a point needs. Only `Di`/`Do` take part in the bake\nand the URScript lowering; `Word`/`Ao` are table vocabulary — the\nnumeric commands a bool coil cannot carry.",
      "type": "string",
      "enum": [
        "di",
        "do",
        "ai",
        "ao",
        "safe_di",
        "safe_do",
        "word"
      ]
    },
    "ConditionMsg": {
      "oneOf": [
        {
          "description": "Always true — fire the actions and move on.",
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "immediately"
            }
          },
          "required": [
            "type"
          ]
        },
        {
          "description": "Every motion/ramp started by this step has finished.",
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "done"
            }
          },
          "required": [
            "type"
          ]
        },
        {
          "description": "The named robot has no motion/ramp in flight (interlock building\nblock).",
          "type": "object",
          "properties": {
            "robot": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "robot_done"
            }
          },
          "required": [
            "type",
            "robot"
          ]
        },
        {
          "description": "On-delay timer from step entry (TON).",
          "type": "object",
          "properties": {
            "seconds": {
              "type": "number",
              "format": "double"
            },
            "type": {
              "type": "string",
              "const": "elapsed"
            }
          },
          "required": [
            "type",
            "seconds"
          ]
        },
        {
          "description": "Level test of a signal.",
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "signal"
            },
            "value": {
              "type": "boolean"
            }
          },
          "required": [
            "type",
            "name",
            "value"
          ]
        },
        {
          "description": "Rising edge: on this scan, off the previous one (`-|P|-`).",
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "rising"
            }
          },
          "required": [
            "type",
            "name"
          ]
        },
        {
          "description": "Falling edge: off this scan, on the previous one (`-|N|-`).",
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "falling"
            }
          },
          "required": [
            "type",
            "name"
          ]
        },
        {
          "description": "A linear axis reached its commanded position.",
          "type": "object",
          "properties": {
            "device": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "device_done"
            }
          },
          "required": [
            "type",
            "device"
          ]
        },
        {
          "description": "Series contacts (AND).",
          "type": "object",
          "properties": {
            "conditions": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/ConditionMsg"
              }
            },
            "type": {
              "type": "string",
              "const": "all"
            }
          },
          "required": [
            "type",
            "conditions"
          ]
        },
        {
          "description": "Parallel contacts (OR).",
          "type": "object",
          "properties": {
            "conditions": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/ConditionMsg"
              }
            },
            "type": {
              "type": "string",
              "const": "any"
            }
          },
          "required": [
            "type",
            "conditions"
          ]
        }
      ]
    },
    "Connection": {
      "type": "object",
      "properties": {
        "cable": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "name": {
          "type": "string"
        },
        "reference": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "source": {
          "type": "string"
        },
        "target": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "source",
        "target"
      ]
    },
    "ConnectionPlan": {
      "type": "object",
      "properties": {
        "links": {
          "type": "array",
          "default": [],
          "items": {
            "$ref": "#/$defs/Connection"
          }
        },
        "ports": {
          "type": "array",
          "default": [],
          "items": {
            "$ref": "#/$defs/Port"
          }
        }
      },
      "additionalProperties": false
    },
    "ConstraintMsg": {
      "description": "A TCP path constraint (see `motion::Constraint`).",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "angle": {
              "description": "Half-angle of the cone (rad).",
              "type": "number",
              "format": "double"
            },
            "axis_local": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "axis_world": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "type": {
              "type": "string",
              "const": "orientation_cone"
            }
          },
          "required": [
            "type",
            "axis_local",
            "axis_world",
            "angle"
          ]
        },
        {
          "type": "object",
          "properties": {
            "max": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "min": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "type": {
              "type": "string",
              "const": "position_box"
            }
          },
          "required": [
            "type",
            "min",
            "max"
          ]
        }
      ]
    },
    "Contact": {
      "description": "Contact type of the field device — documentation for the connection\ntable. Independent of `invert`: an E-stop button is an NC contact whose\nhealthy state reads high, so `estop_ok` needs no inversion.",
      "type": "string",
      "enum": [
        "no",
        "nc"
      ]
    },
    "DeclRole": {
      "oneOf": [
        {
          "description": "An external input, whatever the sequences do with the name.",
          "type": "string",
          "const": "input"
        },
        {
          "description": "An output coil, whatever the sequences do with the name.",
          "type": "string",
          "const": "output"
        },
        {
          "description": "A relay: no I/O even if only read (a constant flag).",
          "type": "string",
          "const": "internal"
        },
        {
          "description": "Not on the table at all.",
          "type": "string",
          "const": "exclude"
        }
      ]
    },
    "DeviceCommandMsg": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "start"
            }
          },
          "required": [
            "type"
          ]
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "stop"
            }
          },
          "required": [
            "type"
          ]
        },
        {
          "type": "object",
          "properties": {
            "speed": {
              "type": "number",
              "format": "double"
            },
            "type": {
              "type": "string",
              "const": "set_speed"
            }
          },
          "required": [
            "type",
            "speed"
          ]
        },
        {
          "type": "object",
          "properties": {
            "position": {
              "type": "number",
              "format": "double"
            },
            "type": {
              "type": "string",
              "const": "move_to"
            }
          },
          "required": [
            "type",
            "position"
          ]
        },
        {
          "description": "Send a vehicle to a named station; await with `device_done`.",
          "type": "object",
          "properties": {
            "station": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "goto"
            }
          },
          "required": [
            "type",
            "station"
          ]
        },
        {
          "description": "Run a stopped conveyor for exactly `distance` metres, then stop\n(indexed transfer); await with `device_done`.",
          "type": "object",
          "properties": {
            "distance": {
              "type": "number",
              "format": "double"
            },
            "type": {
              "type": "string",
              "const": "advance"
            }
          },
          "required": [
            "type",
            "distance"
          ]
        },
        {
          "description": "Send a lift to a named stop; await with `device_done`. The cargo\nis fixed the moment this fires (the doors are closed).",
          "type": "object",
          "properties": {
            "stop": {
              "type": "string"
            },
            "type": {
              "type": "string",
              "const": "move_to_stop"
            }
          },
          "required": [
            "type",
            "stop"
          ]
        }
      ]
    },
    "DeviceKindMsg": {
      "oneOf": [
        {
          "description": "Advects unattached obstacles whose origin lies inside the zone.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "conveyor"
            },
            "running": {
              "type": "boolean"
            },
            "velocity": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "zone_pose": {
              "$ref": "#/$defs/PoseMsg"
            },
            "zone_size": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "zone_pose",
            "zone_size",
            "velocity",
            "running"
          ]
        },
        {
          "description": "Moves the listed obstacles along `axis` within `range`; `stops`\nare its named positions (each a `<axis>/<stop>` input lane).",
          "type": "object",
          "properties": {
            "axis": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "kind": {
              "type": "string",
              "const": "linear_axis"
            },
            "objects": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "position": {
              "type": "number",
              "format": "double"
            },
            "range": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 2,
              "minItems": 2
            },
            "speed": {
              "type": "number",
              "format": "double"
            },
            "stops": {
              "type": "array",
              "default": [],
              "items": {
                "$ref": "#/$defs/LiftStopMsg"
              }
            }
          },
          "required": [
            "kind",
            "objects",
            "axis",
            "speed",
            "position",
            "range"
          ]
        },
        {
          "description": "Feeds parked carriers onto the line every `interval` seconds.",
          "type": "object",
          "properties": {
            "interval": {
              "type": "number",
              "format": "double"
            },
            "kind": {
              "type": "string",
              "const": "source"
            },
            "park": {
              "$ref": "#/$defs/PoseMsg"
            },
            "pitch": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "pool": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "pose": {
              "$ref": "#/$defs/PoseMsg"
            },
            "running": {
              "type": "boolean"
            }
          },
          "required": [
            "kind",
            "pool",
            "park",
            "pitch",
            "pose",
            "interval",
            "running"
          ]
        },
        {
          "description": "Returns carriers that reach its zone to `source`'s magazine.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "sink"
            },
            "source": {
              "type": "string"
            },
            "zone_pose": {
              "$ref": "#/$defs/PoseMsg"
            },
            "zone_size": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "zone_pose",
            "zone_size",
            "source"
          ]
        },
        {
          "description": "A guided transport vehicle: drives station to station along an\nauthored path, carrying its body obstacles rigidly.",
          "type": "object",
          "properties": {
            "aerial": {
              "description": "Aerial drive: present means the machine flies its legs (z free,\neach axis at its own rate); `allow_reverse` / `max_grade` are\nthen unused.",
              "anyOf": [
                {
                  "$ref": "#/$defs/AerialMsg"
                },
                {
                  "type": "null"
                }
              ]
            },
            "allow_reverse": {
              "description": "May it drive a leg backwards instead of turning around for it?",
              "type": "boolean"
            },
            "body": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "holonomic": {
              "description": "Holonomic drive: the machine translates while holding its\nheading — no pivot turns. `allow_reverse` is then unused;\n`max_grade` still applies (it is a ground drive).",
              "type": "boolean"
            },
            "kind": {
              "type": "string",
              "const": "vehicle"
            },
            "max_grade": {
              "description": "Steepest grade the drive may climb, as rise over horizontal run\n(0.10 = 10 %). Absent means level paths only.",
              "type": [
                "number",
                "null"
              ],
              "format": "double"
            },
            "path": {
              "$ref": "#/$defs/VehiclePathMsg"
            },
            "speed": {
              "type": "number",
              "format": "double"
            },
            "start": {
              "type": "string"
            },
            "tray": {
              "description": "Load deck in the vehicle frame: pose plus full size.",
              "anyOf": [
                {
                  "$ref": "#/$defs/VehicleTrayMsg"
                },
                {
                  "type": "null"
                }
              ]
            },
            "turn_speed": {
              "type": "number",
              "format": "double"
            }
          },
          "required": [
            "kind",
            "path",
            "body",
            "speed",
            "turn_speed",
            "start"
          ]
        },
        {
          "description": "A lift (elevator): the car obstacles ride along `axis` between\nnamed stops, carrying what the capture zone held at dispatch —\nloose parts by origin, vehicles whole.",
          "type": "object",
          "properties": {
            "axis": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "car": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "kind": {
              "type": "string",
              "const": "lift"
            },
            "speed": {
              "type": "number",
              "format": "double"
            },
            "start": {
              "type": "string"
            },
            "stops": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/LiftStopMsg"
              }
            },
            "zone_pose": {
              "description": "Capture zone at the reference position (`position = 0`).",
              "$ref": "#/$defs/PoseMsg"
            },
            "zone_size": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "car",
            "zone_pose",
            "zone_size",
            "axis",
            "speed",
            "stops",
            "start"
          ]
        }
      ]
    },
    "DeviceMsg": {
      "description": "A scripted auxiliary device commanded from sequences.",
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/DeviceKindMsg"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "kind"
      ]
    },
    "Electrical": {
      "description": "Electrical facts about a channel or a field device: what a lint can\ncompare when both sides state them.",
      "type": "object",
      "properties": {
        "logic": {
          "description": "The sensor output type a channel accepts / a device provides. Not\n\"sink\"/\"source\": those words swap meaning between vendors.",
          "anyOf": [
            {
              "$ref": "#/$defs/Logic"
            },
            {
              "type": "null"
            }
          ]
        },
        "voltage": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        }
      }
    },
    "FaultMsg": {
      "description": "A forced input of a scenario: a contact stuck at a value, or an open\nwire (input level low — the functional value follows the binding's\npolarity).",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "stuck"
            },
            "target": {
              "type": "string"
            },
            "value": {
              "type": "boolean"
            }
          },
          "required": [
            "kind",
            "target",
            "value"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "open"
            },
            "target": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "target"
          ]
        },
        {
          "description": "An I/O node dropped off: every input wired on it opens.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "node_down"
            },
            "target": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "target"
          ]
        }
      ]
    },
    "FlashKindMsg": {
      "type": "string",
      "enum": [
        "flash",
        "trace",
        "spray"
      ]
    },
    "FlashMsg": {
      "description": "A weld-flash binding: while `signal` is true, draw an arc flash at\n`robot`'s TCP.",
      "type": "object",
      "properties": {
        "cone": {
          "description": "Spray-cone size, `[length, radius]` in meters (spray effects only).",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 2,
          "minItems": 2
        },
        "kind": {
          "description": "Rendering kind; absent in older payloads (= flash).",
          "$ref": "#/$defs/FlashKindMsg",
          "default": "flash"
        },
        "name": {
          "type": "string"
        },
        "robot": {
          "type": "string"
        },
        "signal": {
          "type": "string"
        },
        "spin_link": {
          "description": "Link spun visually while the signal is on (cut traces).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "required": [
        "name",
        "signal",
        "robot"
      ]
    },
    "FrameMsg": {
      "description": "A named world-frame pose (mount point / teach reference).",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "pose": {
          "$ref": "#/$defs/PoseMsg"
        }
      },
      "required": [
        "name",
        "pose"
      ]
    },
    "GaitLegMsg": {
      "type": "object",
      "properties": {
        "contact": {
          "description": "`point`, `sole` or `sole_yaw_free`.",
          "type": "string"
        },
        "foot": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "foot",
        "contact"
      ]
    },
    "GaitMsg": {
      "description": "[`crate::seq::GaitSpec`] as a project carries it.",
      "type": "object",
      "properties": {
        "arm_swing": {
          "description": "`(joint, amplitude)` swung in step with the first leg.",
          "type": "array",
          "items": {
            "type": "array",
            "maxItems": 2,
            "minItems": 2,
            "prefixItems": [
              {
                "type": "string"
              },
              {
                "type": "number",
                "format": "double"
              }
            ]
          }
        },
        "bob": {
          "type": "number",
          "format": "double",
          "default": 0.0
        },
        "body_link": {
          "type": [
            "string",
            "null"
          ]
        },
        "foot_radius": {
          "type": "number",
          "format": "double",
          "default": 0.0
        },
        "lateral": {
          "type": "number",
          "format": "double",
          "default": 0.0
        },
        "legs": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/GaitLegMsg"
          }
        },
        "lift": {
          "type": "number",
          "format": "double"
        },
        "max_step": {
          "description": "Tallest step the machine may take, metres (see `GaitSpec::max_step`).",
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "max_stride": {
          "type": "number",
          "format": "double"
        },
        "pattern": {
          "$ref": "#/$defs/GaitPatternMsg"
        },
        "period": {
          "type": "number",
          "format": "double"
        },
        "stance": {
          "description": "The standing configuration, `(joint, value)`.",
          "type": "array",
          "items": {
            "type": "array",
            "maxItems": 2,
            "minItems": 2,
            "prefixItems": [
              {
                "type": "string"
              },
              {
                "type": "number",
                "format": "double"
              }
            ]
          }
        }
      },
      "required": [
        "legs",
        "pattern",
        "period",
        "lift",
        "stance",
        "max_stride"
      ]
    },
    "GaitPatternMsg": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "walk"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "trot"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "biped"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "duty": {
              "type": "number",
              "format": "double"
            },
            "kind": {
              "type": "string",
              "const": "custom"
            },
            "phases": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              }
            }
          },
          "required": [
            "kind",
            "duty",
            "phases"
          ]
        }
      ]
    },
    "GeometryMsg": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "box"
            },
            "size": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "size"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "cylinder"
            },
            "length": {
              "type": "number",
              "format": "double"
            },
            "radius": {
              "type": "number",
              "format": "double"
            }
          },
          "required": [
            "kind",
            "radius",
            "length"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "sphere"
            },
            "radius": {
              "type": "number",
              "format": "double"
            }
          },
          "required": [
            "kind",
            "radius"
          ]
        },
        {
          "type": "object",
          "properties": {
            "ext": {
              "description": "Lower-case file extension so the client can pick a loader.",
              "type": "string"
            },
            "kind": {
              "type": "string",
              "const": "mesh"
            },
            "scale": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "url": {
              "description": "URL the studio can fetch the mesh from (e.g. `/meshes/0`).",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "url",
            "ext",
            "scale"
          ]
        }
      ]
    },
    "GripperDriveMsg": {
      "description": "A gripper drive as a project carries it: the declaration, re-resolved\nagainst the model on load (empty `joints` = derived from the tool\nmount, `None` numbers = the kind-scaled defaults).",
      "type": "object",
      "properties": {
        "damping": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "finger_mass": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "joints": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "max_force": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "stiffness": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        }
      }
    },
    "IoBinding": {
      "description": "One point wired to one channel. `(point, node)` is the key: a signal\nfanning out to two controllers is two bindings.",
      "type": "object",
      "properties": {
        "auto": {
          "description": "Placed by `auto_assign_io` rather than by hand. Hand bindings are\nnever moved; `auto_assign_io(reassign)` drops only these.",
          "type": "boolean"
        },
        "channel": {
          "type": "string"
        },
        "contact": {
          "anyOf": [
            {
              "$ref": "#/$defs/Contact"
            },
            {
              "type": "null"
            }
          ]
        },
        "device": {
          "description": "The field device's electrical facts (voltage, output type).",
          "anyOf": [
            {
              "$ref": "#/$defs/Electrical"
            },
            {
              "type": "null"
            }
          ]
        },
        "field": {
          "description": "The field device on the far end (`\"BEAM1\"`, `\"-B1\"`, `\"YV1\"`) —\nthe connection table's other column.",
          "type": [
            "string",
            "null"
          ]
        },
        "invert": {
          "description": "Logical inversion between the cell's value and the wire level.\nProjection only: the URScript lowering inverts its tests and\nwrites; the bake is untouched.",
          "type": "boolean"
        },
        "node": {
          "type": "string"
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        },
        "point": {
          "$ref": "#/$defs/IoPointId"
        },
        "safety": {
          "type": "boolean"
        },
        "tag": {
          "description": "PLC tag / symbol.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "point",
        "node",
        "channel"
      ]
    },
    "IoChannel": {
      "type": "object",
      "properties": {
        "address": {
          "description": "PLC address as the controller spells it (`\"%IX0.2\"`, `\"X02\"`) —\na string; dialects live in the Python templates.",
          "type": [
            "string",
            "null"
          ]
        },
        "electrical": {
          "anyOf": [
            {
              "$ref": "#/$defs/Electrical"
            },
            {
              "type": "null"
            }
          ]
        },
        "id": {
          "description": "Node-unique id, `\"DI2\"`.",
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/ChannelKind"
        },
        "port": {
          "description": "The vendor's standard-I/O number, when the channel is one — what\nthe URScript lowering writes (`set_standard_digital_out(port, …)`).",
          "type": [
            "integer",
            "null"
          ],
          "format": "uint32",
          "minimum": 0
        }
      },
      "required": [
        "id",
        "kind"
      ]
    },
    "IoDecl": {
      "description": "An exception to the derivation, or an unmodelled point.",
      "type": "object",
      "properties": {
        "kind": {
          "anyOf": [
            {
              "$ref": "#/$defs/ChannelKind"
            },
            {
              "type": "null"
            }
          ]
        },
        "name": {
          "type": "string"
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        },
        "pair": {
          "description": "The other channel of a two-channel safety input.",
          "type": [
            "string",
            "null"
          ]
        },
        "role": {
          "anyOf": [
            {
              "$ref": "#/$defs/DeclRole"
            },
            {
              "type": "null"
            }
          ]
        },
        "safety": {
          "type": "boolean"
        }
      },
      "required": [
        "name"
      ]
    },
    "IoDirection": {
      "description": "Which way a point faces, from the host's point of view.",
      "type": "string",
      "enum": [
        "input",
        "output"
      ]
    },
    "IoMap": {
      "description": "The assignment layer of a scene: nodes, bindings, declarations. Stored\non the scene and in the project; everything else in this module is\nderived from it.",
      "type": "object",
      "properties": {
        "bindings": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/IoBinding"
          }
        },
        "decls": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/IoDecl"
          }
        },
        "nodes": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/IoNode"
          }
        }
      }
    },
    "IoNode": {
      "description": "The controller box a program runs on and the channels it owns.",
      "type": "object",
      "properties": {
        "channels": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/IoChannel"
          }
        },
        "kind": {
          "$ref": "#/$defs/IoNodeKind"
        },
        "model": {
          "description": "Part number / model (optional, a table column).",
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": "string"
        },
        "place": {
          "description": "A frame or obstacle name marking where the box stands (optional,\ndisplay only).",
          "type": [
            "string",
            "null"
          ]
        },
        "programs": {
          "description": "Sequences this node executes (`Plc` / `RobotController` only).\nPrograms listed nowhere are placed implicitly (one robot driven →\nthat robot's controller, none or several → `<cell>`).",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "uplink": {
          "description": "The controller this node's I/O belongs to (a remote I/O station or\na safety module hangs off a PLC). Beyond that ownership, a label:\nno bus semantics.",
          "anyOf": [
            {
              "$ref": "#/$defs/Uplink"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "name",
        "kind"
      ]
    },
    "IoNodeKind": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "plc"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "safety_plc"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "remote_io"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "description": "A robot controller, possibly driving several arms (a two-arm\nstation on one cabinet).",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "robot_controller"
            },
            "robots": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "kind",
            "robots"
          ]
        },
        {
          "description": "Anything else — documentation only, cannot host programs or take\nbindings.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "other"
            },
            "label": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "label"
          ]
        }
      ]
    },
    "IoPointId": {
      "description": "`(name, aspect, direction)` — with the host, the identity of a point.",
      "type": "object",
      "properties": {
        "aspect": {
          "anyOf": [
            {
              "$ref": "#/$defs/Aspect"
            },
            {
              "type": "null"
            }
          ]
        },
        "direction": {
          "$ref": "#/$defs/IoDirection"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "direction"
      ]
    },
    "IoRef": {
      "description": "A reference into the existing I/O map. The assigned channel is derived.",
      "type": "object",
      "properties": {
        "aspect": {
          "anyOf": [
            {
              "$ref": "#/$defs/Aspect"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "direction": {
          "$ref": "#/$defs/IoDirection"
        },
        "node": {
          "type": "string"
        },
        "point": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "point",
        "direction",
        "node"
      ]
    },
    "LegendMsg": {
      "description": "A colour key for an obstacle whose colours mean something.",
      "type": "object",
      "properties": {
        "stops": {
          "description": "Swatches top to bottom; an empty label is a swatch with no text.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/LegendStopMsg"
          }
        },
        "title": {
          "type": "string"
        }
      },
      "required": [
        "title",
        "stops"
      ]
    },
    "LegendStopMsg": {
      "type": "object",
      "properties": {
        "color": {
          "description": "Linear RGB, like every colour on the wire.",
          "type": "array",
          "items": {
            "type": "number",
            "format": "float"
          },
          "maxItems": 3,
          "minItems": 3
        },
        "label": {
          "type": "string"
        }
      },
      "required": [
        "color",
        "label"
      ]
    },
    "LidarMountMsg": {
      "description": "What a LiDAR scanner is bolted to.",
      "oneOf": [
        {
          "description": "A fixture; the pose is a world pose.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "world"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "description": "Rides a vehicle device.",
          "type": "object",
          "properties": {
            "device": {
              "type": "string"
            },
            "kind": {
              "type": "string",
              "const": "vehicle"
            }
          },
          "required": [
            "kind",
            "device"
          ]
        },
        {
          "description": "Bolted to a robot link.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "link"
            },
            "link": {
              "type": "string"
            },
            "robot": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "robot",
            "link"
          ]
        }
      ]
    },
    "LidarMsg": {
      "description": "A LiDAR scanner: a named scan origin with a planar sweep. Presentation\nonly — it publishes no signal (a field sensor referencing it is the\nplanned signal path).",
      "type": "object",
      "properties": {
        "channels": {
          "description": "Vertical channels (scan rings); `1` is a planar scanner.",
          "type": "integer",
          "format": "uint32",
          "default": 1,
          "minimum": 0
        },
        "fov_deg": {
          "description": "Full scan angle, degrees, up to 360.",
          "type": "number",
          "format": "double"
        },
        "mount": {
          "$ref": "#/$defs/LidarMountMsg"
        },
        "name": {
          "type": "string"
        },
        "pose": {
          "description": "Offset in the mount frame (world pose for a `world` mount). The\nscan plane is local XY, angle 0 along +X, CCW toward +Y (the ROS\nlaser frame).",
          "$ref": "#/$defs/PoseMsg"
        },
        "range": {
          "description": "Measuring range `[min, max]`, meters.",
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 2,
          "minItems": 2
        },
        "resolution_deg": {
          "description": "Angular resolution, degrees (the scan API's default step).",
          "type": "number",
          "format": "double"
        },
        "vfov_deg": {
          "description": "Full vertical field of view, degrees, centered on the scan\nplane; `0` for a planar scanner.",
          "type": "number",
          "format": "double",
          "default": 0.0
        }
      },
      "required": [
        "name",
        "mount",
        "pose",
        "fov_deg",
        "range",
        "resolution_deg"
      ]
    },
    "LiftStopMsg": {
      "description": "One named stop of a lift, metres along its axis from the reference.",
      "type": "object",
      "properties": {
        "at": {
          "type": "number",
          "format": "double"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "at"
      ]
    },
    "LinkMaterialMsg": {
      "description": "One link's authored contact material.",
      "type": "object",
      "properties": {
        "friction": {
          "type": "number",
          "format": "double"
        },
        "link": {
          "type": "string"
        },
        "restitution": {
          "type": "number",
          "format": "double"
        }
      },
      "required": [
        "link",
        "friction",
        "restitution"
      ]
    },
    "Logic": {
      "type": "string",
      "enum": [
        "pnp",
        "npn"
      ]
    },
    "MaterialMsg": {
      "description": "Metalness/roughness, the pair every viewer botrail hands a scene to\nalready speaks (glTF, USD Preview Surface, three.js).",
      "type": "object",
      "properties": {
        "metalness": {
          "type": "number",
          "format": "float"
        },
        "roughness": {
          "type": "number",
          "format": "float"
        }
      },
      "required": [
        "metalness",
        "roughness"
      ]
    },
    "Medium": {
      "type": "string",
      "enum": [
        "power",
        "pneumatic",
        "signal",
        "network"
      ]
    },
    "MotionMsg": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "robot": {
          "description": "Owning robot instance name; `None` means the first robot.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "segments": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SegmentMsg"
          }
        }
      },
      "required": [
        "name",
        "segments"
      ]
    },
    "ObstacleMsg": {
      "type": "object",
      "properties": {
        "attached_to": {
          "description": "Present while the obstacle is attached to (grasped by) a robot link.",
          "anyOf": [
            {
              "$ref": "#/$defs/AttachmentMsg"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "color": {
          "description": "Display colour, linear RGB, from the scene file's\n`primvars:displayColor`. Absent means \"no authored appearance\": the\nstudio then draws the obstacle as a neutral collision proxy.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "number",
            "format": "float"
          },
          "maxItems": 3,
          "minItems": 3
        },
        "enabled": {
          "description": "Disabled obstacles render but are excluded from collision checking.",
          "type": "boolean",
          "default": true
        },
        "geometry": {
          "$ref": "#/$defs/GeometryMsg"
        },
        "legend": {
          "description": "What the colours mean, when they mean something (a film map's\nmicron ramp). Absent for ordinary scenery.",
          "anyOf": [
            {
              "$ref": "#/$defs/LegendMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "material": {
          "description": "How the surface takes light. Absent means \"no authored appearance\",\nsame as `color`: the studio picks. Projects written before this\nexisted simply have no material, which is the same thing.",
          "anyOf": [
            {
              "$ref": "#/$defs/MaterialMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "name": {
          "type": "string"
        },
        "physics": {
          "description": "Authored physics properties. Absent for ordinary scenery — files\nwritten before this existed simply have none, which is what they\nmeant.",
          "anyOf": [
            {
              "$ref": "#/$defs/PhysicsMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "pose": {
          "description": "World pose.",
          "$ref": "#/$defs/PoseMsg"
        },
        "visible": {
          "description": "Invisible obstacles still collide; they are simply not drawn. Files\nwritten before this existed have every obstacle visible, which is\nwhat they meant.",
          "type": "boolean",
          "default": true
        },
        "walkable": {
          "description": "A walkable top face — a stair tread, a mezzanine slab: footfalls of\na walking machine snap onto it. Files written before this existed\nhave no walkable surfaces, which is what they meant.",
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "geometry",
        "pose"
      ]
    },
    "Part": {
      "description": "What a resident *is*: identity and count, nothing else. Every field is\noptional so a part can be as thin as `model=\"GVL-1200\"` and still land\non the BOM; the catalog reference is what `Robot.from_catalog` fills\nin by itself.",
      "type": "object",
      "properties": {
        "attributes": {
          "description": "Free attributes, summed by [`Bom::total`] when numeric. botrail\nnever validates them.",
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/PartAttr"
          }
        },
        "catalog": {
          "anyOf": [
            {
              "$ref": "#/$defs/CatalogRef"
            },
            {
              "type": "null"
            }
          ]
        },
        "category": {
          "description": "BOM category (`conveyor`, `sensor.photoelectric`, `structure.fence`,\n...). Overrides the category the resident's kind implies.",
          "type": [
            "string",
            "null"
          ]
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "manufacturer": {
          "type": [
            "string",
            "null"
          ]
        },
        "model": {
          "description": "Model / part number.",
          "type": [
            "string",
            "null"
          ]
        },
        "qty": {
          "description": "How many of them this target stands for. One resident is one part\nby default; a group generated as twelve fence panels says 12.",
          "type": "integer",
          "format": "uint32",
          "default": 1,
          "minimum": 0
        }
      }
    },
    "PartAttr": {
      "description": "A free part attribute: a number (summable — `mass_kg`, `power_w`, a\nprice) or a text (`ip_rating`, a note). Untagged on the wire, so a\nproject file reads `{\"mass_kg\": 45, \"finish\": \"RAL 7035\"}`.",
      "anyOf": [
        {
          "type": "number",
          "format": "double"
        },
        {
          "type": "string"
        }
      ]
    },
    "PartEntry": {
      "description": "One authored part pinning: `part` describes the resident (or group)\n`target` of kind `kind`.",
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/PartTargetKind"
        },
        "part": {
          "$ref": "#/$defs/Part"
        },
        "target": {
          "type": "string"
        }
      },
      "required": [
        "target",
        "kind",
        "part"
      ]
    },
    "PartTargetKind": {
      "description": "Which kind of resident a part entry is pinned to. Kinds have separate\nname spaces (a conveyor device and its belt slab may both be `belt`),\nso the kind is part of the key.",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "robot",
            "obstacle",
            "sensor",
            "device",
            "io_node"
          ]
        },
        {
          "description": "Every obstacle under `<target>/` — an imported subtree, a generated\nassembly. Counted as one line.",
          "type": "string",
          "const": "group"
        },
        {
          "description": "A camera fixture (the purchasable `sensor.camera` article).",
          "type": "string",
          "const": "camera"
        },
        {
          "description": "A LiDAR scanner (the purchasable `sensor.lidar` article).",
          "type": "string",
          "const": "lidar"
        },
        {
          "description": "An end-effector in a robot's tool stack, by the name its BOM row\ncarries (`<robot>/tool`, `<robot>/tool2`, `<robot>/tool/tool3` for\na tool that is itself a stack). A catalog tool brings its own\nidentity; a made one — a bracket welded on from a URDF string —\ngets it pinned here.",
          "type": "string",
          "const": "tool"
        }
      ]
    },
    "PathTargetMsg": {
      "description": "Serialized form (project files, Python boundary). Positions and axes in\nthe part frame; `spin` optional.",
      "type": "object",
      "properties": {
        "position": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 3,
          "minItems": 3
        },
        "spin": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "tool_axis": {
          "type": "array",
          "default": [
            0.0,
            0.0,
            1.0
          ],
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 3,
          "minItems": 3
        }
      },
      "required": [
        "position"
      ]
    },
    "Pattern": {
      "description": "The footprint an applicator lays on its reference plane, as a *shape*:\nthe integrator normalizes whatever this returns so it integrates to\none over the plane, and [`Applicator::flow`] supplies the magnitude.\nCoordinates are meters on the reference plane, origin on the spray\naxis, `u` across the fan and `w` along it.",
      "oneOf": [
        {
          "description": "Elliptic dual-beta — the literature's standard fit for a flat-fan\nair gun: thick down the middle, thin at the edges, and the extent\nalong `w` narrows toward the ends of the fan. `beta = 1` is a flat\ntop hat, larger is more peaked.",
          "type": "object",
          "properties": {
            "beta_across": {
              "type": "number",
              "format": "double"
            },
            "beta_along": {
              "type": "number",
              "format": "double"
            },
            "height": {
              "description": "Full fan height along (`w` extent at mid-fan) [m].",
              "type": "number",
              "format": "double"
            },
            "kind": {
              "type": "string",
              "const": "dual_beta"
            },
            "width": {
              "description": "Full fan width across (`u` extent) [m].",
              "type": "number",
              "format": "double"
            }
          },
          "required": [
            "kind",
            "width",
            "height",
            "beta_across",
            "beta_along"
          ]
        },
        {
          "description": "Axisymmetric cone — a rotary bell or a round nozzle. `beta = 1` is\na flat disc, 2 is parabolic.",
          "type": "object",
          "properties": {
            "beta": {
              "type": "number",
              "format": "double"
            },
            "diameter": {
              "type": "number",
              "format": "double"
            },
            "kind": {
              "type": "string",
              "const": "round"
            }
          },
          "required": [
            "kind",
            "diameter",
            "beta"
          ]
        },
        {
          "description": "A measured static-pattern profile, sampled radially: what a shop\nactually has after spraying a coupon for a fixed time at a fixed\ndistance. `radii` ascending from 0, `weight` the film measured\nthere (any unit — only the shape survives normalization).",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "measured"
            },
            "radii": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              }
            },
            "weight": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              }
            }
          },
          "required": [
            "kind",
            "radii",
            "weight"
          ]
        }
      ]
    },
    "PhysicsMsg": {
      "description": "Authored physics of an obstacle (design-physics.md). Inert on a\nkinematic bake: absent props — and present ones, when `physics` is not\nrequested — leave the obstacle the static body it always was.",
      "type": "object",
      "properties": {
        "angular_damping": {
          "type": "number",
          "format": "double"
        },
        "ccd": {
          "description": "Continuous collision detection, for small fast parts.",
          "type": "boolean"
        },
        "dynamic": {
          "description": "A dynamic body: gravity, contact and friction own its pose during a\nphysics bake. (Kinematic bodies are rollout-internal — robot links,\ndevice-driven scenery — and never authored on an obstacle.)",
          "type": "boolean"
        },
        "friction": {
          "type": "number",
          "format": "double"
        },
        "linear_damping": {
          "type": "number",
          "format": "double"
        },
        "mass": {
          "description": "Total mass in kg; absent derives it from the collision shape's\nvolume at 1000 kg/m³.",
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "restitution": {
          "type": "number",
          "format": "double"
        }
      },
      "required": [
        "dynamic",
        "friction",
        "restitution",
        "linear_damping",
        "angular_damping"
      ]
    },
    "Port": {
      "type": "object",
      "properties": {
        "io": {
          "anyOf": [
            {
              "$ref": "#/$defs/IoRef"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "medium": {
          "$ref": "#/$defs/Medium"
        },
        "name": {
          "type": "string"
        },
        "reference": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "required": {
          "type": "boolean"
        },
        "role": {
          "$ref": "#/$defs/Role"
        },
        "specs": {
          "$ref": "#/$defs/Specs",
          "default": {
            "capacity_a": null,
            "capacity_l_min": null,
            "current_a": null,
            "flow_l_min": null,
            "flow_reference": null,
            "logic": null,
            "pressure_bar": null,
            "pressure_max_bar": null,
            "pressure_min_bar": null,
            "protocol": null,
            "signal_type": null,
            "voltage_max_v": null,
            "voltage_min_v": null,
            "voltage_v": null
          }
        },
        "target": {
          "type": "string"
        },
        "target_kind": {
          "$ref": "#/$defs/PartTargetKind"
        },
        "terminal": {
          "description": "External drawing/terminal reference only; never a channel address.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "target",
        "target_kind",
        "medium",
        "role",
        "required"
      ]
    },
    "PoseMsg": {
      "description": "Position + quaternion (x, y, z, w), in meters / world frame unless noted.",
      "type": "object",
      "properties": {
        "position": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 3,
          "minItems": 3
        },
        "quaternion": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 4,
          "minItems": 4
        }
      },
      "required": [
        "position",
        "quaternion"
      ]
    },
    "ProjectRobotMsg": {
      "description": "One robot in a project.",
      "type": "object",
      "properties": {
        "base_pose": {
          "description": "World pose of the robot's root link.",
          "$ref": "#/$defs/PoseMsg"
        },
        "gripper_drive": {
          "description": "Force-limited gripper drive (`set_gripper_drive`), the friction-\ngrasp declaration. Absent in older files and for undriven robots.",
          "anyOf": [
            {
              "$ref": "#/$defs/GripperDriveMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "joint_positions": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          }
        },
        "link_materials": {
          "description": "Per-link contact materials for physics bakes (`set_link_material`) —\na fingertip's rubber pad. Absent in older files.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/LinkMaterialMsg"
          }
        },
        "mount": {
          "description": "The vehicle this robot rides, if any (absent in older files, and\nfor a robot bolted to the floor).",
          "anyOf": [
            {
              "$ref": "#/$defs/RobotMountMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "name": {
          "description": "Scene-unique instance name; `None` (older files) falls back to the\nmodel name.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "source": {
          "$ref": "#/$defs/RobotSourceMsg"
        }
      },
      "required": [
        "source",
        "base_pose",
        "joint_positions"
      ]
    },
    "RampTargetMsg": {
      "type": "object",
      "properties": {
        "joint": {
          "type": "string"
        },
        "value": {
          "type": "number",
          "format": "double"
        }
      },
      "required": [
        "joint",
        "value"
      ]
    },
    "RobotMountMsg": {
      "description": "A robot riding a vehicle (`Scene::mount_robot`): the device, where the\nbase sits in its frame, and — for a machine whose legs are the robot —\nthe gait that walks it.",
      "type": "object",
      "properties": {
        "device": {
          "type": "string"
        },
        "gait": {
          "anyOf": [
            {
              "$ref": "#/$defs/GaitMsg"
            },
            {
              "type": "null"
            }
          ]
        },
        "offset": {
          "$ref": "#/$defs/PoseMsg"
        },
        "spin": {
          "description": "Presentation: `(joint, rad/s)` spun while the vehicle flies or\nmoves (a multirotor's propellers). Absent in older files.",
          "type": "array",
          "items": {
            "type": "array",
            "maxItems": 2,
            "minItems": 2,
            "prefixItems": [
              {
                "type": "string"
              },
              {
                "type": "number",
                "format": "double"
              }
            ]
          }
        }
      },
      "required": [
        "device",
        "offset"
      ]
    },
    "RobotSourceMsg": {
      "description": "Where a project robot comes from.",
      "oneOf": [
        {
          "description": "URDF XML (xacro already expanded), embedded verbatim.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "urdf"
            },
            "xml": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "xml"
          ]
        },
        {
          "description": "USD stage reference (local path until asset bundling lands). The\napplication layer re-imports it via botrail-usd on load.",
          "type": "object",
          "properties": {
            "articulation_root": {
              "type": "string"
            },
            "kind": {
              "type": "string",
              "const": "usd"
            },
            "path": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "path",
            "articulation_root"
          ]
        },
        {
          "description": "A catalog package (`Robot.from_catalog`): id + pinned revision for\nprovenance and script export, plus the fetched inner source so\nloading needs no network.",
          "type": "object",
          "properties": {
            "category": {
              "type": [
                "string",
                "null"
              ]
            },
            "flange": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "grasp": {
              "description": "Grasp-surface frames (`frames.grasp_frames`), reapplied on\nrebuild. Absent in older files.",
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "id": {
              "type": "string"
            },
            "inner": {
              "$ref": "#/$defs/RobotSourceMsg"
            },
            "kind": {
              "type": "string",
              "const": "catalog"
            },
            "manufacturer": {
              "description": "Manifest identity (maker, product name, category, numeric\nspecs) — what the BOM names the package by. Absent in files\nwritten before parts existed; those lines then show the id only.",
              "type": [
                "string",
                "null"
              ]
            },
            "mount": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "product": {
              "type": [
                "string",
                "null"
              ]
            },
            "revision": {
              "type": "string"
            },
            "specs": {
              "type": "array",
              "items": {
                "type": "array",
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number",
                    "format": "double"
                  }
                ]
              }
            },
            "tcp": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            }
          },
          "required": [
            "kind",
            "id",
            "revision",
            "inner"
          ]
        },
        {
          "description": "A tool welded onto a base robot (`Robot.attach_tool`): both part\nsources plus the weld parameters, so loading re-runs the attach.",
          "type": "object",
          "properties": {
            "base": {
              "$ref": "#/$defs/RobotSourceMsg"
            },
            "flange": {
              "type": "string"
            },
            "kind": {
              "type": "string",
              "const": "composite"
            },
            "mount": {
              "type": "string"
            },
            "offset": {
              "$ref": "#/$defs/PoseMsg"
            },
            "prefix": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "tcp": {
              "type": [
                "string",
                "null"
              ],
              "default": null
            },
            "tool": {
              "$ref": "#/$defs/RobotSourceMsg"
            }
          },
          "required": [
            "kind",
            "base",
            "tool",
            "flange",
            "mount",
            "offset"
          ]
        }
      ]
    },
    "Role": {
      "type": "string",
      "enum": [
        "supply",
        "load",
        "input",
        "output",
        "peer"
      ]
    },
    "ScenarioJointsMsg": {
      "description": "A robot start configuration a scenario overrides.",
      "type": "object",
      "properties": {
        "positions": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          }
        },
        "robot": {
          "type": "string"
        }
      },
      "required": [
        "robot",
        "positions"
      ]
    },
    "ScenarioMsg": {
      "description": "A named initial-state delta — one row of the cell's test-case matrix.\nThe unmodified scene is the reserved scenario `baseline`.",
      "type": "object",
      "properties": {
        "faults": {
          "description": "Inputs forced for the whole run (absent in older files).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/FaultMsg"
          }
        },
        "joints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ScenarioJointsMsg"
          }
        },
        "name": {
          "type": "string"
        },
        "obstacles": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ScenarioObstacleMsg"
          }
        },
        "signals": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ScenarioSignalMsg"
          }
        }
      },
      "required": [
        "name"
      ]
    },
    "ScenarioObstacleMsg": {
      "description": "An obstacle pose a scenario overrides.",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "pose": {
          "$ref": "#/$defs/PoseMsg"
        }
      },
      "required": [
        "name",
        "pose"
      ]
    },
    "ScenarioSignalMsg": {
      "description": "An internal-signal initial value a scenario overrides.",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "value"
      ]
    },
    "SegmentKindMsg": {
      "type": "string",
      "enum": [
        "joint",
        "cartesian_line"
      ]
    },
    "SegmentMsg": {
      "type": "object",
      "properties": {
        "constraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ConstraintMsg"
          }
        },
        "goal_positions": {
          "description": "Goal configuration in DOF order.",
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          }
        },
        "kind": {
          "$ref": "#/$defs/SegmentKindMsg"
        }
      },
      "required": [
        "kind",
        "goal_positions",
        "constraints"
      ]
    },
    "SelectArmMsg": {
      "description": "One arm of a branching step: its guard plus the steps it runs.",
      "type": "object",
      "properties": {
        "condition": {
          "$ref": "#/$defs/ConditionMsg"
        },
        "steps": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/StepMsg"
          }
        }
      },
      "required": [
        "condition",
        "steps"
      ]
    },
    "SensorKindMsg": {
      "oneOf": [
        {
          "description": "Presence/area sensor: ON while a watched body overlaps the box.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "zone"
            },
            "pose": {
              "$ref": "#/$defs/PoseMsg"
            },
            "size": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "pose",
            "size"
          ]
        },
        {
          "description": "Photoelectric beam: ON while the segment is interrupted.",
          "type": "object",
          "properties": {
            "from": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            },
            "kind": {
              "type": "string",
              "const": "beam"
            },
            "radius": {
              "type": "number",
              "format": "double"
            },
            "to": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 3,
              "minItems": 3
            }
          },
          "required": [
            "kind",
            "from",
            "to",
            "radius"
          ]
        },
        {
          "description": "Vision presence: ON while a watched body overlaps the named\ncamera's view frustum (occlusion-tested against other obstacles).",
          "type": "object",
          "properties": {
            "camera": {
              "type": "string"
            },
            "detect_range": {
              "description": "Detection band along the view axis (m); `None` = the camera's\nnear/far clip.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 2,
              "minItems": 2
            },
            "kind": {
              "type": "string",
              "const": "vision"
            },
            "occlusion": {
              "type": "boolean"
            }
          },
          "required": [
            "kind",
            "camera",
            "occlusion"
          ]
        },
        {
          "description": "Laser-scanner field: ON while a watched body crosses the named\nlidar's scan-plane sector (shadow-tested against other obstacles).",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "field"
            },
            "lidar": {
              "type": "string"
            },
            "range": {
              "description": "Field radius (m); `None` = the lidar's max range.",
              "type": [
                "number",
                "null"
              ],
              "format": "double"
            },
            "sector": {
              "description": "Angular window `[start, end]` in the scan frame (deg, 0 = +X,\nCCW); `None` = the lidar's full sweep.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": "number",
                "format": "double"
              },
              "maxItems": 2,
              "minItems": 2
            },
            "shadowing": {
              "type": "boolean"
            }
          },
          "required": [
            "kind",
            "lidar",
            "shadowing"
          ]
        }
      ]
    },
    "SensorMsg": {
      "description": "A pseudo-sensor: geometric test published as a read-only input signal.",
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/SensorKindMsg"
        },
        "mount": {
          "description": "Vehicle this sensor rides on; its geometry is then read in that\nvehicle's frame. `None` (the usual case) is a floor fixture.",
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": "string"
        },
        "watch": {
          "$ref": "#/$defs/SensorWatchMsg"
        }
      },
      "required": [
        "name",
        "kind",
        "watch"
      ]
    },
    "SensorWatchMsg": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "objects"
            },
            "names": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "kind",
            "names"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "all_objects"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "description": "Any robot's links trip it.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "robot"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "description": "Only the named robot instances' links trip it.",
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "robots"
            },
            "names": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "kind",
            "names"
          ]
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "all"
            }
          },
          "required": [
            "kind"
          ]
        }
      ]
    },
    "SequenceMsg": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "steps": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/StepMsg"
          }
        }
      },
      "required": [
        "name",
        "steps"
      ]
    },
    "SignalDefMsg": {
      "description": "A user-defined internal signal (PLC internal relay).",
      "type": "object",
      "properties": {
        "initial": {
          "type": "boolean"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "initial"
      ]
    },
    "Specs": {
      "description": "Values apply to this endpoint in total, not to an inferred BOM quantity.",
      "type": "object",
      "properties": {
        "capacity_a": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "capacity_l_min": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "current_a": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "flow_l_min": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "flow_reference": {
          "description": "The reference conditions used for both flow and capacity numbers.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "logic": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "pressure_bar": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "pressure_max_bar": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "pressure_min_bar": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "protocol": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "signal_type": {
          "description": "digital, safe_digital, analog or word; not a second channel table.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "voltage_max_v": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "voltage_min_v": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "voltage_v": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        }
      },
      "additionalProperties": false
    },
    "StepMsg": {
      "type": "object",
      "properties": {
        "actions": {
          "description": "Entry actions, fired when the step becomes active.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/ActionMsg"
          }
        },
        "name": {
          "type": "string"
        },
        "select": {
          "description": "SFC selection divergence: when non-empty this is a branching step\n— no actions, `transition` unused, the arms' conditions are the\noutgoing transitions (first to hold wins, authored order =\npriority) and every arm rejoins at the next step.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SelectArmMsg"
          }
        },
        "transition": {
          "description": "The step completes when this condition holds.",
          "$ref": "#/$defs/ConditionMsg"
        }
      },
      "required": [
        "name",
        "actions",
        "transition"
      ]
    },
    "ToolMoveMsg": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "targets": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/PathTargetMsg"
              }
            },
            "type": {
              "type": "string",
              "const": "rapid"
            }
          },
          "required": [
            "type",
            "targets"
          ]
        },
        {
          "type": "object",
          "properties": {
            "brush": {
              "description": "Brush name (absent in files written before brushes existed,\nwhich is the same as none).",
              "type": [
                "string",
                "null"
              ]
            },
            "feed": {
              "type": "number",
              "format": "double"
            },
            "targets": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/PathTargetMsg"
              }
            },
            "type": {
              "type": "string",
              "const": "feed"
            }
          },
          "required": [
            "type",
            "feed",
            "targets"
          ]
        }
      ]
    },
    "ToolpathMsg": {
      "type": "object",
      "properties": {
        "frame": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "moves": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ToolMoveMsg"
          }
        },
        "name": {
          "description": "Defaults to empty: the Python boundary passes the name separately\n(`scene.add_toolpath(name, tp)`) and overrides this field.",
          "type": "string",
          "default": ""
        }
      },
      "required": [
        "moves"
      ]
    },
    "Uplink": {
      "type": "object",
      "properties": {
        "bus": {
          "type": [
            "string",
            "null"
          ]
        },
        "parent": {
          "type": "string"
        }
      },
      "required": [
        "parent"
      ]
    },
    "VehiclePathMsg": {
      "description": "An authored vehicle guide path: waypoints on the guidance surface\n(`[x, y, z]` — z is the floor height there) plus named station stops\n(as waypoint indices).",
      "type": "object",
      "properties": {
        "ring": {
          "type": "boolean"
        },
        "stations": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/VehicleStationMsg"
          }
        },
        "waypoints": {
          "description": "Written as `[x, y, z]`; a legacy two-element `[x, y]` is read as\nz = 0, so pre-3D projects and recordings load unchanged.",
          "type": "array",
          "items": {
            "type": "array",
            "items": {
              "type": "number",
              "format": "double"
            },
            "maxItems": 3,
            "minItems": 3
          }
        }
      },
      "required": [
        "waypoints",
        "stations",
        "ring"
      ]
    },
    "VehicleStationMsg": {
      "type": "object",
      "properties": {
        "index": {
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "index"
      ]
    },
    "VehicleTrayMsg": {
      "description": "A vehicle's load deck, in the vehicle frame.",
      "type": "object",
      "properties": {
        "pose": {
          "$ref": "#/$defs/PoseMsg"
        },
        "size": {
          "type": "array",
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 3,
          "minItems": 3
        }
      },
      "required": [
        "pose",
        "size"
      ]
    }
  }
}