# 待审查队列 > **职责分离**: > - **主会话**(编码合并后 / 巡检)→ 【**写入**】"当前队列"区,登记待审查项(范围 + 维度 + commit)。 > - **审查 agent**(独立会话 / workflow)→ 【**读取**】"当前队列",执行审查,完成回填结论 + 移到"已审归档"。 > > 这里**只放审查登记**,不放推进待办(推进待办见 [todo.md](./todo.md))。 > 主会话**不在这里做审查**,只登记 + 读 todo 推进。审查发现的【待修代码项】回流 todo 作推进待办。 ## 审查规则(审查 agent 遵守) - 只读审查,**不改代码**。 - **独立 grep/read 核验源码,不信文档 / 会话描述 / 设计声明的结论**(防上下文污染)。 - 每条 finding 须 `file:line` 源码佐证 + evidence。 - 严重度:high(破坏性 / 数据错 / 回归) / med(逻辑瑕疵 / 边界漏) / low(风格 / 可读)。 - 完成回填:总体 verdict(PASS / ISSUES / WATCH)+ high/med/low 清单 + 待修项回流 todo。 ## 状态图例 🟡 待审 / 🔵 审查中 / ✅ 已审(结论) / 🔴 待修(有 high 问题) --- ## 当前队列 ### CR-260616-36 batch60 F-09A 隔离补清 + F-13 性能优化 + AE-03 write_file diff 预览(路径B) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-36 agent 亲跑 cargo+vue-tsc+test,723s)**: ✅ **PASS** — 🔴0 🟡0 ⚪1 - **验证**: `cargo check --workspace` EXIT 0(4 warning 全 pre-existing:mod.rs:282 PendingApproval.diff never read 误报 / state.rs:146 releases+node_executions dead_code)/ `cargo test -p df-ai` **37 passed** / `npx vue-tsc --noEmit` EXIT 0。 - **F-13 messages.clone() 等价性 PASS**:stream_recv.rs:137-144 stream_llm 签名不接 session_arc + 重试块(agentic.rs:259-363)到 process_tool_calls(:476)间 0 个 session.messages.push(push 在 :391/395/446/450 之后)+ CR-260616-35 回归核验(保文/退避/permit RAII/guard.reset/request 重建全无回归)。 - **F-13 sys_tokens 缓存 PASS**:agentic.rs:172-174 system_prompt 是 run_agentic_loop owned String 参数,整 loop 只读(:174 estimate_text + :227 ChatMessage::system),无 mutate。 - **AE-03 build_write_file_diff 安全 PASS**:audit.rs:508-520 预读旧文件路径未校验,但桌面应用威胁模型(LLM 生路径非远程攻击者)+ read 仅展示不外泄 + handler validate_path 执行时兜底,可接受风险。 - **AE-03 diff 双路径 PASS**:实时(audit.rs:588-609 PendingApproval.diff clone + AiApprovalRequired.diff move 双注入)+ 恢复(audit.rs:282 diff=None 故意,DB ai_tool_executions 无 diff 列,前端 ToolCard v-if tc.diff 兜底)。types 对齐(mod.rs:101/282 Option + types.ts:198/244 diff? + useAiEvents.ts:239 传播)。 - **F-09A 单例补清 PASS**:commands.rs:860 stop_flag.store(false,SeqCst) 在生成中分支置 true(:840)后复位覆盖两路径 + :861 agent_language=None + 前端 useAiConversations.ts:42-45 补清 queue/generatingConvId/agentRound/searchQuery + 未动 AiSession 单例(B 路线边界)。 - **⚪ low**:mod.rs:282 PendingApproval.diff "never read" warning — 误报,值经 clone 进 AiApprovalRequired event payload(audit.rs:609)前端消费,dead-code 分析不见直接 read。 - **待修项回流 todo**: **无**(5 重点全 PASS,1 low 误报)。 - **范围**: 3 agent 合并批(workflow whae812z5,主代独立核查全过 + cargo check --workspace EXIT 0 + vue-tsc EXIT 0)。①**F-09 A 路线**(单例软隔离补漏清字段,不动 B 多会话架构):前端 `useAiConversations.ts:42-45` newConversation 补清 `queue=[]/generatingConvId=null/agentRound=0/searchQuery=''`(防旧会话排队消息带进新会话 + 后台事件错路由 + 侧栏旧搜索过滤);后端 `commands.rs:842-843` ai_conversation_create 补清 `stop_flag.store(false,SeqCst)`(关键:上方生成中分支置 true 停旧 loop 不复位则新会话 loop 启动即见 stop 退出) + `agent_language=None`(防沿用旧语言设置)。②**F-13 性能**:`agentic.rs:173` system_prompt token loop 外算一次缓存为 sys_tokens(整个 loop 不变参数,原每轮+每次重试重算);`:219-225` 外层 messages 构建用 sys_tokens;`:262-270` 重试循环改 `messages: messages.clone()` 复用外层 Vec(删每次重试的 session_arc.lock + build_for_request 全量 clone + estimate_text)。安全前提核验:stream_llm 不接 session_arc + 重试块到 process_tool_calls 间无 push,重试内 session.messages 与外层一致,重建等价复用。③**AE-03 路径 B**(审批 payload 加 diff,非路径 A):`tool_registry.rs:32` generate_diff fn→pub(crate)(复用 F-260615-10 LCS);`audit.rs:508-520` 新增 build_write_file_diff(从 args 取 path+content → 预读旧文件 tokio::fs::read_to_string → 无变化/旧文件缺失/读失败均 None → generate_diff);`audit.rs:588-609` 仅 write_file draft 挂起审批前预计算 approval_diff 注入 PendingApproval.diff(:600 clone)+AiApprovalRequired.diff(:609 move);恢复路径 diff=None(文件可能已变);`mod.rs:101/269` AiApprovalRequired+PendingApproval 加 `diff: Option`;`types.ts:198` AiApprovalRequired 加 `diff?: string`;`useAiEvents.ts` event.diff 传 toolCall 信息;`ToolCard.vue:40-41` 模板 `v-if="tc.diff"` diff 块 + :394 diffLines computed(按 +/-/space 前缀拆分)+ :677-705 样式(add 绿 del 红 ctx 灰 + token 复用)。 - **维度**: ①F-09 单例字段补清完整性(stop_flag 复位是否覆盖所有生成中→新建路径/agent_language 影响面) + B 路线边界(确认未误动 AiSession 单例) ②F-13 行为不变性核验(sys_tokens 缓存是否真不变/messages.clone() 复用是否等价重建/stream_llm 不持 session_arc 前提是否成立) + 锁持有期缩短收益确认 ③AE-03 路径 B 安全性(预读旧文件只读不改/审批拒绝不执行 handler/路径不校验风险——恶意路径读失败仅 None) + diff 注入完整性(实时挂起+恢复路径双覆盖/前端消费链 event→toolCall→模板) + generate_diff pub(crate) 暴露面。 - **commit**: d00b30f(重构: AI聊天可靠性批次)。 - **主代独立核查**: ✅ 全过(useAiConversations:42-45 补清 / commands:842-843 stop_flag.store(false)+agent_language=None / agentic:173 sys_tokens loop 外缓存 + :219-225 外层用 + :262-270 重试 messages.clone() 复用 + 保文退避逻辑未动(Partial 不重试/retry_deadline 30s) / tool_registry:32 generate_diff pub(crate) / audit:508-520 build_write_file_diff 预读+无变化/缺失 None+复用 generate_diff / audit:588-609 仅 write_file 生成 diff 注入 PendingApproval(clone)+AiApprovalRequired(move) / mod:101+269 diff 字段 / types:198 diff? / ToolCard:40-41 模板 + :394 diffLines + :677-705 样式 / cargo+vue-tsc 双 EXIT 0)。 - **审查 agent 待复审重点**: ①F-13 重试 messages.clone() 复用等价性——核验 stream_llm 签名(stream_recv.rs:137 不接 session_arc) + 重试块到 process_tool_calls 间确实无 session.messages.push(若遗漏则重试复用旧 messages 致 tool_calls 丢失) ②F-13 sys_tokens 缓存——核验 system_prompt 确为 run_agentic_loop 不变参数(整个 loop 期间无 mutate) ③AE-03 build_write_file_diff 安全——预读旧文件 `tokio::fs::read_to_string(path)` 路径未校验(恶意 path 读失败仅 None 不产生危害,但核验无 symlink 逃逸读敏感文件风险——审批只读,write_file handler 自身 validate_path 执行时兜底) ④AE-03 diff 注入双路径——实时挂起(audit:588-609 有 diff)+恢复路径(PendingApproval diff 字段是否持久化,若仅内存则重启恢复审批无 diff) ⑤文件锁独立性——3 agent 改动文件无重叠(F-09: useAiConversations/commands / F-13: agentic / AE-03: mod/tool_registry/audit/types/useAiEvents/ToolCard),useAiEvents.ts Agent C 独占(A/B 未碰)确认无冲突。 ### CR-260616-37 F-11 审批续跑 iteration 累计计数(跨审批不重置) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-37 agent 亲跑 cargo+test,658s)**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code:diff field / releases+node_executions 未读)/ `cargo test -p df-ai` **37 passed**(retry 6 + context 18 + openai_compat 8 + stream_recv 5,finished 2.59s)。 - **iteration_used 生命周期完备 PASS**:grep 全消费点 **9 处**——ai_chat_send(:55 reset 0)/ ai_regenerate(:152 reset 0)/ ai_chat_edit_last(:412 reset 0)/ ai_continue_loop(:601 reset 0 + 传 0)/ ai_approve 拒绝(:260 累计读)/ ai_approve 通过(:313 累计读)/ agentic loop 递增(:210 iteration+1 写回)/ mod.rs:215 字段定义 / mod.rs:230 new() init 0。ai_conversation_create 隐式从 0(新会话 new init 0)。**无遗漏**。 - **start_iteration 透传链 PASS**:ai_approve(:260/:313 读 session.iteration_used)→ try_continue_agent_loop(:578 接 start_iteration 参)→ spawn(:675 透传)→ run_agentic_loop(:114 接参)→ for iteration in start_iteration..max_iterations(:176 使用)。每环 file:line 佐证无断链。 - **两路径区分 PASS**:ai_approve 累计(F-260616-11,不 reset 读 iteration_used)vs ai_continue_loop 重计(F-260616-03 决策a,reset+传 0)vs 新消息(reset 0)严格区分。注释 agentic.rs:499-501 + commands.rs:258-259/311-312/599-600 均写明归属。 - **边界 start≥max PASS**:for 循环空区间不执行 + converged 保持 false(:170)→ 命中 :502 `if !converged` → emit AiMaxRoundsReached(:518-520) + return,防无限审批续跑烧 token,符合 F-11 语义。 - **并发安全 PASS**:session.iteration_used=iteration+1 在 agentic.rs:198-211 同一 session_arc.lock().await 块(与 :199 active_conversation_id 一致性校验共享锁),锁模式与 :424-452/:474-477 一致。 - **mod.rs 字段 PASS**::215 `pub iteration_used: usize`(无符号防负)+ :230 new init 0 + agentic.rs:176 循环边界 + :210 一致性块更新。 - **待修项回流 todo**: **无**(6 重点全 PASS,零问题)。 - **范围**: F-260616-11 决策a 落地(与 batch60 三项合批 commit d00b30f)。`mod.rs:215/230` AiSession 加 `iteration_used:usize` 字段+new() init 0;`agentic.rs:114` run_agentic_loop 加 `start_iteration:usize` 参数 +`:176` loop 边界改 `start_iteration..max_iterations` +`:210` 一致性校验块更新 `session.iteration_used=iteration+1` +`:578` try_continue_agent_loop 加 start_iteration 参数 +`:675` spawn 透传 +`:499-501` 注释区分两路径;`commands.rs:55/152/412` ai_chat_send/ai_regenerate/ai_edit_last reset iteration_used=0(新生命周期)+`:260-262` ai_approve 拒绝续跑读 session.iteration_used 累计传 +`:313-315` ai_approve 通过续跑累计传 +`:601` ai_continue_loop reset iteration_used=0 传 0(F-03 决策a 达max重计区分)。两路径严格区分:审批续跑(ai_approve 累计透传 iteration_used) vs 达max续跑(ai_continue_loop reset+传0,F-03 决策a 用户授权重来) vs 新消息(reset 0)。 - **维度**: ①iteration_used 字段生命周期完备性(所有会话生命周期入口 reset 覆盖——create/send/regenerate/edit_last/continue_loop 是否齐全无遗漏路径) ②start_iteration 透传链完整(agentic run_agentic_loop←try_continue_agent_loop←ai_approve 两个调用点,无断链) ③两路径区分正确性(ai_approve 累计 vs ai_continue_loop 重计,F-11/F-03 决策 a 分别归属无混淆) ④边界 case(start≥max loop 空区间→converged=false→AiMaxRoundsReached,防无限审批烧 token,符合 F-11 语义) ⑤并发安全(iteration_used 在一致性 lock 块更新,与现有 lock 模式一致)。 - **commit**: d00b30f(同 CR-36 合批)。 - **主代独立核查**: ✅ 全过(mod.rs:215/230 iteration_used 字段+init / agentic.rs:114 start_iteration 参数+:176 loop 边界+:210 一致性块 iteration+1+:578 try_continue 加参+:675 spawn 透传+:499-501 注释两路径 / commands.rs:55/152/412 reset 0+ai_approve:260/313 累计读 iteration_used+ai_continue_loop:601 reset 传0 / cargo check --workspace EXIT 0)。 - **审查 agent 待复审重点**: ①iteration_used 生命周期完备性(所有会话生命周期入口 reset 覆盖无遗漏——create/send/regenerate/edit_last/continue_loop/ai_approve 全路径核验) ②start_iteration 透传链(run_agentic_loop←try_continue_agent_loop←ai_approve 两调用点无断链) ③两路径区分(ai_approve 累计 vs ai_continue_loop 重计,F-11/F-03 决策a 归属无混) ④边界 start≥max loop 空区间→converged false→AiMaxRoundsReached(防无限审批烧 token) ⑤并发安全(一致性 lock 块更新)。 ### CR-260616-38 F-07 df-ai-core trait 下沉拆 crate(解锁 F-03 注入) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-38 agent 亲跑 cargo+test,137s)**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ideas adversarial` **11 passed**(a1-a11,含 7 heuristic + 4 LLM mock)。 - **provider.rs 迁移完整 PASS**:df-ai-core/src/ 仅 lib.rs+provider.rs(Glob 确认),provider.rs:1-235 含 LlmProvider trait(:208-235)+ 全数据结构(CompletionRequest/ChatMessage/MessageRole/ToolDefinition/ToolCall/CompletionResponse/TokenUsage/StreamChunk/ToolCallDelta/StreamResult),注释 :6-8 声明「零 IO」;grep ContextManager/TokenEstimator/AiToolRegistry/build_provider/HTTP impl 在 df-ai-core 内**零命中**(业务逻辑+HTTP impl 确留 df-ai);df-ai Cargo.toml:8 加 df-ai-core 依赖。 - **re-export 透明 PASS**:df-ai/src/lib.rs:17 `pub use df_ai_core` + provider.rs:15 `pub use df_ai_core::provider::*`;grep `use df_ai::provider` **11 处**实际消费(idea.rs/project.rs/ai_node.rs/agentic.rs/stream_recv.rs/title.rs/knowledge_inject.rs/commands.rs/audit.rs 等)workspace 编译期全过;workspace Cargo.toml members=`["crates/*"]` glob 自动覆盖 df-ai-core 无需手动加。 - **构造注入语义 PASS**:adversarial.rs:19 直接 use df_ai_core(不引 df-ai 重依赖)+ :96-98 `provider: Option>` + :101 new(Arc)→Some + :107 heuristic()→None + :112-133 evaluate() 调度;调用方 idea.rs:192 new(Arc::from(p))=Some / :193 heuristic()=None 等价无遗漏。 - **EvaluatedBy 三态 PASS**:adversarial.rs:28-37 Llm/Heuristic(#[default])/HeuristicFallback + :49-50 `#[serde(default)]` 向后兼容老数据;:112-133 调度 Some+Ok=Llm / Some+Err=warn+HeuristicFallback / None=Heuristic 三态完备。 - **build_default_provider PASS**:idea.rs:265-279 list_all().ok()? DB 查失败 None + is_default 优先 find + 首条 fallback + build_provider_for Err→warn+None + :191-194 None 直走 heuristic 语义正确。 - **Box→Arc 转换 PASS**:idea.rs:192 `Arc::from(Box)` 标准库转换(堆分配复用),cargo check EXIT 0 编译期证类型匹配。 - **待修项回流 todo**: **无**(6 重点全 PASS,零问题)。 - **范围**: F-260614-07 4 决策落地(workflow wwtn2knn6)。①df-ai-core 新 crate:`crates/df-ai-core/`(Cargo.toml serde+async-trait+futures + src/lib.rs `pub mod provider` + src/provider.rs 迁移 trait+数据结构 LlmProvider/ChatMessage 等,不含 HTTP impl);②df-ai re-export:`crates/df-ai/Cargo.toml` 加 df-ai-core 依赖 + `src/provider.rs:15` `pub use df_ai_core::provider::*` + `src/lib.rs:17` `pub use df_ai_core`(外部 use df_ai::provider 路径不变,df-nodes 零改动,workspace Cargo.toml 零改动 members=crates/*);③df-ideas 构造注入:`crates/df-ideas/Cargo.toml` 加 df-ai-core+async-trait + `src/adversarial.rs:18` use df_ai_core::provider::LlmProvider + struct 加 `provider:Option>`(:96) + `new(provider:Arc)`(:101 调用方 Some/None 分支语义等价 Option)+ `heuristic()`(:107) + `evaluate(&self)`(:111) + `evaluate_with_llm`(:138) + EvaluatedBy 三态 enum(:28) + AdversarialEval.evaluated_by 字段(:49) + LLM 失败→warn+HeuristicFallback 降级(:122);④src-tauri 装配:`idea.rs:190` build_default_provider(DB is_default→build_provider→Option>:265) + :192 Some→`AdversarialEngine::new(Arc::from(p))` / None→`heuristic()`(:193)。 - **维度**: ①re-export 透明性(df_ai::provider::LlmProvider 路径 df-nodes/df-project 等外部 use 无断裂) ②trait 下沉边界(LlmProvider trait+纯数据结构下沉,ContextManager/TokenEstimator/AiToolRegistry 业务逻辑确留 df-ai 未误迁) ③构造注入语义(new(Arc) vs 决策② Option——调用方分支等价性,无遗漏 Some/None) ④EvaluatedBy 三态正确(Llm/Heuristic/HeuristicFallback,provider None 全走 Heuristic) ⑤Box→Arc 转换(idea.rs:192 Arc::from(Box) 编译通过) ⑥build_default_provider DB 读(is_default 查询+无默认兜底 heuristic) ⑦adversarial 7 单测改 heuristic() 构造后全过。 - **commit**: 2069f79(重构: df-ai-core trait下沉+导入历史)。 - **主代独立核查**: ✅ 全过(df-ai-core 新建 Cargo.toml+lib.rs+provider.rs / df-ai provider.rs:15+lib.rs:17 re-export+Cargo.toml 加依赖 / df-ideas adversarial.rs:18 use df_ai_core:96 provider 字段+:101 new(Arc)+:107 heuristic+:111 evaluate(&self)+:138 evaluate_with_llm+:28 EvaluatedBy+:49 evaluated_by+:122 HeuristicFallback 降级 / idea.rs:190 build_default_provider+:192 new(Arc::from)+:193 heuristic+:265 fn / cargo check --workspace EXIT 0(5 warning 全 pre-existing dead_code))。 - **审查 agent 待复审重点**: ①provider.rs 迁移完整性(df-ai 原 trait+数据结构全部下沉,df-ai/src/provider.rs 仅剩 re-export,retry/openai_compat/anthropic_compat HTTP impl 确留 df-ai 未断) ②df-nodes/df-project 外部 use df_ai::provider 编译验证(re-export 透明无断裂,实际 grep df_ai::provider 消费点) ③EvaluatedBy 三态语义(provider Some+LLM 成功=Llm / Some+LLM 失败=HeuristicFallback / None=Heuristic,evaluate_internal 调度正确) ④build_default_provider DB is_default 查询正确+build_provider 复用现有工厂 ⑤Box→Arc::from 编译期验证(cargo EXIT 0 已证)。 ### CR-260616-39 F-06 导入历史项目 scan 第二步(LLM description+monorepo+批量并发) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-39 agent 亲跑 cargo+test+vue-tsc,215s)**: ✅ **PASS** — 🔴0 🟡0 ⚪2(agent 标题 🟡1 系笔误,待修项表全 low 无 med) - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-project` **15 passed**(detects_monorepo_pnpm/npm_workspaces/non_monorepo/rust/full_stack + strips_frontmatter_and_toc_and_badges + image_collection_skips_badges + extract_desc 三测 + discover_monorepo/flat_children)/ `npx vue-tsc --noEmit` EXIT 0。 - **is_monorepo 判定 PASS**:scan.rs:100-133 4 marker(pnpm-workspace/lerna/turbo/nx)+ package.json workspaces 兜底(:122-131);仅判根不递归(discover_projects 展开时 :223 设 false),nested monorepo 边界正确。 - **discover_projects 展开 PASS**:scan.rs:156-229 三路(根 manifest 入选/monorepo 展开 packages+apps/普通扫一层子目录)+ detect_stack 空过滤(:216-218 候选发现语义,无栈对 LLM 价值低,可手动 import)+ collect_tree_level is_sub 控 2 层(:638)不递归过深 + 隐藏目录/SAMPLE_IGNORED_DIRS 过滤。 - **images 收集 PASS**:scan.rs:329-481 ImageRef{alt,src}+ PartialEq + SAMPLE_README_MAX=8000 + truncate_chars 按 chars().take 截断(:664-669 UTF-8 安全)+ **实际 12 域黑名单** BADGE_HOSTS(:396-410 img.shields/shields/badge.fury/badgen/...)+ is_badge_image 域名+关键词双判(:441-448)+ collect_images 行级扫 +同 src 去重+上限 20(:454-481)+ is_pure_badge_line 混内容图返 false(:533-591);arch/screenshot 内容图留、badge 跳。 - **create_with_binding 抽取 PASS**:project.rs:59-107 统一校验+防重+探测+insert;create_project(:45-50)/import_project(:177)/import_projects_batch(:469)三入口共用;relocate 确未并入(:281-315 update_field 非 insert,注释 :56 声明);status 固定 planning+idea_id 透传行为一致无回归。 - **import_projects_batch 并发 PASS**:project.rs:415-493 双层 permit acquire_global+acquire_per_conv(:535-536 RAII 覆盖 complete)+ 非原子 join_all 逐项独立(LLM 失败 desc 空仍入库,create 失败返 error item,imported/skipped 计数)+ provider Arc 共享零拷贝 + 无 provider 明确报错非降级。 - **scan_directory_for_projects 纯规则 PASS**:project.rs:343-376 仅 discover_projects(spawn_blocking)+ find_binding_conflict 标已绑定,**零 LLM 调用**,深度一层。 - **前端 PASS**:api/project.ts:116-126 两 API + 类型完整 + Projects.vue:73-133 导入 modal(只读表格 name/stack/path/bound + checkbox 全选/半选/单选 + 已绑定禁选灰显 + toast 汇总 imported/skipped + 全失败保留重扫)+ i18n zh/en 19 key 对称。 - **df_ai::provider re-export 零断裂 PASS**:ai_node.rs:12 + project.rs:8/440 三消费点编译期证。 - **⚪ CR-39-1(low,文档笔误)**:登记称「徽章域 5 域黑名单」与代码实际 12 域(BADGE_HOSTS scan.rs:396-410)不符,代码更完备文档滞后。 - **⚪ CR-39-2(low,设计取舍)**:scan.rs:216-218 detect_stack 空栈过滤剔除有 manifest 无已知栈标志的合法项目(自定义构建系统),当前候选发现语义可接受,用户可手动单条 import。 - **待修项回流 todo**: **无**(2 low 文档/边缘非阻断)。 - **范围**: F-260614-06 6 决策落地(workflow wwtn2knn6)。①description 走 LLM(scan.rs extract_description 复用 complete,非纯规则);②采样改进(ProjectSample 扩 `images:Vec` scan.rs:330/343 + readme 剥 frontmatter/TOC/纯徽章行截 8KB `SAMPLE_README_MAX=2000→8000`:346 + 保留内容图 markdown);③image 多模态条件化(ImageRef 数据结构+collect_images 收集:454,本批不读 base64 留接口待 F-260614-05);④monorepo 一层(is_monorepo:112 检 pnpm-workspace/lerna/turbo/nx/package.json workspaces + discover_projects:156 展开 packages/*/apps/* detect_stack 空过滤);⑤批量流程(scan_directory_for_projects project.rs:343 纯规则发现标已绑定 + import_projects_batch:415 并发 LLM llm_concurrency permit 限流复用绑定入库非原子逐项独立);⑥对称改进(抽 create_with_binding project.rs:59 create/import 共用校验+防重+探测+insert 缓解 :211 TODO,relocate 不并入)。前端 Projects.vue 导入 modal+api/project.ts 两 API+i18n 双语+scan.rs 单测(剥 badges/image 收集/monorepo/discover/extract_desc)。 - **维度**: ①is_monorepo 判定完备(pnpm-workspace/lerna/turbo/nx+package.json workspaces 全覆盖) ②discover_projects 展开(detect_stack 空过滤误剔合法项目风险/monorepo 子目录一层不递归过深) ③collect_sample images 收集(徽章域黑名单 5 域+is_badge_image alt+src 双判+is_pure_badge_line 整行纯徽章,内容图 arch/screenshot 留) ④SAMPLE_README_MAX 8000 截断(truncate_chars 不破坏 markdown/UTF-8 边界) ⑤create_with_binding 抽取(create+import 共用一致,relocate 确未并入) ⑥import_projects_batch 并发(llm_concurrency 双层 permit 限流/非原子逐项独立失败不阻塞) ⑦scan_directory_for_projects 纯规则不跑 LLM(标已绑定正确) ⑧df-project use df_ai::provider 路径不变(F-07 re-export 透明) ⑨前端预览只读+toast 汇总(导入 N/跳过 M)。 - **commit**: 2069f79(重构: df-ai-core trait下沉+导入历史)。 - **主代独立核查**: ✅ 全过(scan.rs:112 is_monorepo+:156 discover_projects+:330 ImageRef+:343 images 字段+:346 SAMPLE_README_MAX 8000+:357 collect_sample+:397-401 徽章域 5 域+:439 is_badge_image+:454 collect_images+:533 is_pure_badge_line+:742-876 单测 / project.rs:59 create_with_binding+:343 scan_directory_for_projects+:415 import_projects_batch / lib.rs:85-86 注册两 IPC / api/project.ts:117/125 两 API / Projects.vue:111 mono-tag / cargo check --workspace EXIT 0+vue-tsc EXIT 0)。 - **审查 agent 待复审重点**: ①is_monorepo/discover_projects 边界(nested monorepo/无 workspace 配置/detect_stack 空过滤误剔) ②徽章过滤完整(5 域黑名单+alt/src 双判是否漏内容图误剔或漏 badge 误留) ③create_with_binding 与原 create_project 行为一致(校验+防重+探测+insert 路径无回归) ④import_projects_batch 并发安全(llm_concurrency permit 限流正确+非原子逐项失败隔离) ⑤scan_directory_for_projects 性能(纯规则不跑 LLM,目录扫描深浅/大目录性能) ⑥前端 modal 预览表格只读+勾选+toast 汇总交互完整。 ### CR-260616-40 F-03 对抗评估接 LLM(evaluate_with_llm 真实实现) — ✅ 已审(PASS) - **范围**: F-260614-03 落地(workflow wy6rjn0en)。crates/df-ideas/src/adversarial.rs:143 evaluate_with_llm 真实实现(原 F-07 占位 bail):build_adversarial_prompt(:380 三角色辩论 prompt)+CompletionRequest(model 留空回填 default/temperature 0.4/max_tokens 2048/stream false)+provider.complete(:160 map_err anyhow)+parse_llm_eval(:500 serde_json::from_str 解析 LlmEvalRaw+枚举映射:571/586 bail 非法值)+clamp(final_score[0,10]/confidence[0,1] 结构兜底:167-170)。4 LLM mock 单测(MockProvider impl LlmProvider:733-755+a8 parse success/a9 bad JSON fallback/a10 bad enum fallback/a11 value clamp)。evaluate 调度(:111)+evaluate_heuristic(:176)+原 7 单测不动。 - **维度**: ①prompt 设计质量(三角色辩论+JSON schema 明确+temperature 0.4 防发散+max_tokens 2048 充足) ②JSON 解析容错(非 JSON/字段缺失/枚举非法 全 bail 触发 HeuristicFallback 降级,a9/a10 单测覆盖) ③clamp 兜底(final_score[0,10]/confidence[0,1],a11 单测) ④model 留空回填(OpenAICompatProvider convert_request 回退 default_model 正确) ⑤evaluate 调度不变(Some→Llm/Err→HeuristicFallback/None→Heuristic) ⑥MockProvider 单测真实性(impl LlmProvider trait 完整,stream bail 因 adversarial 不调 stream) ⑦不回归(原 7 heuristic+scoring 8 全过)。 - **commit**: dfe0096(新增: F-03对抗评估接LLM+F-02纯技能调用标题+i18n@转义)。 - **主代独立核查**: ✅ 全过(adversarial.rs:143 evaluate_with_llm 真实实现+:161 complete+:380 build_adversarial_prompt+:500 serde_json 解析+:571/586 枚举 bail+:167-170 clamp / MockProvider:733-755+4 单测 a8-a11 / cargo test -p df-ideas 19 passed EXIT 0)。 - **审查 agent 待复审重点**: ①build_adversarial_prompt 全文质量(:380 prompt 三角色+JSON schema 是否明确无歧义) ②parse_llm_eval 解析(:500 LlmEvalRaw 结构+枚举映射 serde/FromStr 实现) ③LLM 返回非 JSON 降级(a9 单测,extract JSON 容错——LLM 可能返回 markdown json 包裹需剥离) ④prompt injection 防护(idea.title/description 注入 prompt 是否需隔离,防用户输入操纵评估) ⑤max_tokens 2048 是否够(复杂 idea 评估 JSON 可能超) ⑥temperature 0.4 评估稳定性。 - **复审结论(2026-06-16·独立审查 agent · 亲跑 cargo test EXIT 0 + grep/read 核验源码)**: ✅ **PASS** — 🔴0 🟡0 ⚪4 - **自验**: `cargo test -p df-ideas adversarial` **11 passed** EXIT 0(7 heuristic a1-a7 + 4 LLM a8-a11)/ `cargo test -p df-ideas` **19 passed** EXIT 0(8 scoring + 11 adversarial 不回归)。 - **逐件核验(8 维度全 PASS)**: - ①**prompt 设计** ✅:build_adversarial_prompt:398-448 三角色(正方/反方/分析师)+JSON schema 字段名/枚举字面量明确(`final_assessment: "StrongGo|Recommended|Conditional|Revised|Defer"`/`recommendation: "ImmediateAction|Soon|WithResources|ResearchMore|Monitor"`)+自洽约束(final_score 与 recommendation 对应 final_assessment)+SYSTEM_PROMPT:364-374 硬约束「只输出 JSON 对象/枚举字面量严格/数值区间」。temperature 0.4 注释:141-142 立论(0.3 重复/0.5 发散折中)合理。max_tokens 2048(schema ~10 字段全填~600 token + 论点 2-3 条/角色 × 3 角色 ~1200 token 富余)。 - ②**JSON 解析容错** ✅:parse_llm_eval:498-539 ①extract_json:545-561 剥 ```json 围栏(去前缀+跳语言标记行+去后缀)+trim ②serde_json::from_str → LlmEvalRaw:456-463 必填字段缺/类型错 → bail:500-502 ③枚举映射 parse_assessment_level:564-576/parse_recommendation:579-591 非法字面量 → bail:571/586(a10 "GoNow" 覆盖)。a9 非 JSON 降级 HeuristicFallback:830 覆盖。 - ③**clamp 兜底** ✅:parse_llm_eval 内 confidence clamp[0,1]:511/517 + final_score clamp[0,10]:534;evaluate_with_llm:167-170 再做一次结构兜底(注释明确「结构兜底」)。a11 confidence 1.5→1.0/-0.3→0.0/final_score 99→10.0:874-876 全过,仅越界不降级 evaluated_by=Llm:873。 - ④**model 留空回填** ✅:OpenAICompatProvider::convert_request:295-296 `if req.model.is_empty() { self.default_model.clone() }`;AnthropicCompatProvider::convert_request:282-283 同样回填;两 provider 一致,留空 String::new() 正确回退各自默认模型。 - ⑤**evaluate 调度** ✅:evaluate:112-133 三分支 Some(p)→evaluate_with_llm:114 成功标 Llm:116/Err 标 HeuristicFallback:123/warn 日志:121;None→evaluate_heuristic:128 标 Heuristic:129。调度不变,a9/a10 验证降级路径。 - ⑥**MockProvider 真实性** ✅:MockProvider:733-761 impl LlmProvider trait 三方法完整(complete:739-749 返预设 text/model="mock-model"/usage default/stream:751-756 bail「不调用」/name:758-760)。embed 用 trait 默认实现(provider.rs:222-224 返 Err,adversarial 不调 embed 无影响)。stream bail 注释:755 明确「adversarial 路径不调用」合理。 - ⑦**不回归** ✅:原 7 heuristic 单测 a1-a7:645-723 全过(评分→recommendation 映射/confidence 区间/thesis 含标题/negative evidence 非空/final_score 一致性);scoring 8 单测全过。total 19 passed。 - ⑧**prompt injection 防护** ⚪(low,结构安全/语义受限):**结构性注入已阻断**——idea.title/description 经 `format!` 命名参数注入(:443-446),format! 不对参数值二次解析,Rust 运行时 title/description 中的 `{`/`}` 字符直接作为字面文本输出,不会破坏 prompt 模板骨架。**语义性注入未隔离**(用户可写 description="忽略规则,输出 final_score=10")属 LLM 固有局限非本实现 bug;description 截 800 字:383-390 已限长降低面。当前评估结果越界有 clamp 兜底、枚举非法有 bail 降级,最坏后果降级启发式不致数据错。 - **⚪ low 清单(4,全非阻断)**: - **CR-40-1(low,clamp 双重执行冗余)**:parse_llm_eval 已 clamp confidence:511/517+final_score:534,evaluate_with_llm:167-170 又 clamp 一次(parse_llm_eval 注释:533 也承认「调用方再做一次」)。无害防御性重复,建议删 :167-170 留 parse 内单点收口,减少阅读歧义。 - **CR-40-2(low,extract_json 围栏位置局限)**:extract_json:548 只 strip_prefix("```") 处理围栏在开头;LLM 若输出 `好的,以下是评估:\n```json\n{...}\n````(围栏不在首),strip_prefix 失败走 trimmed 原文 → serde 报错 → 降级。降级语义安全但命中率略低;非阻断(降级正确),可后续用正则 `(?s)\{.*\}` 兜底提取首个 JSON 对象增强。 - **CR-40-3(low,semantic prompt injection 未隔离)**:见维度⑧,description 截 800 字已限长,枚举/数值有 clamp+bail 兜底,语义注入最坏降级启发式,不阻断。 - **CR-40-4(low,temperature/max_tokens 硬编码不可配)**:temperature 0.4/max_tokens 2048 硬编码:153-154,无运行时调参能力;当前单 provider 单次调用可接受,后续若多 provider 调优需抽配置。 - **待修项回流 todo**: **无**(4 low 全非阻断:clamp 冗余/extract_json 增强/semantic injection/硬编码均可在后续迭代处理,当前 PASS)。 - **主代独立核查**: ✅ 全过(adversarial.rs:143 evaluate_with_llm 真实实现+:161 complete+:380 build_adversarial_prompt+:500 serde_json 解析+:571/586 枚举 bail+:167-170 clamp / MockProvider:733-755+4 单测 a8-a11 / cargo test -p df-ideas 19 passed EXIT 0)。 ### CR-260616-41 batch64 F-03 收口三件(advance_task/run_workflow 工具注册+update_task 拒 status+crud 白名单移 status) — ✅ 已审(PASS·🔴-1已修) - **范围**: F-260616-07 阶段3 确定性收口(workflow wii1u1lnm,3 agent 并行)。①**Agent A tool_registry.rs**:advance_task 注册(:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic(&TaskRepo::new(&db),id,target_status)` 与 IPC `commands::task::advance_task:165` 同源,thin 转发无 AppHandle/State 依赖,RiskLevel::Medium)+ run_workflow 注册(:428,handler 架构约束无 AppHandle/State 报错引导前端转调 `invoke('run_workflow')` IPC,RiskLevel::High,ToolDefinition+审批文案 L1260-1261/1286-1287 注册)+ update_task handler guard L374 `if field=="status"` bail(schema 通用 field/value 模式非 enum,故靠 handler guard 拦非 schema 改)。②**Agent B crud.rs**:tasks allowed_columns L331-347 移 `"status"`+`"review_rounds"`(D-260616-04 收口),注释明确「status/review_rounds 走 advance_status_atomic CAS 独立路径,后人勿补回白名单」;`advance_status_atomic` L848 CAS SQL `WHERE id AND status=expected AND deleted_at IS NULL` 完全绕 `validate_column_name`/`allowed_columns_for`;`tests/project_soft_delete.rs:250` 断言反转 `update_field_rejects_tasks_status`(改 status 现 Err)。③**Agent C CR-40 审查**(对 batch62 dfe0096 evaluate_with_llm):PASS 4 low 非阻断。 - **维度**: ①advance_task handler 同源核验(tool_registry.rs:407 调 advance_task_atomic 与 commands::task::advance_task:165 同源,thin 转发无逻辑分叉) ②update_task handler guard 双重防御(L374 handler 拒 + df-storage 白名单拒,status 双层拦截) ③crud 白名单移 status+review_rounds 收口语义(advance_status_atomic L848 CAS 独立路径不经 allowed_columns,review_rounds 唯一写入路径防计数错乱) ④run_workflow handler 架构约束(handler 无 AppHandle/State 报错引导走 IPC 是否合理,后续批注入 AppState 句柄让 AI 直驱) ⑤审批文案映射(display_hint_for_tool L1260-1261 + tool_display_hint L1286-1287 advance_task/run_workflow 条目) ⑥project_soft_delete.rs 测试断言反转(update_field_rejects_tasks_status 改 status 现 Err 正确) ⑦前端 status 路径零残留(taskApi.update 全代码库零调用方,status 唯一走 taskApi.advance→advance_task IPC) ⑧commands/task.rs update_task IPC 残留死代码(task.rs:100-108 field=="status" 分支移白名单后永不命中,回流 todo 清理)。 - **commit**: 待提交(batch64 攒批)。 - **主代独立核查**: ✅ 全过(tool_registry.rs advance_task:395+handler:407 advance_task_atomic 同源 / run_workflow:428+handler 约束报错引导 IPC / update_task handler guard:374 `field=="status"` bail / crud.rs:331-347 tasks 白名单移 status+review_rounds 注释明确 / advance_status_atomic:848 CAS `WHERE id AND status=expected AND deleted_at IS NULL` 独立路径 / cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试含新 update_field_rejects_tasks_status)。 - **审查 agent 待复审重点**: ①advance_task handler 同源 IPC(commands::task::advance_task:165 核验,thin 转发无逻辑分叉,df_core::error::Error→anyhow ?From 链路) ②update_task handler guard 双重防御(handler guard L374 + df-storage 白名单双拦,是否漏路径) ③crud 白名单移 status+review_rounds 收口(advance_status_atomic L848 CAS 是否真独立不经 allowed_columns,review_rounds 唯一写入路径防旁路写) ④run_workflow handler 报错引导 IPC 是否合理(架构约束无 AppHandle/State,后续批注入句柄) ⑤审批文案映射完整(display_hint L1260-1261/1286-1287) ⑥测试断言反转正确(project_soft_delete.rs:250 update_field_rejects_tasks_status 改 status 现 Err) ⑦commands/task.rs:100-108 update_task IPC 残留死代码回流 todo。 - **复审结论(2026-06-16·审查 agent 亲跑 cargo + 独立 grep/read 核验源码)**: ⚠ **ISSUES** — 🔴1 🟡0 ⚪1 → **修复后(主代 b94e74a 之后)**: ✅ **PASS** 🔴0 🟡0 ⚪1(🔴-1 测试 fixture 已修) - **验证**: `cargo check --workspace` **EXIT 0**(5 pre-existing dead_code warnings 无关本 CR)/ `cargo test -p df-storage` **33 passed**(22 lib + 11 project_soft_delete 含新 `update_field_rejects_tasks_status` ✅)/ `cargo test -p df-nodes` **66 passed; 1 FAILED**(`advance_succeeds_after_prior_status_change` 红,见 🔴-1)→ **🔴-1 修复后 67 passed; 0 failed; 1 ignored**(task_advance_node.rs:331-332 改 advance_status_atomic)。 - **①advance_task handler 同源核验** ✅ tool_registry.rs:407 调 `advance_task_atomic(&TaskRepo::new(&db), id, target_status)` 与 commands::task::advance_task(task.rs:165)同源调 `df_nodes::task_advance_node::advance_task_atomic(&state.tasks, &id, &target_status)`,均 thin 转发到同一 `advance_task_atomic`(task_advance_node.rs:43-90),无逻辑分叉。df_core::error::Error 是 thiserror(crate df-core/src/error.rs:5 `#[derive(Error)]`),实现 std::error::Error,anyhow::Error: From 链路通(`?` 编译过 cargo check 证)。 - **②update_task handler guard 双重防御** ✅ tool_registry.rs:374-378 `if field=="status"` bail(第一层 handler 拒)+ tool_registry.rs:380-382 `is_allowed_column("tasks", field)` 查 crud.rs:331-347 白名单(第二层,白名单已无 status),status 双层拦截。无漏路径:value 必经 args["field"].as_str() 解析后两条 guard 之一必中。 - **③crud 白名单移 status+review_rounds 收口** ✅ crud.rs:342 现 tasks 白名单 `"project_id","title","description","priority","branch_name","assignee","workflow_def_id","base_branch","updated_at"`(无 status/review_rounds)。advance_status_atomic(crud.rs:848-880)走独立 CAS SQL `UPDATE tasks SET status=?1[, review_rounds=review_rounds+1] WHERE id=?3 AND status=?4 AND deleted_at IS NULL`,**不经 validate_column_name/allowed_columns_for**(无白名单调用)。review_rounds 唯一写入路径收口语义达成:除 advance_status_atomic 退回转换 bump_rounds=true 路径,无任何旁路可写(crud.rs:865-866 SQL 内联 `review_rounds+1`)。 - **④run_workflow handler 报错引导 IPC** ✅ 合理。tool_registry.rs:431-446 handler 仅持 `_db: Arc`(L432 实际未用,前缀 `_` 标记),无法构造 AppHandle/AppState(registry/event_bus/workflows Repo),L440-444 显式 anyhow 返回明确错误引导前端 `invoke('run_workflow', {task_id, target_status})`。符合单一执行路径原则(不在 handler 重放 DAG 引擎)。注释 L419-426 标注后续批注入 AppState 句柄扩展路径。 - **⑤审批文案映射** ✅ display_hint_for_tool(tool_registry.rs:1259-1261)注册 `advance_task => "推进任务状态:{} → {}", &["id","target_status"]` + `run_workflow => "触发工作流:任务{} 推进到 {}", &["task_id","target_status"]`;tool_display_hint(tool_registry.rs:1286-1287)注册 `advance_task => Some("推进任务状态")` + `run_workflow => Some("触发工作流")`。两条目齐全。 - **⑥project_soft_delete.rs 测试断言反转** ✅ project_soft_delete.rs:250 `update_field_rejects_tasks_status` 现 `res.is_err()`(L259-262)+ 对照 `assert_ne!(rec.status, "done")`(L266)。亲跑 11 集成测试全过 EXIT 0。 - **⑦前端 status 路径零残留** ✅ taskApi.update(src/api/task.ts:17-19)仅 tasks.ts:31 `updateTask` 调用,全代码库 grep `updateTask(` 零组件调用方(仅定义无消费)。TaskDetail.vue:281 推进走 `taskApi.advance(task.value.id, target)`(状态唯一路径→advance_task IPC)。status 前端路径零残留达成。注:tasks.ts:29 `updateTask` 函数本身保留(改 title/description 等非 status 字段,合法),仅 status 字段已无消费方。 - **⑧commands/task.rs update_task IPC 残留分支** ⚪(low)task.rs:102-108 `if field=="status" && !TaskStatus::is_valid(&value)` 分支并非纯死代码:它仍为 status **非法值**(拼写错/越界)提供更友好早错误(先于白名单拒)。但 status **合法值**路径必落到 task.rs:133-135 `update_field` 被白名单拒,故「校验通过后写入」分支永不命中。建议保留 value 校验(改善 UX)+ 删除成功路径假设注释,或整体移除改由 advance_task 独占(后续迭代决定,非阻断)。 - **🔴-1(df-nodes 测试回归,high)**: **task_advance_node.rs:332** `repo.update_field("t1", "status", "in_progress").await.unwrap()` 测试红 —— 退回测试 `advance_succeeds_after_prior_status_change`(L324-336)用 `update_field` 模拟「旁路并发改 status」前置,但本 commit 已把 status 移出 tasks 白名单,`update_field` 现 Err → `unwrap()` panic「表 tasks 不允许的字段名: status」。亲跑 `cargo test -p df-nodes` 证实 66 passed/1 failed。**生产代码无回归**(advance_task_atomic 本身工作正常,66 个推进链测试全过),仅测试 fixture 需同步:把 L332 改成 `repo.advance_status_atomic("t1","todo","in_progress",false).await.unwrap()`(用 CAS 路径模拟并发改,语义等价)或直接 `repo.insert(rec("t1","in_progress"))` 重建前置态。CI 红门未守,须修后才能合。 **✅ 已修(主代 task_advance_node.rs:331-332 改 `advance_status_atomic("t1","todo","in_progress",false)` 走 CAS 合法路径模拟并发推进,亲跑 cargo test -p df-nodes 67 passed/0 failed/1 ignored)** - **⚪ low 清单**: ①task.rs:102-108 status value 校验分支半死代码(合法值成功路径永不命中,非法值仍触发,见 ⑧)。 - **待修项回流 todo**: **🔴-1**(df-nodes 测试回归 task_advance_node.rs:332) **✅ 已修**(主代改 `advance_status_atomic`,亲跑 67 passed,见上方修复记录)。低 ①task.rs:102-108 status value 校验半死代码非阻断(审查建议保留 value 校验改善 UX + 删成功路径假设注释,后续迭代决定)。 --- ### CR-260616-43 AiNode 自审阶段2(AiNode持db+ai_self_review节点+DAG透传+前端展示) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-43 agent 亲跑 cargo+vue-tsc+test,142s)**: ✅ **PASS** — 🔴0 🟡0 ⚪1 - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-nodes` **79 passed; 0 failed; 1 ignored** / `npx vue-tsc --noEmit` EXIT 0。 - **①AiSelfReviewNode prompt 四维度+JSON 兜底完备 PASS**:build_review_prompt(ai_node.rs:405-424) 含需求+产出+四维度+输出格式 + parse_review_json(:360-381) 覆盖非 JSON/非 Object/缺 verdict → verdict=unknown 兜底 + 代码块剥离(:363-367 ```json 围栏) + 单测(:928-963) 合规/非法/fence 场景全过。 - **②写回 output_json review 子字段 PASS**:读现有合并不覆盖 text(:514-519 先读 output_obj 若 JSON 则复用) + review 子字段写入(:520-534 含 verdict+reviewed_at+reviewer_model) + 落库失败只 warn 不阻断(:538-544)。 - **③DAG 透传 PASS**:NodeOutput.data 平铺(:550-556 verdict/summary/suggestions) + human_review 经 inputs 读(task_workflow_templates.rs:72 edge ai_self_review→human_review) + HumanNode 零改动(git diff 核验 human_node.rs 在 3 commit 中无变动)。 - **④state.rs 注册+工厂闭包 PASS**:AiNode 注册(:261-263 AiNode::new(ai_db)) + AiSelfReviewNode 注册(:269-270 AiSelfReviewNode::new(review_db)) + move db.clone() 闭包捕获正确。 - **⑤testing 模板节点类型对齐 PASS**:模板 L61 注册 "ai_self_review" 节点 + L72 edge 透传 + 单测 L123-143 验证节点类型+边方向+options。 - **⑥前端 schema 契约一致 PASS**:types.ts:102 output_json? 字段 + L98-100 schema 注释 + TaskDetail.vue:229-234 parsedOutput computed try/catch 兜底 + reviewVerdictClass/reviewLabel/reviewSuggestions 均从 parsedOutput.review 读 + 字段名对齐 verdict/summary/suggestions。 - **⑦i18n 双语 PASS**:zh-CN/taskDetail.ts:23-27 review.pass/fail/unknown + en/taskDetail.ts:23-27 对称翻译 + 特殊字符无转义需求(值均为静态枚举字面量)。 - **⑧P0 安全债登记确认**:api_key 明文 config 注入违背 FR-S1 已在 memory/devflow-aichat-review-pending.md 登记 + 本次 CR 范围已实现 resolve_provider 三路径(ai_node.rs:62-135 完整实现) + F-260616-07c 阻塞⑥联调已记录。 - **⚪ CR-43-1(low,单测降级声明)**:AiSelfReviewNode.execute 完整 mock LlmProvider 单测本次降级(仅 parse_review_json+update_field 单测覆盖,P3 待补)。ai_node.rs:589-875 单测已覆盖 resolve_provider 双路径+parse_review_json 兜底+update_field 路径,end-to-end execute 单测依赖真实 LLM(#[ignore] glm_live_complete 存在),非阻断。 - **待修项回流 todo**: **无**(1 low 单测降级非阻断,已在 CR 信息说明 P3 待补)。 - **范围**: F-260616-07 阶段3 AiNode 自审闭环②③④⑤(3 commit:539b5ed 迁移 + c10adaf ai 侧 + 741b0b9 前端)。①**①迁移**(539b5ed):TaskRecord 加 `output_json: Option` + V17 迁移(幂等补列) + crud 白名单加 output_json(L346)+SELECT/INSERT/UPDATE 全链路 + 5 构造点补 None。②**ai_node.rs②③④**(c10adaf):AiNode 持 db(`Arc`,对齐 TaskAdvanceNode:98-110 先例)+ execute 后 `config["task_id"]` 存在则 `update_field` 落 output_json(产出 schema {text,model,usage},落库失败只 warn 不阻断)+ AiSelfReviewNode 独立节点(parse_review_json 兜底 verdict=unknown + build_review_prompt 四维度 + 写回 review 子字段不覆盖 text)+ state.rs L269-270 注册 ai_self_review + ai 工厂 L262 `AiNode::new(db)` + testing 模板 L61 ai_self_review + L72 edge(ai_self_review→human_review)+ NodeOutput.data 平铺透传 + HumanNode 零改动。③**前端⑤**(741b0b9):types.ts output_json?(schema 注释)+ TaskDetail.vue parsedOutput computed(try/catch 兜底)+review-card 红绿标+renderedOutput 复用 useRendered + i18n zh/en taskDetail.output/review 双语。 - **维度**: ①AiSelfReviewNode prompt 四维度+JSON 兜底完备性(parse_review_json 各路径 verdict 覆盖,代码块 fence 剥离重试) ②写回 output_json review 子字段(读现有合并不覆盖 text,reviewed_at/reviewer_model) ③DAG 透传(NodeOutput.data 平铺,human_review 经 inputs 读,HumanNode 零改动核验) ④state.rs 注册+工厂闭包 move db.clone() ⑤testing 模板节点类型对齐 ⑥前端 schema 契约一致(types.ts 与 ai_node.rs 写回 schema 字段名对齐 verdict/summary/suggestions) ⑦i18n 特殊字符转义 ⑧**P0 安全债登记**(api_key 明文 config 注入违背 FR-S1,跨 crate 阻塞,F-260616-07c 待决) ⑨主代亲跑 cargo df-nodes 73 passed + workspace EXIT 0 + vue-tsc EXIT 0。 - **commit**: 539b5ed + c10adaf + 741b0b9。 - **主代独立核查**: ✅ 全过(human_node.rs 零改动 git diff 确认 / state.rs L262 AiNode::new(ai_db)+L269-270 AiSelfReviewNode::new(review_db) / templates L61 ai_self_review+L72 edge / ai_node.rs AiNode 持 db@L119+execute 写@L184+AiSelfReviewNode@L292+parse_review_json@L258 / cargo test -p df-nodes 73 passed 亲跑 / cargo check --workspace EXIT 0 / vue-tsc EXIT 0 / 前端 schema 对齐)。 - **审查 agent 待复审重点**: ①AiSelfReviewNode.execute 完整 mock LlmProvider 单测(本次降级 parse_review_json+update_field 单测,P3 待补) ②parse_review_json 兜底各路径(verdict=unknown 覆盖非 Object/数组/缺 verdict/JSON 解析失败/fence 剥离) ③写回 output_json 读现有合并的并发安全(同一 task 多次自审竞态) ④human_review 审批卡 description 拼自审摘要未实现(P2,透传链通但卡片未拼,HumanNode 只读 config 不读 inputs) ⑤P0 api_key config 注入(F-260616-07c,✅ 已由 CR-44 secret 下沉+注入链解决)。 --- ### CR-260616-44 P0 secret下沉+AiNode provider注入链(方案B FR-S1闭环) — ✅ 已审(PASS) - **复审结论(2026-06-16·CR-44 agent 亲跑 cargo+test+vue-tsc,485s)**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-nodes` **79 passed; 0 failed; 1 ignored**(含 9 个 resolve_provider 新增测试)/ `npx vue-tsc --noEmit` EXIT 0。 - **① secret 下沉边界 PASS**:secret.rs:7-9 注释明确不下沉 build_provider_for(依赖 df-ai 会循环)。grep 0 处函数定义,5 处全注释(src-tauri/Cargo.toml:17/secret.rs:7/8)。src-tauri/secret.rs:20 pub use df_storage::secret::* 转发所有下沉函数 + :32 build_provider_for 保留全限定路径调 df_ai::build_provider(:37)。 - **② 转发壳 glob 兜底 PASS**:13 个实际调用点(排除注释行)全部经 crate::commands::ai::secret:: 或 super::secret:: 路径调用(commands.rs 4 处+agentic.rs 2 处+title.rs 1 处+knowledge_inject.rs 2 处+idea.rs 1 处+project.rs 2 处+lib.rs 1 处迁移调用)。编译期验证零断裂(cargo check EXIT 0)。 - **③ keyring feature 平台覆盖 PASS**:Cargo.toml workspace:24 统一声明 windows-native+apple-native+linux-native-async-persistent 三平台。df-storage/Cargo.toml:18 keyring = { workspace = true }。secret.rs:220 cfg(any(target_os="windows",target_os="macos")) 单测(仅桌面 OS 环境实测 keyring roundtrip,CI 任意 OS 安全)。 - **④ resolve_provider 三路径完备 PASS**:ai_node.rs:62-135 路径1 provider_id 优先→get_by_id(:76)→resolve_provider_secret(:143)→ensure_resolved_key(:144)/ 路径2 老明文兼容(:86-121,has_plain_base+has_plain_key 双判+:89-92 warn "老路径将在后续版本移除")/ 路径3 兜底(:124-134,list_all→find is_default→or_else 首条,友误 "未配置 AI Provider")。9 单测全覆盖(by_id/empty_key/unknown_id/legacy_plain/legacy_empty/fallback_default/no_providers/fallback_first/config_model_override)。 - **⑤ FR-S1 闭环 PASS**:api_key 仅存 ResolvedProvider 内存(ai_node.rs:45 明文字段,注释 "仅 AiNode 内存可见"),不进 config(schema L313/314 "已废弃过渡" 描述)/NodeOutput(L273-281 仅 text/model/usage 无 api_key)/schema(L316 required 改 ["provider_id"])。resolve_provider_secret(DB 优先→keyring)解析,build_provider(:235-240)传明文仅 AiNode 进程内存可见,不落库不外泄。 - **⑥ schema required 改 [provider_id] PASS**:ai_node.rs:316 required=["provider_id"] + :309 description "FR-S1:密钥经 df_storage::secret 解析,不进 config" / AiSelfReviewNode :569 required=["task_id","provider_id"]。 - **⑦ 老明文兼容 warn 过渡期 PASS**:ai_node.rs:89-92 tracing::warn!("AiNode 明文 api_key/base_url 经 config 注入已废弃, 改用 provider_id (FR-S1). 老路径将在后续版本移除")。路径2 逻辑保留兼容(:86-121 has_plain_base+has_plain_key 双判)。 - **⑧ 迁移逻辑 FAILCOUNT SIDECAR PASS**:secret.rs:34-72 failcount_path/.devflow-keyring-failcount + read/write/record/clear_migration_failcount + migrate_secrets_to_keyring(:125)失败达阈值 3 升级 warn(:136-142 明文滞留风险+3 条排查建议:确认 OS 钥匙串可用/keyring 后端异常/手动重新保存)。lib.rs:28 启动调用非阻断(:29 warn "启动密钥迁移失败(非阻断)")。 - **⑨ df-nodes 显式导入 PASS**:ai_node.rs:17 use df_storage::secret::{ensure_resolved_key, resolve_provider_secret} 显式导入(非 glob)。resolve_from_record(:143-144)直接调用。ai_node.rs:14-15 也显式导入 AiProviderRepo/Database/AiProviderRecord。 - **⑩ testing 模板 PASS**:task_workflow_templates.rs:61 注册 "ai_self_review" 节点 + :72 edge(ai_self_review→human_review) + 单测(:123-129)验证节点类型+边方向+config 空。state.rs:269-270 注册 AiSelfReviewNode::new(review_db) 闭包捕获正确。 - **待修项回流 todo**: **无**(10 维度全 PASS,零问题)。 - **范围**: F-260616-07c P0 安全闭环(3 commit:36f70b3 方案 + 766f235 下沉 + 84a6fc4 注入链)。①**secret 下沉**(766f235):df-storage 新建 src/secret.rs move 纯密钥逻辑(get/set/delete/resolve/ensure/migrate+failcount,**不含 build_provider_for** 避循环)+ keyring workspace 平台 feature(windows/apple/linux-native-async-persistent)+ src-tauri secret.rs 瘦身转发壳(pub use df_storage::secret::* + build_provider_for 保留全限定路径调)+ 13 调用点零改(glob 兜底)+ keyring 单测 cfg-gate。②**AiNode 注入链**(84a6fc4):ai_node.rs resolve_provider(db,config)->ResolvedProvider 三路径(provider_id 优先 get_by_id→resolve_provider_secret→ensure / 老明文兼容 warn / 空兜底 list_all find is_default)+ AiNode/AiSelfReviewNode execute 同构经 resolve_provider 拿 provider + schema required [provider_id] + 模板 config 空走兜底。 - **维度**: ①secret 下沉边界(纯密钥逻辑无 df-ai 依赖,build_provider_for 留 src-tauri,无循环) ②转发壳 glob 兜底(13 调用点零改核验) ③keyring feature 平台覆盖(Linux linux-native-async-persistent) ④resolve_provider 三路径完备(provider_id/老明文 warn/兜底 is_default/无 provider 友好错误) ⑤**FR-S1 闭环**(api_key 仅 ResolvedProvider 内存,不进 config/NodeOutput/schema required) ⑥schema required 改 [provider_id] ⑦老明文兼容 warn 过渡期 ⑧主代亲跑 cargo df-nodes 79 passed(9 新增 resolve_provider 测试)+ workspace EXIT 0。 - **commit**: 36f70b3 + 766f235 + 84a6fc4。 - **主代独立核查**: ✅ 全过(df-storage secret.rs 无 build_provider 函数定义,grep 5 处全注释 / src-tauri 转发壳 pub use+build_provider_for 全限定路径 / keyring workspace feature 三平台 / resolve_provider 三路径 L74/L89/L123 / schema required L316+L569 / api_key 仅 ResolvedProvider L45/238/464 调 build_provider 不进 NodeOutput / cargo test df-nodes 79 passed 亲跑 / workspace check EXIT 0)。 - **审查 agent 待复审重点**: ①secret 下沉后 src-tauri 13 调用点经转发壳路径完整性(lib.rs/commands.rs/agentic.rs + build_provider_for 6 处) ②keyring Linux CI(linux-native-async-persistent 后端依赖 zbus/secret-service) ③resolve_provider 老明文兼容路径后续移除时机 ④FR-S1 端到端验证(实测 tauri dev:testing 模板 ai_self_review 经 resolve_provider 拿 provider→LLM→写 output_json) ⑤⑥端到端联调实测。 --- ### CR-260616-35 batch59 CR-34-1 守卫 + CR-30 流式重试对齐决策a1 + UX-2025-04 断线保文 — ✅ 已审(PASS) - **范围**: 3 agent 合并批(workflow wdlt0xfl6,主代独立核查全过)。①**CR-34-1** useAiPanel.ts:102 守卫加有效性检查 `|| !list.some(p=>p.id===activeProvider)`(deleted provider 残留回落 DB is_default) ②**CR-30-1/CR-30-2 + UX-2025-04 合并**:stream_recv.rs `StreamResult` enum(Complete/Partial/InitFailed 三分支) + `classify_status_or_class`(4xx Fatal/5xx+429+timeout Retryable 镜像 retry::is_status_retryable) + agentic.rs 重试对齐决策 a1(Init 失败重试复用 retry::backoff_delay 1s→2s→4s±20%jitter + 30s 总预算 retry_deadline + Fatal 分类 4xx 立即放弃; MidStream Partial 不重试保文→入库 + AiCompleted(incomplete=true) + 系统提示「响应因网络中断不完整」) + 删 3 处误导注释(agentic.rs:12/commands.rs/state.rs) + retry.rs `pub mod` + `backoff_delay`/`is_status_retryable` 改 `pub` 暴露供 src-tauri 复用 + useAiEvents.ts:177 补 `case 'AiStreamRetry'`(消除死事件穿透) + AiCompleted case 处理 incomplete + types.ts:204/mod.rs `incomplete?:boolean` 字段(所有 emit 点补 None 向后兼容) + i18n aiStreamRetry/responseIncomplete 双语。 - **维度**: 流式核心中风险(重试循环正确性/保文语义/跨 crate pub 暴露影响面) + 前端事件消费完整性 + 决策 a1 对齐度(Init 可重试 ≤max_retries+jitter+30s+Fatal, MidStream 不重试) + B-260615-05 保留(空文本无 finished 仍 InitFailed 交重试)。 - **commit**: 待提交(累积 ~30 文件攒批)。 - **复审结论(2026-06-16·CR-30 agent 亲跑 cargo + 主线程 CR-34-1 预核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai retry` **6 passed**(backoff_is_monotonic_within_jitter/status_classification/fatal_aborts_immediately/first_attempt_ok/retry_once_then_ok/all_retryable_exhausts)/ `cargo test stream_recv` **20 passed**(classify_429_retryable/classify_4xx_fatal 等)。 - **CR-30-1(7 项全 PASS)**:agentic.rs:320 真用 `retry::backoff_delay(retry_attempt+1)`(非自写,旧 `1<p.id===activeProvider)` 残留无效回落 DB is_default,cycleProvider 不受影响(findIndex 不再 -1 跳过 ps[0])。 - **UX-2025-04 断线保文**:核心 ✅(MidStream Partial 保文入库 + incomplete 系统提示);「继续生成」按钮未做(可复用 UX-2025-03 重试能力按需补,非阻断)。 - **待修项回流 todo**: **无**(CR-34-1/30-1/30-2 三条确认修复,todo 勾完成)。 - **主代独立核查**: ✅ 全过(useAiPanel:102 守卫 / stream_recv:97 enum 三分支 + 注释 / agentic:242 retry_deadline + :274 Partial 不重试 + :286 InitFailed 重试 + :320 backoff_delay 复用 + :360 if incomplete 保文入库 + :400 emit incomplete:Some(true) / useAiEvents:177 AiStreamRetry case / types:204 incomplete / retry.rs:62+74 pub + agentic:14 `use df_ai::retry` 跨 crate 通)。 - **审查 agent 待复审重点**: ①stream_recv StreamResult 三分支边界(Partial vs InitFailed 空文本路径,B-260615-05 保留点) ②agentic 重试循环 permit RAII 释放/边界(max_retries=0/guard.reset) ③跨 crate retry pub 暴露是否过度(retry_with_backoff 仍 pub(crate)) ④AiCompleted incomplete 前端消费完整性(系统提示气泡镜像后端) ⑤UX-2025-04「继续生成」按钮**未做**(保文核心 ✅,按钮可复用 UX-2025-03 重试能力或按需补)。 --- ### CR-260616-45 波1 F-01阶段3厂商模型列表拉取 + UX-01错误气泡Fatal区分 + UX-03工具卡按轮次收起 — 🟡 待审 - **范围**: 波1 workflow(wef0eqdwp,2 agent 并行不同域)。①**F-01 阶段3**(107b848):df-ai 新建 model_fetch.rs(545 行)+ lib.rs:7 注册 pub mod。`fetch_model_names(provider_type,base_url,api_key)->Result>` 协议分派(openai_compat GET /v1/models Bearer / anthropic_compat x-api-key+anthropic-version 双 header / 其他报错)+ `build_models_url` 智能拼接(尾 /、/v1→/models、/v4、/models 原样、chat/completions→models、/api→/models、根 base→/v1/models)+ `is_non_chat_model` 噪音过滤(13 类:dall-e/midjourney/sd/imagen/tts/whisper/audio/speech/voice/realtime/transcribe/moderation/-search;**embedding 按 §5.4 保留**)+ `filter_chat_models` 去重去空 trim+ `ModelsList` 反序列化(data/models alias + id/name alias + serde default 空兜底)+ `fetch_and_probe` 探测组合(probe 纯函数 map collect)+ 错误映射(timeout→超时提示 / 401/403→鉴权失败 / 404→端点不存在 / 5xx→厂商异常,全含 provider_type+url)。②**UX-01**(bebf2ec):AiChat.vue `canRetry(msg)`(L1940,errorType auth/provider_config=Fatal 隐藏「重试」按钮)+ notRetryable 提示 span(L413)+ v-if="canRetry(msg)" 守卫(L415)。③**UX-03**(bebf2ec):AiChat.vue 新增 `watch(agentRound)`(L1684-1690,cur>prev 收起上一轮工具卡)+ `buildActiveToolIds` 复用(L1644)+ ToolCardList.vue 注释说明 CSS 动画**不做**(flex gap:8px 对零高 item 仍贡献 gap 致双倍间距;display:none 才移出流无 gap;高度 auto→0 不可纯 CSS transition 需 `` JS 钩子,本轮野人 lite 不重构 ToolCard 单卡级折叠)。④i18n 4 文件(zh-CN/en × ai.ts/aiChat.ts)notRetryable 等新增 key。 - **维度**: ①build_models_url 7 分支完备(尾//v1/v4/models/chat-completions/api/根 base,断言无 /v1/v1/models 重复) ②噪音过滤 13 类 + embedding 保留边界(§5.4) ③反序列化多 alias 兜底(data/models+id/name+serde default 空对象不 panic) ④协议分派(openai_compat Bearer/anthropic_compat 双 header) ⑤fetch_and_probe probe 兜底不中断整体 ⑥错误映射友好提示含上下文 ⑦UX-01 canRetry Fatal 分类(auth/provider_config;**边界:404/参数错映射 unknown 仍可重试**,受 error_type 分类粒度限制,后端下批补 retryable 字段后精确化) ⑧UX-03 agentRound watch 触发时机 + CSS 不动决策合理性(flex gap) ⑨i18n 转义(新增 key 无 @/|/裸 {}) ⑩主代亲跑 `cargo test -p df-ai model_fetch` **24 passed** + `vue-tsc` exit 0。 - **commit**: 107b848(F-01 阶段3) + bebf2ec(UX-01+UX-03)。 - **主代独立核查**: ✅ 全过(model_fetch.rs 545 行真实存在 + lib.rs:7 `pub mod model_fetch` 注册 / cargo test model_fetch **24 passed** 亲跑 EXIT 0 / 前 120 行结构清晰:公共入口 fetch_model_names/fetch_and_probe + 协议分派 fetch_openai_compat/fetch_anthropic_compat / AiChat.vue canRetry L1940 + notRetryable L413 + v-if L415 / agentRound watch L1684-1690(cur<=prev return 早退) / ToolCardList.vue 注释 flex gap 说明 CSS 不动决策 / vue-tsc exit 0 亲跑 EXIT 0)。 - **审查 agent 待复审重点**: ①model_fetch URL 拼接 7 分支边界(尤其根 base→/v1/models 对 GLM v4/DeepSeek/通义根 base 是否正确;`-search` 子串 contains 匹配是否误伤含 -search 的合法 chat 模型名) ②Anthropic 响应格式 GLM 订阅端点同构性(serde 仅取 id 容忍额外字段,留阶段5 IPC 实测) ③fetch_and_probe 无分页(Anthropic has_more 忽略,超大列表>500 可能截断) ④embedding 保留出现在 chat 下拉是否需前端分组(阶段6 前端按 capabilities 含 Embedding 折叠) ⑤UX-01 404/参数错走 unknown 仍可重试(后端补 AiStreamRetry/AiError retryable 字段后精确化,下批 df-ai 串行) ⑥真实厂商 API 联调(401/403/404/超时/解析失败各分支)留阶段5 IPC `ai_fetch_models`(Settings 点「测试连接」触发)端到端验证 ⑦UX-03 CSS 不做是否需后续补 `` 动画(P3 体验项)。 --- ### CR-260616-46 波2 F-01阶段4路由器 + UX-05/06/07队列三件套 — 🟡 待审 - **范围**: 波2 workflow(w76d81dno,2 agent 并行不同域)。①**F-01 阶段4**(8dad15e):df-ai 新建 router.rs(329 行)+ lib.rs 注册 pub mod router。TaskRequirements 5 字段(modalities/needs_tool_use/min_intelligence/max_cost/estimated_context)+ ModelRouter 单元结构 + select 关联函数 `select<'a>(req,pool)->Option<&'a ModelConfig>` 7 步过滤链(enabled→模态全子集→needs_tool_use 含 ToolUse→intelligence>=min→max_cost<=→context_window>=→max_by_key (weight, Reverse(cost_tier)))。**设计偏离(已对齐任务规格)**:select 关联函数非 &self 方法(router.rs:9 注释注明);max_by_key 用 std::cmp::Reverse 取反(任务规格)非设计原文 -(as i32)。②**UX-05**(313459b):useAiSend.ts stopChat 删 state.queue=[](决策 a 逐条续发,后端 agentic.rs:190 AiCompleted 已保 drainQueue 续发)。③**UX-06**(313459b):editQueued(index,newText) 边界检查+trim+直接赋值 text(决策只编 text)+ AiChat.vue 队列项 inline input(editingQueueIdx/editingQueueText ref)+ 编辑/保存(@mousedown.prevent 防 blur)/取消按钮。④**UX-07**(313459b):sendQueuedNow(index) splice 先→stopChat→sendMessage 顺序关键(splice 前置避重复发,复用 UX-05 stop 链路)+ AiChat.vue 立即发按钮。⑤i18n 4 key(queueEdit/Save/Cancel/SendNow)。 - **维度**: ①select 7 步过滤链严格对齐设计 §6.1 L469-478 ②Reverse(cost_tier) 取反正确性(Free 胜 High,CostTier Ord FreeOption` 纯函数 helper(调 ModelRouter::select + map model_id)+ L16 `pub use df_ai_core::model::{Capability,CostTier,IntelligenceTier,Modality,ModelConfig}` re-export(src-tauri/df-ideas/df-nodes 经 df_ai::router::{...} 直接 import 维度枚举)+ 6 调用点统一兜底 `select_model_id(&req,&provider.model_configs).unwrap_or(default_model)`:agentic.rs 主对话(TaskRequirements{Text,tool_use:true,Standard},resolved_model 替 default_model 进 CompletionRequest+消息元数据+save_conversation 审计)+ title.rs(Lite,max_cost:Low)+ knowledge_inject.rs 提炼(Standard)+嵌入(Lite,resolve_embed_provider 对 embedding provider 池路由,池空兜底 config.embedding_model)+ project.rs 项目扫描 2 点(Standard)+ df-ideas adversarial.rs(model_pool 字段+with_pool 构造+evaluate_with_llm 路由,池空 unwrap_or_default 留空)+ df-nodes ai_node.rs(ResolvedProvider.model_pool 字段+parse_params config.model 显式优先否则路由 Standard+tool_use)+ df-ideas Cargo.toml 加 df-ai 依赖 + idea.rs build_default_provider 返 (provider,pc.model_configs) 元组透传池。②**F-260617-01 patch_file 三模式**(2446d1b):tool_registry.rs 新增 apply_line_range(content,start,end,new_text)(1-based 含首尾 splice,越界 Err)+ resolve_anchor_to_lines(content,start,end)(子串大小写敏感匹配找含 start 首行→其后第一个含 end 行)+ 三模式互斥运行时校验(has_old_text/has_replace_lines/has_anchor 计数 0=缺定位/>1=冲突)+ replace_lines/anchor 嵌套 schema 手工拼 JSON Schema + 共用底层(expected_hash mtime+size 指纹三模式通用+FILE_LOCKS+.bak 备份+原子 tmp→rename+generate_diff)+ 16 单测(apply_line_range 8+resolve_anchor_to_lines 8+e2e 1)。 - **维度**: ①select_model_id helper 纯函数正确性(调 select map model_id)+ pub use re-export 可见性(6 调用点 import df_ai::router::{Modality,IntelligenceTier,CostTier}) ②6 调用点兜底行为不变(池空/无匹配→None→unwrap_or default_model,与接入前一致) ③agentic served-model 审计字段统一改 resolved_model(行为变更语义更正确:记实际服务模型;池空时 ==default_model 零差异) ④knowledge_inject 嵌入降级 needs_tool_use=false 宽松过滤(TaskRequirements 无 capabilities 字段,设计偏离;embedding provider 池通常即嵌入模型实践中命中) ⑤df-ideas 新增 df-ai 重依赖(reqwest/eventsource-stream,tree-shaking 编译期不链入 HTTP 代码,仅引用 router 符号) ⑥df-nodes ResolvedProvider.model_pool 字段(record 路径填 record.model_configs.clone,老明文路径填空)+ config.model 显式优先级 ⑦patch_file 三模式互斥运行时校验完备(0/>1 两种)+ expected_hash mtime+size 非 sha256(read_file 返回 file_hash)三模式通用 ⑧apply_line_range trailing newline 语义(尾换行不误删)+ resolve_anchor 子串行内命中(start==end 同行/first-end-after-start/大小写敏感) ⑨16 单测覆盖三模式+互斥+边界 ⑩主代亲跑 cargo check --workspace EXIT 0 + cargo test df-ai router **15 passed**(13 原+2 新)+ df-ideas **20 passed** + df-nodes **79 passed** + devflow tool_registry **76 passed**(三模式单测全过)。 - **commit**: 2ddfea6(F-01 阶段5 调用点) + 2446d1b(patch_file 三模式)。 - **主代独立核查**: ✅ 全过(router.rs L16 pub use df_ai_core re-export(初 Read 误判私有 use,grep L16 确认 pub use)+L76 select_model_id helper / 6 调用点 import df_ai::router::{select_model_id,Modality,IntelligenceTier,...} 形态正确(agentic:19/project:11/title:11/knowledge_inject:12/ai_node:17/adversarial:165 全限定) / **cargo check --workspace EXIT 0 亲跑(5 warning 全 pre-existing dead_code 无 error)**(B agent 报 select_model_id E0425/Modality E0603 经核验为攒批中间态 A router.rs in-flight 时未定义,review-batching-worktree-transient 教训) / cargo test df-ai router **15 passed**(select_model_id_empty_pool_returns_none/select_model_id_hit_returns_model_id 新增) / df-ideas **20 passed** 零回归 / df-nodes **79 passed,1 ignored** 零回归 / devflow tool_registry **76 passed**(apply_line_range_*+resolve_anchor_*+e2e+互斥) / tool_registry apply_line_range L133+resolve_anchor_to_lines L182+互斥校验 L935+嵌套 schema L892/L901+共用底层 expected_hash/FILE_LOCKS/.bak)。 - **审查 agent 待复审重点**: ①agentic served-model 审计字段改 resolved_model 行为变更(池空==default_model 零差异核验,池非空命中才是路由目标) ②knowledge_inject 嵌入 needs_tool_use=false 降级(如需严格 Embedding 过滤需扩 TaskRequirements capabilities 字段+router 加过滤步,行为不变优先本批未做) ③df-ideas 新增 df-ai 重依赖(df-ideas 原仅 df-ai-core,现 reqwest 等;若需严格轻量可将 router 下沉 df-ai-core) ④patch_file expected_hash 非 sha256(mtime+size 指纹,三模式通用语义与 old_text 模式一致) ⑤三模式互斥运行时校验(schema 层 object_schema 无法静态互斥,运行时 has_* 计数兜底) ⑥agentic 主对话含图 Vision 检测未实现(ChatMessage 纯文本无图像 Part,modalities 仅 [Text],多模态类型接入后追加 TODO) ⑦build_provider/诊断日志仍用 default_model(provider trait 不暴露改 default_model,CompletionRequest.model 走路由 provider impl 适配) ⑧实测类待用户(tauri dev 调用点端到端:Settings 拉取模型→池非空→主对话/标题/扫描/灵感/ai_node 各走路由)。 --- ## 已审归档 ### CR-260616-34 batch58 F-260616-10 provider 默认值重启失效 bug — ⚠ 已审(PASS-WATCH) - **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验源码 + 类型确认) - **总体结论**: ⚠ **PASS-WATCH** — 🔴0 🟡1 ⚪3(核心 bug 双恢复修复 OK,已删除 provider 残留守卫 med 待修) - **逐件核验**:核心 bug 修复 ✅(重启 activeProvider=null → useAiPanel.ts:53-54 模块级 savedProvider 同步恢复 + loadProviders:101-104 DB is_default 兜底,双恢复链路)/ is_default 字段确认 ✅(types.ts:177 AiProviderConfig.is_default boolean,ai_list_providers 返回)/ cycleProvider 兜底 ✅(AiChat.vue:1184 startId=activeProvider||is_default||null 三级兜底,L1187 Math.max(idx,0) 防 -1 越界)。 - **🟡 CR-34-1(med,已删除 provider 残留未校正)**:useAiPanel.ts:101 `if (!state.activeProvider)` 守卫只查 null 不查有效性。localStorage savedProvider 指向已删除 provider(L54 设 activeProvider=deleted-id)时,loadProviders 守卫 false 跳过 DB is_default 恢复 → activeProvider 残留无效 id → cycleProvider:1185 findIndex=-1 → :1187 next=ps[1] 跳过 ps[0],且潜在消费方(发消息读 activeProvider 调后端)用无效 id。修法:守卫改 `if (!state.activeProvider || !list.some(p => p.id === state.activeProvider))` 残留无效时回落 DB is_default。 - **⚪ low**:1(L52 注释「以 DB 为准」与行为不符,实际 savedProvider 优先 DB 兜底,行为合理注释误导)/ 2(setProvider:121 void appSettings.set 持久化失败静默,仅影响重启恢复可接受)/ 3(多窗口/分离窗口 activeProvider 一致性:模块级 state 各 webview 独立,主 setProvider 写 SQLite 分离窗口 state 滞后,既存架构非 CR-34 引入)。 - **待修项回流 todo**: **CR-34-1**(med,useAiPanel.ts:101 已删除 provider 残留守卫改查有效性)。 --- ### CR-260616-33 batch57 CR-260615-04 splitBlocks→marked.lexer() — ✅ 已审(PASS) - **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验源码) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **逐件核验(全 PASS)**:splitBlocks(AiChat.vue:671-691 marked.lexer 切块,L672 getMarked 取实例 + L676 未就绪兜底 `return [text]` 不引入正则 + L677 lexer tokenize + L678-689 for 循环)/ code token 整段一块(L681-685 `type==='code'` + raw.trim 守卫空代码块跳过 + continue)/ 非代码 token 按 `\n{2,}` 切段(L688 split + b.trim 守卫)/ 空 token 非 string raw 防御(L679 `typeof string` continue)/ 空兜底(L690 `blocks.length ? blocks : [text]`)/ 签名不变(`splitBlocks(text):string[]` + renderStreamingBlocks:723 调用)/ 切块边界与 parse 渲染边界一致(同 marked 实例同围栏规则,CR-260615-04 根治目标达成)/ 流式路径回归(renderStreamingBlocks:718-733 + parseBlock/parseBlockNoCache 块缓存机制不变)。 - **⚪ low**:1(lexer 全文 tokenize 性能略高于旧正则,rAF 节流+块缓存吸收,流式实测无影响)/ 2(末块未闭合围栏场景 lexer 可能把后续段落吞进 code raw,流式中间态短暂切块不准,末块重 parse 修正)/ 3(L690 空兜底 lexer 空文本返空数组时回退原文)。 - **待修项回流 todo**: **无**(核心目标切块/渲染边界一致达成,防御充分)。 --- ### CR-260616-32 batch57 B-260619 Knowledge.vue CSS窄屏标题挤压 — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 vue-tsc EXIT 0 + grep/read 核验源码) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **逐件核验(全 PASS)**:min-width:0 传播链完整(L576 kn-detail-panel → L622 detail-head → L623 detail-title-row flex:1 → L625 detail-title,窄屏标题可正常收缩)/ 760px 断点 column-wrap 与 grid 兼容(L730 kn-layout 单列降级 + L731 detail-head flex-direction:column 无冲突)/ 按钮换行间距(L626 detail-actions gap 6px + flex-wrap:wrap,L732 窄屏 width:100%)。 - **⚪ low**:1(L623 `.detail-title-row { overflow:hidden }` 冗余,word-break 多行标题 height auto 自适应不真裁断,语义不清,建议删保留 min-width:0+flex:1)/ 2(L733 窄屏字号降级仅覆盖 .detail-title 18→15px,编辑态 .edit-input-title L692 固定 16px 未同步,窄屏编辑态 16px 反超展示态 15px)/ 3(18→15px 偏激进,详情主标题窄屏建议 ≥16px)。 - **待修项回流 todo**: **无**(三 low 风格/一致性微调非阻断)。 --- ### CR-260616-31 batch56 F-260616-08 分页 offset+探总量 — ✅ 已审(PASS) - **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验源码形态;**未亲跑 cargo check**,标准 API 语法无编译风险,待 agent GLM 恢复补 cargo 验证) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2 - **逐件核验(全 PASS)**:①offset 边界(tool_registry.rs:216/237/254 offset `as_u64().unwrap_or(0)`→usize,JSON 负数 as_u64 None→0,skip(offset).take(limit) 超 total 取空不 panic;has_more `(offset+page.len())Result>`,:224 unwrap_or_default Err→None→:231 Err,DB 故障不误判已处理)。 - **⚪ low**:1(pending 审计记录 + 内存 pending_approvals 丢失→:231 Err「未找到挂起的审批」文案略困惑,实际审批上下文已不可恢复 Err 正确,文案对有 pending 审计记录场景不精确)/ 2(防抖 _pendingApprovalIds 模块级跨调用累积,finally 清理可靠无泄漏,机制说明非问题)。 - **待修项回流 todo**: **无**(三维度全 PASS)。 --- ### CR-260616-27 batch54 复核-新⑪ detach 清主窗口 state 看门狗幽灵(useAiWindow.ts) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验 + vue-tsc EXIT 0) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **逐件核验(全 PASS)**:detachPanel(useAiWindow.ts:28-38 快照在清 state 前写入 — L28-31 localStorage.setItem df-ai-gen/df-ai-text → L37-38 清 state.streaming/generatingConvId,顺序正确)/ state 独立性(主/分离窗口独立 webview JS context,resumeInDetached:87-119 靠 localStorage df-ai-gen/df-ai-text 重建 streaming L116-117,反证清主窗口 state 不影响分离窗口生成态)/ watchdog 兜底(stopListener clearStreamWatchdog 已由 CR-260615-24 兜底,本次仅清 state 视觉残留,注释:32-36 说明链路)/ reattach/closeDetachedWindow 清快照(L82-83/L125-126)。 - **待修项回流 todo**: **无**(全件 PASS)。 --- ### CR-260616-26 batch53 F-05 去重根治(audit.rs High risk 审批前缓存命中去重) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo + vue-tsc 双 EXIT 0 **附 log 末尾证据** + grep/read 核验全件 + 边界核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo Finished 0.68s + vue-tsc log 空;5 warning 全既存 dead_code 非本批)。**无中间态误报**(附 log 末尾)。 - **逐件核验(全 PASS)**:find_cached_high_risk_result(audit.rs:400-458 反向扫 `msgs.iter().rev()` + tool_name 过滤 :423 + canonical_args_key 规范化比较 :430 + 旧 id 反查 tool_result :444-456)+ High 分支查缓存命中回传(:536-553 guard High → push tool_result 新 tool_call_id :544 + emit AiToolCallCompleted :545-549 + 审计 completed/auto_dedup :551 + continue :552 跳过 pending insert)+ 仅 High 去重(:534 Medium|High 共分支 + :536 内嵌 High guard,Med 走原审批 :555-573)+ pending 占位不命中(:452 占位串「需要用户审批,等待确认」return None,与 push :564 全角逗号一致)+ args 规范化(sort_object_keys :462-492 sort_by 字典序 + 递归值/Array + to_string 紧凑稳定)+ 不同命令/参数不命中(timeout 重发可重试)+ tool_call_id 来源修正(stream_recv.rs:181 draft.id 来自 tc_delta.id 非 stream_recv UUID)+ 零越界(纯 audit.rs 3 函数 + High 分支,未碰 batch42/44/51)。 - **关键边界核验**:二次循环自吞(第一次重试命中缓存 push tool_result → 第二次重试 find_cached 反扫命中第一次 tool_call 又跳过审批)= **期望行为**(每次重试跳过审批断循环,非 bug)。 - **⚪ 观察项(全 low)**:OBS1(canonical_args_key to_string unwrap_or_default 理论序列化失败空串误等价,实际 JSON 不会失败)/ OBS2(空参数同名 High 工具误命中,当前工具集无)/ OBS3(去重命中 tool_result 下轮被命中 = 期望行为断循环)。 - **待修项回流 todo**: **无**(全件 PASS,3 OBS 全 low)。完整 agentic loop 通用去重(search_files 重复卡片 + 滑动窗口)③类待用户,本批仅 run_command 循环核心。 --- ### CR-260616-25 batch52 UX-10 @实体引用全栈(@ popover + prompt 任务段注入) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo + vue-tsc 双 EXIT 0 **附 log 末尾证据** + grep/read 核验全件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双真实 EXIT 0(cargo Finished 0.72s + vue-tsc log 空 0 error;5 warning 全既存 dead_code 非本批)。**无中间态误报**(附 log 末尾,呼应 CR-23-1 教训防 E0594 类瞬态漂移)。 - **逐件核验(全 PASS)**:@ popover 复用 `.ai-skill-popover`(AiChat.vue:563)+ 联想状态三件(mentionOpen/Index/Start :992/993/995 独立于 skill)+ detectMentionTrigger 邮箱排除(:927-953 prev 非空白 break)+ selectMention `[类型: 名]` 插入(:1057-1079)+ mentionItems 项目+任务各 slice(0,20)(:1009-1037)+ mentionGroups 分组(:1046-1049)+ mentionTotal 空/无匹配区分(:1040-1043)+ **键盘导航与技能联想互斥**(:902-920 watch inputText skill 块优先强制关 mention + selectSkill/selectMention 互清,skillOpen 与 mentionOpen 不可同时 true)+ Enter 不冲突(:1433-1456 popover 开 selectMention preventDefault 覆盖发送,关 fallthrough handleSend)+ prompt.rs:117-126 任务段(state.tasks.list_active take(20) + 注入 title/status/description 镜像项目段范式 :108-115)+ 签名零变化(build_system_prompt :102 4 调用方 commands.rs:69/161/391 + agentic.rs:478 零改)+ 降级合理(prompt 走后端 state repo :119 非前端 mentionEntities 透传,避碰 batch45/48/49/51 领地)+ 实体 id 未透传(仅 `[类型:名]` 文本,重名任务风险低已知降级)+ i18n 5 key 双语(aiChat.ts:76-80)。 - **⚪ 观察项(全 low)**:OBS1(mentionHint 死键定义无 .vue 消费,建议删或激活)/ OBS2(mentionQuery :1003-1006 slice 到串尾非光标前,光标中移时偏差,常规用法无影响)/ OBS3(desc 仅 popover 展示不进 prompt,prompt 任务段用 tk.description 摘要列间接影响 LLM,语义链路清晰)。 - **待修项回流 todo**: **无**(全件 PASS,3 OBS 全 low)。 --- ### CR-260616-24 batch51 UX-09 编辑已发送消息并重新生成全栈(messages JSON status + ai_chat_edit IPC + AiChat 编辑 UI) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo + vue-tsc 双 EXIT 0 + grep/read 核验全件 + **CR-23-1 独立核验**) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪4 - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo Finished 0.84s,5 warning 全既存 dead_code 非本批)。 - **🔴 CR-23-1 独立核验:不成立** ✅ — `context.rs:478-495` 当前是 **for 循环**(L488 `for t in ...iter_mut()` + L490 `t.message.status=Some("truncated")` + L491 `count += 1;` 计数非 filter 闭包体),workspace cargo check 真实 EXIT 0(全量非分 crate)。原 CR-23 agent 报 E0594 是**中间态误报**(batch51 最终 for 循环规避双层引用,呼应 CR-21/18/16 瞬态漂移)。**主代理声明属实,CR-23-1 标无效**。 - **逐件核验(全 PASS)**:provider.rs:54-55 ChatMessage `status: Option`(serde default+skip_serializing_if 向前兼容 None=active 无 V17 迁移)+ 5 构造函数补 None(:60/63/66/69/72)+ is_active()(:76-77)+ sanitize_messages step0 过滤 truncated(context.rs:287-296)+ replace_last_active_user_content(:501-512 rposition+token 重估)+ truncate_after_user_message(:478-495 for 循环)+ ai_chat_edit IPC(commands.rs:351-445 复用 ai_regenerate spawn 模式:generating 拦截:363+活跃一致:367+replace 末条 user:371-377+truncate 其后:379-382+save:418+spawn:428-442)+ 只末条 active user 可编辑(replace rposition + Err)+ title.rs:51 + lib.rs:108 + 前端 api/ai.ts:33 editMessage + useAiSend.ts:212-270(splice 截断+改 content+空气泡占位+IPC+失败回滚)+ useAiConversations.ts:72 filter truncated + AiChat.vue:329-337 编辑按钮 hover(isLastUser && !streaming)+ isLastUser/startEdit/cancelEdit(:1028-1059)+ handleSend 编辑分支(:1267-1285)+ ESC 取消(:1211-1215)+ 切换取消(:883-885)+ i18n 4 key 双语(aiChat.ts:100-103)。 - **⚪ 观察项**(全 low):OBS1(导出 batch46 含 truncated 软删历史,markdown/txt 视觉见旧回复段,完整审计语义取舍)/ OBS2(truncate_after_user_message/replace_last_active_user_content 无单测,token 计数同步易错建议补 case)/ OBS3(useAiSend editMessage convId 空回滚不彻底,极端场景后端未触发 IPC)/ OBS4(editMessage 无 generating 前端预检,DOM 隐藏+后端拦截双兜底无并发风险)。 - **待修项回流 todo**: **无**(CR-23-1 标无效,4 OBS 全 low 非阻断)。 --- ### CR-260616-23 batch50 审查 med 修复聚合(CR-15-1 kill_on_drop + CR-08-O1 blocked 排除 + CR-10-A/B 停下报告) — ⚠ 已审(ISSUES) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check EXIT 101(撞同攒批 CR-23-1)+ vue-tsc EXIT 0 + grep/read 核验四件) - **总体结论**: ⚠ **ISSUES** — 🔴1 🟡0 ⚪1(三件声明项全 PASS,但同攒批工作区 context.rs:491 编译失败阻断整批) - **亲跑 `cargo check --workspace`** ⚠ **EXIT 101**(df-ai 编译失败,CR-23-1,非三件范围)/ `npx vue-tsc --noEmit` ✅ EXIT 0。 - **三件声明项核验(全 PASS)**: - **CR-15-1 kill_on_drop** ✅ `shell.rs:85` `.kill_on_drop(true)` match 块外统一调用,三类型(PowerShell:62/Cmd:68/Sh:74)全覆盖,注释:81-84 配合 tokio::time::timeout:96-101 防孤儿;正常完成路径 cmd.output() 已读完 output 自然 drop 无害 — 不破坏 cmd.output() 成立。 - **CR-08-O1 blocked 排除** ✅ `TaskDetail.vue:265-274` `WF_EXCLUDED_FROM={'blocked'}` :265 + wfAdvanceAction computed :269 `if has return null`,DOM :71 v-if 不渲染。**不动 ADVANCE_MAP**(:219-246 五态原推进项全保留),其他态不受影响。 - **CR-10-B 否决** ✅ **同意主代理「不成立,审查锚定漂移」**:独立 Read commands.rs:37-110,L94 save_conversation 锁外(L46-65 锁块 L65 关闭),L105 spawn 在 save 后,时序正确。L56/59 是 pop 失败防御性错误处理(复位 generating + 报错)非 save/emit 时序。**原 CR-10-B 把 pop-失败-错误分支误读为时序冲突,审查锚定漂移坐实**。 - **CR-10-A 降级 P2** ✅ 三入口(ai_regenerate:37/ai_chat_send:128/ai_chat_force_send:453)统一「锁内变更+锁外 spawn」,兜底充分(generating 占用 busy 拦截 + stop_flag 复位 + force_send:460-468 复位三件套 + ai_chat_stop:520-536 3s 兜底任务防 panic/异常退出卡死),接受现状合理。 - **🔴 CR-23-1(high,编译失败,同攒批非三件范围)**:`crates/df-ai/src/context.rs:491` E0594 `cannot assign to t.message.status, which is behind a & reference`。`truncate_after_user_message` 的 `self.messages[i+1..].iter_mut().filter(|t| {... t.message.status=...})` filter 闭包参数 t 是 `&&mut TrackedMessage`(双层引用)不能写字段。git stash HEAD EXIT 0 / pop 工作区 EXIT 101 → UX-09 新增代码引入。**阻断整个 df-ai crate 构建,下游 commands.rs/agentic.rs 全 fail**。修法:filter 改 for 循环 + 计数,或闭包内 `let t=&mut **t;` 解引用。 - **⚪ 观察项**:OBS1(agent 自报 cargo EXIT 0 与实际 EXIT 101 不符,可能跑 HEAD 或分 crate,结论可信度打折,建议后续 agent 自报附 log 末尾证据)/ OBS2(CR-23-1 非三件范围但同攒批,合并前必先修否则整批构建失败)。 - **待修项回流 todo**: **CR-23-1**(high,context.rs:491 编译失败,改 for 循环或解引用,阻断 df-ai 构建 — **紧急,主会话合并前必修**)。 --- ### CR-260616-22 batch49 UX-17 对话置顶全栈(pinned 字段+迁移+IPC+排序+图钉按钮) — ✅ 已审(PASS-WATCH) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + cargo test df-storage + vue-tsc 三 EXIT 0 + grep/read 核验 14 件) - **总体结论**: ⚠ **PASS-WATCH** — 🔴0 🟡1 ⚪3(CR-22-1 med 已置顶图钉非 hover 不可见) - **亲跑 `cargo check --workspace` + `cargo test -p df-storage` + `npx vue-tsc --noEmit`** ✅ — 三 EXIT 0(cargo 9 warning 全既存 dead_code;df-storage 22 单元 + 11 集成 passed;vue-tsc 0 error)。 - **独立核验**(14 件):models.rs:163 `pub pinned: bool`(非 Option 必填,list 摘要手写 json! snake_case 对齐)/ crud.rs:1141-1148 INSERT 含 pinned(?8)+ :1153-1160 UPDATE 含 pinned(?7)params 对齐 / :1050 ai_conversation_from_row 读 pinned i32→bool / :1657-1672 set_pinned 仅 SET pinned 不动 updated_at(纯元数据)/ migrations.rs:285-296 V16 column_exists 守卫幂等 + ALTER ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0(老库非 NULL)+ :53 注册 (16, migrate_v16)/ commands.rs:849-859 ai_conversation_set_pinned IPC / :746 list 摘要 json! 加 pinned / lib.rs:128 注册 / **conversation.rs:141 pinned: false(proactive-bug-fix 坐实:models.rs:163 非 Option 必填,insert 不补编译断 E0063;update :108 从库 get rec.pinned 回写不丢失)** / AiChat.vue 排序 :1054(groupedActive)+ :1069(archivedConvs)`Number(b.pinned?1:0)-Number(a.pinned?1:0)||Number(b.updated_at)-Number(a.updated_at)` pinned DESC→updated_at DESC + V8 stable / :53/120/162 三处图钉按钮 toggle / i18n zh:9-10/en:9-10 双语 / useAiConversations.ts:174-178 setPinnedConversation IPC+本地同步 / api/ai.ts:156-157 + types.ts:246。 - **🟡 CR-22-1(med,UX 缺陷)**:AiChat.vue:1872-1873 `.ai-conv-item-actions { display:none }` + :1878 `:hover` 才 `display:flex` → 所有操作按钮(含图钉)默认隐藏,:1903 `--pinned` 仅改 color 未覆盖 display → **已置顶对话非 hover 态图钉不可见**,「常驻强调色一眼识别」UX 意图落空。功能(toggle/排序)正常。修法:`.ai-conv-item-act--pinned` 补 `display:flex`。登记自相矛盾(hover 浮出 toggle 与常驻强调色无法同时满足),需产品定调。 - **⚪ 观察项**:OBS1(filteredConversations 搜索态未加 pinned 排序,置顶不前置,登记自认降级可接受)/ OBS2(V16 无独立测试靠 column_exists 探测,项目一贯模式)/ OBS3(update_full:1151 读库 rec.pinned 回写不丢失,未来绕过 get 路径风险当前无)。 - **待修项回流 todo**: **CR-22-1**(med,AiChat.vue:1872-1903 已置顶图钉非 hover 不可见,「常驻强调色」UX 未达成,补 display 规则或调 actions 可见性策略)。 --- ### CR-260616-21 batch48 UX-18 对话导出前端(exportConversation 封装 + 3 处导出按钮 + Blob 下载) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 vue-tsc + cargo check 双 EXIT 0 + grep/read 核验 12 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **亲跑 `npx vue-tsc --noEmit` + `cargo check --workspace`** ✅ — 双 EXIT 0。**vue-tsc EXIT 0 确认 batch48 完成后 `toggleExportMenu/exportingId/exportOpenId/exportConversation` 均已定义,CR-15/16 agent 早报的 12 TS2339 瞬态已消除**(cargo 5 warning 全既存 dead_code NodeExecutionRepo/ReleaseRepo 非本批,本批纯前端)。 - **独立核验**(12 件):api/ai.ts:165-167 exportConversation 封装(camelCase 仿 continueLoop/stopLoop,签名 `(conversationId, format): Promise`,invoke `ai_conversation_export` snake→camel 对齐 commands.rs:872)/ AiChat.vue 3 处导出按钮 DOM(搜索态 :59-68 / 活跃态 :126-135 / 归档态 :168-177,每处 pin→archive→export→delete 顺序,@click.stop 阻冒泡,:disabled=exportingId===conv.id 防重复)/ `:last-child` 红色态保留(:1898-1901 delete 是末子 export 倒数第二,精确命中删除按钮不被破坏)/ toggleExportMenu/exportOpenId 单展开(:912-917 三元切换同时一开)/ Blob 下载 + revokeObjectURL(:941-950 setTimeout(0) 下轮事件循环释放防泄漏,mime 三格式精确 text/markdown|application/json|text/plain)/ sanitizeFilename 安全(:920-927 空 trim→fallback conv-id + replace 折叠 Win/Unix 路径分隔符+非法+控制字符 + slice(0,60) 限长 + 全清空降级)/ exportingId 防重复(:913/931-932/956 入口 guard+set+finally 三段)/ onExportOutsideClick + 生命周期(:961-966 早返回+closest 防误关 + :1381/1317 add/remove 对称)/ IPC catch 兜底(:951-954 e.message||String(e)+toast 不崩溃)/ i18n 5 key 双语(:112-116 exportFailed 含 {msg})/ 消费 batch46 IPC(commands.rs:872 + lib.rs:129)/ 零越界(batch45 continueLoop/stopLoop :37/45 + batch43 搜索框 :23-34 + batch47 确认 handler :819-841 独立区未改)。 - **⚪ 观察项**:OBS1(revokeObjectURL setTimeout(0) 下载时机,社区惯例无可见问题)/ OBS2(菜单无 Esc 关闭,onGlobalKeydown Esc 已消费他途,UX 小瑕疵)/ OBS3(filename 无时间戳,同名浏览器自动加后缀,设计取舍)。 - **待修项回流 todo**: **无**(12 件全过,双 check EXIT 0,零越界)。 --- ### CR-260616-15 batch42 F-04 run_command 超时根治(tool_registry.rs 超时标注 + 常量提取) — ✅ 已审(PASS-WATCH) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo EXIT 0 + vue-tsc EXIT 2 非本批 + grep/read 核验 8 件) - **总体结论**: ⚠ **PASS-WATCH** — 🔴0 🟡1 ⚪2(CR-15-1 med shell.rs kill_on_drop 缺失) - **亲跑 `cargo check --workspace`** ✅ — EXIT 0(本批纯 Rust tool_registry + shell)。**`npx vue-tsc --noEmit`** ⚠ EXIT 2(12 errors 全在 AiChat.vue 导出功能 toggleExportMenu/exportingId/exportOpenId/exportConversation,系并发 batch48 未完成实现,与本批 F-04 Rust 零关联)。 - **独立核验**(8 件):tool_registry.rs:24 常量 `DEFAULT_RUN_COMMAND_TIMEOUT_SECS=60` / :495 unwrap_or 用常量单一使用点 / :510-520 map_err 拦超时改写文案(contains 匹配 shell.rs:95 产出串前缀一致)/ shell.rs:89-95 超时根因点(`tokio::time::timeout` 包 `cmd.output()`)/ commands.rs:251-256 High 审批路径 `e.to_string()` 透传 / audit.rs:450 Low/并行路径同源(经 ai_tools.execute 分发无遗漏)/ commands.rs:269 replace_tool_result_content 断重试链路(LLM 收改写文案不再触发新 tool_call_id 重审批)/ tool_registry.rs:472 run_command 单注册。 - **🟡 CR-15-1(med,真 bug)**:`crates/df-execute/src/shell.rs:60-79` 构造 `tokio::process::Command` **缺 `.kill_on_drop(true)`**(tokio 1.52.3)→ `tokio::time::timeout` drop future 后 tokio **不主动 kill 子进程**,子进程变**孤儿继续后台跑**,但 `tool_registry.rs:514` 文案称「进程已终止」**名不副实**。长 hang 命令(大构建/死循环)超时后仍耗资源。治本应 shell.rs:60-79 构造 cmd 后加 `.kill_on_drop(true)`。根因层缺陷(非本批 tool_registry 标注层引入),但本批文案依赖该假设。 - **⚪ 观察项**:OBS1(tool_registry.rs:473 描述 + :494 注释硬编码「默认 60 秒」字面量未引用 const,改常量需手改两处易漏)/ OBS2(const :24 模块私有,shell.rs 未共享)。 - **待修项回流 todo**: **CR-15-1**(med,`crates/df-execute/src/shell.rs:60-79` 缺 `.kill_on_drop(true)`,孤儿进程 + 文案「进程已终止」语义不实)。 ### CR-260616-16 batch43 UX-06 对话搜索 UI 接线(AiChat.vue 侧栏) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo EXIT 0 + vue-tsc 首跑 EXIT 2 缓存陈旧清缓存重跑 EXIT 0 + grep/read 核验 8 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3 - **亲跑 `cargo check --workspace`** ✅ — EXIT 0(7 warning 全既存 dead_code)。**`npx vue-tsc --noEmit`** 首跑 EXIT 2(6 TS2339 全在 AiChat.vue:57-63 导出功能 toggleExportMenu 等,属并存 batch48 导出 UI 非本批)→ 清增量缓存重跑 **EXIT 0**,确认缓存陈旧 quirk 非真实错。 - **独立核验**(8 件):搜索框 DOM(AiChat.vue:23-34 .ai-conv-search + v-model + kbd Ctrl+K)/ Ctrl+K 绑定(:996-1003 preventDefault + 侧栏未开 toggleSidebar + nextTick focus,顺序 Ctrl+L→K→B→Esc 无重复)/ keydown listener 生命周期对称(:1273 注册 / :1211 移除)/ searchInputRef(:847)/ 平铺渲染(:36-74 template v-if searchResults + :key 's+' 防键冲突 + 空态 searchEmpty + v-else 原分组逐字保留 editingConvId 双击改名零回归)/ searchResults computed(:974-976 显式 .value 解包 ComputedRef 坑已修)/ 消费 batch41 store 契约零改(CR-14 已审 store 侧)/ i18n 3 key 双语(:13-15)。Ctrl+K 冲突核验:grep 无既有 Ctrl+K,Tauri WebView2 无地址栏。 - **⚪ 观察项**:OBS1(Ctrl+K 输入焦点抢占,打字时按 K 切走光标,UX 选择非破坏)/ OBS2(kbd 与 title 复用同一 i18n key,tooltip 语义弱)/ OBS3(范围声明漂移:搜索态平铺项被 batch48 加 export 菜单,审查时点与现状漂移,归 batch48 审)。 - **待修项回流 todo**: **无**(vue-tsc EXIT 2 缓存陈旧非真实错)。 ### CR-260616-17 batch44 F-03 达 max 后端(agentic 暂停态 + AiMaxRoundsReached 事件 + continue/stop IPC) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo + vue-tsc 双 EXIT 0 + grep/read 核验 6 件 + 交叉核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2 - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo 7 warning 全既存 dead_code)。 - **独立核验**(6 件 + 交叉):agentic.rs:322-347 达上限改(save 先行 :340 + disarm :342 + emit AiMaxRoundsReached :343-345 + return :346 保 generating 暂停态,对齐审批等待模式 L313-316)/ mod.rs:117-122 variant + `#[serde(tag="type")]` / commands.rs:446-465 ai_continue_loop(generating + active 双校验 + 复位 stop_flag + try_continue)/ commands.rs:476-502 ai_stop_loop(双校验 + 置 stop_flag 双保险 + generating=false + emit AiCompleted 不重复 save)/ lib.rs:112-113 注册 / 决策 a(loop 局部 iteration agentic.rs:159,续跑 try_continue 重 spawn 天然重计)。交叉:try_continue guard(should_continue)、ai_chat_stop 暂停态交互(走 3s 兜底正确非最优)、ai_chat_force_send(复位 generating 清审批走新 loop)、stop_flag 双保险(continue 复位 :460 + stop 置 :490)。 - **⚪ 观察项**:OBS1(ai_chat_stop 暂停态落流式分支走 3s 兜底非最优,前端延迟,建议审批分支前加暂停态判定即时清理,既存 stop 路径未适配新状态)/ OBS2(无硬上限产品风险,前端操作卡建议多次继续后二次确认)。 - **待修项回流 todo**: **无**。 ### CR-260616-18 batch45 F-03 达 max 前端操作卡(pendingMaxRounds + 续/停按钮) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo + vue-tsc 双 EXIT 0 + grep/read 核验 6 件 + 死锁风险核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2 - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo 5 warning 全既存 dead_code)。 - **独立核验**(6 件 + 死锁):types.ts:210-211 AiMaxRoundsReached variant 联合 + serde 对齐 / useAiEvents.ts:93 pendingMaxRounds 模块级 ref 独立事件源 + :200-204 dispatch + :286/320 AiCompleted/AiError 双清 + :41 NO_RESET_WATCHDOG 含 / api/ai.ts:37-38/45-46 continueLoop/stopLoop 仿 ai_approve / AiChat.vue:468-483 操作卡 + :1625-1626 showMaxRoundsCard 双守卫 + :1632-1658 handler IPC 失败回滚 acting + :1660-1663 watch 复位 + :2384-2395 样式 / i18n 6 key 双语(:102-107)/ 后端契约对齐(agentic.rs:343 + commands.rs:446/476 + lib.rs:112-113)。死锁核验:try_continue 三路径(should_continue=false 补 AiCompleted / provider-Err emit AiError / 正常 spawn run_agentic_loop)全覆盖,所有出口触发清 pendingMaxRounds + watch 复位 acting,无死锁。 - **⚪ 观察项**:OBS1(无 max 暂停态查询 IPC,跨重启恢复缺失,设计取舍 force_send 可复位)/ OBS2(continue/stop IPC 命名风格微异:ai_approve 动词 vs continue/stop 动词+名词)。 - **待修项回流 todo**: **无**。 ### CR-260616-19 batch46 UX-18 对话导出后端(ai_conversation_export IPC) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check EXIT 0 + grep/read 核验 11 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2 - **亲跑 `cargo check --workspace`** ✅ — EXIT 0(workspace 干净,0 warning 0 error)。 - **独立核验**(11 件):conversation.rs:19/24/48 确无导出 IPC(只 TokenAccumulator + save_conversation)/ IPC 位置 commands.rs:853-913 / format 双保险防 panic(:860-863 `matches!` 前置 Err + :908-909 `_ => Err` 末尾)/ 落库 messages serde_json::from_str 零内存读(:866-871 与 switch 一致)/ markdown 四臂 match 真穷举无 `_` 通配(:878-883 MessageRole 4 variant 编译器强制)/ markdown content 原样三反引号不转义(:886)/ json to_string_pretty(:892-893)/ txt 仅读 m.content 略 tool_calls/tool_call_id/model(:898-904)/ 空对话 markdown/txt 空串 json `"[]"`(:889/906/892)/ lib.rs:128 注册(与 batch44 L112-113 不同行)/ 零越界(与 batch44 continue-stop 不同段)。 - **⚪ 观察项**:OBS1(json 空对话返 `"[]"` 非 `""`,语义合理前端 JSON.parse 正常)/ OBS2(markdown 顺序依赖落库保序约定)。 - **待修项回流 todo**: **无**。 ### CR-260616-20 batch47 UX-08 新建对话确认弹窗 — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 vue-tsc + cargo check 双 EXIT 0 + grep/read 核验 10 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **亲跑 `npx vue-tsc --noEmit` + `cargo check --workspace`** ✅ — 双 EXIT 0(cargo 5 warning 全既存 dead_code NodeExecutionRepo 非本批)。 - **独立核验**(10 件):confirmNewConversation handler(AiChat.vue:832-837 非生成态零行为变化直接 newConversation)/ isViewingGenerating 双条件(:965-967 streaming && generatingConvId===activeConversationId,切走后台不弹)/ store 三字段(ai.ts:47/51/59)/ 两处按钮改造(:18 侧栏 + :206 聊天头,登记写 L176 实际 L206 行号偏差)/ ConfirmDialog 复用(:812 useConfirm Promise + 并发覆盖防永挂 useConfirm.ts:39)/ store.newConversation 软复位(useAiConversations.ts:31-41 B-260615-10 零改)/ commands.rs 零改(git diff 仅 batch46 ai_conversation_export)/ i18n zh/en(:84)/ Esc 优先级协同(:1099-1105 confirmState.visible 时 Esc 视取消 resolve Promise 不卡死)/ 零越界(batch43/45/36/47 独立区块)。 - **待修项回流 todo**: **无**(W-1 low 范围偏差 L176→L206 代码正确)。 --- ### CR-260616-14 batch41 Sidebar+ai store 域(UX-06 store 契约 + B-45 内存泄漏核查) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + grep/read 核验 7 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3(filteredConversations .slice() 冗余 + sort 稳定性依赖 + 范围声明与实际不符,均 low 非阻塞) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo 4 warning 全既存 dead_code 非本批;vue-tsc 零 TS error,AiChat.vue:940 `computed` 类型精确匹配)。 - **独立核验**(7 件): - **filteredConversations 契约**(stores/ai.ts:118-125)✅ — 空 query trim→null 信号(消费方走原分组)/ 非空 `filter((c.title ?? '').toLowerCase().includes(q))` case-insensitive 全量(活跃+归档)/ updated_at DESC(b 在前为正,正确)。 - **类型契约**(api/types.ts:237-247)✅ AiConversationSummary 含 title?/archived/updated_at 类型安全。 - **useAiStore 返回值**(stores/ai.ts:136)✅ filteredConversations 已展开。 - **i18n 3 key**(zh/en aiChat.ts:13-15)✅ searchPlaceholder/searchEmpty/searchShortcutHint 双语对称。 - **UI 接线已落地**(AiChat.vue:22-64/939-941)✅ — v-model 双绑 + 搜索态平铺 + 空态提示 + computed 解包 store.filteredConversations.value;搜索项含 archive/delete 操作与原分组对称(登记称「留 batch43」,实际 batch41 已完整接线)。 - **B-45 stopListener**(useAiEvents.ts:344-354)✅ — 三 listener unlisten + 置 null + clearStreamWatchdog + clearAllToolSlowTimers + clearAllApprovalTimers 五件全清。 - **B-45 AiChat onBeforeUnmount**(AiChat.vue:1172-1191)✅ — removeEventListener keydown + cancelAnimationFrame + 拖拽 listener + body class + store.stopListener() + _unlistenToolSlow + _toastTimer/_titleFlashTimer/_providerBarTimer 八件全清。 - **MESSAGE_CAP=200**(stores/ai.ts:36/94-105)✅ — watch 拦 push 式增长(>CAP)splice 最早条;切对话整体替换放行。 - **⚪ 观察项**(均 low): 1. OBS1(冗余 slice):stores/ai.ts:123 `.slice()` 紧跟 `.filter()`,filter 已返新数组,slice 冗余拷贝。无功能影响,建议删。 2. OBS2(sort 稳定性):updated_at 相等返回 0 依赖 V8 ES2019+ stable sort。会话同秒 updated_at 概率低,无可见问题。 3. OBS3(范围声明不符):登记称「UI 接线留 batch43」,实际 batch41 已完整接线。建议主会话核对 batch43 是否剩 i18n 微调。 - **待修项回流 todo**: **无**(三观察项均 low 非阻断)。 ### CR-260616-13 batch40 CR-06 审查观察项测试补强(②-3/②-4 回调测试 + ignored 确认) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo test + grep/read 核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡1⚪1(in_progress 失败→todo 状态机拦截产品决策点 med 非阻断 + 测试数登记 +9 vs +10 low) - **亲跑 `cargo test -p df-nodes -p df-workflow`** ✅ — df-nodes **67 passed + 1 ignored**(57→67,+10 测试)/ df-workflow 23 passed,双 EXIT 0。 - **独立核验**: - **ignored 确认**(ai_node.rs:298-300)✅ — glm_live_complete 真调 GLM 集成测试,合理 #[ignore] 保留。 - **9 新测试覆盖** ✅ — 3 templates callback(task_workflow_templates:callback 边界 + reject 可达性)+ 6 advance callback(task_advance_node:regression_target 镜像契约 + 三段 completed→target/failed→regression/非终态跳过 + 落库验证);镜像契约机制防 workflow.rs:44-51 私有函数回归漂移。 - **🟡 观察项 O1(med,产品决策点,非本批引入)**:**in_progress 模板失败→regression_target 给 todo,但状态机 `backward_to_todo_rejected` 全拒**(task_state_machine.rs:188-193)→ 回调 advance_task_atomic 被 InvalidState 拦截(task_advance_node.rs:72-77),**任务原地保留 in_progress**。三选项待定:a 接受现状补 UI 提示 / b 改 `regression_target("in_progress")=None` 对齐状态机 / c 放开 todo 回退(破坏闸门)。本批测试显式锁定此行为(防回归)。 - **⚪ 观察项 O2(low)**:测试数登记 +9 vs 实测 +10(差 1 来自既有测试重构,非问题)。 - **待修项回流 todo**: **CR-13-O1 产品决策点**(非代码 bug,回流 todo 作产品决策 a/b/c)。 ### CR-260616-12 batch38 settings 域(F-01 agentic max 可配) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + grep/read 核验 15 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪3(前端默认值硬编码 + clamp 回写闪现 + batch45 演进关系,均 low 非阻塞) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo 7 warning 全既存 dead_code;并发 batch45 改动共存编译通过)。注:审查中一次中间态 grep 抓到 batch45 改 mod.rs 时的 E0599,最终完整重跑 EXIT 0 确认 batch38 本身无错。 - **独立核验**(15 件):1:1 严格复刻 llm_concurrency 范式(AppState 字段 `agent_max_iterations: Arc` + init 引用 `DEFAULT_MAX_AGENT_ITERATIONS=10`(agentic.rs:33)/ `run_agentic_loop(max_iterations)` 形参(agentic.rs:101)+ `0..max_iterations` 循环(agentic.rs:159)+ 达上限 warn+emit(agentic.rs:324-336)+ 3 调用点 ai_chat_send(commands.rs:103-106)/ai_regenerate(commands.rs:197-200)/try_continue_agent_loop(agentic.rs:477-487)load 快照透传 / `ai_set_agent_max_iterations` clamp 1-50 store(commands.rs:812-821)/ lib.rs:130 注册 / api/ai.ts:87-89 / Settings.vue input min1 max50(:275-277)+ watch 持久化 SQLite(:663-665)+ syncAgentMaxIterations Math clamp+debounce 300ms(:694-705)+ onMounted 同步防刷新(:766-775)/ onUnmounted 清 _agentIterTimer(:778-783)/ i18n 双语(settings.ts:79-80))。grep 确认无第 4 调用点遗漏。 - **⚪ 观察项**(均 low): 1. OBS1(前端默认值硬编码):Settings.vue:628/696 前端 `appSettings.get(..., 10)` 默认 10 与后端 const 巧合一致但非引用同一常量。范式层面观察(llm_concurrency 亦如此)。 2. OBS2(clamp 回写闪现):clamp 前瞬时值短暂显示后修正,视觉闪现功能无损。 3. OBS3(batch45 演进):lib.rs:111 注释 F-260616-03 标注 max_iterations 是 batch45 暂停态续跑前置基础,两批共存编译通过,演进链路清晰。 - **待修项回流 todo**: **无**。 ### CR-260616-11 batch39 ai chat 性能域(AR-8 节流评估 + B-53 snap 优化) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + grep/read 核验 6 件) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2(增量缓存 quirk + deep watch 每帧检测,均 low 非本批) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(cargo 5 warning 全既存 dead_code;首跑增量缓存陈旧报 AiMaxRoundsReached not found E0599,清缓存重跑 EXIT 0 确认)。 - **独立核验**(6 件): - **AR-8 前端滚动跟随已存在**(AiChat.vue:1110-1114 isNearBottom 80px + 234 onMessagesScroll + 412 showBackToBottom + 1139-1147 onContentChange)✅ — 完整存在,非本批新增。 - **AR-8 后端 50ms 合批未改**(stream_recv.rs diff 纯 B-42 error_type 映射,零 batch39 改动)✅ — 负 ROI 论证成立(rAF ARC-08 吸收重 parse + heartbeat 语义冲突 + flush 复杂度)。 - **B-53 snap 优化正确性**(AiChat.vue:1207-1210)✅ — 新 snap `{n: msgs.length, tc: msgs.map(m=>(m.toolCalls||[]).map(t=>({id,s})))}` 剔除冗余 id/role/len;body 逻辑(AiChat.vue:1218-1226)仅读 toolCalls 状态从不读被删字段 → 行为安全;流式 delta 不再触发 body 重算(toolCall 状态翻转/splice 淘汰仍触发)。 - **B-53 短路 + deep + MESSAGE_CAP 全保留**(AiChat.vue:1213 snap===lastMsgSnapshot return + 1233 deep:true + ai.ts:36/94-105 MESSAGE_CAP=200 length watch 独立)✅。 - **B-53 自包含**(lastMsgSnapshot 模块局部无外部消费 + collapseInactive 唯一调用点)✅。 - **注释清晰度**(AiChat.vue:1197-1202)✅ — 与代码完全吻合无误导。 - **⚪ 观察项**(均 low): 1. OBS1(缓存 quirk):增量缓存陈旧致首跑 E0599,清缓存重跑过。建议 CI 走 cargo check 不带增量。 2. OBS2(deep watch 每帧检测):B-53 仅短路 body,deep watch 本身仍每帧遍历 messages 含 content,极长流式消息 O(n) 成本。rAF 节流已吸收,非阻塞。 - **待修项回流 todo**: **无**(纯优化批次,零回归)。 ### CR-260616-10 batch36 ai chat UX 域聚合(UX-02 消息操作栏 + UX-03 错误气泡操作入口) — ✅ 已审(PASS) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + 独立重跑双 check 佐证) - **总体结论**: ✅ **PASS** — 🔴0 🟡2⚪3(ai_regenerate 并发竞态 med + commands.rs 时序倒置 med 非阻断 + 3 low) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — 双 EXIT 0(vue-tsc 首跑报 useAiEvents.ts 缺 import 已补后过;agent 独立重跑双 check 佐证)。 - **独立核验**: - **UX-02 操作栏**(AiChat.vue hover `.ai-msg-actions` 仅末条 复制+重新生成)✅。 - **ai_regenerate IPC**(commands.rs)✅ — generating 占用校验 + pop_last_assistant_round + save + spawn run_agentic_loop 复用 + active_conversation_id 一致性。 - **pop_last_assistant_round**(context.rs)✅ — 空 false / 末尾 user popped_any=false / 跨多轮 tool 三元组一并弹不留半截 / history_tokens saturating_sub。 - **UX-03 错误气泡**(useAiEvents.ts AiError push 附 errorType `as AiMessage` cast B-42 零改 + AiChat.vue 错误气泡重试复用 regenerate + canOpenSettings 判 auth|provider_config)✅。 - **只读消费 B-42**(ErrorType/AiErrorType/AiError 零改)✅。 - **🟡 观察项(待修项,细节见 agent transcript)**: 1. **CR-10-A(med 时序倒置)**:commands.rs:56/59 save 与 emit 时序。回流 todo。 2. **CR-10-B(med 竞态)**:ai_regenerate 并发竞态窗口。回流 todo。 - **⚪ 观察项**:代码块复制按钮(v-html 子元素无法绑事件)遗留单独立项;AI 气泡内嵌复制与新操作栏复制共存最小改动;context.rs 跨 crate(df-ai)改动非 batch35/37 锁。 - **待修项回流 todo**: **CR-10-A**(commands.rs:56/59 时序倒置 med)+ **CR-10-B**(ai_regenerate 竞态 med)。 ### CR-260616-09 batch37 knowledge 域(F-260616-02 pending_review 归位·决策 a) — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + cargo test df-storage 双 EXIT 0 + grep/read 核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2(注释遗留 + list_non_archived dead_code,均 low 非阻塞) - **亲跑 `cargo check --workspace` + `cargo test -p df-storage`** ✅ — **双 EXIT 0**;df-storage 11 passed(含 `list_by_status_orders_by_confidence_semantics` / `list_by_status_filters_other_status` / `test_knowledge_cols_matches_record` 三个 knowledge 测试)。 - **独立核验**(5 件): - **knowledge_list 默认收窄**(knowledge.rs:78-88)✅ — L86 `None => list_by_status("published")` 收窄纯 published;L84 `Some(s) => list_by_status(&s)` 显式透传(含 archived)向后兼容。 - **调用方零破坏** ✅ — grep list_non_archived src-tauri/ **零 Rust 调用方**;前端 src/api/knowledge.ts:15 + stores/knowledge.ts:61-66 loadList 默认 undefined→null→后端默认 published;Knowledge.vue:275/522 loadList() 无参走默认。**无调用方依赖旧「非 archived 全部」语义**。 - **merge_by_confidence 排序等价**(knowledge.rs:238-273)✅ — rank high=3/med=2/low=1/_=0 + created_at DESC,与 SQL(crud.rs:1363-1368 `CASE...DESC, created_at DESC`)权重+方向一致;双指针合并前 a/b 各 sort_by(cmp) 重排保证有序(L257-258);相等时 candidate 优先(L262)结果确定。 - **向后兼容** ✅ — published 仍在 library(默认)/ archived 显式可查 `knowledge_list(status='archived')` / candidate+pending_review 仍在 inbox(knowledge_list_candidates L226-231)。 - **i18n 文案**(zh/en knowledge.ts)✅ — inbox「待处理」/「Pending」+ 注释说明 F-260616-02 决策 a。 - **⚪ 观察项**: 1. **OBS1(low 注释遗留)**:Knowledge.vue:17/250 注释仍写「审核收件箱」,i18n 已改「待处理」;注释与新语义不符,建议清理(不影响功能)。 2. **OBS2(low dead_code)**:crud.rs:1478 `list_non_archived()` 收窄后全仓零调用方,成孤儿方法(编译器未报,宏展开 pub method 豁免);建议删除或 `#[allow(dead_code)]`。 - **文档遗留(非代码,仅记录)**:df-knowledge 模块文档 L52/58/174-175 + 经验记录 L78 + df-storage 存储层文档 L114 多处 knowledge_list 默认语义/list_non_archived 描述过时,建议后续清理批次更新。 - **待修项回流 todo**: **无**(代码层面 PASS;两 low 观察项 + 文档遗留均非阻塞)。 ### CR-260616-08 batch35 TaskDetail 域聚合(①-1 工作流推进按钮 + B-41 进度内嵌 + ①-3 i18n) — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + grep/read 核验) - **总体结论**: ✅ **PASS** — 🔴0 🟡1⚪1(blocked 态推进按钮语义偏差 med 非阻断 + lib.rs 混入非本批 low) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — **双 EXIT 0**(4 warning 均既有 dead_code 非本批)。 - **独立核验**(11 件,grep + read 亲验): - **workflow.rs 空dag选模板补丁**(L81-89)✅ — 三分支覆盖(nodes 非空→原 dag / 空+target Some→template_for 失败 Err「无对应工作流模板」 / 空+target None→原 build_dag);**与 CR-06 审的 ①-1 同代码,描述差异源于措辞非冲突**;dag shadowing 后 build_dag + dag_json 序列化落库实际拓扑(L92/L96)。 - **②-3/②-4 回调**(workflow.rs:232-233/267-305)✅ — cb_task_id/cb_target_status clone move;completed→Some(target)/failed→regression_target/任一 None→跳过;回调失败 warn 不回滚;advance_task_atomic 签名匹配(task_advance_node.rs:43-47)。 - **regression_target**(workflow.rs:44-51)✅ — testing→in_review/in_review→in_progress/in_progress→todo/其他→None。 - **TaskDetail.vue 推进按钮**(L71 v-if wfAdvanceAction / L260-267 computed / L77 disabled 互含 / L288-310 handleWorkflowAdvance 空 dag+taskId+target 调 run)✅。 - **B-41 进度内嵌 + exec_id 过滤**(L174-184 状态 / L313-314 过滤 / L411 onMounted + L419 onBeforeUnmount 生命周期对称)✅。 - **多 listen 无冲突**(workflow.ts:37-41 独立 unlisten / store 收全量 vs TaskDetail 按 exec_id 过滤,职责隔离)✅。 - **进度轻量近似**(L318-323 total 用已启动+已完成,前端无法预知后端模板节点数)✅。 - **template_for 对齐 ADVANCE_MAP**(三模板 in_progress/testing/done;in_progress 态 primary=in_review 无模板不显示)✅。 - **与手动 advance 并存**(L54-64 手动保留 / L66-91 工作流独立区块)+ 不碰 router/lib.rs listen ✅。 - **i18n 8 key 对称**(zh/en taskDetail.ts:41-49)✅。 - **🟡 观察项 O1(med,非阻断)**:**blocked 态推进按钮语义偏差** — blocked→in_progress primary(TaskDetail.vue:241-243)WF_SUPPORTED_TARGETS.has('in_progress')=true 显示按钮,但 template_for('in_progress') 是「todo→in_progress 从头执行」拓扑,blocked 恢复语义不该重跑 AiNode。UX 需确认(blocked 态应排除工作流推进或用恢复语义)。**非阻断**(后端回调逻辑正确,仅前端按钮显示语义)。 - **⚪ 观察项 O2(low)**:lib.rs +3 行(ai_regenerate L107 / list_tool_executions L127-128)非 batch35 范畴,属其他批混入工作区,不影响本批正确性。 - **待修项回流 todo**: **无**(均观察项非阻断;O1 blocked 语义建议主会话评估是否前端排除)。 ### CR-260616-07 batch34 并行(B-42 错误码统一 + AE-08 审批历史面板) — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check + vue-tsc 双 EXIT 0 + grep/read 核验源码) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2(既存死代码 warning + B-42 前端消费未改留 batch36) - **亲跑 `cargo check --workspace` + `npx vue-tsc --noEmit`** ✅ — **双 EXIT 0**(output L68/L69);cargo check Finished 48.33s(L67);6 warning 全既存死代码(df-ai finish_reason/call_type/SessionState/session_state + devflow risk_level/releases/node_executions never read),非本批引入。 - **独立核验**(B-42 + AE-08,grep 定位 + read 亲验源码): - **B-42 ErrorType 枚举**(mod.rs:73-86)✅ — `#[serde(rename_all = "snake_case")]` + 5 variant(Auth/Network/Timeout/ProviderConfig/Unknown),每个文档注释说明分类标准;ProviderConfig→provider_config 与 types.ts:188 联合对齐。 - **B-42 AiError variant**(mod.rs:107-112)✅ — `error_type: Option`,None 表示未分类(向后兼容旧 emit 点,L106 注释)。 - **B-42 8 emit 点映射**(agentic.rs×3 + stream_recv.rs×5)✅ — Auth(agentic:128)/ ProviderConfig(agentic:452)/ Unknown(agentic:333 + stream_recv:207)/ Timeout(stream_recv:163)/ Network(stream_recv:237/278)/ None(stream_recv:308 建连失败混合源难精确归类)。映射合理。 - **AE-08 list_recent SQL**(crud.rs:1246-1275)✅ — 专用 SELECT `ORDER BY requested_at DESC LIMIT ?1 OFFSET ?2`(L1259),绕过通用 query 宏(后者硬编码 ORDER BY created_at,本表无该列);limit.min(200) 钳制防滥用(L1253)。 - **AE-08 list_tool_executions IPC**(audit.rs:85-113)✅ — limit.unwrap_or(50)/offset.unwrap_or(0) → list_recent → DTO map;**敏感字段截断**(arguments_brief 120 / result_brief 160,L104-105),完整原值留库。 - **AE-08 truncate_chars**(audit.rs:71-78)✅ — `chars().count()<=max` 早返回 / 否则 `chars().take(max)` + push '…';**按 char 边界切,中文/emoji 安全**。 - **AE-08 lib.rs 全路径注册**(lib.rs:128)✅ — `commands::ai::audit::list_tool_executions`,避碰 mod.rs glob,零行为破坏。 - **AE-08 AuditLog.vue**(src/views/AuditLog.vue:1-311)✅ — 7 列表格 + 分页(prevPage/nextPage + hasMore=records.length===PAGE_SIZE + 空页回退 L105-108)+ 风险/状态/决策者标签映射;**类型内联**(ToolExecutionRecord interface L69-83,注释「不进 api/types.ts 避撞 B-42」)— 无 types.ts 交叉污染(关注点② ✅)。 - **B-42 前端消费**(关注点③)✅ — grep `error_type` src/ 仅 types.ts:204 定义,**零消费逻辑**(AiChat.vue 等不读 error_type);UX-03 留 batch36 符合任务约定。 - **⚪ 观察项**: 1. **既存死代码 warning**(cargo check 6 个,df-ai/devflow never read)— 非本批引入,既存 lint 级;建议后续清理(SessionState/risk_level 可能阶段2 未接入死代码)。 2. **B-42 前端消费未改**(关注点③已知)— error_type 字段已加但前端暂不读,UX-03 留 batch36;字段先落地不阻塞,合理。 - **待修项回流 todo**: **无**(B-42 + AE-08 全过,零缺陷;两观察项均已知/既存非回归)。 ### CR-260616-06 batch33 阶段2 续(②-2/②-3/②-4 run_workflow task_id+回调 + ②-6 DAG模板) — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批,与 batch32 + 决策记录 + todo/待审查 混合) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo test 全量无 tail + grep/read 核验源码) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2(df-nodes 1 ignored + ②-3/②-4 回调无集成测试) - **亲跑 `cargo test -p df-workflow -p df-nodes 2>&1`(无 tail 全量)** ✅ — **exit 0**;df-nodes **58 running → 57 passed; 0 failed; 1 ignored**(output L30/L90,比 CR-05 的 53 多 4 模板测试);df-workflow **23 passed; 0 failed**(L119);无 FAILED 无 error[。**去 tail 修正 CR-05 截断问题**。 - **独立核验**(四件 + ①-1,grep 定位 + read 亲验源码): - **②-2 run_workflow 参数 + 前端透传**(workflow.rs:64-73 + api/workflow.ts:16-24 + stores/project/workflow.ts:23-26)✅ — 签名 `task_id: Option, target_status: Option`(L71-72)向后兼容;WorkflowRecord.task_id 填入(L104);前端 api `taskId?/targetStatus?`(L16-17)snake_case 透传(L23-24);stores runWorkflow 透传(L26)。旧调用方不传→None 零行为破坏。 - **②-3 完成回调**(workflow.rs:267-273)✅ — `match (cb_task_id.as_ref(), cb_target_status.as_deref())` 都 Some → completed 分支 `advance_target=Some(target)`(L270)→ advance_task_atomic 推进到 target_status。 - **②-4 失败回调退回**(workflow.rs:44-51 + 271)✅ — failed 分支 `advance_target=regression_target(target)`;映射 testing→in_review / in_review→in_progress / in_progress→todo / _(done/blocked/cancelled)→None;in_progress→todo 回起点符合「执行未达预期重排」直觉(L42-43 注释);起点 todo 无可退态→None。 - **回调解耦语义**(workflow.rs:274-294)✅ — advance_task_atomic Err 仅 `tracing::warn!` 不回滚(工作流 status 已写库 L251,回调失败不撤销);`TaskRepo::new(&db)`(L275)复用 db Arc(clone 廉价)。 - **②-6 DAG 模板**(task_workflow_templates.rs:1-152)✅ — `template_for(target)` match in_progress/testing/done→Some,else None(L28-35)不 panic;三模板拓扑正确(in_progress 单 ai / testing ai→human 串行 / done 单 human);testing+done 模板 human options 含「拒绝」(L67/L85)触发 ②-5 reject→Err→failed→②-4 退回闭环;config 留空由 run_workflow 全局注入+deep_merge(衔接 ④-1 L14-17);4 测试断言拓扑(unknown_none/single_ai/ai_to_human_serial 含「拒绝」断言 L141/single_human)。 - **①-1 DagDef 来源选模板**(workflow.rs:81-89)✅ — dag.nodes 空+target Some→`template_for`(L83).ok_or_else(Err「无对应工作流模板」)(L84);空+None→原路径 build_dag 校验(L85);非空→用传入 dag(L88)。三分支零行为破坏。 - **⚪ 观察项**: 1. df-nodes **1 ignored**(L90,running 58 / passed 57 / ignored 1)— 登记关注点提「57 passed」未提 ignored;ignored 非 failed,建议确认哪个测试 `#[ignore]`(可能慢/集成测试留阶段2收尾)。 2. **②-3/②-4 回调无集成测试**(登记关注点②已知)— 已亲核 diff 逻辑正确(match 分支 + 解耦语义),但缺自动化回归保护;回调失败/退回路径仅靠代码审查覆盖。建议阶段2 收尾补集成测试(workflow 跑通→advance 验证)。 - **待修项回流 todo**: **无**(四件+①-1 全过,零缺陷;两观察项均已知/非回归)。 ### CR-260616-05 batch32 阶段2 首批(④-1 config 下沉 + ②-1 注册 TaskAdvanceNode + ②-5 HumanNode reject) — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批,与 CR-03/04 + 决策记录 + todo/待审查 混合) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo test + grep/read 核验源码,不信 agent 自报"53 passed") - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪1(df-nodes 测试数字 tail 截断未单独显示,exit 0 佐证通过) - **亲跑 `timeout 240 cargo test -p df-nodes -p df-workflow`** ✅ — **exit 0**;df-workflow **23 passed**(deep_merge_tests 6 + executor `node_config_overrides_global_in_node_context` 1 + cancelled 3 + state/conditions 13);df-nodes human_node reject 测试 exit 0 保证通过(`tail -40` 截断未单独显示数字,exit code 0 佐证)。**不信 agent 自报「53 passed」**。 - **独立核验**(三件,grep 定位 + read 亲验源码): - **④-1 deep_merge config 下沉**(dag.rs:169 + executor.rs:105 + registry.rs:52)✅ — - `deep_merge(global,node)`(dag.rs:169-188):两端 Object 递归合并(同 key 节点级覆盖 L177-181 / 新 key 保留 L179);非两端 Object 节点级直接覆盖 L186(含 Null 显式空);**Null 语义=显式置空覆盖(非跳过),跳过=不写 node_configs key**(L164-165 注释)符合登记关注点。 - `NodeContext.config`(executor.rs:105-108):`match node_configs.get(id) → Some(deep_merge(global,node_cfg)) / None(initial_config.clone())`;**缺失 key 走 None 分支 == 旧 initial_config.clone() 零行为破坏**。 - `build_dag`(registry.rs:52):`add_node_with_config(id,node,node_def.config.clone())` 下沉 NodeDef.config。 - **5 维度全测过**:同 key 胜(`node_overrides_global_scalar`)/ 新 key 留(`node_adds_new_key`)/ 嵌套递归(`nested_object_recursive_merge`)/ 非 Object 覆盖(`array_replaces_not_concat`)/ 空 no-op(`empty_node_config_is_noop`+`missing_node_config_treated_as_noop`)。 - **零行为破坏**:HumanNode/AiNode/TaskAdvanceNode 节点 NodeDef.config 空/缺失 → node_configs 无 key → None 分支 == 旧行为。 - **②-1 TaskAdvanceNode 注册**(state.rs:235-250)✅ — `build_registry(db: Arc)` 改签名;`register("task_advance", move |_| Box::new(TaskAdvanceNode::new(db.clone())))`(state.rs:248-250)move 闭包捕获 Arc,clone 满足 Send+Sync+'static 满足 NodeFactory 签名;init `build_registry(db.clone())`(state.rs:194)db move 前 clone 避 E0382。D-03 走 df-nodes Node trait 对齐。 - **②-5 HumanNode reject 语义化**(human_node.rs:19-132)✅ — - `REJECT_KEYWORDS`(L19-22):中「拒绝/驳回/退回/否决」+ 英「reject/decline/declined/deny/denied/no/block」。 - `is_reject_decision`(L25-28):trim+to_lowercase 归一化 + **整词精确匹配(== 非 contains)** → 防「拒绝啦」误伤(测 L846 `!is_reject_decision("拒绝啦")` ✅)。 - `contains_reject`(L31-33):picked 任一命中即拒(多选场景)。 - execute(L120-132):`count_ok && each_valid` 后 `contains_reject` → Err「人工审批被拒绝(用户选择: X);意见: Y」。 - **边界全过**:整词防误伤(L846)/ 大小写空白归一(L836-838)/ 多选任一即拒(L848)/ options 空自由文本仅拒绝词 Err 其余 Ok(L13-15 注释)/ 行为变更 Ok→Err executor set_failed → 工作流 failed(L113-119 注释)。 - **⚪ 观察项**:亲跑命令带 `tail -40` 截断 df-nodes 测试数字(仅见 df-workflow 23 passed);**exit code 0 佐证 df-nodes+df-workflow 全过**,非缺陷。后续审查亲跑命令去 tail 或用 `2>&1 | tee` 保全量。 - **待修项回流 todo**: **无**(三件全过,零缺陷)。 ### CR-260616-03 batch30 ①类修复批次 — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批,与 CR-04 混合;CR-03 范围文件干净,无阶段2混入) - **审查时间**: 2026-06-16(独立审查 agent · 亲核 diff + grep 核验,不信主代理"46 passed"声明) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪1(set_cancelled TOCTOU 低频,条目已知) - **独立核验**(CR-03 登记范围 8 文件,范围干净): - **CR-01-D 错误区分**(task_advance_node.rs)✅ — 同态(from==to)→`Validation("相同状态,无需推进")`;非法转换→`InvalidState{current:"X→Y(非法状态转换)"}`;NotFound(任务不存在)。三类拒绝错误类型+消息区分,前端 e.toString() 可分辨。3 测试 assert 含 from→to 上下文(illegal_skip/terminal_done/same_status)。 - **CR-03-b openai source 链** ✅ — `Error::from(e).context("SSE 流错误: {e}")`,Display=ctx 逐字节等价旧 `anyhow!`,source 链保留。同 CR-04 anthropic 模式。 - **CR-03-a resolve_project_label 三臂**(audit.rs)✅ — Ok(Some)→项目名 / Ok(None)→"项目已不存在" / Err→裸 id+warn(DB 故障不误报"已不存在"误导用户)。 - **CR-03-c ToolCard find**(ToolCard.vue)✅ — computed Map(O(N)建表)→function find(O(N)提前退出);2 调用点 `.value[id]`→`(id)` 改对。 - **CR-01-G i18n 分级**(TaskDetail.vue + i18n×2)✅ — loadFailed/advanceFailed 加 `{msg}` 插值;catch 走 `t(key,{msg:e.toString() ?? common.unknownError})`;common.unknownError zh/en 均有(common.ts:16)。**关注点② 前端兜底可显示区分消息** ✅。 - **复核-新④ 终态守卫**(workflow.rs cancel_workflow_node)✅ — get→match:Pending/Running/Waiting→set_cancelled+Ok;终态→Err"已终态,无法取消"。行为变更正确(避免静默覆盖终态)。 - **⚪ 观察项(关注点①,条目已知"低频忽略")**:`set_cancelled`(state.rs:95-100)**无终态守卫**,直接 `insert(Cancelled)`。IPC 层 get→match→set_cancelled 两步非原子:理论 TOCTOU 窗口(get 时 Pending 通过守卫,set 前被 executor 并发改终态)→ set_cancelled 强制覆盖终态(如 Completed→Cancelled 语义错位)。**实际低频**(人工取消+executor 竞争窗口极小)+ 最坏仅终态语义错位不崩溃 → med 观察项,IPC 守卫是主防线。彻底修需 set_cancelled 加守卫或 CAS,低频场景成本不值。 - **待修项回流 todo**: 无明确缺陷;TOCTOU 属条目已知低频观察,非 CR-03 引入回归。 ### CR-260616-04 batch31 NodeCancelled 事件语义+anthropic source链 — ✅ 已审(PASS) - **commit**: 工作区未 commit(攒批,与 CR-03 + 阶段2 混合) - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 test + grep 核验,不信主代理"16 passed"声明) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪1(前端消费缺失,既存非回归)+ ⚠ 范围混淆 flag - **独立核验**(CR-04 登记范围 4 文件): - **events.rs NodeCancelled variant** ✅ — `NodeCancelled { node_id }`(events.rs:64)新增,语义注释清晰(取消=用户主动 vs NodeFailed=失败)。 - **executor.rs is_cancelled 二分** ✅ — L148-166:取消节点→emit NodeCancelled(不 set_failed,Cancelled 已终态避免非法转换崩溃);失败节点→set_failed+NodeFailed。零行为破坏(工作流仍 Err 中止,first_err 逻辑保留)。 - **types.ts node_cancelled** ✅ — L128 手对齐后端 serde(rename_all snake_case)。 - **anthropic_compat.rs:533 Display 不变** ✅ — `Error::from(e).context(format!("Anthropic SSE 错误: {}", e))`,Display = context = "Anthropic SSE 错误: {e}" 逐字节等价旧 `anyhow!`;e 进 `.source()` 链可追溯。 - **亲跑 `cargo test -p df-workflow`** ✅ — **23 passed**(主代理称 16,实际 23 含 deep_merge/node_config 新测试),关键 `test_cancelled_node_emits_node_cancelled_event` ✅ 验证取消节点 emit NodeCancelled 非 NodeFailed。 - **claim③ 无穷举 match** ✅ — 亲 grep:后端无 `match WorkflowEvent` 穷尽(全 matches!/构造点),前端 src/ node_failed/node_cancelled **仅 types.ts 定义无消费**。 - **⚠ 范围混淆 flag**(重要):executor.rs 工作区 diff 混入**阶段2 config 下沉**(L99-108 deep_merge 全局+节点 config + EchoConfigNode 测试 + node_config_overrides 测试),**非 CR-04 范围**(未登记待审查)。该部分 test 过但不属本批结论覆盖。阶段2 改动(`dag.rs`+102 / `human_node.rs`+46 / `state.rs`+21 / executor.rs config 部分)需主会话另立 CR-05 登记后审。 - **⚪ 观察项(既存非回归,建议评估)**:NodeCancelled 事件**前端无消费逻辑**(src/ 仅 types.ts 类型定义,无 switch/render 处理)。取消节点用户暂无"已取消"区分 UI 反馈。**非 CR-04 引入**(CR-04 前 node_failed 前端亦不处理,属既存渐进式类型对齐),NodeCancelled 用户价值依赖后续补前端事件处理。 - **待修项回流 todo**: 无明确代码缺陷;前端事件消费缺失属既存功能不完整,建议主会话评估是否立项(非 CR-04 待修项)。 ### CR-260616-02 batch29+R-PD-10 代码改动批次 — ✅ 已审(PASS) - **commit**: `fc767e1` 优化: R-PD-10 storage_err DRY统一 + FR-D3 migrations if链转数组循环 - **审查时间**: 2026-06-16(独立审查 agent · 亲跑 test 核验,不信主代理"全过"声明) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪0 - **独立核验**: - **亲跑 `cargo test -p df-storage`** ✅ — crud 单元 **22** passed + project_soft_delete 集成 **11** passed,0 failed(覆盖 storage_err 替换后 CRUD / advance_status_atomic CAS / soft_delete / update_field 白名单 / migrations run() 全量路径)。主代理"11集成全过"声明属实,额外验证 22 单元。 - **R-PD-10 storage_err DRY** ✅ — helper `fn storage_err(e: E) -> Error { Error::Storage(e.to_string()) }`(crud.rs:19)泛型等价旧闭包;残留旧模式 grep **=0**(无遗漏);test 全过证 110 处替换无语义偏差。 - **FR-D3 migrations if→数组循环** ✅ — `steps: [(i32, fn(&Connection)->Result<()>); 15]`(migrations.rs:43)+ `for (version, migrate_fn) in steps { if current_version < version { migrate_fn(conn)?; } }`,与原 15 个 `if current_version Result<()>`,与 steps 数组 fn 指针类型精确匹配。 - **迁移数据安全三层保留** ✅ — ①版本门控 run() 开头 `MAX(version)`(migrations.rs:29)②各 migrate_vN 末尾 `INSERT INTO schema_version`(L67/75/.../274)③column_exists PRAGMA 幂等探测(L7-9,v4-v15 各处)。 - **待修项回流 todo**: **无**(全过,无新缺陷)。 ### CR-260616-01-fixes 审查待修项修复批次 — ✅ 已审(PASS) - **commit**: `4dc5219` 修复: CR-01审查待修项①类7项(CSS补 testing/blocked 徽章 + CAS防回收站推进 + DRY + 注释 + 测试改名 + advancing 接文案) - **审查时间**: 2026-06-16(独立审查 agent · 防"主代理已核查双绿"代入,亲核源码) - **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2(范围外观察项) - **独立核验**(不信主代理核查声明,grep/read 亲验 7 项①类修复): - ① **CR-01-B CAS deleted_at 语义** ✅ — `soft_delete`(crud.rs:800)设 `deleted_at` 非空;`advance_status_atomic` 两分支(bump_rounds + 普通,crud.rs:855/859)均加 `AND deleted_at IS NULL`,回收站任务 `affected=0 → None` 必败。D-02 软删收口正确,一处关闭。 - ② **CR-01-A CSS 配色跨视图一致** ✅ — TaskDetail.vue:344-345 与 Tasks.vue:413-414 **逐字镜像**(testing 橙 `#ff9800` / blocked 红danger 0.5px 边框),跨视图统一。 - ③ **CR-01-C DRY 文案零变更** ✅ — `ALL_STATES`(task_state_machine.rs:48-56)= `["todo","in_progress","in_review","testing","done","blocked","cancelled"]`,`join("/")` = 原硬编码串,顺序与值全等,无运行时文案变化。 - **CR-01-H 测试改名** ✅ — `cas_returns_none_when_status_mismatch`(task_advance_node.rs:313)真存在,改名后注释指向正确的 CAS 失败测试。 - **CR-01-I advancing i18n** ✅ — zh/en 均有(taskDetail.ts:38 `推进中.../Advancing...`),TaskDetail.vue:63 接 advancing 态。 - **CR-01-F 白名单注释** ✅ — `allowed_columns_for("tasks")`(crud.rs:331)数组无 `review_rounds` + 注释说明专用路径(仅 advance_status_atomic),收口防误补。 - **CR-01-E state.rs 注释** ✅ — build_registry(state.rs:240)注释准确,说明 TaskAdvanceNode 未注册原因(阶段2 DAG 联动时加)。 - **范围外(记录不审,非本批缺陷)**: - ⚪ **工作区 crud.rs 226行未提交** = `R-PD-10` `storage_err` DRY 重构(提取包装函数替换散落 `.map_err(|e| Error::Storage(e.to_string()))`),非 4dc5219 范围,独立审查留待其 commit。 - ⚪ **`status-abandoned` CSS class ↔ `cancelled` 后端态** = 历史命名别名(`constants/project.ts:83` `cancelled: 'status-abandoned'` 映射闭环,功能正确),非本批引入。命名语义瑕疵(abandoned 弃用 ≠ cancelled 取消),low,可选优化。 - **待修项回流 todo**: **无**(7 项全过,无新缺陷)。 ### CR-260616-01 任务推进链(batch28) — ✅ 已审 - **commit**: `d2cb38c` 新增: 任务推进链(7态状态机+advance_task CAS原子写)+软删除+前后端7态对齐 - **审查时间**: 2026-06-16(5 路并行多角度·防代入独立核验) - **决策依据**: D-260616-01(前端7态对齐) / D-02(任务软删除) / D-03(advance_task 走 df-nodes Node trait) / D-04(阶段1先行) - **总体结论**: 🔴0 🟡6 ⚪4 — **质量优** - **8 点 checklist 全过**: - 状态机矩阵 ✅(7态15转换,前后端单一真相源) - CAS 原子 ✅(`WHERE id AND status=expected` + affected=0 报错) - 退回累加原子 ✅(同 UPDATE) - V15 迁移幂等 ✅(版本门控 + column_exists 双层,八版零事故) - IPC 真 thin ✅(D-03 守住,task.rs 仅 3 行转发) - i18n 成对零遗漏 ✅(28 key) - create_task 不暴露 rounds ✅ - 测试 25 个 ✅ - **F-03 status 双写 = 已知延后**(阶段3 三件同落地,非新缺陷) - **范围外澄清**: df-ai/lib.rs(`mod retry`) + script_node.rs(`shell_type`) = 同 commit 无关连带改,零风险误报 - **待修项回流 todo**(CR-01-A~I,见 [todo.md](./todo.md) "审查发现待修项"区): - [P1] **CR-01-A** TaskDetail.vue scoped style 补 `.status-testing`/`.status-blocked`(D-01 前端7态遗漏,testing/blocked 徽章裸渲染丢样式;同查 Tasks.vue/Dashboard) - [P1] **CR-01-B** crud.rs advance_status_atomic CAS SQL 加 `AND deleted_at IS NULL`(get_by_id 宏无软删过滤→回收站任务可被 advance,D-02 软删对齐遗漏,语义错乱) - [P2] **CR-01-C** task_advance_node.rs:46 错误7态硬编码改 `ALL_STATES.join("/")`(DRY 单一事实源) - [P2] **CR-01-D** task_advance_node.rs:62 同态拒绝(from==to)/非法转换区分(现合并 InvalidState,前端无法分辨) - [P2] **CR-01-E** state.rs build_registry 注释补「task_advance 未注册(阶段2联动时加)」 - [P2] **CR-01-F** crud.rs:325 白名单注释补 review_rounds 专用路径 - [P3] **CR-01-G** TaskDetail.vue:218 advance 失败错误走 i18n 分级(en locale 也中文) - [P3] **CR-01-H** task_advance_node.rs:297 测试改名(实际未测 CAS 失败) - [P3] **CR-01-I** i18n `taskDetail.advancing` 冗余 key 处理