# 工作流规范:SOP → 可执行状态机 > **当前状态**:这是待实现的发布与物化规范。数据库中已经保存一份符合该结构的 > 演示工作流,但 `WorkflowSpec` 完整 schema 校验、YAML 发布器与 `planner.py` > 尚未落地;当前 API 不接受任意工作流上传。 工作流必须是**确定性的结构化配置**,不是让大模型自由生成命令(方案 §6.3、§8.1)。 AI 可以把自然语言 SOP 草拟成下面这份 YAML,但**必须过 schema 校验 + 人工发布**才能执行。 目标实现位置:`engine/planner.py`(物化)、`schemas.py::WorkflowSpec`(校验) 与 `workflows/fw-ab-regression.yaml`(版本化示例)。 ## Schema ```yaml key: fw-ab-regression # 全局唯一,改 key = 新工作流 name: 固件 A/B 回归 version: 3 # 每次发布 +1;spec_hash 变了但 version 没变 → 拒绝发布 danger_level: high # none | low | high —— high 需要审批记录 source_sop: "客户X_固件回归SOP_v2.1(脱敏)" params: # 运行时参数,带类型与默认值 loops: {type: int, default: 100, min: 1, max: 10000} dut_serial: {type: string, required: true} host: {type: string, required: true} fw_a: {type: string, required: true} # firmware_artifact id fw_b: {type: string, required: true} resources: # 独占锁,PREFLIGHT 阶段获取,终态释放 - {type: dut, ref: params.dut_serial, exclusive: true} - {type: test_host, ref: params.host, exclusive: true} policy: heartbeat_timeout_s: 30 max_recovery_per_loop: 3 # 单轮循环内恢复超过 3 次 → FREEZE max_recovery_total: 20 on_data_integrity_failure: freeze # 硬编码值,写在这里只是为了显式 steps: - key: preflight type: precheck adapter: safety - key: flash-fw-a type: command adapter: nvme_cli template: nvme_fw_download_commit # 只能引用已注册的签名模板 params: {firmware: "{{ params.fw_a }}", slot: 1, action: 3} danger: high timeout_s: 300 idempotent: false # 默认值,写出来是为了提醒:挂了不自动重跑 checkpoint: true - key: regression-loop type: loop count: "{{ params.loops }}" body: - key: workload type: command adapter: fio template: fio_seq_rw timeout_s: 600 retry: 1 idempotent: true - key: enumeration-check type: device_check adapter: nvme_cli expect: {enumerated: true, link_speed_min: "8GT/s"} - key: integrity-check type: command adapter: shell template: sha256_readback on_fail: freeze # 覆盖默认处置 checkpoint: true # 每轮结束落检查点 - key: report type: report adapter: builtin ``` ## 步骤类型(MVP 六种) | type | 语义 | 备注 | |---|---|---| | `command` | 执行签名命令模板 | 唯一能碰危险动作的类型 | | `device_check` | 设备探针断言(枚举/固件版本/链路速率/SMART) | 不改变设备状态 | | `wait` | 等待固定时长或条件 | 用于温度稳定、上电间隔 | | `loop` | 固定次数循环,body 是步骤列表 | 支持嵌套一层 | | `precheck` | 安全门禁 | 每个工作流的第一步都应该是它 | | `report` | 生成报告与 Evidence Bundle | 内置适配器 | 企业版才加的:`http`、`parallel`、`subflow`、`approval`、`instrument`。 这一波故意不做——`planner.py` 里遇到未知 type 直接拒绝发布,不静默跳过。 ## 模板变量 只支持 `{{ params.X }}` 和 `{{ loop.index }}` 两种插值,**不支持表达式求值**。 理由:能求值就能注入。需要计算的场景写进适配器,不写进 YAML。 ## 版本与 Git 工作流 YAML 进 `workflows/` 目录、进 Git。发布时控制平面记录: ``` workflows.spec 原文 workflows.spec_hash sha256(规范化后的 spec) workflows.version 发布号 ``` `spec_hash` 变了而 `version` 没变 → 拒绝发布。理由:A/B 对比的结论必须能追溯到 **具体哪一版流程**,否则"上周跑的和这周跑的是不是同一个流程"就说不清了。 ## `on_fail` 的可选处置 | 值 | 行为 | |---|---| | `retry`(默认,受 `retry:` 次数约束) | 重试该步骤 | | `fail_run` | 整个 Run 判 FAIL 并结束 | | `continue` | 记失败,继续下一步(用于非关键采集步骤) | | `freeze` | 立刻冻结现场,停止一切覆盖性动作 | | `recover` | 进入恢复阶梯 | 数据完整性相关的步骤**只允许** `freeze`,planner 校验时强制。