构建边界必须可检查

Run 是作者意图,ExecutionPlan 是 Runtime 可以直接执行的不可变投影。组件解析、后端 variant、资源摘要、插件绑定、能力授权和控制优先级都应在启动前确定。

这个原则排除了三类隐式行为:运行中重新读取 YAML、通过 import 顺序改变插件集合、根据当前目录碰巧找到资产。代价是编译阶段更严格,但收益是 CI、部署与复现能够讨论同一份事实。

检查问题时先问:“这个事实是否已经进入 Plan?”如果没有,它就不应成为运行时偶然选择。

稳定身份高于后端句柄。 实体、joint、link、frame、sensor、control group 和资源都使用稳定身份。USD Prim path、MuJoCo 数组下标、PyBullet body ID 或 Python 对象地址可以作为 Provider 内部映射,却不能成为插件之间的公共合同。

稳定身份允许同一个 Mission、Model 或 Teleop 逻辑跨 Provider 工作,也让输出能够在原进程结束后解释。当后端确有额外能力时,应通过显式 capability 暴露,而不是泄漏一整个原生对象图。

运行边界必须可组合

Core、领域插件、Provider 与离线工具的职责划分
Core、领域插件、Provider 与离线工具的职责划分放大查看

Core 负责生命周期、generation、服务、授权、控制准入、Operation 和输出发布。插件负责“任务是什么意思”“模型如何推理”“设备输入怎样映射”“哪些通道需要录制”等领域逻辑。

边界的判断不靠代码目录,而靠权威:能够改变公共世界、控制资源或运行终态的动作必须回到 Core 服务;只在某个业务能力内部成立的概念留在插件。这样插件可以独立版本化,却不能绕过平台安全与时序。

能力按需出现,并遵循最小授权。 安装包不等于启用实例,启用实例也不等于获得全部服务。Compiler 根据插件 Manifest、Run bindings、access 和当前 Provider 能力计算授权;Runtime 只把已批准的 scoped service 放进插件上下文。

可选能力应保持 pay-for-play:未选择 Kinematics、Camera、Record 或 Server 时,不应导入对应 Provider、创建线程、分配 GPU 缓冲或开放网络端口。能力缺失必须是可发现、可分类的状态,而不是首次调用深处的随机异常。

generation 是失效边界。 reset 或 Run 替换后,旧世界的结果不能落到新世界。状态、订阅、Operation、资源 lease 和长时间计算都携带 generation;提交前与发布前重新校验,确保在旧世代计算出的轨迹不会误控制新场景。

调用等待超时只说明调用方不再等待,不等于已经取消 Operation。取消必须显式请求并观察终态。这个区分避免“客户端超时后物理动作仍继续”的隐蔽错误。

执行与失败必须最终收敛

后端世界与应用状态由一个 owner loop 串行化。算法、编码器或网络 I/O 可以在 worker 中执行,但只能通过有界队列、不可变请求和明确结果返回权威边界。

这一原则让状态变更顺序可推理,也提供背压位置。若生产者比消费者快,系统必须定义阻塞、丢弃、降采样或失败策略,不能用无限队列把问题推迟到内存耗尽。

失败要尽早、分类并最终收敛。 能在编译时发现的问题不推迟到运行时;运行时错误包含稳定 code、run/generation 和相关实体或插件上下文;异常路径仍需完成 stop/close 与资源回收。

“返回错误”不是完整失败模型。文档和实现都需要回答:现有 Operation 怎样结束、订阅是否终结、输出 staging 是否清理、Provider 是否仍可关闭、重复 close 是否安全。

输出先校验,再原子发布。 Record 写入的文件只有通过结构或严格完整性检查并完成原子 commit 后,才能成为正式产物。写入失败、校验失败或进程终结不能留下一个看似完整的目标文件。

离线 Viewer、导出器和分析工具不修改源 FSR;派生产物记录源摘要、工具版本、字段映射和验证结果。这样在线实时路径和离线重计算可以独立演进。

配置、指南与参考各自承担不同职责。 概念文档解释为什么存在某个对象及其边界;How-to 指南围绕一个可验证目标给出步骤;Reference 精确列出字段、签名、端点和返回值;示例用于验证组合。把它们分开能避免用户为了查一个字段阅读整篇教程,也避免仅凭 API 名称猜测平台模型。

让原则落实到具体设计

假设 Model 插件读取相机并计算一段机械臂轨迹:

  1. Run 显式选择 Model 实例、机器人 binding 和需要的服务;
  2. Compiler 确认相机与控制能力存在,并把授权写入 Plan;
  3. Runtime 在当前 generation 发布相机数据与 control targets;
  4. Model 在 worker 中推理,不持有后端对象;
  5. 结果返回时重新校验 generation 和目标 revision;
  6. Control 服务验证单位、时序、资源与 authority,返回 Operation;
  7. reset 会使旧推理结果和旧 Operation 失效;
  8. Record 若启用,只通过已授权通道消费数据并事务发布输出。

如果设计需要跳过其中任一边界,例如直接从插件线程写后端 joint target,就说明它正在破坏平台原则。

新增能力前至少回答下面这些问题:

  • 这是 Core 的通用事实、插件领域语义、算法 Provider 还是仿真 Provider 行为?
  • 输入是否进入不可变 Plan,来源是否能解释,资源是否可验证?
  • API 使用什么稳定身份,是否泄漏后端原生句柄?
  • 能力如何发现、授权、撤销,未选择时是否零成本?
  • reset/stop/close 与超时、取消、异常如何收敛?
  • 背压与容量上限在哪里?
  • 哪些结构化证据可以证明它实际运行,而不是只看截图?
  • 参考页、How-to 和示例应该分别记录什么?

继续阅读Run、ExecutionPlan 与 Lock可以看到“声明先编译”如何落地;阅读Plugin 与能力边界可以看到能力发现与最小授权的具体机制。