重构: 文档汇总+进度看板+孤儿任务清理脚本+gitignore 噪音排除

- docs/02 架构设计: 新增 aichat审查/异步审批构想/流式渲染调研/generating状态机/密钥迁移健壮性/工作流脚本执行边界/条件表达式引擎/F-07 trait下沉/Agent架构说明/任务推进构想/功能创意池;更新功能决策记录+归档/对抗论证/文档记录规范/经验记录
- docs/03 模块文档: 新增 AI对话引擎/DAG引擎详解;更新 df-knowledge/df-nodes/df-storage/df-workflow/df-ai
- docs/05 代码审查: 新增 全栈审查/全局review/架构审查/近期改动审查/工作区多角度走查/自研memo流式渲染审查
- docs/09 问题排查: 新增 aichat-apikey-401
- docs/INDEX+README 索引同步;docs/todo 待办看板(2026-06-15 汇总)
- PROGRESS.md Sprint 22-25;URGENT.md 加急清单快照(5 项 P0 已全修)
- scripts/cleanup_orphan_tasks.{py,sh} 孤儿任务清理工具
- .gitignore 补 *.broken.bak + tmp/ 噪音排除
This commit is contained in:
2026-06-15 05:14:21 +08:00
parent 4b5f096d1c
commit 04032a2a8d
43 changed files with 5372 additions and 163 deletions

View File

@@ -0,0 +1,150 @@
# 全局代码 Review2026-06-15
> 来源7 维度 general-purpose agent 并行深入扫workflow `wnrzlw8ub`DRY / 架构 / 潜在bug / 简洁性 / 安全 / AI可靠 / 工作流引擎)。汇总 agent stall处理 7 维度大 JSON + 输出大 schema 卡死 6 次重试),主代理从 workflow journal 提取 7 维度 result 接手汇总。
> 原则:全局最优、不过度设计、务实修复(最小改动根治)、聚焦真问题(不报 style/吹毛求疵)。
> 去重源本轮已修项排除CR-01 keyring 清理 = security#1、CR-02 AiChat confirm = DRY#3embed timeout 在 AI可靠 与 架构 两维度重复(合并)。
---
## 总览
- 原始 38 findings → 去重/排已修后 **~30**
- **P1 可执行 6**(明确小修,立即推进)
- **P1 需设计 3**(跨层/A-B/行为变更,进设计文档)
- **P2 可执行 ~13**(清债/死代码批)
- **P2 需设计 ~10**(进 todo
---
## 🔴 P1 — 可执行(立即推进)
| # | 维度 | 问题 | 位置 | 修复方向 |
|---|---|---|---|---|
| R-P1-1 | AI可靠 | **Anthropic 流式 error 事件误判正常完成**`apply_anthropic_event``type=="error"` 分支返 `finished:true`stream_llm 视为正常完成 → 残缺响应持久化、无 AiError、用户以为答完实际中途出错OpenAI 路径走 Err行为不一致 | `anthropic_compat.rs:201-205` + `stream_recv.rs:142-145` | error 事件走 Err 路径StreamChunk 加 `error: Option<String>` 字段stream_llm 识别后走 AiError + return None 丢弃残缺) |
| R-P1-2 | bug | **shell::execute 超时未 kill 子进程**`timeout` 包裹 `cmd.output()`,超时返 Err 但子进程未 killtokio Child 默认无 kill_on_drop→ 僵尸/孤儿累积 + fd 泄漏 | `crates/df-execute/src/shell.rs:55-63` | 改 `spawn()` 拿 Child + `kill_on_drop(true)` 兜底,或超时分支显式 `child.kill().await` + wait 回收 |
| R-P1-3 | workflow | **审批 Ok 后取消 TOCTOU**HumanNode Ok 返回后 join_all 让出执行权窗口内前端 cancel → set_cancelled 改 Cancelled → 随后 set_completed 命中 is_legal 拒绝(Cancelled→Completed) → bail已批准审批报失败与 B-03b-R1 Err 分支防护对称的另一半) | `crates/df-workflow/src/executor.rs:126` | set_completed 前加 `if !is_cancelled(node_id)` 短路(与 139 行 Err 分支对称) |
| R-P1-4 | DRY | **build_provider+resolve+ensure_resolved_key 7 处复制6 处漏空 key 早失败校验**:只有 agentic.rs 含 ensure_resolved_keytitle/knowledge_inject×2/project/ai_node×2 跳过 → keyring 读不到时空 key 照发请求致 401用户看「key 已保存」反复重试无解 | 7 调用点:`agentic.rs:48-58`/`title.rs:60-65`/`knowledge_inject.rs:33,320`/`project.rs:378`/`ai_node.rs:118,309` | `secret.rs` 加工厂 `build_provider_for(record) -> Result<Box<dyn LlmProvider>, String>`resolve→ensure_resolved→build7 处替换 |
| R-P1-5 | workflow | **update_task status 无值校验**白名单只校验列名不校验值任意字符串落库TaskStatus 枚举形同虚设 → 拼写错误in-progess/in progress静默落库按枚举查询全漏任务「消失」todo T-260614-03 旁路未根治) | `src-tauri/src/commands/task.rs:74-85` | field=="status" 时校验 value ∈ TaskStatus variantstypes.rs 补 is_valid 反查),非法返 Err |
| R-P1-6 | workflow | **HumanNode 无效 decision 直接 Err 无重试**decision ∉ options 或空 → return Err → 节点 Failed → 整层中止,用户一次手误(多尾空格/'y')杀死工作流,无法纠错 | `crates/df-nodes/src/human_node.rs:67-73` | return Err 改 continuewarn 日志select! 续等下一条合法 Response超时兜底 |
---
## 🔴 P1 — 需设计(进设计文档/todo
### R-PD-1 编辑 provider 不改密钥默默清 DB 明文密钥永久丢失security P1
- 位置:`commands.rs:329-355`ai_save_provider 空密钥分支)+ `crud.rs:896`INSERT OR REPLACE 全字段覆盖)
- 问题api_key 空=编辑不改约定下,无条件把 record.api_key 置空走 INSERT OR REPLACE。**未迁移态** provider迁移失败 DB 仍有明文 keyring 空)用户仅改 name/base_url空 api_key 把 DB 明文覆盖成空 → keyring 一直空 → resolve 返空 → provider 报废,密钥静默丢失。
- 修复方向:空 api_key 时先确认 keyring 有/DB 有再决定清 DB——若 keyring 无且原 DB 非空,先 set_provider_secret 补迁再清 DB即时迁移保住密钥不丢。
-todo 新增条目 + 功能决策记录(密钥迁移健壮性取舍)
### R-PD-2 run_workflow IPC 经 ScriptNode 执行前端任意 shellsecurity P1
- 位置:`workflow.rs:36-44`run_workflow 接 DagDef+ `script_node.rs:34-42`config.command 直传)+ `shell.rs:38/42`cmd /C \| sh -c
- 问题:前端可构造任意 DagDef 提交ScriptNode 从 config.command 取原始串交 shell 解释器,**无白名单/无工作目录锚定/无审批**workflow 系统独立于 AI 工具 RiskLevel 审批链)。前端可 `del /S` 或 curl 外发。
- 修复方向(三选一):① state.rs build_registry 不注册 'script' 掐断(最安全最小);② 限定工作目录在已绑定项目 path 内 + 高危命令前缀走 HumanNode 审批;③ ScriptNode 命令白名单npm/git/mvn 前缀 + 参数过滤)。
-todo 新增 + 安全设计文档(工作流脚本执行边界)
### R-PD-3 DAG 条件边从不求值条件分支整体失效workflow P1
- 位置:`executor.rs:62-97`adjacency_in 构建 + inputs 收集不区分 condition+ `conditions.rs:16`ConditionEngine 从未被 executor 调用)
- 问题:边有 `condition: Option<String>`build_dag 写入 runtime Dag但 executor 无条件把每条边前驱输出灌入 targettopological_layers 把条件边计入入度。`add_edge_with_condition("a","c","false")` 实际 c 永远执行。**条件分支这一 DAG 核心能力整体失效,且对用户静默**。
- 修复方向executor inputs 收集处用 ConditionEngine.evaluate(edge.condition, &pred_output) 过滤topological_layers 前过滤无效边或执行时按条件短路 target 为 Skipped。起步先打通数据流过滤调度短路二期。
- 与 todo `T-260614-11 条件表达式引擎升级`同一根因conditions 仅 true/false 字面量 + 从未接线)。进:任务推进构想 / 条件引擎设计文档
---
## 🟡 P2 — 可执行(清债/死代码批)
| # | 维度 | 问题 | 位置 | 修复 |
|---|---|---|---|---|
| R-P2-1 | AI可靠 | MAX_AGENT_ITERATIONS(=10) 达上限静默截断,末轮 tool_result 不回传 LLM 且无提示 | `agentic.rs:81-217` | 因达上限退出时 emit AiError/warn「达到最大轮次可能未完成」 |
| R-P2-2 | bug | write_file 每次覆盖非空文件生成 .bak 不清理(污染 git/构建/list_directory AI 上下文) | `tool_registry.rs:471-476` | rename 成功后 remove .bak保留 rename 失败分支不删兜底) |
| R-P2-3 | bug | build_dag 不校验边 source/target 存在,野边静默吞(节点空输入跑错无报错) | `registry.rs:52-61` | add_edge 前校验两端存在bail 早返回 |
| R-P2-4 | bug | TokenAccumulator.add 用 u32 += 无饱和,恶意/异常 provider 返巨大值溢出回绕打乱 budget | `conversation.rs:26-29` | saturating_add |
| R-P2-5 | DRY | now_millis 时间工具两层重复commands::now_millis + crud::now_millis_str | `commands/mod.rs:15` + `crud.rs:357` | df-core 提供 pub fn两处 use |
| R-P2-6 | 架构 | extract_error_diag 字节窗口扫描 UTF-8 边界脆弱(依赖中文恰好 3 字节巧合) | `stream_recv.rs:28-40` | 改白名单码 raw.contains(code_str),去滑窗 |
| R-P2-7 | DRY | **client builder 三级降级逐字复制 + 中间级重建无意义**(第一级 connect_timeout(30s) 失败→第二级同配置重建必同因失败→只有第三级 Client::new() 不同)。⚠️ 本轮 B3 刚加的「中间级重建」审查质疑冗余 | `anthropic_compat.rs:235-247` + `openai_compat.rs:232-244` | 抽 `build_llm_client()` 到 df-ai/http.rs + 简化为两级(删中间同配置重建级) |
| R-P2-8 | 简洁 | df-ai::router 整模块死代码route() 6 分支全返 default_model零外部引用 | `router.rs:8-51` | 删文件 + lib.rs 删 mod |
| R-P2-9 | 简洁 | df-ai::stream::StreamCollector 整模块死代码(与 TokenAccumulator 重叠未用) | `stream.rs:6-45` | 删文件 + lib.rs 删 mod |
| R-P2-10 | 简洁 | Dag::predecessors/successors 死方法executor 已自建 adjacency | `dag.rs:65-80` | 删两方法 |
| R-P2-11 | 简洁 | NodeRegistry::is_registered/registered_types 死方法 | `registry.rs:66-74` | 删两方法 |
| R-P2-12 | 简洁 | DagDef::from_dag_edges 死方法(注释自承无法还原节点配置) | `dag_def.rs:77-97` | 删方法 |
| R-P2-13 | 简洁 | set_waiting/set_skipped 死代码(全仓零调用,误导状态机认知) | `state.rs:87-111` | 删两方法set_cancelled 保留+注释「唯一受控旁路」) |
---
## 🟡 P2 — 需设计(进 todo
- **R-PD-4** keyring 迁移失败明文密钥长期滞留 SQLite 文件(无加密)— `secret.rs:50-72`:补 N 次失败阈值警告(不改兼容时序)
- **R-PD-5** approve_human_approval IPC 不校验 decision ∈ options放行非法值依赖下游 HumanNode 兜底IPC 成功+工作流失败割裂)— `workflow.rs:179-210`
- **R-PD-6** AiSession 单例try_continue 读 active_conversation_id 竞态(靠 switch readonly 间接保护,脆弱耦合)— `agentic.rs:255-291`:从 pending_approvals 取 conversation_id 解耦
- **R-PD-7** LlmProvider trait 抽象缺口name() 语义错位OpenAI 返模型名/Anthropic 返固定串)+ supported_features/ProviderFeatures 死代码(两 provider 实现但零读取)— `provider.rs:155-160,217`:补 endpoint() 默认方法 + 删 supported_features
- **R-PD-8** AiProviderRecord 整条穿透 IPC 边界DB schema 演进直接破坏前端契约models 字段 provider 返串/conversation 返数组不一致)— `commands.rs:282-295`:定义 ProviderDto/ConversationSummary 映射层
- **R-PD-9** 命令层承担业务逻辑agentic loop/tool_registry 717 行/audit reason 映射堆 commands/ai`agentic.rs`+`tool_registry.rs`+`audit.rs`:最小起步把 audit 工具名→文案映射作 display_hint 注册进 AiToolRegistry消除双份agentic loop 下沉 df-ai 较大进 todo
- **R-PD-10** .map_err(\|e\| e.to_string()) 10 文件 85 处复制(强类型 Error 拍平成自由文本,分类信息丢弃)— 全 commands/:加 err_str helper 统一日志点(是否带分类前缀需设计)
- **R-PD-11** 目录防重复绑定逻辑两处重复find_binding_conflict vs bind_dir_to_project`project.rs:249-260` + `tool_registry.rs:92-102`:抽公共 find_path_conflict
- **R-PD-12** run_workflow AI 工具是 no-op 桩但 prompt/audit/ToolCard 当真实能力宣传LLM 调用走审批拿空结果,体验断裂)— `tool_registry.rs:383-390`+`prompt.rs:57`+`audit.rs:120-123`+`ToolCard.vue:367`:删假能力 or 真接线(与 R-PD-2 协同)
- **R-PD-13** run_workflow 转发任务 Lagged 静默丢前端事件无补偿256 容量击穿时关键终态事件永久丢)— `workflow.rs:70-97`:终态事件兜底重发 or Lagged 时从 DB 重读补发
- **R-PD-14** df-ideas::promotion IdeaPromoter/PromotionPolicy/try_promote 死代码且 do_promote 是空壳 TODO误接入得虚假成功`promotion.rs:21-92`:删死码保留 PromotionResult确认归属后
---
## 架构洞察
1. **df-ai 多处重构残留死代码**routerroute 全返 default_model/stream与 TokenAccumulator 重叠)/supported_features零读取——「为不存在需求预建的抽象」零引用可删。
2. **LlmProvider trait 抽象缺口**name() 语义错位 + 无 base_url/endpoint 访问器 → 诊断stream_recv fmt_diag只能用 name() 近似 provider_type401 排查难定位是 key/url/model 哪个配置错(与本轮 B-260615-01 stream_recv 诊断约束呼应)。
3. **命令层臃肿**agentic loopReAct 编排)/tool_registry项目CRUD+文件+安全校验 717 行闭包)/audit工具名→文案映射堆在 src-tauri/commands/ai无法被 df-nodes/AiNode 复用AiNode 自己重写 LLM 调用。最小起步audit 映射下沉 registrydisplay_hint
4. **df-workflow ConditionEngine 从未接线**DAG 条件分支整体失效R-PD-3与 todo `T-260614-11 条件表达式引擎升级`同根因——conditions 仅 true/false 字面量 + executor 不调用。需统一在条件引擎设计文档收敛。
5. **工作流 ScriptNode 任意 shellR-PD-2**:独立于 AI 工具审批链的安全缺口,前端 IPC 直接触发,需工作流脚本执行边界设计。
---
## 推进编排
1. **P1 可执行 6 项**R-P1-1~6→ workflow 并行推进(文件域隔离 + 实现/审查 pipeline
2. **P1 需设计 3 项**R-PD-1/2/3→ 进设计文档(密钥迁移健壮性 / 工作流脚本执行边界 / 条件引擎)+ todo
3. **P2 可执行清债批**R-P2-1~13→ 顺带推进(死代码删除 + 小修,零风险减法)
4. **P2 需设计**R-PD-4~14→ 补 todo
---
## 推进状态2026-06-15
**批1 核查闭环**(主代理独立 cargo check 全量 + 三 crate test 全绿):
- ✅ R-P1-2 shell kill_on_drop`df-execute/shell.rs`kill_on_drop(true)+spawn+wait_with_outputtimeout drop child 触发 kill
- ✅ R-P1-3 审批 Ok TOCTOU 短路(`df-workflow/executor.rs:126`Ok 分支加 is_cancelled 短路,新测 `test_cancelled_node_skips_set_completed`15 passed
- ✅ R-P1-5 update_task status 校验(`df-core/types.rs` is_valid+valid_values+7 单测;`task.rs` status 值校验 Err 含合法值清单df-core 7 passed
- ✅ R-P1-6 HumanNode 无效 decision continue`df-nodes/human_node.rs`Err→continue+warn2 测改 `_ignored_then_timeout`15 passed
**批2 核查闭环**(主代理独立核查 git diff + cargo check 全量 exit 0 + df-ai 10 passed:
- ✅ R-P1-1 Anthropic 流式 error 误判完成(`provider.rs` StreamChunk 加 `error: Option<String>` 字段 `#[serde(skip)]``anthropic_compat.rs:205` error 分支 finished→false+error=Some全分支补 error:None 初始化);`stream_recv.rs:142` chunk.error 识别 emit AiError(MidStream)+return None与 Err 分支对称;新单测 2 个df-ai **10 passed**
- ✅ R-P1-4 build_provider DRY`secret.rs` build_provider_for 工厂已落resolve→ensure_resolved→build5 调用点全用;`ai_node.rs:50-53` parse_params 内联空 key 校验df-nodes 架构边界不依赖 src-tauri语义对齐 ensure_resolved_key裸 build_provider 残留 = lib.rs 工厂定义 + ai_node 生产(前面 parse_params 已校验) + ai_node 测试,合理)
**P1 可执行 6 项全闭环**批1 4 + 批2 2
---
## 推进状态2026-06-15— P2 清债批
**批3 核查闭环**(主代理独立核查 git diff + cargo workspace 全量 exit 0 + 5 crate test 共 126 passed:
- ✅ 批3-A df-ai 域R-P2-7 client builder 三级→两级(删中间同配置重建级,两 provider 对称)+ R-P2-8 router.rs 删 + R-P2-9 stream.rs 删lib.rs 删 2 mod决策不抽 build_llm_client过度设计df-ai **31 passed**
- ✅ 批3-B df-workflow 域R-P2-3 build_dag 边校验node_ids HashSet O(1) + bail 野边)+ R-P2-10 dag 删 predecessors/successors + R-P2-11 registry 删 is_registered/registered_types + R-P2-12 dag_def 删 from_dag_edges同步删 use Dag+ R-P2-13 state 删 set_waiting/set_skippedset_cancelled 保留扩注释df-workflow **15 passed**
- ✅ 批3-C df-core+DRY 域R-P2-4 TokenAccumulator saturating_add 3 处add prompt/completion + total+ R-P2-5 now_millis 统一 df-coretypes.rs pub fn + lib.rs re-export + commands/mod.rs/df-storage crud.rs 转发保留函数名37 调用点零破坏review crate 归属错df-core/conversation.rs 实为 src-tauri/commands/ai/、src-tauri/crud.rs 实为 df-storage/已按真实位置修正df-core **7 passed**
- ✅ 批3-D src-tauri 域R-P2-1 agentic converged 互斥(达 MAX 且 has_tool_calls 仍 true 时 emit AiError+warn 前置警示「末轮工具结果未回传」,仍入库 Completed 保留内容,与 break 收敛分支互斥不重复 emit+ R-P2-2 write_file .bak 清理rename 成功后 old_size>0 时 remove忽略清理失败+ R-P2-6 extract_error_diag 重写(`&bytes[i..i+3]` 字节切片→`chars().collect()`+char 迭代+HTTP_CODES 白名单,修多字节 UTF-8 panic 风险 + 长数字串内嵌误命中devflow **58 passed**
**P2 可执行 13 项全闭环**
---
## 推进状态2026-06-15— P1 需设计 3 项设计文档
**3 设计文档已起草**general-purpose agent 读码 + 细化方案,待用户核对):
- 📄 **R-PD-1 密钥迁移健壮性** → [密钥迁移健壮性-2026-06-15.md](../02-架构设计/密钥迁移健壮性-2026-06-15.md):推荐方案 A「编辑路径即时迁移」——DB 有明文+keyring 无时先 `set_provider_secret` 补密钥,成功后清 DB 明文,失败 Err 阻断 DB 不动(最小局部改动 commands.rs 一处,放弃 B 反方向维持未迁移态 / C 显式标志位 schema 演进成本)
- 📄 **R-PD-2 工作流脚本执行边界** → [工作流脚本执行边界-2026-06-15.md](../02-架构设计/工作流脚本执行边界-2026-06-15.md):推荐方案① 删 `src-tauri/src/state.rs:227-229` script 注册掐断入口 + 下线 demoDagDevFlow workflow 纯演示,唯一构造点 demoDag 三步 echo一行删换零攻击面黑名单/参数过滤是不完备运行时博弈),未来真要脚本能力新建独立 BuildNode。**路径修正**build_registry 实在 `src-tauri/src/state.rs:225`review 原文 `state.rs` 无 crate 前缀易歧义——`crates/df-workflow/src/state.rs` 是 StateMachine 状态机与节点注册无关)。**R-PD-2 必须先于 R-PD-12**(否则 LLM 暗道绕过 RiskLevel 审批链)
- 📄 **R-PD-3 条件表达式引擎** → [条件表达式引擎-2026-06-15.md](../02-架构设计/条件表达式引擎-2026-06-15.md):推荐分阶段 Phase1 数据流过滤executor inputs 收集处调 ConditionEngine + adjacency_in 携 condition零终态冲突独立可 ship/ Phase2 调度短路(层调度前求值条件全 false 则 target 不执行,复活 `set_skipped` 旁路与 `set_cancelled` 同型,注释区分语义)。**待用户确认 3 决策点**A 引擎实现(手写最小求值器 vs evalexpr 库)/ B 终态机制set_skipped 复活 vs 其他)/ C 求值失败兜底(默认 true vs false
**与 R-P2-13 的张力**R-P2-13 删了 set_waiting/set_skipped当时全仓零调用扩写 set_cancelled 注释「唯一受控旁路」R-PD-3 Phase2 接线条件引擎后 set_skipped 有了真实消费者需复活——set_cancelled「唯一旁路」注释届时需同步改。
**review 路径归属勘误汇总**(本轮 3 处,已分别在对应设计文档/实现修正):
- R-P2-4 `df-core/conversation.rs` → 实 `src-tauri/src/commands/ai/conversation.rs`TokenAccumulator
- R-P2-5 `src-tauri/src/crud.rs` → 实 `crates/df-storage/src/crud.rs`now_millis_str
- R-PD-2 `state.rs` build_registry → 实 `src-tauri/src/state.rs:225`(非 `crates/df-workflow/src/state.rs`