124 lines
4.7 KiB
Markdown
124 lines
4.7 KiB
Markdown
# 工作流规范: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 校验时强制。
|