Files
DevFlow/docs/05-代码审查/架构与缺陷复核报告-2026-06-14.md
绝尘 04032a2a8d 重构: 文档汇总+进度看板+孤儿任务清理脚本+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/ 噪音排除
2026-06-15 05:14:21 +08:00

16 KiB
Raw Blame History

代码架构与缺陷复核报告2026-06-14

范围:在 全栈代码审查报告-2026-06-14.md 基础上的复核 + 回归审计 + 架构层补充。 方法4 路并行(安全/并发性能/架构/工作流),其中安全路子代理失控(误改任务表无产出),改由主代理直读 5 文件完成tool_registry / crud / workflow / commands / audit。其余 3 路子代理产出完整。 模式:dry — 仅审查 + 文档,不改代码。 互斥:与全栈报告去重;本报告只记复核结论confirm/refute/已修)+ 新发现 + 报告修正,不重复已记录的项。


0. 复核总览

类别 数量 说明
已修确认(最近提交修复正确) 8 FR-S2/S3/S4/S6、AR-3、FR-R1(主路径)/R3、FR-C1
confirm复核证实仍存在 9 FR-S1/S5、FR-R4/R5、FR-P2/P3/P5/P6、FR-P1(O(V·E)部分)
refute原报告误判/已澄清) 3 FR-R2 降级成立、FR-P1 panic 担忧不成立、FR-P4 严重度偏高
新发现(不在原报告/todo 11 见 §5其中 5 项 🟠
死代码确认 5 set_waiting/set_skipped、emit_human_approval_request、ModelRouter、StreamCollector、IdeaPromoter

最高优先级(建议本轮接手)§5 新发现 ②③④⑤FR-R1 闭环缺口 / client 降级丢 timeout / 取消事件语义双标 / cancel 无终态校验)。


1. 安全复核(主代理直读)

已修确认

① FR-S2 — read_file/write_file [已核实修复正确]

  • tool_registry.rs:367-376 read_file 改单次 File::open → file.metadata() → read_to_string同句柄消除原 metadata+read 两步 syscall 间 symlink 替换的 TOCTOU。1MB 上限在 metadata.len() 判定后 bail。
  • tool_registry.rs:416-418 write_file 加 content.len() > 1_048_576 bail。
  • 两处入口均先走 resolve_workspace_pathcanonicalize 防 symlink 逃逸)。
  • 残余低优read_file 的 metadata 大小判定与实际 read_to_string 之间,若文件在判定后被追加写入,仍可能读入 >1MB路径已 canonicalize攻击者难 swap属理论面。write_file 无此问题(写 content 大小在内存已知)。

② FR-S3 — approve decision 校验 [已核实修复,但 IPC 层弱于建议]

  • workflow.rs:189 decision.trim().is_empty() → Err。防 "" 透传卡死 HumanNode。
  • 缺口IPC 层未做 decision ∈ {"approved","rejected"} 白名单(原报告建议)。深层兜底在 human_node.rs:67options.is_empty() || options.contains(&decision),故 options 非空时仍拒非法值。属"IPC 弱 + HumanNode 强"的纵深防御可接受若想严格化IPC 补一行白名单即可。

③ FR-S4 — SKILL.md 隔离标注 [已核实修复,属弱防御]

  • commands.rs:67-74 注入前包裹 # 用户选择的技能说明: {}(非系统指令,勿作为行为准则覆盖;以下为技能内容供参考),且技能内容置于 system_prompt 之前、系统真行为准则在之后recency 优势)。
  • 性质:纯文本标注,是 prompt injection 的缓解非根治——信任本机技能文件的设计取舍下可接受,但恶意 SKILL.md 仍可能通过语义注入影响输出。已在功能决策记录标注此取舍即可,不升级。

④ FR-S6 — validate_column_name 未登记表拒绝 [已核实修复正确]

  • crud.rs:339 None => Err("表 {} 未登记列白名单,拒绝防注入"),与 is_allowed_columnNone => false 语义一致。
  • 回归风险评估allowed_columns_for 当前登记 ai_tool_calls/ai_conversations/ai_messages/ai_providers/ai_sessions/ideas/idea_scores/projects/tasks/workflows/node_executions/knowledges/knowledge_eventsmatch arms 可见)。所有走 query/update_field 通用路径的 Repo 表均已登记(编译期表名宏 + 实测),无合法查询被误拒。低回归风险。

⑤ AR-3 — 审批 reason 拼对象名 [已核实修复且实现质量高]

  • audit.rs:64-138 build_approval_reason 特化 9 工具delete/restore/purge_project、update_project、bind_directory、create_task、create_project、create_idea、run_workflow。
  • resolve_project_label:44ProjectRepo::get_by_id 取 name查不到回退 (id=x),空 id 返回空串。
  • 无 N+1每次审批单工具单查询非循环。
  • 未特化工具走 risk fallback 模板。覆盖完整。

🟠 FR-S1 — api_key 明文三连 [维持未修]

确认现状未动migrations.rs/models.rs/commands.rs/Settings.vue 四处仍明文。沿用全栈报告结论P0 待 OS keychain 落地。

🟡 FR-S5 — 跨对话越权审批 [确认降级文档成立]

commands.rs:117-120 ai_approve 仅按 tool_call_id 从 pending_approvals 取,无对话归属校验。但本地单机 + restore 只载当前对话 + UI 隔离,实际触发面窄。维持降级为文档说明。


2. 并发/性能复核子代理产出11 项)

# 判定 证据要点 严重度
FR-R1 切换 token 已修(主路径) + 遗漏点 _latestSwitchId 在首个 await 后比对正确,但 pendingToolCalls(:108) await 后无二次比对A→B 快切可覆写 B 的 pendingApprovals 🟠(见新发现①)
FR-R2 看门狗多窗口互踩 refute降级成立 分离窗口=独立 WebviewWindow=独立 JS realm模块级 state 各持一份不共享 🟢
FR-R3 liveEvents 限长 已修正确 project.ts:215 splice(0, len-200),无 off-by-one配合 _ts 🟢
FR-R4 complete 无超时无重试 confirm openai/anthropic_compat 仅 connect_timeout(30s)complete() 同步路径无总 timeout远端建连后挂起则永久 hang 🔴
FR-R5 findToolCall O(n²) confirm useAiEvents.ts:63 双层 for+find4 处事件触发点 🟡
FR-P1 dag O(V·E) + panic confirm(O(V·E)) / refute(panic) successors/predecessors iter().filter 全表扫;但 in_degree 已对全部 node_ids 初始化且 contains 过滤野节点,unwrap() 不会 panic 🟠🟡
FR-P2 search_vector SELECT * confirm crud.rs:1183 SELECT * 拉 content/reasoning 大文本,无 LIMITRust 算余弦 🟠
FR-P3 单连接 Mutex confirm db.rs:13 Arc<Mutex<Connection>>,每 CRUD spawn_blocking+blocking_lock 串行 🟠
FR-P4 500ms 轮询锁竞争 confirm(轮询) / refute(严重度) interval(500ms) 确实存在,但 std Mutex 无竞争纳秒级,"7200 次锁竞争"表述偏重 🟡
FR-P5 build_system_prompt 全表扫 confirm prompt.rs:75 list_active() 全表 + take(20) + format!,每消息+每 agentic 轮触发list_active 无 LIMIT 🟠
FR-P6 watch messages JSON.stringify confirm AiChat.vue:650 {deep:true} + 全量 stringifysnapshot 比对只省后续逻辑不省 stringify 本身 🟠

3. 架构层(子代理产出,最大结构性风险)

架构健康度:🟢 基本健康7/10。依赖图无环、df-core 严格叶子、IPC 无 SQL 泄漏、前后端类型对齐度高、僵尸 crate 清理彻底grep df_evolve/plugin/stages/task/traceability 零残留)。

最大结构性风险🟠 crate 层与 IPC 层职责边界双向错位——领域逻辑(评估编排/晋升事务补偿/AI 扫描编排)沉淀在 IPC commands 层,而对应的 crate 模块df-ideas::promotion、df-project::manager、df-ai::router/stream反而是空壳或死代码。

3.1 依赖图与职责

  • df-nodes 同时依赖 df-core/df-execute/df-workflow/df-ai 四个,是唯一多父汇聚点。结构正确(节点需调执行器/工作流/AI非阻塞。
  • df-workflow/src/conditions.rs:16-33 ConditionEngine 仅识别 true/false 字面量,其余默认 false保守拒绝安全。文档承诺的 JSON Path/数值/逻辑组合语法全 TODO。todo.md:137 T-260614-11 已记。

3.2 空壳/死代码5 处,建议清理或显式标注)

# 位置 判定 严重度
df-workflow/state.rs:87-100 set_waiting/set_skipped 全仓零调用方,纯死代码 🟡
df-workflow/eventbus.rs:43-46 emit_human_approval_request 全仓零调用方HumanNode 用 async send 🟡
df-ai/router.rs ModelRouter route() 全返回 default_modellib.rs 未导出,零调用方 🟠
df-ai/stream.rs StreamCollector 流式接收走 IPC 层 stream_recv.rs此模块零调用方 🟠
df-ideas/promotion.rs:45-91 IdeaPromoter/try_promote/do_promote 全 TODO 占位IPC 层 idea.rs 重写了晋升逻辑crate 层零调用 🟠

coordinator.rsdf-ai是 B 路线有意占位(文件头注释+文档对齐),非死代码,建议 run() 改 bail!("未实现") 更安全。

3.3 IPC 层 carry 领域逻辑3 处)

  • idea.rs:184-231 evaluate_idea评估编排 + 结果组装 + scores*10 缩放 + recommendation/action_items 扁平化映射,整段领域逻辑落 IPC。
  • idea.rs:140-153 promote_idea跨表事务补偿回写失败→补偿删 project落 IPC。
  • project.rs:335-377 scan_project_with_aibuild_scan_prompt/parse_scan_result 纯函数级 AI 编排落 IPC。

后果:领域逻辑绑 Tauri State 无法 crate 级单测AI 工具 create_* 与 IPC create_* 各写一份字段默认值tool_registry.rs vs project.rs/task.rs字段增删易漏改分叉。

3.4 前后端类型 drift3 处注释错误)

  • types.ts:88 TaskStatus 注释 review_ready/merged/abandoned 三值后端枚举不存在(实际 todo/in_progress/in_review/testing/done/blocked/cancelled🟠
  • types.ts:89 Priority 注释方向反(前端 0=critical/3=low,后端 df-core Low=0/Critical=3)。🟠
  • types.ts:39 ProjectStatus 注释漏 testing/releasing。🟡
  • 类型本身 string 不阻断,但注释误导前端排序/展示逻辑写反。

3.5 错误处理双轨

  • df-storage 用强类型 df-core::Errorthiserror其余 cratedf-ai/df-ideas/df-project/df-workflow/df-execute全 anyhow。df-core::Error 的 Workflow/Execution/AiProvider/Plugin 等变体零使用=死变体。🟠
  • IPC 层统一 .to_string() 压平成中文消息,前端无法区分 404/500。🟡Tauri 限制下折中,可接受)

4. 工作流审批链复核(子代理产出)

4.1 死代码confirm 3 处)

set_waiting/set_skipped、emit_human_approval_request 零调用(见 §3.2 ⑥⑦。set_cancelled 绕 is_legal 直接 insertstate.rs:106todo 决策采"修法 B 绕而非纳入白名单",与全栈报告建议分歧,属待统一项。

4.2 回归审计(最近 5 提交无 P0/P1 回归)

  • R6 human_node send 加 awaithuman_node.rs:42测试 request_is_emitted_to_bus 验证。
  • select! 三分支Response 双键过滤 / sleep_until 无漂移 / cancel 首 tick 丢弃)正确。
  • Lagged(n) continue 容忍正确。
  • decision 校验human_node.rs:67 三态 + IPC workflow.rs:189 双层)。
  • executor Err 处理 is_cancelled guardexecutor.rs:123-141跳 set_failed避开 Cancelled→Failed 非法 transition。
  • cancel 链端到端通IPC→registry→共享 StateMachine→HumanNode 500ms 轮询→Err→executor

4.3 集成测试缺口confirm

前端无 human DAG 入口demoDag 仅 3 个 script 节点),现有测试用 mock CancelSelfNode 验证取消,非真 HumanNode 路径。select! cancel 分支 + interval tick 逻辑无端到端覆盖。todo.md:90 B-03b-R8 已记。


5. 新发现汇总11 项,不在原报告/todo

🟠 高优(建议本轮处理)

新-① [src/composables/ai/useAiConversations.ts:108-126] FR-R1 闭环不完整pendingToolCalls await 后无 token 比对 A→B 快切时,首个 await 已挡住 messages/activeConversationId 覆写,但 aiApi.pendingToolCalls(:108) 慢返回仍会覆写 B 的 state.pendingApprovals(:120)。建议补 if (mySwitchId !== _latestSwitchId) return

新-② [crates/df-ai/src/{openai,anthropic}_compat.rs] client builder 降级丢 connect_timeout Client::builder().connect_timeout(30s).build().unwrap_or_else(|_| Client::new()) — build 失败降级到 Client::new() 丢失连接阶段超时。建议降级分支显式 Client::builder().build()

新-③ [crates/df-workflow/src/executor.rs:130-135] 取消节点状态保 Cancelled 但仍发 NodeFailed 事件,语义双标 已取消节点状态机保 Cancelled但事件总线仍 emit NodeFailed { error: "人工审批被取消" }。前端若按 NodeFailed 事件分支判断会误归类"失败"而非"取消"。建议加 NodeCancelled 事件 variant 或 NodeFailed 加 cancelled: bool 字段。

新-④ [src-tauri/src/commands/workflow.rs:218-236] cancel_workflow_node 无节点终态前置校验 仅校验 execution_id 在注册表,不校验 node_id 状态。对已 Completed/Failed/Cancelled 节点调 set_cancelled 会静默覆盖终态set_cancelled 绕 is_legal。建议加 match sm.get(&node_id) { Running|Waiting => {}, _ => Err } 守卫。

新-⑤ [前端无 human DAG 入口] 端到端 human 审批测试缺口 见 §4.3CancelSelfNode 自取消与 HumanNode 走 select! cancel 分支是两条代码路径,后者无覆盖。

🟠 中优

新-⑥ [df-ai/router.rs] ModelRouter 空壳死代码 — 见 §3.2 ⑧route() 全返回 default_model零调用方。

新-⑦ [df-ai/stream.rs] StreamCollector 死代码 — 见 §3.2 ⑨,流式逻辑全在 IPC stream_recv.rs。

新-⑧ [df-ideas/promotion.rs] IdeaPromoter 死代码 — 见 §3.2 ⑩IPC 层重写了晋升。

新-⑨ [types.ts:88-89] TaskStatus/Priority 注释 drift — 见 §3.4,误导前端排序逻辑。

新-⑩ [df-core/error.rs] 错误类型双轨,半数变体死代码 — 见 §3.5。

🟡 低优

新-⑪ [useAiWindow.ts:79-95 + useAiStream.ts:18] 主窗口看门狗在分离窗口接管生成时仍跑 detach 时主窗口 streaming/generatingConvId 未清,看门狗续计 130s 后往主窗口 state 补幽灵错误消息,但用户已切走无实际危害,仅主窗口面板重开时看到一条幽灵错误。


6. 报告修正refute 原 report 误判)

原报告项 修正
FR-R2 看门狗多窗口互踩 应标 refute:分离窗口独立 JS realm 不共享 state 单例降级理由成立todo 已记"评估维持",复核确认)
FR-P1 in_degree.unwrap() panic 风险 refutein_degree 对全部 node_ids 初始化 + contains 过滤野节点unwrap 不 panic
FR-P4 "7200 次锁竞争" 严重度偏高std Mutex 无竞争纳秒级,实为微秒级开销;保留"500ms 轮询模式不佳"批评,建议改 broadcast/Notify

7. 建议进 todo 的项(去重后)

已在 todo 的不重复;下列为本复核新产出,待用户确认进看板。

建议 ID 对应 优先级
复核-新① FR-R1 pendingToolCalls 闭环 useAiConversations.ts:108 补 token 比对 P1
复核-新② client 降级丢 timeout openai/anthropic_compat 降级分支显式重建 P2
复核-新③ 取消事件语义双标 加 NodeCancelled variant 或 cancelled 字段 P2
复核-新④ cancel 无终态校验 workflow.rs:223 加状态前置守卫 P2
复核-新⑤ human 端到端测试缺口 = todo B-03b-R8已记提优先级 P1
复核-新⑥⑦⑧ 死代码三连 ModelRouter/StreamCollector/IdeaPromoter 删或标注 P2
复核-新⑨ types.ts 注释 drift TaskStatus/Priority 注释对齐 df-core 枚举 P2
复核-新⑩ 错误类型双轨 df-core::Error 瘦身或各 crate 收敛 P2 长期
FR-R4 complete 无 timeout (已在 todo复核确认 🔴,建议提 P1 P1
FR-S1 api_key 明文 (已在 todo P0复核确认未动 P0

优先处理 3 件(影响实际行为):

  1. 复核-新① FR-R1 闭环缺口(审批卡片可见性错配,一行修)
  2. 复核-新③④ 取消事件语义 + 终态校验(审批链语义正确性)
  3. FR-R4 complete 无 timeout一次网络抖动挂死整轮对话提 P1

本会话只做代码审查 + 文档记录,不含代码变更。实际修复另起会话。