Files
DevFlow/docs/待审查.md

543 lines
86 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 待审查队列
> **职责分离**:
> - **主会话**(编码合并后 / 巡检)→ 【**写入**】"当前队列"区,登记待审查项(范围 + 维度 + 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) — ⏳ 待审
- **范围**: 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<String>`;`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**: 待提交(累积 ~12 文件攒批 + 4 决策回填)。
### CR-260616-37 F-11 审批续跑 iteration 累计计数(跨审批不重置) — ⏳ 待审
- **范围**: F-260616-11 决策a 落地(与 batch60 三项合批 commit)。`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**: 待提交(同 CR-36 合批)。
- **主代独立核查**: ✅ 全过(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-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<<retry_attempt` 仅留注释)/ backoff 含 ±20% jitter(retry.rs:74-85 SystemTime nanos)/ 30s 总预算(agentic.rs:242 retry_deadline + :290 budget_exhausted + :321 min 截断)/ 4xx Fatal 立即放弃(agentic.rs:293 `!retryable` return + stream_recv.rs:411-434 classify_status_or_class 镜像 is_status_retryable)/ 三处注释修正(commands.rs:1114 / state.rs:184 / agentic.rs:38)/ DRY 无重写(retry_with_backoff 仍 pub(crate) 流式不走)/ AttemptOutcome+is_reqwest_error_retryable 暴露合理。
- **CR-30-2(7 项全 PASS)**:useAiEvents.ts:177-186 `case 'AiStreamRetry'` 完整(取最后 isError 消息 + i18n t 更新「重试 n/m」,break 非穿透)/ 看门狗 reset 正确(AiStreamRetry 不在 NO_RESET_WATCHDOG Set)/ MidStream 决策 agentic.rs:274-285 Partial→incomplete=true + break 不重试,:286-342 InitFailed retryable 分支重试 / currentText 清空(useAiEvents.ts:316 AiError case `state.currentText=''` 再 AiStreamRetry 更新气泡,无累积重复)/ AiCompleted incomplete 系统提示(后端 agentic.rs:384 push session.messages 落库 + 前端 useAiEvents.ts:278-285 气泡)/ types.ts:215-217 variant + :202-204 incomplete?:boolean / i18n aiStreamRetry+responseIncomplete 双语齐。
- **回归(4 项无回归)**:permit RAII 释放(agentic.rs:405-406 显式 drop + loop 作用域 Drop)/ guard.reset 耗尽复位(8 exit 点全覆盖 :137/:300/:314/:352/:393/:457/:480/:543)/ request 每轮重建(:251-266 messages 从 session lock 重构)/ 无新死代码(AttemptOutcome/is_reqwest_error_retryable/is_status_retryable/backoff_delay/AiStreamRetry/classify_status_or_class 全有消费)。
- **CR-34-1(主线程预核验 PASS)**:useAiPanel.ts:102 守卫 `|| !list.some(p=>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-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())<total` 超时 false)/ ②total 准确(list_projects:214-215 `list_active().len()` 分页前全量=真实总量;list_tasks:236/list_ideas:253 同)/ ③探总量与 list 一致(get_project_count:1091-1093 list_active().len()=list_projects:214-215 同源;get_task_count:1104-1108 project_id 分支+list_all 兜底=list_tasks:231-234 同逻辑,range 一致)/ ④knowledge_search 解钳(knowledge.rs:110 `.min(20)` 替原 `.min(3)`,允许 LLM 翻更多候选合理)/ ⑤schema 描述(:208/225/246 含 offset/limit/total/has_more 翻页语义 LLM 可理解)。
- **⚪ low**:1(内存放大:list_*/get_*_count 先全量 load(list_active/list_all)再 skip/take,数据量大时内存放大;AI 工具调用 + MAX_LIST_RESULTS 钳 limit 实际可控)/ 2(list_tasks/get_task_count 用 list_all 未排除软删任务,而 list_projects/get_project_count 用 list_active 排除软删,任务与项目软删语义不对称;但 get_*_count 与 list_* 同源一致,且为既存逻辑非 CR-31 引入)。
- **待修项回流 todo**: **无**(五维度全 PASS)。
---
### CR-260616-30 batch56 F-260616-07 流式自动重试 — ⚠ 已审(ISSUES)
- **审查时间**: 2026-06-16(独立审查 agent · 亲跑 cargo check EXIT 0 + cargo test df-ai retry 6 passed **附 log 末尾** + grep/read 核验 + 重试循环正确性专项)
- **总体结论**: ⚠ **ISSUES** — 🔴0 🟡2 ⚪3(核心重试循环逻辑正确 permit/guard.reset/request 重建均 OK,但退避策略注释误称对齐 + AiStreamRetry 死事件 med)
- **亲跑 `cargo check --workspace`** ✅ EXIT 0(8 warning 全既存 dead_code 非本批)+ **`cargo test -p df-ai retry`** ✅ EXIT 0 **6 passed**(backoff_is_monotonic_within_jitter/status_classification/fatal_aborts_immediately/first_attempt_ok/retry_once_then_ok/all_retryable_exhausts,finished 3.35s)。
- **重试循环正确性专项(全 PASS)**:permit 持有释放 ✅(`_global_permit`/`_per_conv_permit` agentic.rs:239-240 外层函数体作用域,耗尽 return:285/成功 drop:333-334 均 RAII 释放无并发槽泄漏,重试期间持 permit 有意防新请求挤占)/ 耗尽路径 guard.reset ✅(:284 `guard.reset().await` return 前显式复位 generating,max_retries=0 时 `0..=0` 单次失败 is_last→reset+return 边界正确)/ request 每轮重建 ✅(:248-263 每轮 `session_arc.lock()`+`build_for_request` 重建 messages 无累积/丢失,与循环外:207-215 `request` 变量重复轻微冗余无害)。
- **🟡 CR-30-1(med,退避策略注释误称对齐)**:agentic.rs:298 `1u64 << retry_attempt` 纯指数无 jitter/无总预算/一律重试(含 MidStream);retry.rs:67-78 `backoff_delay` 有 ±20% jitter + 30s 总预算(:34)+ 4xx 不可重试分类(:102-104 Fatal 立即放弃)。三处注释(agentic.rs:37/commands.rs:1106/state.rs:185)均称「对齐 retry.rs 退避策略(1s→2s→4s+jitter)」与实现不符。修法:要么 agentic 加 jitter+总预算+错误分类真对齐,要么删注释「对齐 retry.rs」改为「独立简化策略」。
- **🟡 CR-30-2(med,AiStreamRetry 死事件 + MidStream 重试文本混乱)**:后端 emit AiStreamRetry(agentic.rs:310),types.ts:214 有 variant,但 useAiEvents.ts switch 无 `case 'AiStreamRetry'`(十 case 无 retry)事件穿透忽略。更严重:MidStream 失败重试时已 emit 的 AiTextDelta 累积进前端 currentText,重试新 delta 追加 → 文本重复/混乱(stream_recv.rs:210/240 MidStream return None,agentic.rs:294 一律重试无清空 currentText 机制)。修法:useAiEvents 补 case + 重试前清空 currentText;或后端 MidStream 失败不重试只重试 Init 失败(对齐决策 a 只重试流前失败)。
- **⚪ low**:1(agentic.rs:243/317 `last_error` 声明赋值但 result 分支:323-330 未读 dead code)/ 2(:294-296 MidStream 一律重试注释承认「简化」,与决策 a 只重试流前失败相悖)/ 3(run_agentic_loop 3 调用点 commands.rs:108/204/444 非登记「4 入口」,try_continue 走审批恢复复用非独立入口)。
- **待修项回流 todo**: **CR-30-1**(med,退避策略注释对齐)+ **CR-30-2**(med,AiStreamRetry 死事件+MidStream 重试文本混乱)。
---
### CR-260616-29 batch56 ARC-06 composable 循环依赖破环 — ✅ 已审(PASS)
- **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验 import + 源码)
- **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪1
- **逐件核验(全 PASS)**:①循环依赖真破(useAiEvents.ts import L14-23 无 useAiSend,改 L284 `emit('ai-drain-queue')` 事件总线;useAiSend.ts:25 仍 import useAiEvents(startListener)单向,双向环已断)/ ②事件总线桥接(useAiEvents:284 emit + useAiSend:395-398 initDrainQueueListener `if(_drainUnlisten) return` 幂等守卫 + stores/ai.ts:134 void 注册)/ ③findToolCall 下沉一致(aiShared.ts:41-51 反向扫 messages + 同消息正向扫 toolCalls,两调用方 import 同一实现,注释:37-40 说明反向扫描避免陈旧引用)/ ④startApprovalTimer 幂等(aiShared.ts:66 `if(_approvalTimers.has) return` 同 id 不重建 + clearApprovalTimer/clearAllApprovalTimers 清理完整)。
- **⚪ low**:1(事件总线桥接理论竞态:initDrainQueueListener stores/ai.ts:134 void 不 await,注册前若 AiCompleted 到达事件丢失 drainQueue 不触发;实际初始化远早于首次生成完成低风险,原 direct call 同步无此问题,可靠性微小降级)。
- **待修项回流 todo**: **无**(四维度全 PASS,循环依赖真破)。
---
### CR-260616-28 batch56 F-260616-06 审批幂等+防抖 — ✅ 已审(PASS)
- **审查时间**: 2026-06-16(主线程兜底·agent 撞 GLM 限流 1305 · grep/read 核验源码 + 方法存在性确认)
- **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪2
- **逐件核验(全 PASS)**:①幂等三态覆盖(commands.rs:220 内存 pending_approvals.remove None → :224 查审计表 find_by_tool_call_id → :227 executed/rejected/failed 返回 Ok「已处理」三态全覆盖;pending 态落 :231 Err 合理——内存 pending_approvals 丢失即审批上下文不可恢复,DB pending 占位仅日志非可恢复审批)/ ②防抖(useAiSend.ts:307 _pendingApprovalIds.has 守卫无时间窗口竞态 + :308 add + :332-334 finally delete 无论成功异常均清理,模块级 Set 不泄漏)/ ③find_by_tool_call_id 存在确认(crud.rs:1198-1201 `pub async fn(&self,&str)->Result<Option<AiToolExecutionRecord>>`,: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<String>`(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<string>`,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<AiConversationSummary[] | null>` 类型精确匹配)。
- **独立核验**(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<AtomicUsize>` + 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<ErrorType>`,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<String>, target_status: Option<String>`(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<Database>)` 改签名;`register("task_advance", move |_| Box::new(TaskAdvanceNode::new(db.clone())))`(state.rs:248-250)move 闭包捕获 Arc<Database>,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: ToString>(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<N { migrate_vN(conn)?; }` **逐字等价**(顺序[1..15]+条件`<version`+`?`传播)。零行为变更属实。
- **migrate_vN 签名匹配** ✅ — migrate_v1~v15 全 `fn(&Connection) -> 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 处理