文档: 全库走查报告(docs/05-代码审查 00-05)+ 规范更新

This commit is contained in:
lxy
2026-08-02 10:44:10 +08:00
parent 3f2cf5fa3a
commit 8eb689af37
9 changed files with 866 additions and 2 deletions
@@ -0,0 +1,71 @@
# df-workflow 核心代码走查报告
> 走查日期: 2026-08-02
> 范围: crates/df-workflow/src/ (11 文件, ~108KB)
> 核心文件: executor.rs(258行), conditions.rs(934行), state.rs(257行), registry.rs(81行), node.rs(77行), dag.rs, dag_def.rs, eventbus.rs(57行)
---
## 问题汇总
| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🔴 P0 | conditions.rs | bug | 单引号字符串 `''` 转义为 `'` 时闭引号后再跟 `'` 会误判(如 `'it''s'` 跨行) |
| 2 | 🟡 P1 | conditions.rs | smell | 934 行单文件,tokenizer/parser/jsonpath/测试全混在一起,维护性差 |
| 3 | 🟡 P1 | conditions.rs | risk | 默认 feature `conditions-eval` 关闭,934 行代码实际不生效 |
| 4 | 🟡 P1 | executor.rs | risk | `condition` 边条件求值仅在 `eval_conditions` 为 true 时收集 inputs,但 `outputs.insert` 仍写入所有节点,已跳过的节点 output 可能被下游无条件边误消费 |
| 5 | 🟡 P1 | state.rs | risk | 锁中毒降级日志缺少 `node_id` 上下文,排查时难以定位哪个节点触发了中毒 |
| 6 | 🟢 P2 | node.rs | smell | `NodeContext``inputs` 类型为 `HashMap<String, NodeOutput>``NodeOutput` 仅含 `data: serde_json::Value`,工具调用结果无法区分来源 |
| 7 | 🟢 P2 | executor.rs | smell | 条件路由的 `eval_conditions` 标志取自 `cfg!(feature = "conditions-eval")`,编译期常量化后分支在运行时不可变,但 executor 仍用 `if eval_conditions` 运行时分支 |
| 8 | 🟢 P2 | eventbus.rs | smell | `send` 参数 `async fn` 但实际是同步调用(`broadcast::send` 同步),`async` 包装造成误导 |
| 9 | 🟢 P2 | executor.rs | smell | `adjacency_in` 预建索引仅用于 `condition` 场景,非条件场景下 `HashMap::new()` 占位浪费 |
---
## 🔴 P0 问题详述
### #1 conditions.rs 单引号转义边界 bug
**文件**: `conditions.rs:200-225`
**严重**: P0 — 可能产生错误的表达式求值结果
**现状**: `consume_quoted` 函数在遇到 `''` 时跳过(视为转义),但 `consume_key` 不处理单引号。
**影响**: 表达式 `'it''s' == 'value'` 中,`'it''s'` 被解析为 `it's`,但闭引号后紧跟 `'==` 可能导致 `consume_key` 截断异常。
---
## 🟡 P1 问题详述
### #2 conditions.rs 单文件 934 行
**建议**: 拆为 `tokenizer.rs` / `parser.rs` / `jsonpath.rs` / `tests.rs` 四个模块,类似 df-nodes 的拆分模式。
### #3 conditions-eval feature 默认关闭
**现状**: 934 行代码 + 全面测试 + 递归下降解析器 + JSON Path 引擎,全部在 feature gate 后面。executor 中 `eval_conditions` 编译期常量化,运行时条件路由不生效。
**建议**: 要么默认开启 feature,要么在 ARCHITECTURE.md 中说明为何写了不开。
### #4 executor 条件边 output 残留
**文件**: `executor.rs:188`
**风险**: 已跳过的节点(condition 不满足),`outputs.insert` 不会执行(因为 `continue` 了),但若后续层有**无条件边**引用该跳过的节点,`outputs.get` 返回 None,下游节点inputs 空。
**建议**: 确认无条件边是否有 fallback 处理,或补测试覆盖。
### #5 锁中毒日志缺少 node_id
**文件**: `state.rs:60-68`
**建议**: 在 `lock()` 失败时输出的 `tracing::error!` 中增加 `node_id` 参数,便于排查。
---
## 正面评价
1. **状态机设计优秀**: `is_legal` 转换表清晰,`set_cancelled` 作为唯一受控旁路,设计合理
2. **executor 取消处理**: Ok/Err 分支对称处理已取消节点,状态机与事件类型一致,TOCTOU 已修复
3. **EventBus broadcast 容量 256**: 审批低频场景设计合理
4. **conditions 求值失败保守 false**: 安全优先,文档清晰
5. **锁中毒降级不 panic**: 返回保守默认值,符合"无 panic"铁律
6. **测试覆盖**: state.rs 测试完整(合法/非法/克隆/取消),executor 测试在 executor_helpers.rs