chore(repo): initialize team collaboration repository
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# ADR-0003:自研状态机,不用 Temporal / Airflow
|
||||
|
||||
- 日期:2026-07-27
|
||||
- 状态:已接受(方案 §6.1 把 Temporal 列为演进建议,这里给出不在首版用的理由)
|
||||
|
||||
## 背景
|
||||
|
||||
工作流编排有成熟方案:Temporal、Airflow、Prefect、OpenTAP。
|
||||
自研状态机通常是坏味道。
|
||||
|
||||
## 决定
|
||||
|
||||
首版自研:`engine/states.py` 转移表 + `engine/state_machine.py` 纯函数
|
||||
+ `engine/runtime.py` 异步循环。
|
||||
|
||||
## 理由
|
||||
|
||||
产品的差异化恰好落在通用引擎**不管**的那一层:
|
||||
|
||||
1. **恢复语义不是重试。** Temporal 的重试模型是"再调一次这个 activity"。
|
||||
我们需要的是"主机蓝屏了 → 带外触发主板 Reset → 等主机起来 → 验证 DUT
|
||||
重新枚举 → 从检查点续跑"。这是跨越进程、跨越主机生死的物理恢复,
|
||||
不是函数重试。硬套通用引擎会变成在 activity 里塞一堆状态判断,比自研更难维护。
|
||||
|
||||
2. **非幂等步骤默认不重跑。** 通用引擎的默认值是"重试是安全的"。
|
||||
我们这里最贵的一条规则是"刷写步骤挂了**不要**自动重跑"。
|
||||
跟框架默认值对着干,不如自己控制。
|
||||
|
||||
3. **冻结现场是一等状态。** 数据完整性失败要立刻停止一切覆盖性动作并保留现场。
|
||||
通用引擎里这是"失败",我们这里它是一个需要保持很久、等人来看的活状态。
|
||||
|
||||
4. **可测试性。** 纯函数状态机能把"30 秒心跳超时后带外在线则走 L3"这种规则
|
||||
在毫秒内测完。挂在 Temporal 上要起测试环境。后面几波会疯狂往状态机加分支,
|
||||
测试成本是决定性因素。
|
||||
|
||||
5. 依赖成本:Temporal 需要自己的数据库和服务集群。客户是**内网私有化部署**,
|
||||
多一个必须运维的组件就多一个交付摩擦。
|
||||
|
||||
## 代价
|
||||
|
||||
- 跨节点调度、持久化定时器、版本化工作流这些 Temporal 白送的能力要自己做。
|
||||
这一波不需要(单工位),Phase 3 多工位时会痛。
|
||||
- 自研状态机容易随时间腐化成 if 堆。缓解:转移表是纯数据、`decide()` 是纯函数、
|
||||
每加一个分支必须配单测,三条规矩写进了 `docs/state-machine.md §6`。
|
||||
|
||||
## 什么时候推翻
|
||||
|
||||
多工位跨节点调度成为瓶颈时(Phase 3,预计 20+ 工位)。
|
||||
届时替换范围仅限 `runtime.py`——`states.py` / `state_machine.py` / `recovery.py`
|
||||
是纯逻辑,可以原样搬进 Temporal 的 workflow 定义里。
|
||||
这也是把它们做成纯函数的第二个理由。
|
||||
Reference in New Issue
Block a user