# Flow 引擎重写：最终可行性分析

3 轮 agent 调研（R1 现状+方案 / R2 对抗 / R3 收敛）。本报告综合 11 份 evidence。

---

## 一、可行性结论

**CONDITIONAL GO**，前提:
1. Phase 0 跑完 tenancy + formula 函数审计，确认关键未知数
2. 性能卖点正确 framing — **不能宣称"Lead save 变快"**
3. DSL 单 surface（放弃 R1 Hybrid），加 flowlint 静态分析器
4. 3 个 P0 架构风险在 Phase 0 闭环

**真实工作量**: 中位 **75 人日 / ~3-4 月（单工程师）**，可 2 人 5 周完成 MVP。远低于"全部重写 215 flow"直觉估计，因为**过去 30 天真正执行的只有 7 个 flow**。

---

## 二、颠覆性发现

| 原估计 | 真相 | 证据 |
|-------|------|------|
| 215 flow 全部重写 | 真正在跑的 **7 个**，119 个是死库存，59 Paused 全是僵尸 | R2-C 近 30 天 FlowInterview 实查 |
| 新 runtime 能解 Lead save 44s | 不能，99% 是 TiDB Cloud RTT，DSL 不碰 | R2-D 性能分析 |
| Hybrid 双 surface (YAML + JSON-AST) 更好 | 单 surface 更好，双 surface 把 LLM 失败面加倍 | R2-B 29 卡点实测 |
| 扔掉 Wait 可以 | 不可以，业务未来必需；sleep_until 逻辑不复杂，但 Phase 2 实现时需明确 resume 语义（见下方批注） | R2-A P1-3 |
| 19 test-fixture KEEP flow 代表线上 | 19 个全部**零执行历史**，test 假设不等于生产 | R2-C 交叉核查 |

---

## 三、核心方案（3 行概括）

1. **单 YAML source** + canonical JSON IR + Go 原生 interpreter；`old.`/`new.` 明确 ref，`set:` block 显式 per-field transform 覆盖用户核心诉求
2. **12 op-code MVP** (fieldSet/decision/assign/lookup/create/update/delete/action.email/invokeFlow + v2 wait/loop/customError)，18 个 formula 函数子集
3. **Shadow mode 上线** + `FLOW_ENGINE_ROUTE` feature flag，按 flow-name 白名单灰度，30s 回滚；老 engine 冷备 4 周后删除

> **架构批注（来自 Gemini 独立评审）**
>
> **A — Macro 双轨存储**：IR schema 目前只存编译后的展开 AST（纯 13 原语）。Agent 写入 `convertLead` macro 后，若第二次读取 Flow 时只能看到展开后的 5 个原语，macro 带来的"认知减负"在修改阶段彻底失效。
> **修复**：Flow 定义表增加两列，`source_yaml`（Agent 原始写入的高层 YAML，含 macro，用于 Agent 读/改/版本管理）和 `compiled_ir`（展开后纯原语 JSON，专供 Go Executor 加载执行）。**Phase 1 IR schema 定稿前必须确认**。
>
> **B — Fault Path / `onFault` 字段**：当前 IR Step schema 只有 `next` 跳转字段，无错误路由出口。若 `recordCreate` 因 Validation Rule 失败，流只能直接 panic，无法走到"发报错通知"等容错分支（Salesforce 原生的 fault connector 能力）。
> **修复**：在 IR Step schema 增加可选字段 `onFault: "<step_id>"`；`fail` opcode 同时作为显式抛错节点。R3-A DSL spec 需同步更新。**Phase 1 实现时一并加入，工作量小**。
>
> **C — Wait Resume 语义（Phase 2 待定项）**：flow 在 wait 节点暂停时，ContextData 只需序列化用户定义的变量（assign/lookup 产生的值）；record 上下文（`new.`/`old.`）**不序列化，resume 时重新 fetch**。理由：wait 通常是长程等待（数天至数月），序列化快照会造成 resume 后操作过期数据；re-fetch 能拿到真实当前状态，语义更正确。实现时需处理 record 已被删除的边界情况（resume 直接 fail + 记录原因）。

---

## 四、3 个必须修的 P0（Phase 0 gate）

### P0-1 — Cascade depth guard
- 现代码 0 防护（`executor_helpers.go:13-50` 仅追同 goroutine 的 subflow）
- Flow A → publishRecordEvent → Flow B → 写回 → 死循环
- 修复：IR 顶层 `cascadeDepth` + `originChain` ctx 透传，硬限 8 层

### P0-2 — 多租户 flow 数量真相
- FlowDefinition struct **无 tenant_id 字段**（`salesforce_flow.go:52-64`）
- 如 prod 多租户按 tenant 分化，工时可能翻数倍
- 修复：Phase 0 跑 3 条 SQL（见 R3-B）确认 tenancy 模型

### P0-3 — TypedRecord 替换 map[string]interface{}
- 现 `ContextData map[string]interface{}` + `json.Marshal` 把 int 变 float64
- 写回 DB 触发 system_tables.json VARCHAR 窄化（staging 已出事故，memory 有记录）
- 修复：IR `TypedValue` sum type + protobuf 持久化

---

## 五、核心 DSL 样本

用户原话"对 old_record/new_record 所有 field 如何处理明确"— 直接响应：

```yaml
apiVersion: 1
flow: Lead_BeforeSave_Normalize
object: Lead
trigger:
  type: beforeSave
  events: [create, update]

set:                                   # 每个 field 一条，显式声明
  Email:
    expr: LOWER(TRIM(new.Email))
    reads: [Email]
  Region:
    expr: IF(new.Country = "China", "APAC",
          IF(CONTAINS("US,CA", new.Country), "AMER",
             old.Region))
    reads: [Country, Region]           # 静态可分析的 DAG
  Phone:
    expr: REGEX_REPLACE(new.Phone, "[^0-9]", "")
    reads: [Phone]
```

**agent 友好度**：
- YAML + canonical ref (`old.`/`new.`) + SF-formula + 18 函数白名单
- JSON Schema 约束结构；**flowlint** 静态分析器挡 schema 挡不住的 11 类错误
- 错误消息四段式 `[NF-E-xxx] path:line | what | why | hint` + `--format=json`

---

## 六、Phase 计划（Go-No-Go 门）

| Phase | 目标 | 人日 | 可回滚 | Go-No-Go Tripwire |
|-------|------|------|--------|-------------------|
| 0 | 基线闭环 + free-lunch PR | 8-12 | 是 | prod tenant flow > 500 → 放弃 |
| 1 | 新 runtime + 7 真 flow + shadow | 25-35 | 是 | shadow diff > 5% → 放弃 |
| 2 | 119 library 自动翻译 + 16 workflow rule | 14-20 | 是 | translator 覆盖率 < 85% → 扩 scope |
| 3 | 3 批灰度切主 | 15-20 | 是（30s） | Batch 1 error rate > 2x → rollback |
| 4 | 老 engine 下线 + schema 清理 | 5-10 | 部分 | — |
| **总** | | **67-97** | | |

---

## 七、收益清单（诚实区分）

### ✅ 能兑现
- **代码复杂度下降 30-50%**：删 processMetadataValues / Screen/Loop/Subflow 空壳 / 3 套 MetadataJSON 存储 / FlowDefinition+FlowVersion 两表分裂 / SF Tooling API 280 LOC 等（R1-A TOP-10）
- **Pure-interpreter throughput 50-70%↑**：fmt.Sprintf 换 typed comparator + TriggerIndex O(N)→O(1)（R2-D free-lunch，独立于 DSL 重写）
- **agent 稳定写 flow**：单 surface + canonical + flowlint 三件套同时到位
- **彻底抛 SF 兼容**：apiVersion:1 新 schema, `old./new.` 替代 `$Record__Prior/$Record`
- **统一 CRM 其他模块**：`trigger/formula/validation` 共享 TypedRecord context（不完全统一，formula rewrite 保留）

### ❌ 不能兑现（禁止作为卖点）
- Lead save 44s → 变快：99% 是 DB RTT，interpreter 改写不碰
- "完全统一"trigger/formula runtime：formula engine 不能接 `$` 符号，`$→__Record` rewrite 保留
- Salesforce flow 直接导入：apiVersion:1 破坏性，不兼容 SF metadata API

### ⚠️ 有条件兑现
- Cold cross-object `$Record.Account.Name` 10-50× 加速：需要 static prefetch（可加在新旧 DSL 上，**不必换 DSL**）

---

## 八、3 个最大风险 + 缓解

1. **多租户 flow 漏算** (P0-2)
   - 缓解：Phase 0 必跑 SQL 审计；若 > 500 tenant flow 重新 scope

2. **formula 函数缺失**
   - 缓解：Phase 0 跑 `formula.Engine.Validate` 扫 all-formulas.txt；缺 > 5 函数重估工时，缺 > 10 放弃

3. **Shadow mode 假阴性**（新旧 engine 在某些边界值下偶发不一致）
   - 缓解：shadow 期 ≥2 周，diff 监控 per-flow；任何告警阻塞推进

---

## 九、独立于 DSL 重写的 free-lunch PR

**强烈建议先做**（2-3 人日，独立 PR，独立收益）：

1. **TriggerIndex**: `flow_trigger_listener.go:103` 152-way linear scan → `map[object]map[triggerType][]flow`（15μs → 0.5μs）
2. **Typed comparator**: 替换 `fmt.Sprintf("%v",x)` 操作符比较 → interpreter 单次节省 ~30%
3. **Cascade depth guard**: 即使不重写 DSL，P0-1 也必须加（现代码 0 防护是安全隐患）

这三个 PR 独立上线，不依赖任何 DSL 决策，提供 **10-20% 端到端性能提升**（按 R2-D 估算）。

---

## 十、最终推荐

### 立即动作（本周）
1. 跑 Phase 0 tenancy SQL（30 分钟）— 确认是否继续规划
2. 起 free-lunch PR（P0-1 cascade guard + TriggerIndex + typed comparator）— 独立收益

### 2 周后决定
基于 Phase 0 数据 + PoC benchmark + formula 审计，做 Go-No-Go。

### 如果 GO
按 Phase 1-4 执行，总 3-4 月单人或 5-6 周双人。
MVP 仅兼容 7 个 Scheduled flow，其它 208 个 flow 在 Phase 2 自动迁移。
老 engine 冷备 4 周 soak 期后删除。

### 如果 NO-GO
3 个 free-lunch PR 仍然有价值，不浪费。
保留调研产出（`/tmp/flow-research/*.md`）为未来决策参考。

---

## Evidence 索引

- R1-A: SF 历史包袱 TOP-10 `r1-A-burden-audit.md`
- R1-B: 215 flow 节点频度统计 `r1-B-flow-stats.md`
- R1-C: 业内 AI-friendly workflow DSL 对比 `r1-C-industry-survey.md`
- R1-D: 3 候选 DSL 形态 trade-off `r1-D-dsl-candidates.md`
- R2-A: 3 P0 + 3 P1 + 2 P2 架构风险 `r2-A-arch-attack.md`
- R2-B: 29 卡点 AI 可写性实测 `r2-B-ai-writability.md`
- R2-C: "真 KEEP 7 个"颠覆性发现 `r2-C-migration-audit.md`
- R2-D: 性能 framing + BM1-5 设计 `r2-D-perf-attack.md`
- R3-A: 最终 DSL 规格（canonical 化）`r3-A-dsl-spec.md`
- R3-B: 最终 Runtime 架构（3 P0 缓解）`r3-B-runtime-spec.md`
- R3-C: 4 Phase roadmap + Go-No-Go tripwire `r3-C-roadmap.md`
