# 待审查已审归档（2026-06）

> 从 docs/待审查.md 拆出: 已审 ✅ CR 段。
> 拆分: 2026-06-18
> 来源: docs/待审查.md

---

## 当前队列已审项

### CR-260616-58 F-260616-07 阶段3 AiNode 自审闸门(verdict=fail 作 DAG 节点闸门阻断下游)(e16d038) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+test+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪1
  - **验证**: `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)/ `cargo test -p df-nodes --lib` **82 passed; 0 failed; 1 ignored** (+3 gate 单测:1076-1103)。
  - **①gate_should_block 决策矩阵完备 PASS**:ai_node.rs:426-428 `gate && verdict == "fail"` 简洁正确,gate=false 全放行(阶段2 兼容),gate=true+仅 fail 阻断,unknown/非标 verdict 保守不阻断(单测:1096-1103 覆盖)。
  - **②execute 末尾 Err 路径正确 PASS**:ai_node.rs:574-582 update_field 落库在前(审查痕迹不丢) + :615-629 闸门检查后置,verdict=fail 返回 Err 正确。
  - **③复用 executor first_err + ②-4 回调语义 PASS**:df-workflow/src/executor.rs:128-178 first_err 捕获首个 Err 后立即中止后续层(下游 human_review 不跑) + workflow.rs:292-310 failed 触发 regression_target 退回 in_review(review_rounds+1,task_advance_node.rs:515-547 验证)。
  - **④gate 默认 false 向后兼容 PASS**:ai_node.rs:619 `.unwrap_or(false)` 阶段2 行为不变,AiNode schema(:331-346)无 gate 字段,仅 AiSelfReviewNode 有(:644)。
  - **⑤testing 模板 gate:true 启用 PASS**:task_workflow_templates.rs:65-69 `"gate": true` 正确注入 + 单测:139-144 断言验证。
  - **⑥schema 契约正确 PASS**:ai_node.rs:644 gate 布尔字段,非 required,description 完整。
  - **⑦文件锁独立性 PASS**:ai_node.rs + task_workflow_templates.rs 独占,df-workflow 零改动(git show e16d038 确认)。
  - **⑧选型论证 PASS**:ai_node.rs:605-613 注释完整(方案 A 最简 vs B 暂缓条件引擎 vs C executor 改核心),选 A 语义贴近「自审结果作为闸门」且可开关。
  - **⚪ CR-58-1(low,注释冗余)**:ai_node.rs:605-613 选型注释 9 行,但提供设计决策历史,对后续维护有价值,可保留。
  - **待修项回流 todo**: **无**(8 维度全 PASS,1 low 注释冗余非阻断)。

### CR-260616-59 波16 F-04c前端Settings池编辑(enabled toggle+weight输入+IPC绑定)(3f90aad) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 vue-tsc + cargo check + 独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡1 ⚪0
  - **验证**: `npx vue-tsc --noEmit` EXIT 0 / `cargo check --workspace` EXIT 0(6 warning 全 pre-existing dead_code)。
  - **①updateProviderPool wrapper 对齐现有 invoke 风格 PASS**:ai.ts:146-148 `invoke('ai_update_provider_pool', { providerId, enabled, weight })` 与现有 aiApi.setProvider/deleteProvider 风格一致,thin wrapper 无额外逻辑。
  - **②AiProviderConfig enabled?/weight? 可选 PASS**:types.ts:191-193 enabled?/weight? 可选字段(snake_case 对齐 serde),注释完整("老库默认 true"/"老库默认 50")。Settings.vue:61 `p.enabled !== false` 正确处理 undefined 视为 true。
  - **③即时调 IPC 决策合理 PASS**:即时调理由(Settings.vue:616-618 注释)充分——池调度参数 reload_provider_caps 变更即重建,下条消息即生效;避 saveProvider 密钥迁移分支(R-PD-1)。对齐 syncConcurrencyConfig 即时调模式(Settings.vue:822-839 debounce 300ms 证明)。
  - **④debounce 300ms+clamp+NaN 兜底 PASS**:Settings.vue:647 `clearTimeout(_poolTimer)` + :648 `setTimeout(..., 300)` debounce 正确。:645 `Math.min(100, Math.max(0, Number.isFinite(raw) ? Math.trunc(raw) : 50))` 双 clamp+NaN 兜底(回退 50)防 serde u32 失败。
  - **⑤乐观更新 revert 逻辑 部分PASS**:onPoolToggle(:626-637) `const prev = p.enabled` + `p.enabled = prev` revert 完整。**🟡 onPoolWeightChange(:643-656) 缺 revert**—— debounce 异步路径,失败后 `p.weight` 已更新为 clamped 值但 IPC 未成功落库,用户视角显示已改但实际未生效,需补 `const prev = p.weight; p.weight = clamped; catch { p.weight = prev }`(前端乐观更新失败应 revert 状态)。
  - **⑥i18n 双语 PASS**:zh-CN/settings.ts:17-23 + en/settings.ts:17-23 7 key 对称完整(detailPoolEnabled/detailPoolWeight/badgePoolDisabled/descPoolEnabled/descPoolWeight/toastPoolUpdateOk/toastPoolUpdateFail)。特殊字符无转义需求(值均为静态枚举字面量)。
  - **⑦onUnmounted 清理 PASS**:Settings.vue:954 `if (_poolTimer) clearTimeout(_poolTimer)` 清理完整,与 _concurrencyTimer/_agentIterTimer 等同级处理。
  - **⑧Tauri 2 参数命名转换 PASS**:后端 commands.rs:1023-1028 `provider_id/enabled/weight`(snake_case),前端 ai.ts:147 传 `providerId/enabled/weight`(camelCase)。Tauri 2 自动转换 snake_case→camelCase 为设计行为([官方文档](https://v2.tauri.app/develop/calling-rust/)+[StackOverflow](https://stackoverflow.com/questions/78432685/why-does-tauri-modify-the-parameter-names-of-invoked-functions))。
  - **待修项回流 todo**: **🟡 onPoolWeightChange 补 revert 逻辑**(非阻断,用户可刷新重试,但应补 `const prev = p.weight` + catch 内 `p.weight = prev`)。

### CR-260616-60 波17 B-03b-R10前端审批清理(终态清pending+互斥锁防重复IPC)(0289d8c) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 vue-tsc + 独立 grep/read 核验源码)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `npx vue-tsc --noEmit` **EXIT 0** ✅
  - **①⑤ 终态清 execution_id 限定防误清 PASS**:workflow.ts:62-70 三条件判断(`pendingApproval存在` + `execution_id匹配` + `终态事件类型`) + 并发工作流场景(execution_id不等不误清他人pending) + 后端事件类型契约确认(WorkflowCompleted/Failed/NodeCancelled→snake_case) + PendingApproval.execution_id字段契约一致(state.ts:6 ↔ events.rs:86)。
  - **②⑦ 互斥锁 _approvalInFlight 单例防重复 IPC PASS**:模块级单例(workflow.ts:11) + approve/cancel双入口守卫(L99/L135 `if(_approvalInFlight)return`) + finally块全释放(L127/L150 成功/失败均释放) + 共享单例跨调用方统一(所有入口经workflow.ts统一函数无旁路)。
  - **③ 失败重试语义正确 PASS**:approve/cancel catch块只设置state.error不清空pendingApproval(L122-124/L145-147),用户可重试 + 成功后才清空(L121/L144)。
  - **④ 互斥锁释放路径完备 PASS**:approve/cancel try-catch-finally三路径全覆盖,无early return跳过finally(async/await保证finally执行)。
  - **⑤ 事件类型覆盖完整 PASS**:终态三类型(workflow_completed/workflow_failed/node_cancelled)全覆盖,覆盖工作流完成/失败/节点取消三路径。
  - **⑥ 单槽设计边界清晰 PASS**:当前设计pendingApproval单槽(state.ts:23),审查报告P2「改Map」推迟正确,本CR范围只实施⑤⑦未改动架构。
  - **⑦ 调用方入口统一 PASS**:ProjectDetail.vue三入口(409/420/427)经workflow.ts统一函数,stores/project.ts转发无旁路。
  - **⑧ type safety 验证 PASS**:WorkflowEventPayload.execution_id/event.type契约(types.ts:140-142) + WorkflowEventType联合类型含三终态(types.ts:135-136)。
  - **待修项回流 todo**: **无**(8维度全PASS,零问题)。

- **范围**: workflow w9xypd8ug agent B。src/stores/project/workflow.ts ⑤终态不清(startEventListener 加 else-if,workflow_completed/failed/node_cancelled 且 execution_id 匹配清 pendingApproval,按 exec_id 限定防并发误清)+ ⑦互斥(模块级 _approvalInFlight 单例标志,approve/cancel 入口检查+try/finally 释放,防双击重复 IPC)。④单槽→Map 推迟(state 形态变更牵动多消费者,后端串行闸门保证单执行不并发,现实触发需并发工作流)。波17 B-03b 后端 workflow.rs 0 实施(⑧⑩⑪ 报告失效/已修复/已有兜底,③ 真实潜伏跨 crate df-core 缺 execution_id 留观察)。
- **维度**: ⑤终态清 execution_id 限定(防并发误清)+ ⑦互斥 _approvalInFlight 单例(跨调用方统一)+ ④推迟理由(state 形态变更+后端串行)。
- **commit**: 0289d8c。
- **主代核查**: vue-tsc EXIT 0。
- **审查 agent 待复审重点**: ⑤终态清 execution_id 匹配逻辑(防误清)+ ⑦互斥 _approvalInFlight try/finally 释放(异常路径不泄漏)。
- **待修项回流 todo**: **无**(⑤⑦ 实施,④ 推迟有理由)。

### CR-260616-61 波18 ARC df-core改名df-types(机械全workspace 54处)(4be1591) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+test+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)/ `cargo test -p df-nodes` **82 passed; 0 failed; 1 ignored** / `cargo test -p df-workflow` **23 passed** / `cargo test -p df-ideas` **20 passed** / `cargo test -p df-storage` **29 passed**。
  - **①grep df_core/df-core 代码侧归零 PASS**: `grep -r "df_core" --include="*.rs" --include="*.toml" crates/ src-tauri/` 零输出 / `grep -r "df-core" crates/ src-tauri/` 零输出 / `grep -r "df_types" crates/ src-tauri/` 54 处(与 commit 统计一致)。
  - **②import 路径全更新 PASS**:git show 核验 task_advance_node.rs 12 处 `df_core::error::`→`df_types::error::` / executor.rs 8 处 `df_core::events/types`→`df_types::` / task.rs 1 处 `df_core::types`→`df_types::types` / 全部编译期通过(cargo check EXIT 0)。
  - **③注释连字符全更新 PASS**:git show 核验 lib.rs 文档注释 `df-types: 核心类型定义` + task_advance_node.rs/human_node.rs/manager.rs/crud.rs 共 8 处注释 `df-core`→`df-types` 或 `df-types::`。
  - **④Cargo.lock 自动迁移 PASS**:git show 核验 `df-core` 包定义已删除(name + dependencies 块移除) + 所有依赖方(df-ai/df-execute/df-nodes/df-project/df-storage/df-workflow/src-tauri)依赖更新为 `df-types`。
  - **⑤glob members 自动适应 PASS**:workspace Cargo.toml `members = ["crates/*"]` glob 自动覆盖新 df-types 目录 / `ls crates/` 确认 df-core 已删除仅剩 df-types。
  - **⑥目录结构完整 PASS**:ls -la crates/df-types/src/ 确认 error.rs/events.rs/lib.rs/types.rs 四文件完整 / git mv R100/R092 确认文件移动无丢失。
  - **⑦宏自引用零遗漏 PASS**:git show '*.rs' grep `macro` 零新增(本 CR 无宏定义改动,仅 import 路径替换) / 波15 走查核验通过。
  - **⑧字符串字面量零遗漏 PASS**:git show '*.rs' grep `^\+.*"df-core"` 零命中 / 注释外零字符串字面量残留。
  - **待修项回流 todo**: **无**(8 维度全 PASS,零问题。文档 .md/project.ts 注释 deferred 非:blocking,按原始登记不回流 todo)。

### CR-260616-62 波19 UX-19消息列表虚拟滚动(自研IntersectionObserver+sentinel+末条保活)(e38474b) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 vue-tsc + cargo check + 独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `npx vue-tsc --noEmit` **EXIT 0** ✅ / `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)。
  - **①pinnedKeys 末条保活 PASS**:AiChat.vue:2363-2373 `lastStreamingRenderKey` computed 取末条 AI 消息 key(`m-${id}`) + watch 立即传 `setPinnedMsgs(new Set([key]))` + useAiVirtualScroll.ts:130-132 `shouldRender` 双路返回(`renderedKeys U pinnedKeys`) + IO 回调(:68) 跳过 `pinnedKeys.value.has(key)` 项。三重保证流式末条恒渲染。
  - **②sentinel 占位高度不塌 PASS**:useAiVirtualScroll.ts:72-75 卸载前锁 minHeight=`${height}px` + :88 ResizeObserver 实测 `e.contentRect.height` 持续更新 + :102-103 挂载时复用上次 height 占位 + :65 重新可见清 minHeight 让内容自然撑高。AiChat.vue:2941 `.ai-msg-slot { display: flex; flex-direction: column; }` 承担 gap:14px。scrollHeight 计算不受虚拟化影响。
  - **③零新依赖 PASS**:git show 核验仅新增 useAiVirtualScroll.ts 162 行 + AiChat.vue 44 行改动。`grep vue-virtual-scroller package.json` 零命中。自研 IO/RO 方案无外部依赖。
  - **④现有逻辑全保留 PASS**:grep 核验 streamingBlocks(:777-783 rAF 块级 memo)/scheduleStreamParse(:863-873)/wasNearBottom(:1765)/collapseAllToolLists(:1889-1893)/isNearBottom(:1754-1760)/scrollToBottom(:1782-1788)全在。AiChat.vue 模板 :400 `shouldRenderMsg(item.key)` 仅裁内容不裁滚动逻辑。
  - **⑤IO/RO disconnect 无泄漏 PASS**:useAiVirtualScroll.ts:146-152 `onBeforeUnmount` 双 disconnect + io=null/ro=null/itemMeta.clear()。:114-120 卸载分支 `io?.unobserve(prev.el)` + `ro?.unobserve(prev.el)`。三路径完整(组件卸载/单项卸载/观察器重建)。
  - **⑥registerSentinel 生命周期正确 PASS**:useAiVirtualScroll.ts:97-121 挂载时 `io?.observe(el)` + `ro?.observe(el)`(:106-107) + 卸载时 `unobserve`(:116-117) + itemMeta 保留 height 复用(:119)。`el.dataset.vscrollKey = key`(:100) 绑定唯一标识。
  - **⑦rootMargin 预加载 600px PASS**:useAiVirtualScroll.ts:81 `rootMargin: '${rootMargin}px 0px ${rootMargin}px 0px'` 上下各 600px + :38 默认 600px。滚动时内容提前就位,防白屏闪烁。
  - **⑧setPinned 强制进渲染集 PASS**:useAiVirtualScroll.ts:135-144 `setPinned` 更新 `pinnedKeys.value` + 强制合并进 `renderedKeys`。pinned 项即使 IO 不可见也渲染。
  - **待修项回流 todo**: **无**(8 维度全 PASS,零问题。ToolCard 嵌套/超大历史 sentinel 二段留观察,非当前 CR 范围)。

### CR-260616-63 波20 F-05多模态Phase2a后端(ContentPart+parts+provider图片块+truncate)(e3cd448) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+test+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪1
  - **验证**: `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)/ `cargo test -p df-ai-core` **19 passed** / `cargo test -p df-ai` **111 passed**。
  - **①ContentPart serde tag=type 兼容老 JSON PASS**:provider.rs:48-50 `#[serde(tag = "type", rename_all = "snake_case")]` 正确区分 Text/Image + 单测:370-377 `contentpart_legacy_json_no_parts` 老 JSON 反序列化 parts=None 验证 + :381-402 `contentpart_with_image_roundtrip` 新 JSON round-trip 验证。
  - **②parts Option 向后兼容 PASS**:provider.rs:102-103 `#[serde(default, skip_serializing_if = "Option::is_none")]` + 构造器 user/user_parts/assistant 均设 parts=None(:122/:125/:128) + 单测:442-455 `contentpart_struct_literal_compat` 字面量构造合法。
  - **③openai content Value+image_url PASS**:openai_compat.rs:320-348 m.has_image() 判定 + flattened_parts() 拍平 + content 数组 [{type,text},{type,image_url,image_url:{url}}] + base64→data URI 转换(:330-333 `format!("data:{};base64,{}", mt, b)`)。
  - **④anthropic image source base64 PASS**:anthropic_compat.rs:342-375 含图时 content blocks 数组 + Image 片→{type:image,source:{type:base64,media_type,data}} + url 模式 warn 提示预留 2b/2c 预拉(:356-362)。
  - **⑤truncate_parts 防 DB 撑爆 PASS**:conversation.rs:87-127 truncate_parts_for_persist Image base64 替换占位 Text(:100-106 `<image: base64 已省略, 共约 {} 字符>`) + Text 片复用 truncate_for_persist 头尾截断 + url 模式原样保留。
  - **⑥务实偏离合理性 PASS**:grep 核验 4 forbidden 消费点(audit.rs:461/commands.rs:87+678/knowledge_inject.rs:314+316+338)均读 m.content 字符串零改动,title.rs:50-58 结构体字面量 parts:None 补字段唯一越界但零变化(单测:442-455 证字面量合法)。
  - **⚪ CR-63-1(low,Anthropic URL 预拉留 2b/2c)**:anthropic_compat.rs:356-362 url 模式 warn 但未实现预拉(当前 base64 为主流,url 预拉留后续联调),非阻断当前 Phase 2a 就绪。
  - **待修项回流 todo**: **无**(6 重点全 PASS,1 low 预拉非阻断,Phase 2a 就绪)。

### CR-260616-64 波21 F-05多模态Phase2b前端(图片粘贴/拖拽+渲染+parts透传)(e1d396d) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 vue-tsc + cargo check + 独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `npx vue-tsc --noEmit` **EXIT 0** ✅ / `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)。
  - **①@paste 图片检测+base64 PASS**:AiChat.vue:1037-1052 onPaste 遍历 clipboardData.items,`it.kind==='file'&&it.type.startsWith('image/')` 正确检测 + :1050 e.preventDefault() 阻止默认粘贴(避免二进制塞 textarea) + :1051 逐个 addImageFile。readImageFile:997-1003 FileReader Promise 正确。
  - **②@dragover/@drop 拖拽检测 PASS**:onDragOver:1055-1063 `Array.from(e.dataTransfer.types).includes('Files')` 判断含文件 + preventDefault + dropEffect='copy' 正确。onDrop:1066-1074 filter `f.type.startsWith('image/')` 仅取图片 + preventDefault + 逐个 addImageFile。
  - **③img 渲染随虚拟滚动无冲突 PASS**:模板:415 `v-if="msgImageParts(item.msg).length"` + :413-419 注释明确「虚拟滚动裁剪整条消息,气泡内 <img> 随 sentinel 卸载无冲突」。msgImageParts:1083-1086 filter type==='image' 正确。imageSrc:1089-1096 base64 模式拼 data URI(`data:${mt};base64,${img.base64}`) + url 模式兜底空串。
  - **④parts 全链透传 PASS**:handleSend:1830 snapshotImgs 快照 + :1831-1849 ContentPart[] 构造(Text 首+Image 序) + :1863 `store.sendMessage(text,skill?.name,false,parts)` 传参。useAiSend.ts doSend:80 接 parts 参数 + :87 仅非空挂 parts + :114/:117 aiApi.forceSend/sendMessage 透传(Phase 2c 后端接入即生效)。drainQueue:261 `sendMessage(next.text,next.skill,false,next.parts)` 续发正确。queue 扩展:ai.ts:72-73 注释明确 + useAiSend.ts:298-302 入队挂 parts。
  - **⑤虚拟滚动保留 PASS**:AiChat.vue:1106-1111 useAiVirtualScroll 未改动(git show e1d396d 确认) + 模板:401 `shouldRenderMsg(item.key)` 裁剪整条消息,图片在气泡内随消息卸载,无独立冲突。
  - **⑥i18n 双语 PASS**:zh-CN:103-107 + en:102-106 五 key 对称(removeImage/imagePreviewAlt/imageTooLarge/imageLimit/imageReadFailed)。特殊字符无转义需求。
  - **⑦约束正确 PASS**:MAX_PENDING_IMAGES=6 + MAX_IMAGE_BYTES=10MB + addImageFile:1007-1029 size/type/数量三重校验 + toast 提示完整。
  - **⑧发送失败回填 PASS**:handleSend:1869-1871 `if(parts) pendingImages.value=snapshotImgs` 回填正确,用户可重试。
  - **待修项回流 todo**: **无**(8 维度全 PASS,零问题。Phase 2c IPC 接入即全链闭环)。

### CR-260616-65 波22 F-05多模态Phase2c闭环(IPC接parts+api透传+doSend传参)(4317d02) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+vue-tsc+独立 git show/grep/read 核验 2 轮)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)/ `npx vue-tsc --noEmit` **EXIT 0**。
  - **①IPC serde 反序列化 PASS**:commands.rs:8 import `ContentPart`+ :149 `parts: Option<Vec<ContentPart>>` 参数 + :147-148 注释阐明 Tauri 2 自动 serde 反序列化(df-ai-core provider.rs:49 `#[serde(tag = "type", rename_all = "snake_case")]` 与前端 types.ts:288-298 契约严格对齐)。
  - **②user_parts 空兼容 PASS**:commands.rs:182-186 `if let Some(ps) = parts.as_ref().filter(|p| !p.is_empty())` 双重守卫(非 None 且非空)→ user_parts() + else 分支 user() 零回归。provider.rs:139-141 user_parts 实现(parts 挂 ChatMessage.parts 字段,provider.rs:144-145 has_image 判定走 vision 端点)。
  - **③api parts 透传 PASS**:ai.ts:13+24 两函数加 parts 参数 + :19/:30 `parts && parts.length > 0 ? parts : null` 空过滤(与 commands.rs:182 守卫对称)+ :5 import `ContentPart` 类型。invoke 参数对象正确传。
  - **④doSend 传参 PASS**:useAiSend.ts:113+125 force/普通双分支加 parts 传参(`aiApi.forceSend/sendMessage(..., parts)`) + :93 函数签名接 parts + :100 本地 state 挂 `parts && parts.length > 0 ? parts : undefined`。Phase 2c 全链生效(CR-64 前端已挂 parts→本 CR IPC 接入→2a 后端处理闭环)。
  - **⑤FR-S1 Image base64 安全 PASS**:commands.rs:146-148 注释阐明「ContentPart Image base64 是图片数据非 api_key,不入敏感面」。核验:grep `NodeContext.config`/`NodeOutput`/`tool_call` schema 三点零 parts 字段(conversation.rs:153 truncate_parts 仅落 session.messages+provider 转发,audit/title/tool_calls 不读 parts)。base64 仅经 ai_chat_send→session.messages→provider HTTP 出站,不进 IPC 返回/日志/错误/工作流节点上下文。
  - **⑥forceSend 透传 PASS**:commands.rs:740 ai_chat_force_send 加 parts 参数 + :763 透传给 ai_chat_send(`ai_chat_send(..., parts).await`)。注释 :739 澄明「强制发送同款多模态支持」。
  - **⑦truncate_parts 防撑爆 PASS**:conversation.rs:87-127 truncate_parts_for_persist(Image base64 替换占位+Text 头尾截断)+ :153 落库前调用(git show 4317d02 前已有此 Phase 2a 代码,本 CR 零改动但依赖存在)。DB 撑爆防护已就绪。
  - **⑧类型契约对齐 PASS**:types.ts:288-298 ContentPart 联合类型(`type:'text'|'image'` discriminator)与 df-ai-core provider.rs:49-65 serde tag 严格对齐(snake_case ↔ 字面量 'text'/'image')。前端 4 处 `parts&&parts.length>0` 守卫(ai.ts:19/:30/useAiSend.ts:100/:331/useAiConversations.ts:90)保证空/undefined 不传。
  - **待修项回流 todo**: **无**(8 维度全 PASS,零问题。F-05 多模态全链闭环:2a 后端 ContentPart+parts+truncate+ 2b 前端粘贴/拖拽/渲染+ 2c IPC 透传+ doSend 传参,完整落地)。
  - **第 2 轮独立核验补充(2026-06-17·git show 4317d02 快照态)**: ①三文件 diff 全吻合登记声明。②行号微偏(useAiSend.ts 签名实为 :80 非 :93/state 挂 parts 实为 :87 非 :100/forceSend 实为 :114 非 :113/sendMessage 实为 :116 非 :125),代码逻辑无误,仅引用行号需校正。③FR-S1 深度核验追加:ContentPart/base64 grep 仅命中 df-ai + df-ai-core 两 crate,**df-nodes/df-workflow 零命中**(NodeContext.config 是 serde_json::Value 节点配置非消息,无 parts 字段);`all_messages_clone()` 两 IPC 调用点(commands.rs:84/672)均仅读 `.content` 做知识检索,不读 `.parts`;**持久化路径 conversation.rs:153 `save_conversation` 对每条 msg 调 truncate_parts_for_persist(Image base64→占位 Text)→ DB 落盘的 parts 已是占位**;**commands.rs:1444 ai_conversation_export "json" 格式 `serde_json::to_string_pretty(&messages)` 读的是 DB 持久化副本(record.messages),非内存全量**,故导出 JSON 不含原始 base64。FR-S1 全链零泄漏(不进 IPC 返回/日志/NodeContext/tool_call/导出)。④cargo check EXIT 0(6 pre-existing dead_code 全无关)/ vue-tsc EXIT 0,亲跑复现。

### CR-260616-67 波24 B-03b-R10③工作流事件补execution_id治本并发串扰(bac3c76) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+test+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `cargo check --workspace` **EXIT 0**(6 warning 全 pre-existing dead_code)/ `cargo test -p df-types` **7 passed** / `cargo test -p df-workflow` **23 passed** / `cargo test -p devflow --lib` **92 passed**。
  - **①字段 #[serde(default)] 向后兼容 PASS**:events.rs:83-84 WorkflowCompleted `#[serde(default)] execution_id: String` + :91-92 WorkflowFailed 同字段。老事件/老 DB 反序列化无 execution_id 时填空串不炸,serde(default) 自动处理。
  - **②3 emit 点全覆盖 PASS**:executor.rs:185-186 WorkflowCompleted 发 `self.execution_id.clone()`(DagExecutor 字段:20) + workflow.rs:208-210 Lagged 补发 completed 发 `forward_exec_id.clone()` + :212-217 Lagged 补发 failed 发 `forward_exec_id.clone()` + :218-222 Lagged 补发 cancelled 发 `forward_exec_id.clone()` + :364-365 执行失败补发 WorkflowFailed 发 `exec_id.clone()`。**5 emit 点全覆盖**(executor 1 + Lagged 3 + 补发 1),全部携带正确 execution_id。
  - **③消费 exec_id 守卫只 break 自己 PASS**:workflow.rs:167-172 matches! 双守卫(`变体 Completed/Failed` + `if execution_id == &forward_exec_id`)。他人终态事件(execution_id != forward_exec_id)finished=false 继续循环,不误 break。源码佐证: :171 条件守卫 + :180 `if finished { break }`。
  - **④空串匹配语义只漏不误 PASS**:#[serde(default)] 老事件空串 + :171 条件 `execution_id == &forward_exec_id`,自家 forward_exec_id 永远非空(run_workflow_inner:73 生成 UUID),空串匹配永不触发 finished。语义正确:只可能漏(自家老事件空串),不会误 break(他人终态非空但不等)。
  - **⑤前端契约零破坏 PASS**:types.ts:140 WorkflowEventPayload.execution_id 已有字段 + workflow.ts:64-67 终态清空逻辑只看 `payload.event?.type` 不变。execution_id 在事件 payload 内,类型不变,前端零改动。
  - **⑥字段补齐对齐其他变体 PASS**:grep 核验 HumanApprovalRequest/Response(:98/:109)已有 execution_id 字段,Completed/Failed 现在对齐,契约统一。
  - **⑦注释完整 PASS**:events.rs:77-81 + 89 注释阐明 B-03b-R10 ③ 根因 + 解决方案 + serde(default) 理由。workflow.rs:159-166 注释阐明并发串扰 + 双重匹配 + 空串向后兼容语义。executor.rs:182-183 注释阐明 emit 携 execution_id 理由。
  - **待修项回流 todo**: **无**(7 维度全 PASS,零问题。跨 crate 治本完整,并发串扰根除)。

### 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<String> + 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<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**: 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<Arc<dyn LlmProvider>>` + :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<dyn LlmProvider>)` 标准库转换(堆分配复用),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<Arc<dyn LlmProvider>>`(:96) + `new(provider:Arc<dyn LlmProvider>)`(: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<Box<dyn LlmProvider>>: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<Arc>——调用方分支等价性,无遗漏 Some/None) ④EvaluatedBy 三态正确(Llm/Heuristic/HeuristicFallback,provider None 全走 Heuristic) ⑤Box→Arc 转换(idea.rs:192 Arc::from(Box<dyn LlmProvider>) 编译通过) ⑥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<ImageRef{alt,src}>` 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<E> 链路通(`?` 编译过 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<Database>`(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<String>` + V17 迁移(幂等补列) + crud 白名单加 output_json(L346)+SELECT/INSERT/UPDATE 全链路 + 5 构造点补 None。②**ai_node.rs②③④**(c10adaf):AiNode 持 db(`Arc<Database>`,对齐 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<<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-45 波1 F-01阶段3厂商模型列表拉取 + UX-01错误气泡Fatal区分 + UX-03工具卡按轮次收起 — ✅ 已审(PASS)

- **复审结论(2026-06-17·CR-45 agent 亲跑 cargo+test+vue-tsc+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪2
  - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai model_fetch` **24 passed** 全过/ `npx vue-tsc --noEmit` EXIT 0。
  - **①build_models_url 7 分支完备 PASS**:model_fetch.rs:165-178 尾 trim/|v1|v4/|models/chat/completions/api/根 base 7 分支全覆盖;断言成立(无 /v1/v1/models 重复,trim 后单次 suffix 判断);测试 build_url_v1_suffix_appends_models:303 `assert!(!...contains("/v1/v1"))` 编译期证。
  - **②噪音过滤 13 类 + embedding 保留 PASS**:is_non_chat_model:196-219 dall-e/midjourney/sd/imagen/tts/whisper/audio/speech/voice/realtime/transcribe/moderation/-search 13 类;embedding_kept_per_design:410-414 单测证保留;注释 :194 明确按 §5.4 设计。
  - **③反序列化多 alias 兜底 PASS**:ModelsList:248-253 serde(default) + data alias="models";ModelEntry:256-259 id alias="name";into_ids:263-269 filter_map 丢弃 None;parse_openai_format/parse_anthropic_format/parse_ollama_format/parse_missing_data_field_yields_empty/parse_entry_missing_id_skipped 5 单测全覆盖。
  - **④协议分派 PASS**:fetch_openai_compat:66-89 Bearer 鉴权 + /v1/models;fetch_anthropic_compat:92-119 x-api-key+anthropic-version 双 header + /v1/models;fetch_model_names:40-46 其他报 UnsupportedProvider;三路径完备。
  - **⑤错误映射友好提示 PASS**:map_network_error:132-138 timeout→超时提示/其他→网络错误;map_status_error:141-151 401/403→鉴权失败/404→端点不存在/5xx→厂商异常;全含 provider_type+url 上下文。
  - **⑥fetch_and_probe probe 兜底 PASS**:fetch_and_probe:52-59 探测为纯 CPU map collect,单模型探测失败不中断整体(probe 本身有兜底)。
  - **⑦UX-01 canRetry Fatal 分类 PASS**:AiChat.vue:1994-1999 auth/provider_config=Fatal 隐藏重试;notRetryable 提示 L413;v-if L415 守卫;边界说明正确(404/参数错仍可重试,待后端 retryable 字段精确化)。
  - **⑧UX-03 agentRound watch 触发时机 + CSS 不动决策 PASS**:watch agentRound:1738-1744 cur<=prev 早退,collapseAllToolLists(buildActiveToolIds(...));ToolCardList.vue:353-357 flex gap 原理说明明确,本轮不重构 JS 钩子决策合理。
  - **⑨i18n 转义 PASS**:ai.ts/aiChat.ts zh-CN/en 8 key 全纯文本,无 @/|/裸 {} 特殊字符;notRetryable/queueEdit/queueSave/queueCancel/queueSendNow 双语对称。
  - **⚪ CR-45-1(low,`-search` 子串匹配可能误伤)**:is_non_chat_model:218 `id.contains("-search")` 可能误伤含 `-search` 后缀的合法 chat 模型名(如厂商未来发布 `gpt-next-search-assistant` 模型实际支持 chat);当前单测覆盖 `text-davinci-search-001` 旧 GPT 搜索变种合法剔除,但 `contains` 子串匹配设计粒度粗。已知影响面小(主流厂商暂无含 `-search` 的 chat 模型),非阻断;后续可按需收紧为正则 `r#"-(search|search-[a-z]+)$"#` 或加入白名单例外。
  - **⚪ CR-45-2(low,fetch_and_probe 无分页)**:fetch_anthropic_compat:92-119 调 `build_models_url` 通 `/v1/models`,Anthropic 响应含 `has_more` 字段但当前 serde 反序列化 ModelsList:248 未取 has_more(serde 仅取 id);超大列表>500 可能截断。当前阶段3 聚焦拉取主流模型,截断影响可控(头部 500 模型足够覆盖);非阻断,留阶段5 IPC 实测后按需补分页。
  - **待修项回流 todo**: **无**(2 low 非阻断:子串匹配可收紧/分页可后续补)。

---

### CR-260616-46 波2 F-01阶段4路由器 + UX-05/06/07队列三件套 — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+vue-tsc,87s)**: ✅ **PASS** — 🔴0 🟡0 ⚪1
  - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai router::` **15 passed**(13 原+2 新)/ `npx vue-tsc --noEmit` EXIT 0。
  - **①select 7 步过滤链 PASS**:router.rs:57-66 严格对齐设计 §6.1 L470-478(enabled→模态全子集 L60→needs_tool_use 含 ToolUse L61→intelligence>=min L62→max_cost<= L63→context_window>= L64→max_by_key L65)。单测 full_dimensions_match_picks_best:L310-360 5 候选参差验证全链。
  - **②Reverse(cost_tier) 取反正确性 PASS**:router.rs:11 `use std::cmp::Reverse`+L65 `max_by_key(|m| (m.weight, Reverse(m.cost_tier)))`.CostTier Ord Free<Low<Medium<High,Reverse 让小值变"大"选便宜(设计原文 -(as i32) 等价但更 idiomatic)。单测 same_weight_picks_cheaper_cost_tier:L288-307 验证 Free 胜 High。
  - **③select_model_id helper PASS**:router.rs:79-81 纯函数 `select_model_id(req,pool)->Option<String>` 调 select 映射 model_id。单测 select_model_id_*:L117-128 池空/命中全过。
  - **④关联函数 vs &self 方法偏离 PASS**:router.rs:9 注释注明「对齐任务规格,非设计文档的 &self 方法」。关联函数 `ModelRouter::select(&req,&pool)`(L57)适配阶段5 调用点 `ModelRouter::select(&req,&pool)`,零行为差异(无状态单元结构,无需 self)。
  - **⑤pub use re-export PASS**:router.rs:16 `pub use df_ai_core::model::{Capability,CostTier,IntelligenceTier,Modality,ModelConfig}`。阶段5 调用点经 `df_ai::router::{...}` 直接 import 维度枚举构造 TaskRequirements,零跨 crate 路径冗长。
  - **⑥UX-05 决策 a 落地 PASS**:useAiSend.ts:397-404 stopChat 删 `state.queue=[]` 决策 a(逐条续发)。后端 agentic.rs:209 注释「保证前端收事件时后端已可接下一条,队列续发不被拒绝」+drainQueue(useAiSend.ts:228-232)由 AiCompleted 触发链式续发。
  - **⑦UX-06 editQueued PASS**:useAiSend.ts:370-375 边界(index 越界/空白 no-op)+trim+直接赋值 text。AiChat.vue:1567 editingQueueIdx/1569 editingQueueText ref+L519-531 inline input 三路保存一致(回车/blur/@mousedown.prevent 防 blur)。
  - **⑧UX-07 sendQueuedNow 顺序 PASS**:useAiSend.ts:387-392 splice 先→stopChat→sendMessage 顺序关键(splice 前置避重复发,stopChat UX-05 后已不清队列故 splice 必须前置)。复用 UX-05 stop 链路(agentic.rs:209 AiCompleted 就绪核验)。
  - **⑨i18n 双语 PASS**:queueEdit/Save/Cancel/SendNow 4 key 纯文本无特殊字符,zh-CN/en aiChat.ts:L68-71 对称。
  - **⚪ low 清单**:①router.rs:77 select_model_id 注释「池空/无匹配→None→兜底 default_model」。阶段5 调用点 unwrap_or(default_model) 确认行为不变,当前测试见 CR-48 调用点接入验证。
  - **待修项回流 todo**: **无**(9 重点全 PASS,1 low 注释清晰非阻断)。

---

### CR-260616-47 波3 F-15上下文基础(is_active白名单+push token) + F-01模型拉取IPC+Settings UI — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+test+vue-tsc,62s)**: ✅ **PASS** — 🔴0 🟡0 ⚪2
  - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai-core is_active` **1 passed** / `cargo test -p df-ai context::` **14 passed**(零回归)/ `npx vue-tsc --noEmit` EXIT 0。
  - **①is_active 白名单零行为变化 PASS**:provider.rs:85-86 `matches!(self.status.as_deref(), None | Some("active"))` 与旧 `!matches!(self.status.as_deref(), Some("truncated"))` 等价(当前仅 None/active/truncated 三态)。**核验 truncated 时序**:context.rs:484-496 truncate_after_user_message 在 push 之后标记(:496 `t.message.status = Some("truncated")`),push 时刻 is_active 恒 true,history_tokens 累加不变,零行为变化证成。
  - **②push token 修正 PASS**:context.rs:179-185 `if message.is_active() { self.history_tokens += tokens }`。!active 消息仍 push 到 self.messages 全量保留(:186 `self.messages.push(...)`),仅不计 token。修复 restore_from_messages 后 token 虚高致 build_for_request 误判超预算。
  - **③restore_from_messages 路径 PASS**:context.rs:420-425 遍历调 push,token 同步仅 active。单测 push_token_only_active:860-910 验证直接 push+restore 双路径,active/archived/compressed 三条 restore 后 token 只含 active 一条(:902-910 断言通过)。
  - **④ai_fetch_models FR-S1 闭环 PASS**:commands.rs:872 `df_storage::secret::resolve_provider_secret(&provider)` 内存解析,不进日志/返回值。ModelConfig(df-ai-core/model.rs:87-125)无 api_key 字段,:902 返回值 `Vec<ModelConfig>` 天然不含密钥。
  - **⑤normalize_provider_type 归一 PASS**:commands.rs:841-846 anthropic→anthropic_compat 归一(:873 调用)。对齐 model_fetch.rs:40-42 fetch_and_probe 协议分派,双命名收敛。
  - **⑥fetch_and_probe + update_full 写回 PASS**:commands.rs:875 `fetch_and_probe` 调用 + :883-887 `update_full` 写回 model_configs + updated_at。
  - **⑦ai_probe_model 纯 CPU probe PASS**:commands.rs:897-902 调 `df_ai::model_probe::probe(&model_id)`,无网络。
  - **⑧types.ts 4 维度枚举 snake_case 对齐 PASS**:types.ts:200-206 Modality/Capability/CostTier/IntelligenceTier 字面量(df-ai-core/model.rs:20-72 serde `rename_all = "snake_case"` 严格对齐)。
  - **⑨Settings 拉取 UI PASS**:Settings.vue:95 拉取按钮(:96 disabled 非新建态提示先保存)+:102-103 模型列表展示(4 维度标签+enabled toggle+weight input)+:519-536 fetchModels 函数错误分支 toast(超时/鉴权/404/厂商异常)。
  - **⑩i18n 双语 PASS**:zh-CN/en settings.ts:33-37 testConnection/fetchingModels/fetchHintSaveFirst/labelWeight/modelListTitle 对称,无特殊字符。
  - **⚪ CR-47-1(low,Settings enabled/weight 本地态非持久化)**:Settings.vue:525 `providerForm.models = models.map(...)` 仅本地会话态,未存回 model_configs。文案 "下次「测试连接」会以最新探测结果覆盖"(:516-518 注释)明示,阶段6 后续持久化另见 issues。非阻断(本批目标拉取展示 UI,本地编辑态可接受)。
  - **⚪ CR-47-2(low,ai_probe_model UI 未接)**:无单模型探测按钮,留阶段6 AiChat 模型下拉。非阻断(probe 纯 CPU,按需补 UI)。
  - **待修项回流 todo**: **无**(10 重点全 PASS,2 low UI 增强/持久化非阻断)。

---

### CR-260616-48 波4 F-01阶段5调用点接入(select_model_id helper+6点) + F-260617-01 patch_file三模式编辑 — ✅ 已审(PASS)

- **复审结论(2026-06-17·CR-48 agent 亲跑 cargo+test+vue-tsc,342s)**: ✅ **PASS** — 🔴0 🟡0 ⚪2
  - **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai router` **15 passed**(2 新增:select_model_id_empty_pool_returns_none/select_model_id_hit_returns_model_id)/ `cargo test -p df-nodes --lib` **79 passed; 1 ignored**/ `cargo test -p df-ideas adversarial` **12 passed**(含 a12 新增 llm_mock_fenced_not_at_start)/ `cargo test -p devflow tool_registry` **24 passed**(16 三模式单测)/ `npx vue-tsc --noEmit` EXIT 0。
  - **①select_model_id helper 纯函数正确性 PASS**:router.rs:79-81 纯函数调用 ModelRouter::select + map model_id,单测:117-127 覆盖池空 None/命中 model_id 返回字符串,编译期证类型匹配。
  - **②pub use re-export 可见性 PASS**:router.rs:16 `pub use df_ai_core::model::{Capability,CostTier,IntelligenceTier,Modality,ModelConfig}`;6 调用点全经 `df_ai::router::{...}` 全限定导入(agentic.rs:19/project.rs:11/title.rs:11/knowledge_inject.rs:12/ai_node.rs:17/adversarial.rs:165),编译期证零断裂。
  - **③6 调用点兜底行为不变 PASS**:agentic.rs:175-176 `select_model_id(...).unwrap_or_else(|| provider_config.default_model.clone())` 池空/无匹配→None→兜底 default_model;title.rs:86-87/project.rs:539-540/knowledge_inject.rs:350-351/ai_node.rs:214/adversarial.rs:165 同模式;池空时 resolved_model==default_model 零差异。
  - **④agentic resolved_model 审计字段统一 PASS**:agentic.rs:288/410/414/419/465/469 原用 default_model 处统一改 resolved_model,语义更正确(记实际服务模型);池空时 resolved_model==default_model 无行为变更。
  - **⑤knowledge_inject 嵌入降级 needs_tool_use=false PASS**:knowledge_inject.rs:58-65 TaskRequirements(needs_tool_use=false,min_intelligence=Lite),宽松过滤允许嵌入模型入选;TaskRequirements 无 capabilities 字段设计偏离,embedding provider 池通常即嵌入模型实践中命中,可接受。
  - **⑥df-ideas 新增 df-ai 重依赖 PASS**:df-ideas/Cargo.toml:12 df-ai 依赖引入;注释:9-11 说明 router 模块仅依赖 df-ai-core::model,编译期 tree-shaking 零 HTTP 代码链入(仅引用符号),实测 cargo check EXIT 0 证无链接错误;后续若严格轻量可将 router 下沉 df-ai-core 非阻断。
  - **⑦df-nodes ResolvedProvider.model_pool 字段 PASS**:ai_node.rs:54 model_pool 字段+ :205 record 路径填 record.model_configs.clone + :143 老明文路径填空池;config.model 显式优先(:197-203 有则用 model_id 否则路由),单测覆盖。
  - **⑧patch_file 三模式互斥运行时校验 PASS**:tool_registry.rs:932-940 has_old_text/has_replace_lines/has_anchor 计数,0=缺定位 bail/:937 >1=冲突 bail/:939,两边界完备;schema 层 object_schema 无法静态互斥,运行时兜底合理。
  - **⑨patch_file expected_hash mtime+size 非 sha256 PASS**:tool_registry.rs:971-981 `format!("{}_{}", modified.unwrap_or(0), file_meta.len())` 指纹(mtime+size),三模式通用,语义与 old_text 模式一致(read_file 返回 size 非 hash,注释:141 与 read_file 行计数一致),设计正确。
  - **⑩apply_line_range trailing newline 语义 PASS**:tool_registry.rs:133-152 单测:1722-1729 尾换行保留(`\n` 结尾文件替换后仍以 `\n` 结尾),splice 循环 lines() 后单独处理 trailing_newline,边界正确。
  - **⑪resolve_anchor 子串行内命中 PASS**:tool_registry.rs:182-206 子串大小写敏感匹配+start 后第一个含 end 行;单测:1783-1789 行内命中 / 1794-1798 start==end 同行 / 1803-1808 first-end-after-start,覆盖完备。
  - **⑫16 单测覆盖 PASS**:tool_registry.rs:1701-1853 单测 apply_line_range 8 + resolve_anchor_to_lines 8 + e2e 1,亲跑 cargo test 24 passed 全过。
  - **⚪ CR-48-1(low,agentic 主对话含图 Vision 检测未实现)**:agentic.rs:169 modalities 仅 `[Modality::Text]`,ChatMessage 纯文本无图像 Part,多模态类型接入后需检测 Part/Image 追加 Vision,当前非阻断(设计文档已标注:166-167)。
  - **⚪ CR-48-2(low,build_provider/诊断日志仍用 default_model)**:provider trait 不暴露改 default_model,CompletionRequest.model 走路由 provider impl 适配(OpenAICompatProvider::convert_request:295-296 req.model.is_empty() 回退 default_model),诊断日志仍用 default_model 字段名,非阻断。
  - **待修项回流 todo**: **无**(2 low 非阻断:Vision 检测待多模态接入/诊断日志命名,路由核心功能全 PASS)。

### CR-260616-49 波5 F-01阶段6 AiChat模型下拉+override穿透(4c84fbc) / F-15阶段1剩余compress基础(63bff8b) — ✅ 已审(PASS)

- **复审结论(2026-06-17·CR-49 agent 亲跑 cargo+test+vue-tsc+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪1
  - **验证**: `cargo check --workspace` EXIT 0(6 warning 全 pre-existing dead_code,PendingApproval/releases/node_executions)/ `npx vue-tsc --noEmit` EXIT 0/ `cargo test -p df-ai` **107 passed**(含 context 9 新方法 + 8 新单测)/ `cargo test -p devflow --lib compress` **3 passed**。
  - **①F-01 override 兜底零行为变化 PASS**:agentic.rs:181-188 `match model_override.as_deref()` — Some(id) 且 !id.is_empty() 且 `provider_config.model_configs.iter().any(|m| m.model_id == id)` → 用 id;`_ => resolved_model`(落回路由)。override=None(自动模式默认)完全等价改动前。非空但不在池→落回路由兜底正确,防止 override 导致无模型。
  - **②F-01 清零路径完整 PASS**:create(commands.rs:1147 `session.model_override = None`)+ switch(commands.rs:1225)两入口清零,前端 watch activeConversationId 同步清(AiChat.vue:1071)。防跨对话污染,零残留。
  - **③F-01 续跑透传 PASS**:try_continue_agent_loop(agentic.rs:699-702)读 session.model_override.clone() 透传,审批续跑/达 max 续跑保持同一主对话的 override 一致,语义正确。
  - **④F-01 空池降级 PASS**:AiChat.vue:237 `v-if="enabledModels.length"` 隐藏下拉+ :231 toggle :disabled + :1323 setSpecifyMode 早返。空 provider 不 crash。
  - **⑤F-01 i18n 特殊字符 PASS**:aiChat.ts 新 6 key(autoMode/specifyMode/autoModeHint/specifyModeHint/selectModel/noModels)纯文本,无 @/|/裸{} 特殊字符。
  - **⑥F-15 compress 幂等 PASS**:context.rs:629-640 compress_old_messages 只处理 is_active,saturating_sub 扣 token,已 compressed 跳过;返回 newly_compressed 仅本次新标记。DB 原始消息保留,is_active 白名单隔离。单测 push_token_only_active:954 覆盖直接 push+restore 双路径。
  - **⑦F-15 compress_via_llm 兜底 PASS**:compress.rs:66-67 select_model_id 池空/无匹配→unwrap_or_else 兜底 default_model;:89 map_err + :92 空摘要 Err。阶段2 IPC 接调用点后降级。
  - **⑧F-15 纯函数优先 PASS**:compress_prompt(prompt.rs:148 无 IO)/ build_eviction_units/has_compressible_messages/compress_old_messages 无 IO;compress_via_llm 封装 IO(双层 Semaphore 限流复用 title 模式)。单测 3 passed。
  - **⑨F-15 零行为变化 PASS**:push/restore_from_messages/build_for_request/sanitize_messages 未改,仅新增方法 + is_compressing 字段(new/clear 初值 false)。
  - **⚪ CR-49-1(low,前端 ref 单例设计说明)**:modelOverride 模块级 ref(useAiSend.ts:53)非 per-conversation,靠 watch 清理对齐后端 switch 清 session.model_override。当前单实例 AiChat 可接受,多窗口架构未来若需独立模型选择需改 per-conversation ref。非阻断(设计文档已标注)。
  - **待修项回流 todo**: **无**(9 重点全 PASS,1 low 设计说明非阻断)。

### CR-260616-50 波6 F-15阶段2 手动上下文管理(2 IPC+3事件+前端按钮+status渲染)(4194842) — ✅ 已审(🔴 RED 已修 PASS)

- **复审结论(2026-06-17·波7 ww68uamzu agent CR-50)**: ⚠ **ISSUES → 🔴 RED 已修(主代)** — 后端 IPC/事件/保护区/失败路径全 PASS,前端事件分发 RED(审查抓到真 bug,主代即时修)
  - **验证**: `cargo check --workspace` EXIT 0(6 warning 全 pre-existing dead_code)/ `npx vue-tsc --noEmit` EXIT 0(零输出)/ `cargo test -p devflow --lib` **79 passed**(无回归)。
  - **① clear_context 不删 DB PASS**:commands.rs:430-433 经 messages_mut() 标 status="archived_segment"(is_active 自动 false,sanitize 隔离),落库走 save_conversation 全量 upsert(:439),非 clear_messages 删库。
  - **② compress LLM 失败不阻塞 PASS**:commands.rs:543-554 先取 active 克隆(读不改 status)→ compress_via_llm 失败则 set_compressing(false)+ emit AiError(message 不含 api_key)+ 返 Err;消息状态不变(未调 compress_old_messages)。成功路径才 compress_old_messages 标 compressed + insert_at(0, summary system)。
  - **③ 保护区最近 N 条 active 不动 PASS**:PROTECT_COUNT=6(commands.rs:412/465 两处一致),protect_start = len.saturating_sub(6),clear/compress 只对 [0, protect_start) 操作。
  - **④ 空会话/无 active 降级 PASS**:protect_start==0 → emit 对应事件 + Ok(不报错,commands.rs:416-420/485-492);active_msgs 空 → AiCompressed(summary 空,commands.rs:511-515) + Ok。
  - **⑤ 3 事件变体 serde — 🔴 RED(审查抓到真 bug)**:mod.rs AiChatEvent 仅 `#[serde(tag="type")]` 无 `rename_all="snake_case"`,serde 默认序列化 PascalCase(useAiEvents.ts 10 case 铁证)。useAiContext.ts:77/80/84/87 switch case 用 snake_case(`'ai_compressing'` 等)→ 永不命中 → isCompressing 永不复位 → 压缩按钮用过一次永久禁用 + 清空后视图不刷新。**主代修复(2026-06-17)**:case 4 处改 PascalCase(AiCompressing/AiCompressed/AiContextCleared/AiError)+ compressContext 成功兜底复位(后端同步 resolve 非异步)+ doc 注释一致性。vue-tsc EXIT 0 + grep 无残留 snake_case + useAiEvents 对照铁证。未改后端(rename_all 会破坏 useAiEvents 既有 10 case)。
  - **⑥ useAiContext composable 闭环 PASS(RED 修后)**:isCompressing ref 防重入 + 第二 listener 消费 4 事件 PascalCase 分发 + compressContext 成功兜底复位(后端 IPC 同步等 LLM 完成才 resolve 非异步 spawn,成功即复位防事件丢失)。
  - **⑦ status 分组渲染 PASS**:AiChat.vue:2240 messageSegments computed 按 status 分组(archived_segment/compressed 连续合并),:2290 renderItems 扁平化供 v-for;折叠段点击 toggleSegment 展开,正常消息零改动。
  - **⑧ i18n 13 key ×2 无特殊字符 PASS**:aiChat.ts 新 key(clearContext/compressContext/clearContextConfirm/contextArchived/compressed/compressing/clearSuccess/compressSuccess/compressError/clearError/expand/collapse/compressedSummaryLabel)仅合法 {n}/{msg} 插值,无 @/|/裸 {}。
  - **⑨ useAiConversations status 透传 PASS**:useAiConversations.ts:86-90 switchConversation mapper 透传 status 字段(archived_segment/compressed 落 AiMessage 供渲染),types.ts 未含此字段经 as any 读写闭环。
  - **⑩ 已知取舍(history_tokens 口径) PASS**:commands.rs:423-429 注释明确标注口径——clear_context 改 status 后 history_tokens 不立即扣减(私有字段无 setter),后果短暂偏高触发更早裁剪,属保守方向不劣化安全性。
  - **待修项回流 todo**: **无**(🔴 RED 已即时修,主代核验 case PascalCase + vue-tsc EXIT 0 + useAiEvents 10 case 铁证对照)。

- **范围**: 2 agent 并行 workflow w6o61cbqn,文件域隔离(后端独占 commands.rs/mod.rs/lib.rs;前端独占 api/ai.ts/useAiContext.ts(新)/useAiConversations.ts/stores/ai.ts/AiChat.vue/i18n×2)。零重叠。
- **commit**: 4194842(单 commit,功能完整:后端 IPC 无前端无意义,配套提交)。
- **主代核查(2026-06-17)**: cargo check --workspace EXIT 0(6 warning 全 pre-existing dead_code,PendingApproval/releases/node_executions)/ npx vue-tsc --noEmit EXIT 0(零输出)/ cargo test -p devflow --lib **79 passed**(无回归)/ grep 核验形态(lib.rs:122-123 注册 2 IPC + mod.rs:145/147/149 三事件变体 + commands.rs:397/457 两 IPC fn + 6 处 emit + 前端 useAiContext composable + AiChat.vue messageSegments 分组渲染)。
- **审查 agent 待复审重点**:
  - ①**clear_context 不删 DB**:commands.rs:397 经 messages_mut() 标 status="archived_segment"(is_active 自动 false,sanitize step0 隔离),落库走 save_conversation(全量 upsert 非 clear_messages 删库)。
  - ②**compress LLM 失败不阻塞不 panic**:commands.rs:457 先取 active 克隆(读不改 status)→ compress_via_llm;LLM 失败 set_compressing(false)+ emit AiError(message 不含 api_key)+ 返 Err;消息状态不变(未调 compress_old_messages)。成功路径才 compress_old_messages 标 compressed + insert_at(0, summary system)。
  - ③**保护区最近 N 条 active 不动**:PROTECT_COUNT=6(commands.rs:413/465),protect_start = len.saturating_sub(6),clear/compress 只对 [0, protect_start) 操作。
  - ④**空会话/无 active 降级**:protect_start==0 → emit 对应事件 + Ok(())不报错;active_msgs 空 → AiCompressed(summary 空) + Ok。
  - ⑤**3 事件变体 serde**:mod.rs:145/147/149 AiContextCleared/AiCompressing/AiCompressed{summary},继承枚举级 #[serde(tag="type")] rename_all snake_case → 前端 type:"ai_context_cleared"/"ai_compressing"/"ai_compressed"。
  - ⑥**前端 useAiContext composable 闭环**:isCompressing ref 防重入 + 第二 listener 消费 ai_compressing/compressed/context_cleared/error 生命周期;compressContext IPC 受理仅表请求受理,真正完成由 ai_compressed 事件复位 isCompressing(异步闭环,非 IPC 同步等)。
  - ⑦**status 分组渲染**:AiChat.vue messageSegments computed 按 status 分组(archived_segment/compressed 连续合并折叠段),renderItems 扁平化供 v-for;折叠段点击 toggleSegment 展开,正常消息走 v-else 原渲染零改动。
  - ⑧**i18n 13 key ×2 禁字符**:aiChat.ts 新 key(clearContext/compressContext/clearContextConfirm/contextArchived/compressed/compressing/clearSuccess/compressSuccess/compressError/clearError/expand/collapse/compressedSummaryLabel)message 值无字面 `@`/`/`/裸`{}`(仅合法 `{n}`/`{msg}` 插值)。注意 archived 与侧栏冲突 → 重命名 contextArchived(vue-tsc TS1117 已修)。
  - ⑨**useAiConversations status 透传**:switchConversation mapper 透传 status 字段(archived_segment/compressed 落 AiMessage 供渲染折叠),types.ts 未含此字段经 cast 读写闭环。
  - **⚠️ 已知设计取舍(backend 自报,记审查项)**:clear_context 经 messages_mut() 改 status 后,ContextManager.history_tokens(私有字段无 setter)不立即扣减 archived 段 token(与 compress_old_messages 路径不同,compress 内部扣 token)。后果:history_tokens 短暂偏高,build_for_request 超预算裁剪可能比实际更早触发,属保守方向(不超发)不劣化安全性。代码注释已标注口径。

### CR-260616-51 波8 F-15阶段3 agentic loop自动压缩(0202b51) + AE-2025-04会话级授权(6ec191e) — ✅ 已审(PASS-WATCH·🟡1回波11修)

- **复审结论(2026-06-17·波10 wsm0tk95c agent CR-51)**: ⚠ **PASS-WATCH** — 🔴0 🟡1 ⚪2。阶段3(loop语义/触发/防重入成对/延迟mutate/降级/emit路由)+ AE-04(TrustKey/session_trust/audit查trust/ai_approve写trust/toast桥接/FR-S1)逻辑全 PASS,devflow 79 + df-ai 107 passed,vue-tsc EXIT 0。**🟡 yellow 回流波11修**:audit.rs:593-596 trust-hit 放行分支 `.await tools_arc.execute` 持 `&mut session`(注释 L593 声称锁外但代码持锁,与 Low risk spawn 模式不一致),run_command 慢命令阻塞该会话所有触 state.ai_session 的 IPC(30+)。修法:对齐 Low risk spawn+clone(trust_hits Vec 收集→循环外 join_all spawn execute 锁外→串行回填持锁)+ 修正注释。⚪2 非阻断(摘要 insert_at 二次压缩边缘/canonicalize 失败回退边缘)。

- **范围**: 2 agent 并行 workflow wigdmq5s4,文件域零重叠(阶段3 独占 agentic.rs;AE-04 独占 mod.rs/audit.rs/commands.rs/tool_registry.rs/AiChat.vue/useAiEvents.ts/types.ts/i18n×2)。
- **commit**: 0202b51(阶段3)+ 6ec191e(AE-04)。分 2 commit(语义清,功能不同)。
- **主代核查(2026-06-17)**: cargo check --workspace EXIT 0(6 warning 全 pre-existing dead_code)/ npx vue-tsc --noEmit EXIT 0(零输出)/ cargo test -p devflow --lib **79 passed**(无回归)/ grep 核验形态(阶段3 loop 顶部压缩分支 + set_compressing 成对 + AE-04 TrustKey/session_trust/audit 查 trust/ai_approve 写 trust)。
- **审查 agent 待复审重点**:
  - ①**阶段3 loop 语义不变**:agentic.rs loop 顶部加压缩检测分支(L259-374),主 loop 迭代/stream/重试/tool_calls/converge 零改动。压缩是 build_for_request 前预处理。
  - ②**阶段3 触发条件**:`budget*6/10 < history_tokens`(0.6 阈值整数比避浮点)+ protect_start>0 + has_compressible_messages(protect_start)。
  - ③**阶段3 防重入 + 成对**:prev_compressing 守卫(L280)+ set_compressing(true)(L303)↔ 三出口 false(L340 成功/L355 noop/L361 Err)。成对覆盖所有分支。
  - ④**阶段3 延迟 mutate(成功才改)**:先取 active 克隆 → LLM 出摘要 → 成功才 compress_old_messages(标 compressed+扣 token)+ insert_at(摘要)。失败零副作用(消息状态/token 完全不变)。理由:history_tokens 私有无 setter,失败回滚 token 不精确;延迟 mutate 语义最干净。
  - ⑤**阶段3 降级**:LLM 失败/空摘要 → set_compressing(false)+ emit AiError(message 不含 api_key)+ 继续走原 build_for_request 裁剪(不阻塞 loop)。
  - ⑥**阶段3 emit conversation_id 路由**:AiCompressing(L298)/AiCompressed(L348)/AiError(L367)三处均 Some(conv_id)。
  - ⑦**AE-04 TrustKey 设计**:mod.rs:201 enum Write{dir}/Execute{dir}(目录级,derive Hash/Eq)+ trust_key_for(:215 write_file→Write 父目录/run_command→Execute working_dir)+ 首批仅 write_file/run_command 命中(其余 None 走原审批)。
  - ⑧**AE-04 session_trust 内存态**:HashSet<TrustKey>(mod.rs:346),new 初值空(:363),换会话(create/switch)clear(commands.rs:1161/1241)重审,不落库。
  - ⑨**AE-04 audit 查 trust**:audit.rs:568 process_tool_calls 审批前(Med/High 进 pending 前)查 session_trust.contains 命中 → 直接执行(非复用缓存,与 F-05 去重不同)+ emit AiToolAutoApproved(:587)+ Completed + audit decided_by=auto_trust(:613)留痕 + continue 跳过 pending + 二次确认。
  - ⑩**AE-04 ai_approve 写 trust**:commands.rs:294-295 用户批准时 trust_key_for 命中 → session_trust.insert(下次同类自动放行)。
  - ⑪**AE-04 前端 toast 桥接**:AiToolAutoApproved 后端 emit → useAiEvents case(types.ts union)→ emit ai-tool-auto-approved-toast 事件总线 → AiChat.vue listen 弹 info toast(🔓 自动放行),非审批气泡避免干扰。
  - ⑫**AE-04 FR-S1**:TrustKey 只含 tool+dir,不含 api_key/文件内容/命令串(mod.rs:189 注释)。
  - **待修项回流 todo**: **无**(核心 grep+test+构建全 PASS;阶段3 延迟 mutate 口径 + AE-04 目录级粒度为设计取舍非 bug)。

### CR-260616-52 波9 B-260617-01 run_workflow审批执行(方案a2后端分支)(e23b316) — ✅ 已审(PASS)

- **复审结论(2026-06-17·波10 wsm0tk95c agent CR-52)**: ✅ **PASS** — 🔴0 🟡0 ⚪3。a2 实施正确:run_workflow_inner 抽取语义零变化(命令层瘦转发)/ ai_approve 分支(commands.rs:317)仅隔离 run_workflow 其余不变 / execute_run_workflow_for_tool 回填 Ok json(execution_id)让 LLM 收成功 / AE-04 trust_key_for(:294)在分支(:317)前且 run_workflow 返 None 不写 trust / audit trust 查询 + create/switch clear 未动 / High 去重缓存断重试双层 / handler 防御文案正常不可达 / FR-S1 tool_result 仅 task_id+target_status+execution_id。devflow 79 passed。⚪3 非阻断:①workflow.rs:124 triggered_by 注释声称 ai 实际 manual(已知待办)②mod.rs:465 空 config 既有行为(对齐前端)③tool_registry.rs:547 _db 死代码冗余。

- **范围**: 单 agent workflow wf8r5vlno,走查+实施。改 workflow.rs(run_workflow_inner 抽取)/ mod.rs(execute_run_workflow_for_tool)/ commands.rs(ai_approve run_workflow 分支)/ tool_registry.rs(handler 注释+防御文案)。
- **commit**: e23b316。
- **主代核查(2026-06-17)**: cargo check --workspace EXIT 0(6 warning 全 pre-existing)/ cargo test -p devflow --lib **79 passed**(无回归)/ grep 核验形态(workflow.rs:87 run_workflow_inner + mod.rs:446 execute_run_workflow_for_tool + commands.rs:317 ai_approve 分支 + trust_key_for:294 在分支前 AE-04 不破坏)。
- **方案选型**:agent 选 a2(后端分支)而非 a1(前端拦截)。理由:ai_approve 已持 app+state,后端直接调 run_workflow_inner 最干净,无需新 IPC/前端分支/工作流知识泄漏。符合用户推荐 a 大方向(绕开 ai_tools.execute 必 Err)。
- **审查 agent 待复审重点**:
  - ①**run_workflow_inner 抽取语义零变化**:workflow.rs:87 抽取 run_workflow 命令体为共用核心,命令层瘦转发(:79)。核心逻辑(模板选 DAG/落记录/spawn 转发+执行+联动)全部移入,语义不变。
  - ②**execute_run_workflow_for_tool**:mod.rs:446 从 args 取 task_id/target_status + 构造空 DagDef+config + name 拼"AI 推进任务" + 调 run_workflow_inner + 回填 {task_id,target_status,execution_id,status:"running"} 让 LLM 收成功。
  - ③**ai_approve run_workflow 分支**:commands.rs:317 `if approval.tool_name == "run_workflow"` → execute_run_workflow_for_tool,否则原 ai_tools.execute。后续 audit_finalize/emit_data_changed/replace_tool_result/emit/save/try_continue 共用零变化。
  - ④**AE-04 trust 不破坏**:trust_key_for 写 trust(:294)**在 run_workflow 分支(:317)前**;audit trust_hit 查询(audit.rs:568)未动;create/switch clear(:1177/1257)未动。
  - ⑤**重试循环根治**:a2 真实执行→成功 tool_result(非 Err)+ 既有 High 去重缓存(find_cached_high_risk_result audit.rs:406 失败 tool_result 复用)双层保障。不新增 no_retry 标志(无字段+无消费方+crate 改动大,去重缓存已等效)。
  - ⑥**handler 防御文案**:tool_registry.rs:556 run_workflow handler Err 改防御兜底(正常流程不可达,a2 分支接管)+"勿盲目重试"明示(对齐 run_command 超时标注模式)。
  - ⑦**FR-S1**:run_workflow 无 api_key,tool_result 仅 execution_id+task_id+target_status。
  - **待修项回流 todo**: **无**(a2 实施正确,grep+test+构建全 PASS;no_retry 不增为设计取舍,去重缓存等效)。

### CR-260616-53 波11 CR-51 yellow修(audit trust-hit持锁重构) + UX-01工具失败视觉 + UX-03自动收起增强(332a6bb+31ea151) — ✅ 已审(PASS·⚪2)

- **复审结论(2026-06-17·审查 agent 亲跑 cargo+vue-tsc+test+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪2
  - **验证**: `cargo check --workspace` EXIT 0(7 warning 全 pre-existing dead_code)/ `npx vue-tsc --noEmit` EXIT 0/ `cargo test -p devflow --lib` **88 passed**(无回归)。
  - **①CR-51 yellow 并行化(非"移锁外") PASS**:修复前后 session 锁均由调用方(agentic.rs:839 `session_arc.lock().await`)持有至 process_tool_calls 返回,锁持有时长不变。**真正改进是并行化**:修复前 trust-hit 分支内 inline `.await execute` 串行执行每个工具(阻塞期间锁被持有,run_command 慢命令阻塞同会话所有触 state.ai_session 的 IPC);修复后收集 trust_hits Vec→join_all 并行执行(锁仍被持有,但多工具并行减少总阻塞时间)。audit.rs:664-713 实现(trust_hits 收集 L568+join_all 闭包 L671-701+串行回填 L704-712)正确,注释"锁外"应理解为"闭包内不访问 session"而非"锁已释放"。
  - **②语义不变 9 条全 PASS**:真执行(audit.rs:676 tools.execute)+emit toast(L599 AiToolAutoApproved)+emit completed(L680)+emit_data_changed(L687)+audit auto_trust(L711 decided_by=Some("auto_trust"))+Med-High 非 trust 路径零改动(L630-660 pending 分支)+Low risk 零改动(L718-764 独立并行分支)+pending_count 不变(L630 仅 trust-hit 跳过,pending 仍累加)+错误信息格式不变(L680/690 AiToolCallCompleted 结构一致)。
  - **③UX-01 isToolFailure 判定可靠 PASS**:run_command 路径(ToolCard.vue:213-217 parseResult 命中合法 JSON 看 exit_code≠0,可靠)+通用工具路径(ToolCard.vue:220-224 parseResult 返回 null 后正则 `/执行失败|failed|error[:\s]/i` 启发式匹配,对齐后端 AR-6 err_msg 格式 `format!("工具 {} 执行失败: {}", draft.name, e)` audit.rs:744)。正则可能误判含 "error"/"failed" 字样的成功结果(如 "no errors found"、"error handling passed"),但这是启发式检测已知取舍,非阻断(误报概率低,最坏多一次视觉提示)。
  - **④UX-01 视觉完整 PASS**:--failed 红框(ToolCard.vue:727-730 border-color+box-shadow)+banner(L119)+红底结果框(L745-751 background+border)+状态点(L20 failed 分类)+副标题(L17 failed 标签)。CSS 三类(.ai-tool-card--failed/.ai-tool-status-dot--failed/.ai-tool-result--failed)完整。
  - **⑤UX-03 streaming watch 守卫 PASS**:AiChat.vue:1807 `generatingConvId===activeConversationId` 防后台生成误触+防恢复会话 onMounted 时 streaming 初值 true 的误触(仅当前视图正在生成时触发收起)。守卫正确。
  - **⑥UX-03 wasNearBottom 边沿检测 PASS**:AiChat.vue:1749 初值 true+1760 `if (near && !wasNearBottom)` 转换瞬间触发一次+1763 `wasNearBottom = near` 更新。边沿检测防每 scroll tick 收起,正确。
  - **⑦UX-03 userExpandedCards 记忆态 PASS**:ToolCardList.vue:71 定义+197 展开时 add+193 收起时 delete+180 isCardExpanded 优先读+221 collapseInactive 不清除。记忆态防"刚在看被收起",正确。
  - **⑧join_all 保序 PASS**:trust_hits 收集按 tc_list 原始 index 顺序(audit.rs:533 sort_unstable_by_key+540 retain 后顺序不变)→join_all 保持输入顺序→串行回填按结果顺序。LLM 按 tool_call_id 匹配 tool_result,不依赖到达顺序,保序非必需但一致性更优。
  - **⚪ CR-53-1(low,注释"锁外"有误导性)**:audit.rs:664 注释"execute + 即时 emit 在闭包内（不持 session 锁）"易误解为"锁已释放",实则是"闭包内不访问 session"。session 锁在整个 process_tool_calls 期间由调用方持有(agentic.rs:839 `session_arc.lock().await` 至 L840 `process_tool_calls(...).await` 返回),修复前后锁持有时长不变。改进是**并行化**非"移锁外"。
  - **⚪ CR-53-2(low,UX-01 通用工具文本匹配启发式误报风险)**:ToolCard.vue:223 正则 `/执行失败|failed|error[:\s]/i` 可能误判含这些字样的成功结果(如 "error handling passed"、"no errors found")。但这是启发式检测已知取舍,后端 err_msg 格式固定(AR-6 `format!("工具 {} 执行失败: {}", draft.name, e)`),误报概率低,最坏多一次视觉提示不阻断执行,非阻断性问题。
  - **待修项回流 todo**: **无**(2 low 全非阻断:注释误导性可后续澄清/启发式误报风险可接受)。

### CR-260616-54 波12 F-04多Provider负载均衡池(数据层+选择器+并发原语) + CR-52白项清理(79b6a43) — ✅ 已审(PASS)

- **复审结论(2026-06-17·CR-54 agent 亲跑 cargo+test+vue-tsc+独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `cargo check --workspace` EXIT 0(7 warning 全 pre-existing dead_code)/ `cargo test -p devflow --lib` **88 passed**(79 基线+9 provider_pool 新增全过)/ `cargo test -p df-storage` **33 passed**/ `npx vue-tsc --noEmit` EXIT 0。
  - **①provider_pool select 3键排序完备 PASS**:provider_pool.rs:57-91 过滤 `enabled && weight>0`(:64) + 稳定 3 键(:71-88 模型亲和 true 排前 / weight 降序 / is_default 兜底)。9 单测全覆盖(empty_pool_returns_singleton/single/disabled/zero_weight/model_affinity/model_id_none/weight_tie/is_default/full_scenario),亲跑全过。边界完备:model_id None 全亲和(:73-76 map_or true)/ 0 enabled 空 Vec 调用方兜底(:346-352)/ 1 enabled 单元素首位=唯一(:136-138)。
  - **②单 provider 零变化 PASS**:agentic.rs:342-345 ProviderPool::select 取首位,单 provider 池返回 `[p1]`,首位 `p1` 等价原 `provider_config`。老库迁移 V19 migrations.rs:343-356 `DEFAULT 1`/`DEFAULT 50`,crud.rs:1072-1073 unwrap_or(1)/unwrap_or(50),老行启用后等价 F-01 前。空池兜底(:346-352)用 `provider_config.clone()` 作唯一候选,同启动行为。
  - **③per_provider permit Option 安全 PASS**:state.rs:112 HashMap 定义 + :158-172 acquire_for_provider 返 `Option<OwnedSemaphorePermit>`,map.get?+acquireOwned(:164-171)。未配 set_provider_caps 时 HashMap 空,返 None,drop(None) 无副作用(:671 `_provider_permit` 绑定作用域自动释放)。本轮 set_provider_caps 无消费方(留 F-04c 前端),单 provider 零变化达成。
  - **④agentic 接入完整 PASS**:agentic.rs:342 `ProviderPool::select` 选主候选 + :346-352 split_first 取 primary + candidates + :663-710 `for candidate in &candidate_chain` 循环 + :667 `acquire_for_provider` permit + stream_one_provider(:669-670) 单 provider 流式 + :698 F-04b 日志"切换下一 provider"(基建就位,本轮候选链仅 primary 不触发切换)。单 provider 路径不破坏。
  - **⑤triggered_by 透传链完整 PASS**:workflow.rs:93 `run_workflow_inner` 加 `triggered_by: &str` 参数 + :133 落 `triggered_by: Some(triggered_by.to_string())` 入库 + :80 命令层传 "manual"(前端人工) + mod.rs:475 ai 路径传 "ai"(execute_run_workflow_for_tool)。grep 核验仅 2 调用点,无遗漏。tool_registry.rs:547-560 删 `let _db = db.clone();` 死代码,handler 仅返 Err 引导走审批路径,正确。
  - **⑥V19 迁移幂等 PASS**:migrations.rs:342-360 `PRAGMA column_exists` 探测 enabled/weight + :345 `DEFAULT 1`/ :352 `DEFAULT 50` + V9 建表 SQL(:548-549 同步加列)。老库 ALTER 后取 DEFAULT,新库直接有列。crud.rs:1072-1073 ai_provider_from_row `unwrap_or(1)`/`unwrap_or(50)` 兼容缺失列(幂等测试场景)。insert/update SQL(:1159-1168/:1177-1178) 含 enabled/weight 列。
  - **⑦fallback 拆波诚实 PASS**:agentic.rs:330-334 注释明确"F-04b 切换下一 provider"(本 candidate 重试耗尽切下一),:663-710 `for candidate in &candidate_chain` 循环已就位(:698 continue),仅候选链仅 primary(无 fallback 配置)不触发真实切换。基础设施可用,留 F-04b 独立批实际启用多候选切换。
  - **⑧4 处构造点补字段 PASS**:commands.rs:986-1001(ai_save_provider 新建默认 enabled=true/weight=50)/ crud.rs:1762-1777(单测)/ secret.rs:209-215(单测)/ ai_node.rs:714-729(单测),4 处全补 `enabled: true, weight: 50`。
  - **待修项回流 todo**: **无**(7 维度全 PASS,零问题)。

### CR-260616-55 波13 B-21工具卡重复治本(audit tc_list按id去重保证emit Started唯一)(247dc5d) — ✅ PASS

- **范围**: workflow wice28c6y(前端守卫 agent + 后端走查 agent)。①**前端治标守卫**(HEAD 已有,非本轮新加):useAiEvents.ts:205 AiToolCallStarted push 前 `if (!findToolCall(event.id))` 幂等守卫(对齐 startToolSlowTimer:58)。②**后端走查结论**(不改代码):候选A确认——LLM 流式输出两个 tool_use block 不同 content_block index 但相同 id → stream_recv.rs:225 按 index(u32 键非 id)分桶 → 两条同 id 不同 index draft 并存 → audit.rs:538-549 每 draft emit Started → 同 id 两次 → 前端 push 两卡 → Completed 按 id 只 update 首张 → 次张残留 running 0行。候选B排除(provider 每轮新 id)。③**治本兜底(commit 247dc5d)**:audit.rs:533 sort 后加 `seen_ids HashSet + tc_list.retain(draft.id)` 按 id 去重,同 id 保留最小 index 首个,emit Started 唯一。前端守卫双保险。详 docs/02-架构设计/B-260616-21排查方案。
- **维度**: ①去重正确性(同 id 保留最小 index 首个,retain 后 drafts 唯一 id emit) ②不破坏正常多工具(不同 id 不受影响) ③HashSet import(audit.rs:3 加) ④与 F-05(High risk 去重缓存)无冲突(不同维度)。
- **commit**: 247dc5d。
- **主代核查**: cargo check EXIT 0 + 88 passed + grep retain 形态。
- **审查结论**: ✅ PASS。核验通过：
  - ①去重正确性：audit.rs:533 `tc_list.sort_unstable_by_key(|(i, _)| *i)` 先按 index 排序 → :540 `seen_ids.insert(draft.id.clone())` retain 保留首个同 id(即最小 index) → :548 emit AiToolCallStarted 仅一次。逻辑闭环。
  - ②不破坏多工具：retain 仅过滤同 id，不同 id 工具正常通过，stream_recv.rs:225 证实分桶键为 index 非 id。
  - ③HashSet import：audit.rs:3 `use std::collections::{HashMap, HashSet}` 正确。
  - ④与 F-05 无冲突：F-05(audit.rs:612 find_cached_high_risk_result) 按 tool_name+args 去重(不同 id 同命令)，本条按 id 去重(同 id)，维度正交。
  - ⑤前端守卫：useAiEvents.ts:205 `if (!findToolCall(event.id))` 确认存在，双保险兜底。
  - ⑥cargo check：EXIT 0，零编译错误。
- **待修项回流 todo**: **无**(治标守卫 + 治本去重 双保险就位)。

### CR-260616-56 波14 F-04c后端(provider池enabled/weight更新IPC+set_caps启动接入)(b3684f4) — ✅ PASS

- **范围**: workflow wodppv1hd。①ai_save_provider(commands.rs:929-948)编辑路径保 enabled/weight(修原硬编码清回默认 bug)。②ai_update_provider_pool IPC(commands.rs:1022-1056)轻量专用更新 enabled/weight 经 update_full,weight clamp[0,100],落库后 reload_provider_caps 重建 caps。lib.rs:128 注册。③set_provider_caps 启动接入(state.rs:297 AppState::init 调 reload_provider_caps L305-323 从 DB enabled providers 建 per_provider HashMap,cap=global_cap=3)。单点真理 helper(state 不依赖 commands 避免环形)。
- **维度**: ①ai_update_provider_pool 不触 ai_save_provider 密钥迁移分支(避免空 api_key 重发整表) ②reload_provider_caps 单点真理 ③cap=global_cap=3 单 provider 零变化 ④ai_save_provider 编辑保字段(新建默认 true/50)。
- **commit**: b3684f4。
- **主代核查**: cargo check EXIT 0 + 88 passed。
- **复审结论**: PASS。6 维度全核验通过：①ai_update_provider_pool 无 api_key 参数不触密钥迁移(commands.rs:1023-1056) ②reload_provider_caps 单点真理(state.rs:311-327) ③global_cap=3 与单 provider 等价(state.rs:321+324-326) ④编辑保留(commands.rs:945-947) ⑤provider_pool::select 过滤 enabled/weight(provider_pool.rs:64) ⑥weight clamp(commands.rs:1037)。无 high/med/low 发现。
- **待修项回流 todo**: **无**(后端就绪,前端 Settings UI 留波16)。

### CR-260616-57 波15 F-04b多provider fallback切换(stream_one_provider+候选链+resolved_model重算)(80c0955) — ✅ 已审(PASS)

- **审查时间**: 2026-06-17(独立审查 agent · 亲跑 cargo check EXIT 0 + grep/read 核验源码)
- **总体结论**: ✅ **PASS** — 🔴0 🟡0 ⚪0
- **逐件核验(全 PASS)**:①stream_one_provider 抽取 ✅(agentic.rs:155-298 封装单 candidate,StreamOutcome 三态 Success/InitFailedExhausted/Fatal 驱动外层切换)/ ②resolved_model 重算 ✅(:188-199 select_model_id 在 candidate.model_configs 上重算+ model_override 穿透,防切换 provider 后模型池不同吃 400/404)/ ③候选链外层 ✅(:663 'candidate for 循环+ :656-658 Vec clone primary 入链避借用冲突)/ ④InitFailedExhausted 切换 ✅(:691-701 continue + :188-199 resolved_model 重算,stream_one_provider 内重建 build_provider)/ ⑤Fatal 立即放弃 ✅(:703-707 guard.reset+return,对齐 retry.rs Fatal 分类 + provider_pool 文档)/ ⑥per-provider permit 切换释放 ✅(:667 acquire_for_provider 循环内取,切换 Drop 自动释放旧槽)/ ⑦retry_deadline 共享预算 ✅(:642 单次 Instant::now+30s,各 candidate 共享不重置,防串行累加超预算)/ ⑧单 provider 零变化 ✅(:352 空池兜底 candidates=Vec::new,candidate_chain 仅 primary,循环跑一次耗尽 return)。
- **退避策略一致性**: ✅ 复用 df_ai::retry::backoff_delay(agentic.rs:14 use df_ai::retry + :270 调用 retry::backoff_delay((retry_attempt+1) as u32)),retry.rs:74-85 实现 1s→2s→4s ±20% jitter,与 CR-30-1 对齐一致。

---


---

## 已审归档区(更早批次)

### CR-260616-66 波23 Settings拆4子组件(Provider/Connection/General/Knowledge·零行为变化)(9c60c8f) — ✅ 已审(PASS)

- **复审结论(2026-06-17·审查 agent 亲跑 vue-tsc + cargo check + 独立 grep/read 核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
  - **验证**: `npx vue-tsc --noEmit` **EXIT 0** ✅ / `cargo check --workspace` **EXIT 0**(6 pre-existing dead_code warnings 无关本 CR)。
  - **①4 子组件拆分边界 PASS**:ProviderPanel 收纳 AI 模型配置 + Provider 表单(providerForm 双向共享按数据耦合正确收敛:171-204 openProviderForm 编辑/新建完整分支) / ConnectionPanel 独占 localStorage CRUD / GeneralPanel 独占主题/语言/并发/Agent 轮次 / KnowledgePanel 独占知识库配置(embeddingProviders 依赖 ProviderPanel 的 aiProviders props:108-109 正确)。零交叉污染。
  - **②props/emits 接线 PASS**:Settings.vue:26 传 `:confirm-dialog="confirmDialog"` 函数引用 + :75 `useConfirm()` 返回 confirmDialog/confirmState/answerConfirm 三件套 / ProviderPanel.vue:150 接收 props.confirmDialog + :267/ConnectionPanel.vue:174 调用 props.confirmDialog 删除确认 / emit('toast') 上下传完整(Settings.vue:27/35 接收 + :65 showToast) / emit('providers-changed') 上抛同步 aiProviders(Settings.vue:85-87)。
  - **③CSS scoped 零视觉变化 PASS**:各子组件自持 scoped 样式副本(ProviderPanel:347-458 / ConnectionPanel:184-262 / GeneralPanel:280-322 / KnowledgePanel:171-213).panel/.btn/.toggle/.setting-* 类完整重复,父 scoped 无法穿透子根故需子自持(Settings.vue:179-181 注释明确)。视觉 CSS 全保留。
  - **④F-04c enabled/weight 拆 ProviderPanel 保留 PASS**:ProviderPanel.vue:40 `@change="onPoolToggle"` + :50 `@change="onPoolWeightChange"` / :301-312 onPoolToggle(prev 保存 + 乐观更新 + catch 内 revert) / :318-332 onPoolWeightChange(debounce 300ms + clamp[0,100] + NaN 兜底回退 50) / aiApi.updateProviderPool 即时调 IPC(:305/:325) / reload_provider_caps 语义完整(注释:291-294)。
  - **⑤sync×3 拆 GeneralPanel 保留 PASS**:GeneralPanel.vue:92 `@change="syncConcurrencyConfig"` + :114 `@change="syncAgentMaxIterations"` + :125 `@change="syncAgentMaxRetries"` / :202-219 syncConcurrencyConfig(debounce 300ms + clamp 双并发 + 单对话不超过全局) / :226-237 syncAgentMaxIterations(debounce 300ms + clamp[1,50]) / :243-254 syncAgentMaxRetries(debounce 300ms + clamp[0,10]) / :265/:267/:269 onMounted 启动同步一次防止刷新后丢失。
  - **⑥AE-05 useConfirm 留 shell PASS**:Settings.vue:7-15 confirm-mask/confirm-box 模板完整 + :75 `useConfirm()` 返回 confirmState 留 shell共享(confirmState.visible/msg 在 setup 自动解包) + answerConfirm 双按钮绑定。
  - **⑦6 timer 全覆盖 PASS**:Settings.vue:96 _toastTimer 清理 + :97 providerPanelRef.clearPoolTimer() (ProviderPanel.vue:343 暴露) / ProviderPanel.vue:322 _poolTimer clearTimeout / ConnectionPanel 无 timer / GeneralPanel.vue:274-276 三 timer 清理(_concurrencyTimer/_agentIterTimer/_agentRetryTimer) / KnowledgePanel.vue:167 _knowledgeSaveTimer 清理。各子组件 onUnmounted 自管(Settings.vue:95 注释明确)。
  - **⑧i18n 复用无新增 PASS**:grep 核验 panelAiProvider/panelConnection/panelGeneral/panelKnowledge 全在既有 settings.ts(zh-CN:7/62/77/115 + en:7/62/77/115)。零新增 key。
  - **待修项回流 todo**: **无**(8 维度全 PASS,零问题。纯重构零功能,行为零变化硬约束满足,运行时留实测)。

---

### 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 处理

### 当前队列已审项迁入（2026-06-27）

## 当前队列

### CR-260624-01 AI 路径授权三档化(once/session/always) — ✅ 已审(PASS·单 agent + 6-agent 对抗深审 0 finding)

- **范围**:AllowedDirs 加 `once` 层(state.rs:444)+ ai_authorize_dir decision 改 match 四分支 once/session/always(chat.rs:846)+ 执行后 clear_once(chat.rs:924)+ 前端 DirAuthDialog 加"
