Architecture¶
Seven layers, one rule.
Layer 6 dashboard/ the operator's console: fleet, cameras, run, record, e-stop, passkeys
Layer 5 tools/ the agent @tool surface (14 tools) and the CLI, over RobotLike only
Layer 4 app/ Robot, PolicyRunner (RTC), teleop, Recorder impl, skills
Layer 3 sim/ policies/ MuJoCo first-class; Isaac + Newton adapters | lerobot_local, groot, cosmos3, mock, rl, microduck, wbc
Layer 2 drivers/ mesh/ 13 native wires + the lerobot driver + twins | Zenoh transport, ACL, audit, e-stop lockout
Layer 1 registry/ declarative robots, policies, embodiments; the factories
Layer 0 core/ six contracts, UnitFrame, errors, types, knobs
A module at layer N imports only from layers below N. Siblings (drivers and mesh,
sim and policies) never import each other. python3 scripts/check_layers.py walks the
import graph and CI fails on any upward edge.
Two consequences:
- Factories live in the registry.
Robot()asksregistry.make_driverormake_simfor a class by import string; a driver never builds a policy, a sim never knows a runner exists. - Tools depend on
RobotLike, never on a concrete robot. An agent cannot reach a serial port except through the facade, where the clamp and the consent live.
Every module above layer 0 implements exactly one of the six contracts.
Heavy imports (mujoco, torch, lerobot, zenoh, vendor SDKs) happen inside the function
that needs them through core.optional.require, so import strands_robots is a no-op on a
bare venv. Why the shape is what it is:
VISION.md.