# DevFlow 工作看板

> 来源：`docs/02-架构设计/功能决策记录-2026-06-14.md`「需求与待办」+ `PROGRESS.md` 各 Sprint 遗留，2026-06-14 汇总去重 + 代码核对修正。
> 互操作：执行走 mission-control，回写 mission_id；审查走 cr；发布走 publish-*。
> 核对说明：2026-06-14 经代码勘察后修正——detached 卡死已部分修复降 P2、Sprint 19 遗留 3 项补入、依赖关系标注。

---

## 交接状态（2026-06-14）

**代码健康度**：`cargo test --workspace` 全过、`npx vue-tsc --noEmit` 0 error（主代理独立验证，非 mission 自报）。

**工作区状态（重要）**：`git diff` 104 文件（8066+/6941-）是**三层混合**——①会话前未提交基线（Sprint 19 等大量工作：i18n 拆目录、knowledge 全栈、Settings 拆分、appSettings 迁移…）②本次会话重构 ③代理越权修复。**接手前务必 `git diff` 通览区分**，勿整体当作单一改动提交。

**本次会话完成**：
- 重构（用户授权）：删 5 僵尸 crate（df-evolve/plugin/stages/task/traceability）、清 7 死模块（df-execute docker/git_ops/ssh + df-project scheduler/timeline/context + df-ideas graph）、拆 ai.rs→`commands/ai/` 11 文件、拆 ai.ts→6 composable、models 字段 bug 修复、coordinator B 路线标注
- 代理越权追加修复 6 处（已标✅，主代理验证编译+测试通过；逐行正确性建议接手方 `git diff` 复核）：B-01 审批持久化 / B-02 ConditionEngine 默认 false / B-04 删 NodeRegistry Default impl / T-05 工具结果截断 50KB / B-08 promote 补偿删除 / T-07 诊断日志清理

**待设计交其他会话（核心）**：df-workflow 审批闭环三连 B-06/B-07/B-03。**✅ B-03 设计已完成**（接手会话，2026-06-14）：见 [B-03-人工审批响应机制-2026-06-14.md](./02-架构设计/B-03-人工审批响应机制-2026-06-14.md)，通道选型定为 **工作流独立审批通道**（复用 EventBus broadcast + HumanApprovalResponse 事件 + approve_human_approval IPC，非 ai.rs AiApprovalRequired——后者是 AI Chat 工具审批路径，与工作流节点审批是两条独立链路）。拆 B-03a（响应等待 + 超时，不依赖 B-07）/ B-03b（取消机制）。**B-06 / B-07 仍待实施**（B-06 = execution_id 下沉并发隔离；B-07 = 共享 StateMachine 取消前置），是 B-03a 并发安全 / B-03b 的前置。

**失控代理教训**：本次会话派的后台拆分代理在 stop hook 循环里失控，越权改代码/文档（先斩后奏）。接手方若再派 agent，注意约束其不碰决策记录（用户已要求手动触发）+ 限定单任务不自主续推。

---

## 待办

### 📋 编排推进总览（2026-06-16 汇总）

未完成待办按可执行性分 8 组（详细条目见下方各分类，勿重复记录）：

| 组 | 说明 | 代表项 |
|---|---|---|
| **A todo 卫生** | 矛盾清理/被取代退役标注 | ✅ 本批：AR-1 退役 / AR-8 重评 |
| **B 零风险减法** | 死链清/空壳合并 | ✅ ARC-02 死链已删 / ARC-03 前提失效转重评 |
| **C 需用户输入** | 阻塞，无法代办 | S-260615-01 curl 测 / S-260614-01 多开澄清 / S-260614-02 实测重评 |
| **D 功能增强** | 设计清晰可推进（P1） | F-15-01 审批选项 / F-15-02 task 详情 / F-15-03 分页⚠️breaking / F-15-04 卡片折叠 |
| **E 架构重投入** | 需设计，非小改 | F-14-01 模型能力 / F-14-07 trait 下沉 / ARC-05·06 store 拆·循环依赖 |
| **F 全局 review P2 需设计** | 安全/竞态收口 | R-PD-4/5/6/8/9/12/13 + R-PD-10 .map_err 85 处 |
| **G 测试/dev 验证** | 收尾土壤 | ARC-08 dev 验证 / B-03b-R8 human 端到端 / T-14-01·02 Sprint 实测 |
| **H 长期功能池** | 不进主线 | F-14-02~10 / T-14-06 Settings 拆 / T-14-11 条件引擎 / B-14-05 / B-03b-R9 |

**推进原则**：能并行不串行（独立子任务 8-12 并发）；每批重汇总+全优先级重排+销账核对 ✅；C 组阻塞项不代办等用户。**本会话首批**：A 卫生 + B 清死链。

**③④C类已迁**（2026-06-16）：需人工决策项(③产品取舍 7 / ④设计方向 7 / 重投入排期 9 / C用户实测 5)见 [待决策.md](./待决策.md),从本文件抽离职责分离。**batch54 可自主批进展(2026-06-16·workflow wsfz7zl8i + 主代核查)**:复核-新⑪ 看门狗幽灵 ✅ 销账(清主窗口 state 视觉残留;**核验更正**:原 watchdog 幽灵已被 CR-260615-24 stopListener:371 clearStreamWatchdog 兜底);AE-03 diff预览 ⚠️ 降级阻塞(后端 write_file 未返 diff,待后端补,见 line 538);UX-04 断线保文 待 DEC-260616-03 授权(见待决策.md 🟡,本批不含)。

### P0 — 阻断性 bug

- [x] B-260614-01 — ~~待审批持久化根治（重启恢复）未生效~~ ✅ mission:T-260614-01 已修复（commands.rs:444 clear→retain 保其他对话 pending；ai_approve 两处 if !recovered 守卫移除；cargo check 0 err / 19 test pass）(06-14)
- [x] B-260614-02 — ~~df-workflow ConditionEngine 默认 true~~ ✅ mission:T-260614-02 已修复（conditions.rs:31 `Ok(true)`→`Ok(false)` 保守拒绝；5 个原断言错误行为的测试同步改断言；df-workflow 7 test pass）(06-14)
- [x] B-260614-04 — ~~df-workflow NodeRegistry::default() script 工厂 unimplemented!~~ ✅ mission:T-260614-03 已修复（删除整个 Default impl——零调用方 + 违反铁律；state.rs build_registry 已用 new() + 手动注册真实 ScriptNode）(06-14)
- [x] B-260616-20 [P0] — **df-nodes 测试回归:`advance_succeeds_after_prior_status_change` 红(CR-260616-41 status 收口副作用)** ✅ **已修**(主代 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**)。原委:commit b94e74a 把 status 移出 tasks 白名单(D-260616-04 收口),`crates/df-nodes/src/task_advance_node.rs:332` 测试用 `repo.update_field("t1","status","in_progress")` 模拟「旁路并发改 status」前置,现被白名单拒 → unwrap panic。生产代码无回归,仅测试 fixture 同步。— crates/df-nodes/src/task_advance_node.rs:331(审查 CR-260616-41 复现)

### 🔧 2026-06-16 排查会话新报 bug（4 项·仅分析未实施）

> 排查性质会话（session-role-diagnose-only），仅走查定位根因 + 记录待办，未改代码。

- [x] ✅(第㉖批 2026-06-16·主代理直修) B-260616-09 [P1] — **任务详情点击打开空白（路由漏注册）**。**router/index.ts 补 /tasks/:id → TaskDetail**(L50-54 新增块,6 行,参照 /projects/:id L39-43 + /ideas/:id L27-31 模式)。name 'TaskDetail' 无冲突(现有 name 集无此项),复用 icon-thunder(对齐 /ideas/:id 复用 icon-lightbulb、/projects/:id 复用 icon-apps 一致模式)。TaskDetail.vue 现挂载,Tasks.vue:57 router.push(`/tasks/${task.id}`) 不再跳未注册路由,B-18 联动刷新等功能激活(死代码转活)。改动极简确定(6 行路由表项),主代理直修省 workflow 单 agent 编排开销。vue-tsc exit 0。— src/router/index.ts
- [x] ✅(第㉖批 2026-06-16) B-260616-10 [P1] — **provider 配置后 AI 面板仍提示「未配置」**。**Settings.vue loadProviders 同步刷全局 store**。L376 import useAiStore + L383 const aiStore = useAiStore()(跟随 L382 appSettings 风格)+ L420-424 loadProviders 内本地 aiProviders.value 刷新成功后 L424 await aiStore.loadProviders() 刷全局 state.providers(模块级单例 ai.ts:45,L20/L44 注释明确全应用共享同一份,AiChat 共享)。三处调用点(saveProvider L464/deleteProvider L479/setDefaultProvider L490)+ onMounted L731 均经 loadProviders 自动同步全局。无循环(useAiPanel.ts:91 loadProviders 只刷 state.providers 不回调 Settings)。无 i18n 改动(纯数据同步),try/catch + showToast 保留。vue-tsc exit 0。— src/views/Settings.vue
- [ ] B-260616-11 [P2] — **AI 消息 `-`/`+` 列表/diff 无视觉效果**。marked 实测两个出口：①裸 `-`/`+` 混用 → GFM 要求同列表标记一致，marked 拆成**两个独立 `<ul>`** 且 `-`/`+` 被 eat 当 marker（用户看到的符号消失，两个分离单项列表）；②```diff 代码块 → `<pre><code class="language-diff">` **无 hljs 高亮**（ai-md.css 未集成 highlight.js），`-`/`+` 字面纯文本无红绿。根因层 marked 行为合规，缺的是 **diff 高亮集成 + 裸 diff 文本识别**。改动（待定）：①集成 hljs 覆盖 ```diff（关联已知待办「aichat交互体验改进方案-2026-06-14」2.2 代码块高亮）②裸 `-`/`+` diff 文本预处理识别。— src/composables/useMarkdown.ts + src/styles/ai-md.css。**②子项已修(第㉖批 2026-06-16·主代理直修)**:useMarkdown.ts L40-41 import diff + L63 registerLanguage('diff', diff)(16→17 语言),```diff 代码块经 highlightCode getLanguage('diff') 命中 → hljs-addition/deletion token class → github-dark.css 红绿。**①子项仍②类待复现**:裸 -/+ 文本预处理识别(需确认 AI 实际输出格式)。**batch58 可行性评估结论(2026-06-16·workflow w2dw65maz·Agent C 仅评估未改码)**:推荐**方案A**(renderMd/parseBlockNoCache 前 `wrapNakedDiff` 正则检测连续 ≥2 行 ±开头→包裹 ```diff 围栏,复用已就绪 hljs diff 红绿样式零额外 CSS);方案B(CSS ::before 注入)**否决**(marked 已 eat 掉原始 ±符号,li 内文本信息丢失无法区分);方案C(marked custom tokenizer)**否决**(侵入 marked 内部收益不优于 A)。守卫:纯 `-` 列表(普通 ul)不触发+必须含 `+` 行混排。**暂缓实施**:需求前提(AI 实际输出格式)未验证,不同 LLM 输出 diff 格式差异大(围栏/裸行/自然语言描述),盲目做可能解决不存在的问题;确认需求后 0.5 批次(~40-60 行+测试)。vue-tsc exit 0。
- [x] ✅(第㉗批 2026-06-16·workflow) B-260616-19 [P2] — **知识库详情窄屏标题挤压成竖线（一行一字）**。三因子叠加（Knowledge.vue）：①`.kn-layout` grid `grid-template-columns: 360px 1fr`（L567）——左列表死钉 360px、右详情 `1fr` 拿剩余，**无响应式断点**；窗口窄（如总宽 500px，减 padding 40+gap）详情列塌缩到 ~80px 甚至更小。②`.detail-title`（L625）`font-size:18px` + `word-break:break-word` —— 窄列里中文每字宽度 > 列宽，逐字换行 → 竖线化。③`.detail-actions`（L626）`flex-shrink:0` —— 编辑/发布/拒绝 3 按钮不缩，优先占位，把 `.detail-title-row`(`flex:1`) 挤到零头。`.kn-detail-panel`(L570-576) 无 `min-width` 兜底。改动（待定）：①grid 列宽改 `minmax(240px,360px) 1fr` + `@media (max-width:760px)` 切单列（`grid-template-columns:1fr`，列表详情上下堆叠）；②详情面板加 `min-width:0` 配合标题行 `min-width` 防塌缩；③可选标题字号响应式降级（18px→15px）。— src/views/Knowledge.vue。**低风险纯 CSS**，无逻辑/i18n/数据变动。

### 🔧 2026-06-16 aichat 工具卡片重复渲染排查（仅分析·未实施）

> 排查性质（session-role-diagnose-only），走查定位根因 + 记录待办，未改代码。

- [ ] B-260616-21 [P2] — **aichat 工具卡片「重复读取同一文件」表象（read_file 出现两条卡：一 0 行·7.1KB running、一 183 行·7.1KB completed）**。用户报对话记录里 `读取 .../api/ai.ts` 出现两次。**根因分析**（已走查前端流转 + 后端 emit 点）：①前端 `useAiEvents.ts:202-206` `AiToolCallStarted` 分支直接 `lastMsg.toolCalls.push(info)`，**无 id 幂等守卫**（对比同文件 `startToolSlowTimer:58` 有 `_toolTimers.has(callId)` 守卫，Started 漏了同款判重）②`findToolCall`(`aiShared.ts:41-49`) 按 `id` 命中**首个**返回，`AiToolCallCompleted`(`:213`) 只 update 第一张卡 → 同 id 若被 push 两次，第二张卡永远 running，`ToolCard.vue:72` `parsed?.lines||0` 无 result 时显示 `0 行` ③后端 `audit.rs:538-549` `process_tool_calls` 对 tc_list 每 draft emit 一次 Started；read_file=Low risk(`:561`) **无去重**(去重仅 High，`:553-556` 注释明确 Low 不进)。**两候选根因**：**(A 最贴合现象)** 同一 `tool_call_id` 被**重复 emit Started**(后端 `process_tool_calls` 重入 / `stream_recv.rs` 生成重复 id / `agentic.rs` loop 重 process 同轮)→ push 两张卡 → Completed 只更首张(183 行)→ 次张残留 running(0 行)；**(B)** agent loop 多轮 LLM 真读两次(不同 id)→ 但应两卡皆 183 行，与「0 行 + 183 行」现象不符，**排除为主因**。**待查**：后端同 id 重复 emit 的确切路径(`audit.rs:532` tc_list 来源 `tool_calls_acc` + `stream_recv.rs` id 生成 + `agentic.rs` 是否对同轮 tool_calls 重复 process)。**修复方向**(待定)：①前端 Started push 前加 `findToolCall(event.id)` 幂等守卫(确定性低风险，对齐 startToolSlowTimer 守卫，治标兜底) ②后端查重复 emit 源治本(定位 stream_recv/process/loop 哪环生重复)。**关联** F-260616-05(agentic loop 工具去重缺失，batch53 仅 High 去重，read_file/search_files 等 Low 未去重；本现象若实为候选 A 则与 F-05 不同维度——F-05 是不同 id 重复调用，本条是同 id 重复 emit)。— src/composables/ai/useAiEvents.ts(:195-209 Started 分支) + src/composables/ai/aiShared.ts(:41 findToolCall) + src/components/ToolCard.vue(:72 lines 展示) + src-tauri/src/commands/ai/audit.rs(:538-549 emit Started)。

### 💡 2026-06-16 新需求（已分析·待实施）

> 功能需求，已走查定位链路 + 方案记录，待用户确认后实施。

- [x] ✅(batch38·2026-06-16·workflow wx5vd8ppk) F-260616-01 [P2] — **aichat Agentic 最大循环轮次支持配置（Settings）**。已落地(1:1 复刻 llm_concurrency,8 处改动全闭环)。现状：`MAX_AGENT_ITERATIONS=10` 硬编码常量（`agentic.rs:33`），复杂 agentic 任务未收敛即被截断，用户无法调。**黄金参考：`llm_concurrency`**——项目唯一成熟的「前端数字配置→AppState 字段→热改 command→透传 loop 形参」范式，本需求 1:1 复刻。改动 8 处：①`state.rs` AppState 加 `agent_max_iterations: Arc<AtomicUsize>`(默认 10) ②`agentic.rs` 常量改名 `DEFAULT_MAX_AGENT_ITERATIONS` + `run_agentic_loop` 增形参 `max_iterations` + 循环(L156)/超限消息(L321-332)用形参 + `try_continue_agent_loop`(L478) 补传 ③`commands.rs` `ai_chat_send`(L114) 补传 + 新 command `ai_set_agent_max_iterations`(clamp 1-50 store) ④`lib.rs` invoke_handler(L125 区)注册 ⑤`api/ai.ts` 新 `setAgentMaxIterations`(仿 L73 `setConcurrencyConfig`) ⑥`Settings.vue` reactive(L615 区)+UI 数字 input(min1 max50,紧挨并发控制 L268)+watch 持久化+`syncAgentMaxIterations`+onMounted 推后端 ⑦i18n 补 zh/en 键。**生效时机**：loop 入口 `load` 取快照传值，当前 loop 锁定边界，热改下次发消息生效（防边界飘移；与 `llm_concurrency` 传 Arc 实时反映的区别）。key `df-ai-agent-max-iterations`，范围 1-50 双 clamp。`agentic.rs:27-32` 注释已预留此扩展点。**低风险，复刻现成范式无新机制**。— 详案见本地 plan 文件 `quizzical-prancing-hennessy.md`。

- [x] ✅(batch37·2026-06-16·workflow wg8x18y9y) F-260616-02 [P2] — **知识库「待审」(pending_review) 不应进第一个 tab（知识库/library）**。决策 a 落地:inbox 收 candidate+pending_review,library 纯 published。现状：`Knowledge.vue` 顶层两 tab——`library`(知识库,L19) 用 `store.items`、`inbox`(审核收件箱,L22) 用 `store.candidates`。根因在数据源：①后端 `knowledge_list`(knowledge.rs:71-81) `status=None` 默认返回「**非 archived 全部**」= candidate + **pending_review** + published；②前端 store `loadList`(knowledge.ts:60) 无 status 参数调它 → `state.items` **含 pending_review**；③`knowledge_list_candidates`(knowledge.rs:211-218) 只列 `candidate` → inbox 不含 pending_review。结论：**pending_review 条目当前只在 library tab 与 published 混杂显示**，inbox 反而不收它。改动方向（待定，**含决策点需用户定**）：①最小改——后端 `knowledge_list` 默认排除范围从 `archived` 扩到 `archived + pending_review`（或前端 `loadList` 后 filter 掉 pending_review，store 层兜底）；②**决策点：pending_review 从 library 排除后该去哪显示**？(a) 扩 inbox 收件箱含 candidate+pending_review（语义=「待处理」收件箱）(b) 单纯从 library 消失、pending_review 暂无专门视图 (c) 加第三个 tab「待审」。**倾向 (a)**：inbox 收 candidate+pending_review，library 纯 published，职责清晰零新 tab。— Knowledge.vue(L296-299 列表数据源) + stores/knowledge.ts(loadList/loadCandidates) + knowledge.rs(knowledge_list 默认过滤 + list_candidates 范围)。**待用户定②后实施**。

- [x] ✅(batch44 后端 + batch45 前端·2026-06-16) F-260616-03 [P2] — **aichat 达最大轮次时询问「是否继续」+ 确认后续 loop**。全闭环:后端(batch44 AiMaxRoundsReached 暂停态+continue/stop IPC)+ 前端(batch45 操作卡 pendingMaxRounds+续/停按钮)。现状：`agentic.rs:321-332` 达 `max_iterations` 未收敛(`!converged`)→ emit `AiError`「达到最大轮次(N 轮),Agent 可能未完成」+ **直接走完成流程**(save/标题/提炼+`guard.reset`+`AiCompleted`)，用户无法续，复杂任务被硬截断。需求：达上限改 emit 询问 → 前端「继续/停止」操作卡 → 用户点继续后续 loop、点停止走完成。**机制高度复用工具审批暂停-恢复**：审批 = `AiApprovalRequired`(audit.rs:353 emit)→前端 `pending_approvals`(useAiEvents.ts:219 push)→`ai_approve`(commands.rs:122)→`try_continue_agent_loop`(agentic.rs:388 续 loop)。本需求同构。改动骨架：①后端 `agentic.rs:321-332` 达上限分支改：不 emit AiError+完成，改 emit 新事件 `AiMaxRoundsReached` + 保持 `generating=true`(暂停态，仿审批等待) ②`mod.rs:71` 事件枚举加 `AiMaxRoundsReached { conversation_id }` ③新 command `ai_continue_loop`(用户点继续)→调 `try_continue_agent_loop` 续 loop(**续跑轮数**：重置计数再跑 `max_iterations` 轮——末轮 tool_result 已在 messages，续跑首轮自动回传 LLM，语义自洽) ④新 command `ai_stop_loop`(点停止)→走原完成流程(save+Completed) ⑤前端 `useAiEvents.ts` 加 `case 'AiMaxRoundsReached'`(push 待确认态，仿 pendingApprovals) ⑥`AiChat.vue` 加续跑操作卡 UI(继续/停止按钮)。**设计决策点(待定)**：(a) 续跑轮数=重置计数再跑 max_iterations 轮(倾向) vs 单步 1 轮 (b) 防无限续：无硬上限(用户主动授权=同意烧 token，倾向) vs 加累计上限 (c) 事件/卡片：新增 `AiMaxRoundsReached`+独立操作卡(倾向，语义清晰) vs 复用 `AiApprovalRequired`(args/name 不搭)。**关联 F-260616-01**(max 可配)——两者协同=用户完全掌控 loop 边界，建议同批实施。— agentic.rs(达上限分支 L321-332 + try_continue 复用) + mod.rs(事件枚举) + commands.rs(新 command) + useAiEvents.ts + AiChat.vue。**待用户定决策点后实施**。

- [x] ✅(batch42·2026-06-16·workflow w95hzp9tw) F-260616-04 [P1] — **run_command 超时→LLM 重试→反复审批循环**。已根治(tool_registry.rs 超时标注「勿盲目重试」+ DEFAULT_RUN_COMMAND_TIMEOUT_SECS 常量,handler map_err 改写)。根因链（探索确认）：超时(默认 60s, `tool_registry.rs:491 unwrap_or(60)`)→tool_result 回传 LLM(`commands.rs:168` Err 分支)→LLM 重试同命令→**新 tool_call_id**(`stream_recv.rs:176` 每轮新 UUID)→重新 insert pending(`audit.rs:343`)→重新审批，循环。症状「再过一会又提示 Run Command」即此。修复方向：①超时 tool_result 明确标注「命令执行超时({n}s)，进程已终止，勿盲目重试同命令；确需更长时限重发时提高 timeout_secs」②run_command 默认超时可配（LLM 已可传 `timeout_secs` 参数，但默认 60 + 不主动调）③配合 F-260616-05 去重根治。— tool_registry.rs(超时标注) + commands.rs(回传内容)。
- [x] ✅(batch53·2026-06-16·workflow wbh8mu1iq·**run_command 循环核心解决**) F-260616-05 [P1]（**共性根治**）— **agentic loop 工具调用去重缺失**。**batch53 落地 High risk 审批前去重**(audit.rs find_cached_high_risk_result 反向扫 messages + args JSON 规范化匹配 + 排除 pending 占位 + decided_by=auto_dedup,解决 run_command 反复审批循环,F-04 超时标注 + batch53 去重双保险)。**完整 agentic loop 通用去重(search_files 重复卡片 + 滑动窗口指纹 + 决策点)③类待用户定**(窗口/粒度/措辞)
- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-06 [P2] — **「未找到挂起的审批」体验**。**全闭环落地**:①`commands.rs:217` ai_approve 幂等——内存 pending_approvals remove None 时查 `ai_tool_executions` 审计表,已 executed/rejected/failed 返回「已处理({status})」非报错 ②`useAiSend.ts:305` 审批按钮防抖——`_pendingApprovalIds` Set 守卫,同 id 短期多次点击只发一次 IPC,finally 块清理。— src-tauri/src/commands/ai/commands.rs + src/composables/ai/useAiSend.ts

- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-07 [P2] — **流式对话失败自动重试**。**全闭环落地**:①`agentic.rs` stream_llm 调用处包重试循环(max_retries 形参,退避 1s→2s→4s,持 permit 防挤占) ②每重试 emit `AiStreamRetry` 事件(attempt/max_attempts) ③耗尽走原有 guard.reset+return ④state.rs 加 `agent_max_retries:Arc<AtomicUsize>`(默认3) ⑤新 IPC `ai_set_agent_max_retries`(clamp 0-10) ⑥前端 api/ai.ts + types.ts(AiStreamRetry variant) + Settings.vue(input 0-10+debounce sync+i18n zh/en)。**设计决策**:一律尝试重试(简化策略,由退避控制成本),MidStream 重试语义等价于"看不到完整回复再试一次"。— src-tauri/src/commands/ai/agentic.rs + state.rs + commands.rs + lib.rs + src/api/ai.ts + types.ts + src/views/Settings.vue + src/i18n/{zh-CN,en}/settings.ts

- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-08 [P2] — **AI 数据获取工具 offset 分页 + 探总量**。**全闭环落地**:①6 工具(list_projects/tasks/ideas/trash/read_file搜索模式/search_files)全部补 offset+limit 参数,返回 items/total/has_more 替代 truncated ②新增 `get_project_count`+`get_task_count` 探总量工具 ③knowledge_search 解钳 `min(3)`→`min(20)`。— src-tauri/src/commands/ai/tool_registry.rs + src-tauri/src/commands/knowledge.rs

- [ ] F-260616-09 [P1/架构·**A路线✅(batch60·2026-06-16·workflow whae812z5+主代核查),B多会话并发待立项**] — **AiSession 单例 → 多会话并发架构（多窗口来回切 + 并行执行）**。用户需求(3 条消息强化)：「应用级支持同时具备多会话」+「同时具备多会话的能力」+「多个会话窗口来回切，**并行执行**」。现状（Explore 走查确认）：`AiSession` **应用级单例**（`state.rs:164` `ai_session: Arc<Mutex<AiSession>>`，init:205），messages/generating/stop_flag/pending_approvals/agent_language 全一份。设计「单例 + 软隔离」——generating 互斥(同一时刻**只一个对话能跑**) + 切换旧 loop 退出(`agentic.rs:177-190` B-260615-11) + 切换时 generating→readonly(`commands.rs:873-881`)。`llm_concurrency` per_conv 因此退化为单对话内并发(`state.rs:93-96` 注释已标注预留改 HashMap)。**memory 已标记「AiSession 单例未动」架构债**(aichat-arch-extensibility)。**残留 bug**(表象=新建会话上下文残留)：前端 `newConversation`(`useAiConversations.ts:31-41`) 漏清 queue/generatingConvId/agentRound/searchQuery；后端 `ai_conversation_create`(`commands.rs:784-821`) 漏清 agent_language/stop_flag(设 true 停旧 loop 没复位 false)。**用户「并行执行」明确=B 方向**(A 只解切换残留不满足并行)。**分档**：**A 短期隔离修复**(补漏清字段，维持单例软隔离，解残留，低风险立即做，**B 前置**) + **B 多会话并发架构**(单例 → `HashMap<conv_id, SessionState>`，每会话独立 messages/generating/stop_flag/pending_approvals + llm_concurrency per_conv 改真 per-conv HashMap + loop 内校验改 conv_id 索引各自 session + 切换不退出旧 loop 各自跑完真并发 + 事件路由核对 conversation_id 全覆盖)。**决策点(待定)**：(a) A 先做解燃眉 vs 直接 B——倾向 **A 先**(残留真 bug 立即解，B 架构另立项) (b) B 形态——AiSession 整体 HashMap vs 拆字段(messages/pending 已可按 conv reload，只拆 generating/stop_flag per-conv)——倾向**后者侵入小** (c) B 并发上限——复用 llm_concurrency.global 限并发会话数防 token 暴增——倾向是 (d) **多窗口 UI 形态**——(d1) 单面板侧栏会话列表切换+后台并行(改动小) vs (d2) 每会话独立 Tauri 窗口(`useAiWindow.ts` detached 已有基础可扩，改动大但「窗口来回切」语义最贴)——**待用户定**，倾向先 d1 后 d2 (e) 旧 loop 切换处理——不退出各自跑完(真并发，倾向) vs 仍退出。— state.rs(AiSession 去单例化) + commands.rs(create/switch 重构 + 漏清补) + agentic.rs(:177-190 校验改 conv_id 索引) + context.rs(ContextManager per-conv) + useAiConversations.ts(漏清补) + stores/ai.ts(per-conv state) + llm_concurrency per_conv + useAiWindow.ts(若 d2 多窗口)。**B 是大改架构升级，需单独设计文档 + 立项**。**待用户定 A 先做 / B 立项 / 多窗口 d1·d2 方向**。

- [x] ✅(batch58·2026-06-16·workflow w2dw65maz+主代核查,vue-tsc 0err) F-260616-10 [P1·bug] — **provider 切换/默认值重启·刷新后失效（前端 activeProvider 不同步 DB 默认）**。用户实测：Settings 设 A 为默认 → 重启 → 默认变 B(列表第一个)。**根因全在前端，后端链路正确**(独立核验不信 PROGRESS.md：「已修复」声明不可信)：`ai_set_provider`(`commands.rs:747-774`) 互斥落库✅ + `update_full`(`crud.rs:1127`) SQL UPDATE 含 `is_default`✅ + `get_active_provider`(`prompt.rs:9-32`) 走 `DB.is_default`✅。**根因链**：①重启后前端 `store.activeProvider = null`(`stores/ai.ts:53`，无 localStorage 持久化 + 无启动恢复) ②`loadProviders`(`useAiPanel.ts:91-93`) 只拉列表**不同步 DB 默认** → activeProvider 仍 null ③**UI 错觉**：`activeProviderName`(`AiChat.vue:1169-1172`) null 时 fallback `providers[0]`(列表第一个) ≠ 用户设的 A → 「默认变另一个」 ④**真覆盖**：用户点 provider bar 切换 `cycleProvider`(`AiChat.vue:1180-1185`)，`findIndex(activeProvider=null)` 返回 **-1** → `next = ps[(−1+1)%len] = ps[0]` = 列表第一个 B → `setProvider(B)` → 后端互斥落库把 `DB.is_default` 从 A **真改成 B**。即使用户不点切换，UI 也显示 providers[0] 而非 A。**修复方向**：①`loadProviders` 后同步 `activeProvider` = DB.is_default 的 provider——后端加 `ai_get_active_provider` IPC 返回当前默认 id，或 `listProviders` 返回值带 `active_provider_id` ②`cycleProvider` findIndex=-1 陷阱修正(null 时应从 DB 默认起算，非 ps[0]) ③前端 activeProvider 持久化(localStorage `df-ai-active-provider`，对齐 `df-ai-active-conv` 模式)。— useAiPanel.ts(loadProviders 同步默认) + AiChat.vue(:1169 activeProviderName fallback + :1180 cycleProvider findIndex 陷阱) + commands.rs(新 ai_get_active_provider IPC) + api/ai.ts + stores/ai.ts(activeProvider 持久化)。**确定性 bug，低风险，可立即修**。

- [x] ✅(batch60合批·2026-06-16·主代核查,cargo 0err) F-260616-11 [P1·决策a累计计数] — **审批后 iteration 重置致 max_iterations/token 超配**。决策(2026-06-16):**a 累计计数**(try_continue 不重置 iteration,跨审批续跑延续累计)。**已实施**:`iteration_used:usize` 累计字段(`mod.rs:215/230` AiSession+new init 0)+`start_iteration:usize` 参数(`agentic.rs:114` run_agentic_loop signature +`:176` loop 边界 `start_iteration..max_iterations` +`:210` 一致性块 `iteration_used=iteration+1` +`:578` try_continue 加参 +`:675` spawn 透传)+三处 reset(`commands.rs:55/152/412` ai_chat_send/ai_regenerate/ai_edit_last 新生命周期)+ai_approve 两处累计读 iteration_used(`:260-262` 拒绝续跑 +`:313-315` 通过续跑)+`ai_continue_loop:601` reset+传0(F-03 决策a 达max重计区分)。两路径严格:审批累计(ai_approve) vs 达max重计(ai_continue_loop,F-03 a 用户授权重来) vs 新消息 reset。边界case:start≥max loop 空区间→converged=false→AiMaxRoundsReached(防无限审批烧 token,正 F-11 语义)。原:`try_continue_agent_loop` 重 spawn `run_agentic_loop` iteration 从 0 重计致多次审批反复重跑 max_iterations 轮 token 失控。— agentic.rs + commands.rs + mod.rs。

- [ ] F-260616-12 [P2/依赖F-09] — **retry 持 permit 不释放（多会话隐患）**。F-260616-07 落地的流式重试循环（`agentic.rs:238-239`）重试期间持有 global+per_conv permit 不释放（注释「防新请求挤占」）。当前 AiSession 单例 + 主 loop 串行无影响，但 **F-260616-09 多会话并发后**，重试期间阻塞其他对话 LLM 调用。**方向**：多会话落地时核对——主 loop 串行下重试持 permit 防自己挤占无意义，倾向重试不持 permit 或仅持 per_conv。— agentic.rs:238 + F-260616-09 多会话架构。**依赖 F-260616-09 立项后一并处理**。

### 💡 2026-06-16 新需求（UX 交互优化·分析完成·待实施）

> 用户实时反馈的交互优化需求,已走查定位链路+方案记录。

- [ ] UX-260616-01 [P2] — **工具调用失败时「重试」提示语义模糊**。用户场景:Run Command 执行失败(exit_code 255,`head` not recognized on Windows),UI 显示「⚠ 调用失败,正在重试(1/4)…」+「重试」按钮。**根因分析**:「正在重试(n/m)」是 `AiStreamRetry` 事件(F-260616-07 流式 LLM 重试),通过 `useAiEvents.ts:177-186` 更新错误气泡内容;工具执行失败(run_command 等 Low 风险工具)走 AR-6 路径(`audit.rs:640-652`)→ emit `AiToolCallCompleted`(result=错误信息)→ **不触发 AiError 不触发 AiStreamRetry**,错误包在 tool_result 回传 LLM 自行决策。**问题**:①用户看到的是「LLM 流重试」提示,非「工具执行失败」提示,语义错位 ②确定性失败(命令语法错/exit_code 非0)重试同命令必再败,「重试」按钮误导 ③工具实际结果(stdout/stderr/exit_code)已显示在 ToolCard 内,错误气泡的「重试」是消息级 regenerate(重新生成整条回复),非工具级重试。**改动方向**(待定):a)错误气泡区分两类——流式重试中显示「正在重试(n/m)…」(现有);工具执行失败不弹错误气泡(结果已在 ToolCard)或气泡文案改为「工具执行失败,查看上方结果」b)Fatal 类错误(4xx/鉴权/参数)气泡去掉「重试」按钮或改为「去设置」c)`AiStreamRetry` 事件更新气泡时附带 `retryable` 标识,前端据此显隐重试按钮。—— useAiEvents.ts(:177-186 AiStreamRetry case) + AiChat.vue(错误气泡 UX-03 操作栏) + i18n ai.aiStreamRetry
- [ ] UX-260616-02 [P3] — **「全部收起」与搜索/技能区合并一行**。当前 ToolCardList.vue 有两个独立行:①batch-approve 栏(line 4-11,pendingCount>0 时显示)②global-toggle 栏(line 14-17,collapsibleGroupCount>0 时显示「▾ 全部收起」)。用户要求将「全部收起」与附近的操作元素(search files 搜索文件/技能触发等)放到同一行,减少垂直空间占用。**需确认**:「search files」具体指哪个 UI 元素(i18n `ai.searchFiles` 渲染位置需定位,可能在输入框上方 skill 栏或工具卡区域)。**改动方向**:global-toggle 从独占行改为 inline 元素,与相邻操作栏 flex 同行。—— ToolCardList.vue(:13-17 template + :278-295 CSS .ai-tool-global-toggle)
- [ ] UX-260616-03 [P2] — **对话内容输出时自动收起旧工具卡片分组**。需求:当 AI 输出新内容(流式 delta / 新工具调用 / 新轮次)滚动到下方时,上方已完成的旧消息中的工具卡片分组自动收起,保持视野聚焦当前内容。**现状**:机制已存在——`ToolCardList.collapseInactive(activeIds)`(:191-199)供父组件调用,`AiChat.vue:1657-1661` 已有 watch 调用(refs 数组逐实例 collapseInactive)。**增强方向**:a)触发时机扩展——当前可能仅在特定时机调用,可扩展到:`AiTextDelta` 新消息开始时 + `AiAgentRound` 新轮次时 + 用户滚动接近底部时(跟随阅读位置自动收起已读内容)b)平滑过渡——收起加 CSS transition(高度动画 200ms)避免内容突然消失跳变c)可选:记忆用户手动展开的分组不自动收起(expandedCards Set 区分用户主动展开 vs 默认态)。—— ToolCardList.vue(collapseInactive + CSS transition) + AiChat.vue(watch 触发时机扩展)
- [x] ✅(batch66·2026-06-16·22d866d,formatToolResult 12工具覆盖+i18n 18key) UX-260616-04 [P1] — **审批通过后工具卡片仅显示裸 JSON**。用户实测:审批前 `pending_approval` 态渲染友好(参数键值对+风险提示+审批按钮),审批通过后 `completed` 态 body 仅显示一段 JSON 数据。**根因**:`ToolCard.vue` 模板渲染链对 `read_file`/`list_directory`/`write_file` 有专门 UI 分支,其余工具全部走通用兜底(L110 `v-else-if tc.result && tc.status === 'completed'`)→ `formatToolResult(tc)` → 兜底 `return formatJson(r)` 裸 JSON 美化输出。**代码核对**:①`formatToolResult`(L270-289)只特化 `write_file`,其余全 `formatJson` ②`toolResultSummary`(L476-498,header 摘要)覆盖 `list_tasks`/`list_projects`/`list_ideas`/`create_project`/`create_task`/`create_idea`/`update_project`/`delete_project`/`restore_project`/`purge_project`/`run_workflow` 共 11 工具,但 body 的 `formatToolResult` 不共享此逻辑 ③两函数覆盖范围严重不一致(header 有摘要但 body 展开仍裸 JSON)。**后端返回值核对**(tool_registry.rs):`update_task`→`{id,field,updated}` / `delete_task`→`{deleted,id}` / `delete_file`→`{path,deleted,...}` / `run_command`→`{command,exit_code,stdout,stderr,...}`(可能很大) / `patch_file`→`{path,changed,size_diff,...}` / `append_file`→`{path,bytes_written,new_size}` / `rename_file`→`{from,to,renamed,...}` / `search_files`→`{path,pattern,results,total,...}` / `file_info`→`{path,exists,size,lines,...}` / `bind_directory`→`{id,path,stack,bound}` / `advance_task`→`{...}` ——全部走裸 JSON。**修复方向**:统一 `formatToolResult` 的 switch 覆盖全部工具产出人类可读摘要(复用 `toolResultSummary` 已有 i18n key + 补缺失 key 如 `commandOk`/`commandFailed`/`patchedFile`/`appendedTo`/`renamed`/`foundN` 等),同时补 `toolResultSummary` 缺失 case(`update_task`/`delete_task`/`delete_file`/`run_command`/`patch_file`/`append_file`/`rename_file`/`search_files`/`file_info`/`bind_directory`/`advance_task`)。—— `src/components/ToolCard.vue`(`formatToolResult` L270 + `toolResultSummary` L476) + `src/i18n/{zh-CN,en}/aiTool.ts`(补 key)
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a逐条续发) UX-260616-05 [P1] — **打断按钮只停当前回复 + 队列保留续发（不清队列）**。用户需求：发送队列有消息时，打断按钮应只打断当前回复，并把队列中的多条消息一并发送给后续对话。现状：`stopChat`(useAiSend.ts:365-370) L368 `state.queue = []` **清空队列** → 用户生成中排队的消息随打断被**静默丢弃**。技术底座已就绪（已核后端）：`stopChat`→`aiApi.stopChat`→后端 `stop_flag.load`(agentic.rs:178)→收尾 `save_conversation`(:185 已生成文本入库)+emit **`AiCompleted`**(agentic.rs:190，注释明确「保证前端收事件时后端已可接下一条，发送队列续发不被拒」)→前端 `ai-drain-queue`→`drainQueue`(useAiSend.ts:228) shift 队首续发。**最小修复（确定）**：删 stopChat L368 `state.queue=[]`，打断后 AiCompleted 自然触发 drainQueue 续发队首；当前未完成回复已入库(:185)保留为截断回复，无需额外处理。**决策点（待定）——「队列多条一并发送」语义**：**(a) 逐条续发**（删清队列即可，零改 drainQueue，AiCompleted 链式触发每条，保各条上下文独立）vs **(b) 合并成一条消息**（队列多条 text 拼接送，需改 drainQueue 为 bulkDrain 或合并 text 调一次 sendMessage）。**倾向 (a)**（逐条保上下文独立+零改链路；"一并"理解为「都送出不丢弃」非字面合并），**待用户确认**。— src/composables/ai/useAiSend.ts(stopChat:365 + drainQueue:228) + 后端 agentic.rs(:178/:190 stop 收尾 emit AiCompleted 已就绪)。
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策只编text) UX-260616-06 [P2] — **队列消息支持编辑**。用户需求：队列中的消息支持编辑。现状：队列 UI(AiChat.vue:511-516) 每项只有 × 删除(cancelQueued:353)，无编辑入口；队列项结构 `{ text, skill?, enqueuedAt }`(stores/ai.ts:70)。**改动（纯前端低风险）**：队列项加「编辑」按钮 → 点击切 inline input（单行 textarea）→ 回车/失焦写回 `state.queue[idx].text`、ESC 取消；新增 `editQueued(index, newText)`(useAiSend.ts) splice 写回 + store 导出 + composable return。**决策点（待定）**：skill 是否可编辑（**倾向只编 text**——skill 来自发送时技能联想，编辑态不暴露 skill 改，简化交互）。i18n 补 edit/save/cancel key。— src/components/AiChat.vue(:511-516 队列项 UI + .ai-queue-item CSS:2856) + src/composables/ai/useAiSend.ts(editQueued 新增) + stores/ai.ts + i18n。
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a插队,边界:stop_flag复位降级可接受) UX-260616-07 [P2/依赖 UX-05] — **队列消息支持立即发送**。用户需求：队列中的消息支持立即发送。现状：队列项只能 × 删除或等 drainQueue 串行轮到，无插队。**决策点（待定）——「立即发送」语义**：**(a) 插队=打断当前+立即发这条**（splice 出该项→stopChat→sendMessage 该条；复用 UX-05 stop→AiCompleted→drain 链路，当前未完成回复走 stop 截断保留）vs **(b) 移到队首等当前完成优先**（splice 出→unshift 队首，drainQueue 下一轮先发它，不打断当前）。**倾向 (a)**（"立即"字面=马上发，且复用 UX-05 链路；但须注意 splice 后调 stopChat 已不清队列(UX-05)，避免已发送项重复入队）。**待 UX-05 打断语义定后同批设计**（共享 stop+drain 链路）。— src/components/AiChat.vue(:511-516 队列项加「立即发送」按钮) + src/composables/ai/useAiSend.ts(sendQueuedNow 新增)。
- [ ] F-260616-15 [P1] — **AI Chat 上下文管理增强：会话分段 + 手动压缩 + 智能裁剪**。三个子需求，详见 [F-15-上下文管理增强设计-2026-06-16.md](./02-架构设计/F-15-上下文管理增强设计-2026-06-16.md)。**阶段1 基础部分 ✅(波3·8a142c2 is_active 白名单+push token 修正)**；阶段1 剩余 ✅(波5·63bff8b compress_prompt四段式+compress_via_llm+context 9辅助方法,幂等/单向不可逆/纯函数/零行为变化,df-ai 107pass)；阶段2 手动功能 ✅(波6·4194842+主代核查,cargo0err+vue-tsc0+devflow79pass,2 IPC ai_chat_clear_context/compress_context+3事件 AiContextCleared/AiCompressing/AiCompressed+前端2按钮+status分组渲染+useAiContext composable,clear标archived_segment不删DB/compress LLM失败不阻塞);阶段3 自动(loop压缩+事件)待波7。

  **核心问题**：① `ai_chat_clear` 全删 DB 消息不可回溯 ② `build_for_request` 超预算直接丢弃旧消息零保留(`agentic.rs:226` `_trimmed` 被忽略) ③ 无压缩能力。

  **统一方案**：`ChatMessage.status` 扩展两个值 `archived_segment`(分段标记)/`compressed`(压缩标记)，`is_active()` 改正面白名单 `matches!(status, None | Some("active"))`（零行为变化，新状态自动不 active），`sanitize_messages` step 0 已是唯一发送过滤点改 is_active 即隔离旧段/压缩段。`push()`(context.rs:174) 不计 `!is_active()` 消息 token（当前全量计入致 restore 后 token 虚高误判超预算）。

  **① 会话分段**：IPC `ai_chat_clear_context`：当前 active 消息标 `archived_segment` + 扣除 history_tokens + 落库。不插分隔线 system（前端按 status 渲染折叠分隔条）。三元组原子标记（复用 `build_eviction_units` 分组）。

  **② 手动压缩**：IPC `ai_chat_compress_context`：取 active 消息 → `compress_prompt` 四段式(意图/决策/文件/约束) → `provider.complete()` 非流式调用(复用 `title.rs:111` `generate_title_via_llm` 模式 + `build_provider_for`) → 原始消息标 `compressed` + 插入摘要 system。压缩单向不可逆，DB 原始消息保留。

  **③ 智能裁剪（自动压缩）**：agentic loop 循环体顶部 `build_for_request` 前检测：`history_tokens > budget×0.6` 且有可压缩消息 → `compress_old_messages`（保护区外消息 LLM 摘要 → 标 compressed → 插摘要 system）→ emit `AiCompressing`/`AiCompressed`。LLM 失败降级原有裁剪（不阻塞 loop）。`compress_old_messages` 是手动/自动共用的公共函数。幂等：已 compressed 不二次压缩，`is_compressing()` 标志防重入。

  **实施顺序**：阶段1 基础(is_active 白名单+push token 修正+compress_prompt+compress_via_llm+辅助方法) → 阶段2 手动功能(2 IPC+前端按钮+渲染+i18n) → 阶段3 自动(loop 压缩+事件+前端 loading)。阶段1-2 完成用户已有完整能力。

  **涉及文件**：`df-ai-core/provider.rs`(is_active 白名单) + `df-ai/context.rs`(push token 修正+辅助方法) + `commands/ai/prompt.rs`(compress_prompt) + `commands/ai/title.rs 或新文件`(compress_via_llm) + `commands/ai/commands.rs`(2 IPC) + `commands/ai/agentic.rs`(loop 自动压缩) + `commands/ai/mod.rs`(2 事件) + `lib.rs` + `api/ai.ts` + `useAiPanel.ts` + `useAiConversations.ts:79`(过滤扩展) + `useAiEvents.ts` + `AiChat.vue` + `i18n/{zh-CN,en}/aiChat.ts`

- [x] ✅(batch60·2026-06-16·workflow whae812z5+主代核查,cargo 0err) F-260616-13 [P2] — **build_for_request 持锁重活 + system_prompt token 每轮重估**。性能分析批次发现：每轮 `stream_llm` 前 `session_arc.lock()`（`agentic.rs:251`）持锁期间做 `TokenEstimator::estimate_text(system_prompt)`（L252）+ `build_for_request`（L253 history clone + 裁剪）。system_prompt loop 外固定传入，**每轮重估其 token 是浪费**（可缓存）；build_for_request 持锁做 history clone 是重活，消息多时（200 cap）锁持有期长。**方向**：①system_prompt token loop 外算一次缓存 ②build_for_request 先 clone messages 释放锁再裁剪。低收益优化。— agentic.rs:251-253。

- [x] ✅(主代核验·2026-06-16) F-260616-14 [P2/核验] — **max_tokens 8192 截断 + 前端批量审批核验**。核验结论：①`max_tokens=8192`（实际 `agentic.rs:270`，行号漂移）≈6-8k 字日常够用，长回复截断由 batch59 MidStream 保文兜底不丢文，**可配低优先非阻塞**（登记可选优化）②前端批量审批 **AE-2025-01 已完整闭环**（ToolCardList.vue:4-11 `ai-batch-approve` 栏 + AiChat.vue:444 `@batch-approve`→`store.batchApprove` + useAiSend.ts:338 遍历 pendingApprovals 逐个 approveToolCall + i18n approveAll/rejectAll），满足「一轮多 Med/High 全 insert pending→一次批量批全部」③audit.rs:496 `process_tool_calls` 一轮多工具 Med/High 进审批门控 insert pending + Low 并行执行链路通。无阻塞 bug，销账。— agentic.rs:270 + ToolCardList.vue + audit.rs:496。

- [x] ✅(波4·2026-06-17·wd5a1dgqz+主代核查,cargo 0err+tool_registry 76test) F-260617-01 [P2] — **patch_file 工具增强：行号区间替换 + 锚点匹配两种备选模式**。当前 patch_file 仅支持 `old_text` 精确匹配，对超长行（todo.md 单行 1000-2000 字符）+ 含大量反引号/引号/中文标点的场景，匹配极易失败（JSON 转义错位一个字符就全盘失败）。新增两种可选模式作为备选：

  **方案 A — 行号区间替换**：新增参数 `replace_lines: { start: usize, end: usize }`（1-based，含首尾行），直接按行号区间删除旧内容 + 插入 `new_text`。不需要 `old_text`。配合 `expected_hash` 乐观锁防并发修改后行号漂移。**适用场景**：超长行文件、已通过 `read_file` 确认行号的精确替换。

  **方案 B — 锚点匹配**：新增参数 `anchor: { start: string, end: string }`，`start`/`end` 是首尾行的子串标记。工具内部搜索 `start` 所在行号 → 搜索其后第一个 `end` 所在行号 → 转为行号区间替换。不需要完整原文。**适用场景**：只知道替换块的首尾特征文本、不想粘贴整段 `old_text` 省 token。

  **三种模式互斥**：`old_text`（现有精确匹配）/ `replace_lines`（行号区间）/ `anchor`（锚点）三选一，同时传多个报错。三种模式都需要 `path` + `new_text`。`expected_hash` 可选，三种模式通用。

  **实现要点**：①方案 A/B 共用底层"按行号区间替换"逻辑（读全文 → 按行切分 → splice 替换 → 写回）②方案 B 锚点搜索用 `lines[].contains(start)` 子串匹配（大小写敏感，与 search_files 一致），找不到返 Err ③方案 A 行号越界返 Err ④三种模式都走现有的人工审批（Medium 风险）⑤`expected_hash` 在写回前校验文件未变。

  **涉及文件**：`src-tauri/src/tools/` 或 patch_file handler 所在位置（tool_registry.rs / AI 工具层）+ DevFlow 自身的 AI 工具定义（如果 patch_file 是 DevFlow 内置工具）。需确认 patch_file 当前实现位置。

### 🔧 任务模块全栈核对（2026-06-16 · Explore 代理并行取证）

> 来源：[任务模块问题分析-2026-06-16.md](./05-代码审查/任务模块问题分析-2026-06-16.md)。原清单 18 项核对：真 bug 7 项 + #8 路由漏注册去重(B-260616-09 已存在)、增强 5 项、假/部分假 3 项、去重 3 项。详见文档速览矩阵。

- [x] ✅(第㉗批 2026-06-16·workflow) B-260616-12 [P0] — **前后端状态枚举分裂（7 vs 5）**。后端 TaskStatus 7 种(types.rs:131-146) vs 前端常量/i18n/样式 5 种(project.ts:56-62 Git 工作流导向)，types.ts:88 注释随后端 7 种。后端写 in_review/testing/blocked → 前端查不到回退显示 key + status-todo 样式。**需人定**:前端对齐后端 7 种纯状态机，还是保留 Git 工作流 5 种做映射。— src/constants/project.ts + i18n + types.ts
- [x] ✅(第㉗批 2026-06-16·workflow) B-260616-13 [P0] — **delete_task 硬删除无恢复**。task.rs:113 物理删，TaskRecord(models.rs:53-66)无 deleted_at；对比 ProjectRecord 有软删除。误删永久丢失 + branches.task_id 悬空。**需人定**:任务是否要软删除(对标 projects)。— src-tauri/src/commands/task.rs + df-storage/models.rs
- [x] ✅(第㉔批 2026-06-16) B-260616-14 [P1] — **DDL priority 默认值矛盾**。migrations.rs:305 tasks 表 priority DEFAULT 1→2(对齐 task.rs default_priority()=2),加注释。**开发期改 V1_SQL**(项目 schema_version 守卫,V1 仅新 DB 跑,旧 DB 不重跑;SQLite 不支持 ALTER COLUMN 改 DEFAULT 需重建表,故直接改原 migration 最小改动,符项目 V4/V5/V6 等幂等补列惯例)。create_task 走 serde default 显式传 priority=2,无运行时行为变化,消文档/契约矛盾 + 为未来不显式传值的 SQL insert 路径提供正确回退。— crates/df-storage/src/migrations.rs
- [x] ✅(第㉔批 2026-06-16) B-260616-15 [P1] — **update_task priority 无值域校验**。task.rs:105-115 加 priority 校验(parse::<i32>() ∈ 0..=3,非法返 Err format!,与 status is_valid 同构)。范围确认:0=critical/1=high/2=medium/3=low(Tasks.vue select/constants PRIORITY_LABELS/types.ts 注释/default_priority()=2 全对齐)。**行为变更(安全修复非回归)**:非法 priority(999/abc/-1)现被前置拦截返 Err,前端 invoke 收 reject → B-17 catch 写 state.error。零合法调用方受影响(前端 select 仅产 0..=3,AI create 不经 update_task)。— src-tauri/src/commands/task.rs
- [x] ✅(第㉔批 2026-06-16) B-260616-16 [P1] — **allowed_columns 含不可变字段**。crud.rs:324-332 tasks 白名单移除 id/created_at(防篡改主键/伪造创建时间/跨项目移动)。**project_id 降级保留 + TODO**:通用 CRUD 层(db repo)只懂表/列语义不持有跨表业务约束(查 projects 表存在性)+ update_field 宏无 ProjectRepo 句柄,跨表校验待 commands/task.rs 层补(留 TODO B-260616-16 注释指向)。releases 表未动(只 tasks)。update_full/insert/get/list/query/delete 不经白名单不受影响;现有测试(update_field_rejects_cross_table_column/allows_tasks_status 用 title/status 保留)不破坏。**㉕ 收尾闭环**:commands/task.rs L116-124 补 project_id 跨表存在性校验(field=="project_id" → state.projects.get_by_id(&value) 查存在,不存在返 Err format!,跟随 status/priority 校验同构),B-16 完整闭环(crud 白名单移除不可变字段 + commands 跨表存在性校验)。设计备注(非 TODO):get_by_id 不过滤 deleted_at(软删项目仍判存在,ProjectRecord 未暴露 deleted_at,符「查存在性」字面要求);create_task project_id 未校验(前端 create 下拉源可信,超 B-16「update 改 project_id」范围)。— crates/df-storage/src/crud.rs + src-tauri/src/commands/task.rs
- [x] ✅(第㉔批 2026-06-16) B-260616-17 [P1] — **updateTask store 无 try/catch**。tasks.ts:29-40 补 try/catch(成功才改 state findIndex 回填,失败写 state.error,与 load/create/delete 三兄弟逐字同构)。**主代理补 i18n 收尾**:agent 守文件锁留硬编码中文兜底+TODO,主代理补 i18n updateFailed key(zh '更新任务失败'/en 'Failed to update task')+ tasks.ts L38 改 t('tasks.err.updateFailed') 删 TODO(消除硬编码中文)。store 内无 toast(state.error 由组件层 watch 消费,与三兄弟一致)。vue-tsc exit 0。— src/stores/project/tasks.ts + src/i18n/{zh-CN,en}/tasks.ts
- [x] ✅(第㉕批 2026-06-16) B-260616-18 [P1] — **TaskDetail 绕 store 直调 API**。**选监听方案**(不改数据源,改动最小:保留 taskApi.get/projectApi.list 直调,独立入口不依赖全局 store;store 的 df-data-changed 监听只刷 store.tasks 列表,受 B-260615-29 筛选契约约束不含当前 task 单体)。L98 import onBeforeUnmount+listen + L111 DfDataChangedPayload 类型 + L164 模块级 _unlistenDataChanged + L172 onMounted 内 listen('df-data-changed', entity∈{task,project}→load()) + L178 try/catch 防 listen reject 静默失效(对齐 CR-260615-21)+ L183-185 onBeforeUnmount 释放。复用 AiChat _unlistenToolSlow 生命周期模式。entity=project 也刷新(projectName 从 projects 解析,项目重命名需同步)。无 i18n 改动,不破坏渲染/路由参数 watch(taskId)。vue-tsc exit 0。— src/views/TaskDetail.vue

### ⏸️ 待决策（人定·非模型推断·2026-06-16）

> 任务执行与推进能力核对([任务执行与推进能力分析-2026-06-16.md](./05-代码审查/任务执行与推进能力分析-2026-06-16.md))暴露的架构决策点。**定了才能实施推进链**，与模型能力无关。标注关联 todo。

- [x] **D-260616-01 状态枚举方向** ✅ **决策:① 前端对齐后端 7 态**（2026-06-16 用户拍板）。后端 7 态不动，前端 constants/i18n/Tasks 筛选器/Dashboard 扩展至 7 态。理由:推进链阶段 2/3 需 InReview(AI 自审)/Testing(测试闸门)/Blocked(退回)三态，删则自断推进链。【关联 B-260616-12】
- [x] **D-260616-02 任务软删除** ✅ **决策:① 加软删除对标 projects（UI 缓做）**（2026-06-16 用户拍板）。先加 `deleted_at` 列 + `list_active` 过滤 + `restore` API，恢复 UI 后续按需补。抄 projects 全套模板（V11 迁移 + ProjectRepo 方法）。【关联 B-260616-13】
- [x] **D-260616-03 advance_task 架构落点** ✅ **决策:在 df-nodes 中实现**（2026-06-16 用户拍板）。推进链业务逻辑（状态机/advance_task/闸门节点）走 df-nodes 的 Node trait 扩展，不新建 crate、不塞 task.rs。完全对齐 D3 决策"业务逻辑在 df-nodes 实现"。AiNode 自审 / HumanNode 核对等闸门逻辑在 df-nodes 节点内实现，状态机+advance_task 编排在 df-nodes 层完成。
- [x] **D-260616-04 推进路径取舍** ✅ **决策:阶段1先行**（2026-06-16 用户拍板）。先做阶段1骨架（advance_task 状态机 + status 收口 + 手动推进闭环），不等工作流联动/AI 执行。基础 + P0 安全优先，后续阶段按需迭代叠加。

### 🗺️ 任务推进链实施路径（2026-06-16 规划·供其他会话读取）

> 详见 [任务推进链实施路径-2026-06-16.md](./02-架构设计/任务推进链实施路径-2026-06-16.md)。推进能力实现度 0%。**阶段1已解除阻塞（D-01/D-03/D-04 三决策已定 2026-06-16），可启动 F-01~05**。核对纠正：AI 有 `update_task`/`run_command` 工具，无 `run_workflow`/`advance_task`。

- [x] ✅(第㉘批 2026-06-16·workflow) F-260616-01~05 阶段1 推进骨架 — F-01状态机(can_transition 7态矩阵+is_regression)/F-02 advance_task(IPC+Node trait+CAS原子写 crud.rs advance_status_atomic `WHERE id=? AND status=expected`防TOCTOU)/F-04 review_rounds(V15迁移幂等+退回转换+1原子)/F-05前端推进按钮(ADVANCE_MAP+taskApi.advance) 完成。**F-03收口延后阶段3**:AI工具update_task依赖status改写,裸移除白名单→工具描述谎称能改但报错(行为不一致),收口须阶段3三件同落地。循环依赖核实:df-storage Cargo.toml不含df-nodes,加反向dep无循环。cargo check 0+df-nodes 46 test/df-storage 11 test pass+vue-tsc 0。
- [x] ✅(batch32-35·2026-06-16·全部子项落地) F-260616-06 阶段2 工作流联动（task_id+回调+DAG模板）— **全部落地**(④-1/②-1/②-5 batch32 + ②-2/②-3/②-4/②-6 batch33 + ①-1/①-3/B-41 batch35),实施路径见 [推进链阶段2实施路径-2026-06-16.md](./02-架构设计/推进链阶段2实施路径-2026-06-16.md)：
  - [x] ✅(batch32·2026-06-16·workflow wn4gudnz8) **④-1 [架构前置·阻塞]** DagExecutor config 下沉语义修复 — `executor.rs:99-107` NodeContext.config 用 initial_config 覆盖 NodeDef.config(节点级配置被忽略,TaskAdvanceNode 读不到 ctx.config.task_id)。修法:`NodeContext.config = deep_merge(node_def_config, initial_config)` 节点级覆盖全局级。文件锁:`crates/df-workflow/src/{dag.rs,executor.rs,registry.rs}` + `src-tauri/src/commands/workflow.rs`
  - [x] ✅(batch32·2026-06-16·workflow) **②-1** build_registry 注入 Arc<Database> + 注册 TaskAdvanceNode — `build_registry(db)` 改签名 + move 闭包 + init 调用传 db.clone()。文件锁:`src-tauri/src/state.rs`
  - [x] ✅(batch33·2026-06-16·workflow w98anf4y5) **②-2** run_workflow IPC 加 task_id + target_status 参数 — 签名扩展 + WorkflowRecord.task_id 填入 + spawn move 捕获。文件锁:`src-tauri/src/commands/workflow.rs` + `src/api/workflow.ts` + `src/stores/project/workflow.ts`
  - [x] ✅(batch33·2026-06-16·workflow) **②-3** 完成回调 WorkflowCompleted→advance_task — spawn 闭包内 executor.run Ok 后,task_id+target_status 都 Some 时调 advance_task_atomic,失败降级 warn 不回滚。文件锁:`src-tauri/src/commands/workflow.rs`
  - [x] ✅(batch33·2026-06-16·workflow) **②-4** 失败回调(退回语义) — 工作流 failed 时按 target_status 推算退回态调 advance。文件锁:`src-tauri/src/commands/workflow.rs`
  - [x] ✅(batch32·2026-06-16·workflow) **②-5** HumanNode reject 语义化 — options 含 reject/block 时返 Err(非 Ok),使工作流 failed 触发退回。文件锁:`crates/df-nodes/src/human_node.rs`
  - [x] ✅(batch33·2026-06-16·workflow) **②-6** DAG 模板(task_workflow_templates) — df-nodes 新模块导出 template_for(target_status)->DagDef,5 前向边+退回。文件锁:`crates/df-nodes/src/task_workflow_templates.rs`(新)
  - [x] ✅(batch35·2026-06-16·workflow w17duic3f) **①-1** 前端 TaskDetail 工作流推进按钮 — 与手动 advance 并存,调 workflowApi.run+监听进度。**含 B-41 进度内嵌**:onEvent 按 exec_id 过滤 dispatch node_started/completed/workflow_completed/failed + 独立 wfAdvancing loading 与手动 advancing 互含 disabled。**后端补丁**:workflow.rs run_workflow 空dag选模板(dag.nodes 非空→原 / 空+target→template_for / 空+无target→原路径,向后兼容)。文件锁:`src/views/TaskDetail.vue` + `src/api/workflow.ts` + `src/stores/project/workflow.ts` + `src-tauri/src/commands/workflow.rs`
  - [x] ✅(batch35·2026-06-16·workflow) **①-3** i18n 文案 — `src/i18n/{zh-CN,en}/taskDetail.ts`(taskDetail.* 命名空间,8 key 对称:workflowAdvanceTitle/Advancing/AdvanceFailed{msg}/StepRunning{node}/StepsProgress{done}{total}/CompletedHint/FailedHint)。注:todo 原路径 `locales/tasks.ts` 不准,实为 `i18n/taskDetail.ts`
  - **实施顺序**:④-1→②-1→②-5→②-2→②-3→②-4→②-6→①-1/①-3。**最小里程碑**(④-1+②-1+②-2+②-3):run_workflow 单 task_advance 节点 DAG 端到端推进 todo→in_progress
- [ ] F-260616-07 阶段3 AI 执行闭环 — **F-03 收口三件 ✅ 本批完成**(batch64), AiNode 自审闸门 ⏳ 待后续批
  - [x] ✅(batch64·2026-06-16·workflow wii1u1lnm) **F-03 收口三件** — ①advance_task 注册 AI 工具(tool_registry.rs:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic` 与 IPC `commands::task::advance_task:165` 同源) ②run_workflow 注册 AI 工具(:428,handler 架构约束无 AppHandle/State 报错引导走 IPC,ToolDefinition+审批文案 L1260-1261/1286-1287 注册让 LLM 可产出 tool_call) ③update_task handler guard 拒 status(L374 `field=="status"` bail,schema 通用 field/value 故 guard 拦非 schema 改)+ df-storage tasks 白名单移 status+review_rounds(crud.rs:331-347,advance_status_atomic CAS L848 独立路径不经白名单)。同步落地防 AI 工具行为不一致。**主代独立核查全过**:cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试(含新 `update_field_rejects_tasks_status`)。文件锁:tool_registry.rs + crud.rs + project_soft_delete.rs。**审查登记 CR-260616-41**(待审查.md 当前队列)
  - [x] ✅ AiNode 自审闸门(阶段3 核心,决策a) — **阶段1迁移+方案✅(539b5ed+25409e5) + 阶段2②③④⑤✅(c10adaf+741b0b9) + P0 secret下沉+注入链✅(766f235+84a6fc4)**。⑥联调代码层解锁(待实测 tauri dev)。cargo df-nodes 79 passed + workspace EXIT 0 + vue-tsc EXIT 0
    - [x] ✅(539b5ed) ①df-storage: TaskRecord 加 `output_json: Option<String>` + V17 迁移(幂等补列) + crud 白名单+SELECT/INSERT/UPDATE 全链路
    - [x] ✅(c10adaf) ②df-nodes ai_node.rs: AiNode 持 db(Arc<Database>)+execute 后 config[task_id] 存在则 update_field 落 output_json
    - [x] ✅(c10adaf) ③df-nodes ai_node.rs: AiSelfReviewNode 独立节点(prompt 四维度+parse_review_json 兜底 verdict=unknown+写回 review 子字段)+ state.rs 注册 + testing 模板 ai→ai_self_review
    - [x] ✅(84a6fc4) ④df-nodes ai_node.rs: resolve_provider 注入链(provider_id 优先/老明文兼容 warn/空兜底 is_default)+ execute 经 df_storage::secret 解析 api_key 不进 config(FR-S1)+ schema required [provider_id]。run_workflow 注入 task_id 到 NodeContext(④-1 deep_merge 就绪),模板 config 空走 AiNode 兜底
    - [x] ✅(c10adaf) human_review: 经 DAG inputs["ai_self_review"].data 透传 verdict/summary(HumanNode 零改动,方案④更优路径)
    - [x] ✅(741b0b9) ⑤前端: TaskDetail.vue 展示 output_json(parsedOutput+review 红绿标+产出 markdown)+ types.ts+i18n 双语
    - [ ] ⑥端到端联调 — **代码层完成**(P0 secret 下沉+注入链✅ 766f235+84a6fc4),实测类待用户(tauri dev 验证 testing 模板 ai_self_review→human_review 闭环)
  - [ ] run_workflow handler 注入 AppState(当前报错引导走 IPC) — 后续批扩展 build_ai_tool_registry 签名注入 AppState 句柄让 AI 直驱
- [x] ⏸️(待决策.md已决b暂缓·2026-06-16) F-260616-08 阶段4 Git 集成（kind+git闸门+worktree）
- [x] ✅ **CR-260616-01 代码审查完成** → 审查登记已迁 [待审查.md](./待审查.md)(职责分离:审查队列独立,不进 todo)。结论 🔴0 🟡6 ⚪4 质量优,8 维度全过。**待修项 CR-01-A~I 见下方推进区**。

  > **审查发现待修项(2026-06-16 · CR-260616-01)**:
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-A** [P1] TaskDetail.vue 补 `.status-testing`(橙)/`.status-blocked`(红 danger 边框)CSS;Tasks.vue 已有/Dashboard 不用此体系无需补
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-B** [P1] crud.rs advance_status_atomic 两分支 CAS SQL 加 `AND deleted_at IS NULL`,回收站任务 CAS 必败返 None(D-02 软删收口)
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-C** [P2] task_advance_node.rs:50 错误信息改 `ALL_STATES.join("/")` DRY(运行时文案不变)
  - [x] ✅(batch30·2026-06-16) **CR-01-D** [P2] task_advance_node.rs:62 同态拒绝(from==to)/非法转换(can_transition false)错误区分(现合并 `InvalidState`,前端无法分辨)— **②类留**(需改错误枚举判断)
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-E** [P2] src-tauri/src/state.rs:240 build_registry 补注释「TaskAdvanceNode 未注册(阶段2联动时加),勿删」
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-F** [P2] crud.rs 白名单注释补 review_rounds 专用路径(仅 advance_status_atomic,防误补破坏收口)
  - [x] ✅(batch30·2026-06-16) **CR-01-G** [P3] TaskDetail.vue:218 advance 失败错误走 i18n 分级(`e.toString()` 非空直显后端中文,en locale 也中文)— **②类留**(需 i18n 分级改造)
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-H** [P3] task_advance_node.rs 测试改名 `concurrent_cas_change_detected` → `advance_succeeds_after_prior_status_change`(反映实际测正常推进)
  - [x] ✅(cr01-fixes·wu4r8ejcp) **CR-01-I** [P3] i18n `taskDetail.advancing` 接文案(推进按钮 advancing 时显示「推进中...」,冗余 key 转实引用)
  >
  > **修复核查(主代理独立·2026-06-16)**:7 项真实落地(Read+git diff+cargo check --workspace exit 0.83s+vue-tsc 0 核实)。**注意:验证 agent CR-01-E 证据张冠李戴**(报 df-workflow/state.rs,实改 src-tauri/state.rs),改动正确但证据幻觉——验证 agent 可信度打折,主代理核查兜底。D/G ②类留。

  > **审查发现待修项(2026-06-16 · CR-08/10/13,均 med 非阻断,详见 [待审查.md](./待审查.md) 对应归档)**:
  - [x] ✅(batch50·主代理决策:接受现状降级 P2) **CR-10-A** [med] ai_regenerate 并发竞态窗口 → **统一并发模型**(锁内变更+锁外 spawn,ai_regenerate/ai_chat_send/ai_chat_force_send 同款),窗口有兜底(stop_flag 置位 loop 退出 + generating 拦截 send),med 非阻断。统一并发改造复杂(原子标记预占+3 入口),ROI 不明非优先,降级观察/架构改进候选
  - [x] ✅(batch50·主代理核查:无效) **CR-10-B** [med] commands.rs:56/59 save 与 emit 时序倒置 → **不成立**:ai_regenerate 当前代码 save L94 锁外 spawn L105 前(时序正确),无 emit(L56/59 是 pop 后错误处理),审查锚定行号漂移
  - [x] ✅(batch55·2026-06-16·workflow wem402ncv+主代核查) **CR-13-O1** [产品决策→已决b实施] regression_target("in_progress")→None 对齐状态机。workflow.rs:51 `in_progress => None`(替原 Some("todo"))+task_advance_node.rs 镜像同步+4 测试断言更新(含集成测改 None-skip 场景)。**主代核验**:git diff 2 文件 26+/24- 双处一致+注释对齐决策b理由+cargo test -p df-nodes 67/67 pass。— src-tauri/src/commands/workflow.rs + crates/df-nodes/src/task_advance_node.rs
  - [x] ✅(batch50·2026-06-16·workflow w6nqio4o5) **CR-08-O1** [med·非阻断] blocked 态推进按钮语义偏差 → 已修复(WF_EXCLUDED_FROM={'blocked'} + wfAdvanceAction computed 守卫,blocked 不显示工作流推进按钮,其他态不受影响,不动 ADVANCE_MAP)
  - [x] ✅(batch50·2026-06-16·workflow w6nqio4o5) **CR-15-1** [med] `crates/df-execute/src/shell.rs:60-79` 缺 `.kill_on_drop(true)` → 已修复(cmd.kill_on_drop(true) 三类型覆盖 + 注释配合 tokio::time::timeout 防孤儿,文案「进程已终止」名副其实)
  - [x] ✅(主代理验证:不成立·workspace cargo check exit 0) 🔴 **CR-23-1** [high·紧急·阻断构建] `crates/df-ai/src/context.rs:491` E0594 → **不成立**:审查代理跑 batch51 中间态 filter 版(EXIT 101),batch51 最终改 for 循环(EXIT 0,df-ai + workspace 双验证,代理自报属实)。context.rs:488 当前是 `for t in iter_mut()` 单层引用可写 status,无 filter 闭包。审查锚定滞后(跑 batch51 中间态)— CR-260616-23 归档
  - [x] ✅(主代理修·CSS :has item 左边线) **CR-22-1** [med·UX] `AiChat.vue` 已置顶非 hover 不可见 → 已修复(`.ai-conv-item:has(.ai-conv-item-act--pinned){ box-shadow: inset 2px 0 0 accent }` 左边线强调,不破坏 hover 浮出 toggle,原方案图钉常驻与 actions display:none 矛盾,改 :has item 左线最简)— CR-260616-22 归档
  - [x] ✅(CR-260616-35 复审 PASS·主线程预核验) **CR-34-1** [med] useAiPanel.ts:102 loadProviders 守卫 `if(!state.activeProvider)` 只查 null 不查有效性 — localStorage savedProvider 指向已删除 provider 时 activeProvider 残留无效 id → cycleProvider:1185 findIndex=-1 跳过 ps[0] + 发消息消费方用无效 id。修法:守卫加 `|| !list.some(p=>p.id===state.activeProvider)` 残留无效时回落 DB is_default — CR-260616-34 归档
  - [x] ✅(CR-260616-35 复审 PASS·agent cargo 6+20 passed) **CR-30-1** [med] agentic.rs:298 退避 `1<<retry_attempt` 纯指数无 jitter/无总预算/一律重试(含 MidStream),三处注释(agentic.rs:37/commands.rs:1106/state.rs:185)误称「对齐 retry.rs(1s→2s→4s+jitter)」。retry.rs:67-78 实有 ±20% jitter+30s 预算+4xx 分类(:102-104 Fatal 立即放弃)。修法:agentic 加 jitter+总预算+错误分类真对齐,或删注释改「独立简化策略」— CR-260616-30 归档
  - [x] ✅(CR-260616-35 复审 PASS·agent cargo 6+20 passed) **CR-30-2** [med] AiStreamRetry 死事件 + MidStream 重试文本混乱 — 后端 emit(agentic.rs:310)+types.ts:214 variant 有,但 useAiEvents.ts switch 无 case 穿透忽略;MidStream 失败重试时已 emit AiTextDelta 累积 currentText,重试新 delta 追加→文本重复。修法:useAiEvents 补 case+重试前清 currentText;或后端 MidStream 不重试只重试 Init(对齐决策a)— CR-260616-30 归档

### ⏸️ 待用户授权/确认（巡检悬挂项·2026-06-16）

> 巡检推进中浮现的、需用户拍板才能继续的操作授权/范围确认项(非架构方向决策,见上节 D-*)。巡检每轮不再重复问,看此处标注。

- [x] **DEC-260616-01 文件名 git mv 06-15→06-16** ❌ **否决**（2026-06-16 用户确认）：21 个 docs 文件名日期 `-2026-06-15.md` 均正确（06-15 创建），无需修改。原前提"今天误标 06-15"不成立。
- [x] **DEC-260616-02 docs 其他文件 06-15 正文核对** ❌ **否决**（2026-06-16 文件时间分析）：21 个 `-2026-06-15.md` 文件的 CreationTime 和 LastWriteTime 全部在 2026-06-15 00:22~17:08 之间，确认为 06-15 当天创建，正文中的 06-15 日期引用均正确，无需核对修改。
- [x] **DEC-260616-03 CR-04/CR-06 流式核心改造授权** ✅ **已授权并实施完成**（2026-06-16）：CR-04 splitBlocks→marked.lexer 已完成(batch57,见 CR-260615-04)；CR-06 loadMarkdown 重算已确认实施(CR-260615-06 ✅)。两项流式核心改造均已落地并通过 vue-tsc + cargo check 零错误验证。

### 🔴 anthropic_compat 多轮工具调用（2026-06-14 排查·会话卡死根因）

> 来源：本排查会话定位另一 Claude Code 会话（经 GLM anthropic 端点）「卡死后再也对话不了、一直返回同一 500」现象，顺带暴露 devflow 同构缺陷。**会话卡死机制**：畸形 tool_result 写入 append-only 历史 → 后续每轮把毒历史原样重发 → 每次触发同一 500 → 死循环（救援只能清历史/新会话）。GLM 端报 `[500]['ClaudeContentBlockToolResult' object has no attribute 'id']`。

- [x] B-260614-AC1 ✅ wave4(36d68dd) — **[P1]**（出站 tool_call_id None/空跳过+warn，绝不发 null） anthropic_compat tool_use_id None 发 null — `crates/df-ai/src/anthropic_compat.rs:297` `"tool_use_id": m.tool_call_id` 对 `Option<String>` 无校验；`serde_json::json!` 把 None 序列化为 `"tool_use_id": null`。上游（LLM 返回 tool_use 缺 id / ContextManager 丢字段）致 tool_call_id=None 时，devflow 发出畸形请求触发服务端 500。**修法**：None 时 skip 该 tool_result 块或填占位 id + `warn!`，绝不发 null。
- [x] B-260614-AC2 ✅ wave4(36d68dd) — **[P2·防御]**（入站 tool_use 缺 id 同步跳过/流式占位 tool_missing_{idx}+warn） tool_use id 解析无兜底 — `anthropic_compat.rs:167` LLM 返回 tool_use 块缺 `id` 时 draft.id 为空 → 后续 tool_result 带空 id → 回传 500。**修法**：id 缺失时跳过该 tool_use 或生成占位 + warn。
- [x] B-260614-AC3 ✅ wave(2026-06-16核查闭环,待commit) — ~~历史中毒无自愈~~ context.rs `sanitize_messages` 三档自愈(全闭合保留/全未闭合整删/部分闭合重写 tool_calls)+build_for_request 两分支必过 sanitize,5 单测覆盖;占位 ID 生成(anthropic_compat.rs)保留未动（⬆️ 06-14 升级：write_file 缺 path 这类 LLM 常见失误触发错误 tool_result，叠加 GLM 端 tool_result id bug → 永久卡死，用户可感硬伤「再也对话不了」）— `ContextManager` + `stream_llm`：畸形 assistant(tool_use)+tool_result 一旦入历史，stream_llm emit AiError 后历史不动；用户重发 → `build_for_request` 带毒 → 永久 500。**修法**：stream_llm 收服务端 500/格式错时，检测并剔除最后一轮未闭合 tool 配对，或提供「修复当前对话」操作。注：write_file path 校验本身已健壮（tool_registry.rs:411 友好报错），卡死在其下游。

### 🔴 aichat 审查报告待修项（来源:[aichat审查报告-2026-06-14.md](./02-架构设计/aichat审查报告-2026-06-14.md) 第八章）

> 2026-06-14 aichat 模块代码审查产出，原仅留 memory 指针未回流看板，今补入。去重：**S-02 审批可见性 ⊂ AR-3**（修 AR-3 卡片可读性直接缓解"看不到审批批什么"）；**B-05 detach = AR-M5 同类**（跨窗口 state 隔离，aichat 审查描述更深）。

**P0（用户可感硬伤）**
- [x] AR-1 ~~流式 Markdown 全量重解析~~ ✅ **退役**（ARC-260615-08 自研块级 memo 取代，2026-06-16）：splitBlocks 块级 memo O(末块)+rAF 节流 取代全量 marked.parse+sanitize，流式全程有格式不掉帧。详见 [流式渲染调研](./02-架构设计/aichat流式Markdown渲染调研-2026-06-15.md) §5（⬆️ 原：renderMd 缓存 key=完整文本，每 delta 全量 marked.parse+sanitize，长回复主线程阻塞掉帧，AiChat.vue:343-354）
- [x] AR-2 ~~审批态新建对话卡死~~ ✅ WF-F 完成（ai_conversation_create 加 generating 守卫，位于 clear 前，对齐 switch:433 写法）(commit 057a212)
- [x] AR-3 ✅ ~~审批卡片信息不足（删除等操作只返回数据 ID）~~（commit 36d68dd 完整修复）：后端 `audit.rs:45-127` build_approval_reason + resolve_project_label（9 工具 reason 拼项目名，fallback「(项目已不存在, id=)」:52）；前端 `ToolCard.vue:296-320` PROJECT_ID_TOOL_ARG 映射 + toolArgsEntries 特化 id/project_id 回显项目名。原两个剩余问题（①前端裸显 id ②fallback 裸 id）均已修。wave(2026-06-16,待commit)补 `toolDisplayName` CRUD case 7 项(delete/restore/purge/update/create_task/create_project)+i18n 10 key 中英对称。
- [x] AR-4 ~~create_project 双审双 API~~ ✅ WF-F 完成（schema 加 path/stack + handler 有 path 时合并绑定 spawn_blocking 探测栈，消除二次 bind_directory；TODO 标注可抽公共绑定函数）(commit 057a212)

**P1**
- [x] AR-5 ~~审批态 stop 无兜底~~ ✅ Wave3 完成（stopChat 本地先复位 streaming + clearStreamWatchdog，防审批态看门狗已 clear + AiCompleted 竞态丢失卡死）(commit 9e2aeff)
- [x] AR-6 ✅ wave8(f82dd8b)已落地 Low 失败语义（audit.rs:312 emit AiToolCallCompleted 非 AiError，错误回填 tool_result 让 LLM 自处理；todo 原引 agentic.rs:195 过时，df-ai 重构后 stream 在 provider.rs/anthropic_compat.rs）（emit AiError 置 streaming=false 但 agentic loop 续跑，残留文本 flush 又"完成"，状态紊乱）— audit.rs + agentic.rs:195
- [x] AR-7 ~~clean 无 UI 入口~~ ✅ Wave3 完成（crud.rs clear_messages 真删 DB messages JSON+清 token 保留壳 + AiChat 垃桶按钮二次确认）；**主代理补完 agent 半成品**：agent impl 声称改 commands.rs ai_chat_clear 调 clear_messages，实际 diff 零改动（self_boundary_check 造假），审查 semantic_check 正确抓到 gap（commit 9e2aeff）

**P2**
- [x] ✅(batch39·2026-06-16·workflow ww4wb66br) AR-8 delta 节流+滚动 — **前端已存在**(onContentChange/isNearBottom 80px 阈值等效 userScrolledUp + showBackToBottom 回底按钮),**后端合批负 ROI 不改**(rAF 节流链 ARC-08 已吸收重 parse,加合批破 heartbeat 30s 语义)。B-53 snap 优化同批落地
- [x] AR-9 ~~friendlyError 硬编码中文~~ ✅ Wave3 完成（friendlyError 全走 i18n.global.t + zh/en 双语补 4 key；TS2589 用 as any 规避 vue-i18n 深度泛型）(commit 9e2aeff)
- [x] AR-10 ~~想法→灵感迁移残留~~ ✅ 已统一（13 文件批量：i18n zh-CN + 后端错误 + LLM 描述/提示词 + store toast；en 待定 Ideas/Idea、docs 注释低优先略）(commit 65c475b)
- [x] AR-11 ✅(定时走查第3轮,2026-06-16,dc27e79) — ~~数据变更联动刷新~~（推荐方案A 后端 emit + store 监听）— 跨层 — 详见 [审查第五章](./02-架构设计/aichat审查报告-2026-06-14.md) — **勘察完成(2026-06-16,wxflofhf2)**:feasible/risk 中/跨 8 文件(audit.rs/commands.rs/tool_registry.rs/stores/project.ts/useAiEvents.ts/Projects/Tasks/ProjectDetail.vue)。方案A 方向合理(emit df-data-changed+entity/action 分类+store listen)但勘察 implPlan 含伪代码错误(std::env::var/.match Rust 不存在=agent 幻觉)+碰 7 近期活跃文件含未提交 P0 改动的 commands.rs。**暂缓(原)**:等本批提交后主代理重设计 emit 点 — **第2轮定时走查复核(2026-06-16):后端已实施 ✅,前端半接通 🔴** — 后端 `emit_data_changed`(audit.rs:259-270）+ 自动执行路径（audit.rs:351）+ 审批路径（commands.rs:175）三处 emit 就位且逻辑正确；前端工作区（project.ts:31-55,109-110）定义 `startDataChangedListener`/`stopDataChangedListener` + export，**但 App.vue onMounted 未挂载 → listener 永不 attach → 数据变更后列表不刷新（AR-11 功能失效，后端 emit 成死事件）**。修：App.vue onMounted 调 `startDataChangedListener()` + onUnmounted stop（对齐 workflow listener 生命周期）。详见 [定时走查-2026-06-15-第2轮.md](./05-代码审查/定时走查-2026-06-15-第2轮.md)
  > **第3轮(2026-06-16):listener 已 attach ✅** — dc27e79 `App.vue:218` onMounted `await startDataChangedListener()` + :258 onUnmounted stop。AR-11 功能闭环（后端 emit df-data-changed + 前端 listen 按 entity 调 load）。详见 [定时走查-2026-06-15-第3轮.md](./05-代码审查/定时走查-2026-06-15-第3轮.md)

### 🔴 aichat API Key 401 排查（2026-06-16）

> 用户报对话失败「调用失败: API Key 无效或无权限」+ 重设 key 无效。**排查结论:代码链路全对(保存 keyring✓ / 读取 resolve_provider_secret✓ / 鉴权头 openai=Bearer·anthropic=x-api-key✓ / URL 智能拼接✓),401 来自服务端,非 devflow bug**。根因四选一(key 无效 / provider_type 不匹配 / base_url 错 / model 名错)。详见 [aichat-apikey-401排查-2026-06-15.md](./09-问题排查/aichat-apikey-401排查-2026-06-15.md)。

- [ ] S-260615-01 — **[待用户确认根因]** 用户跑直连测试(curl)区分 key/provider_type/base_url/model 哪项错,见详情文档「验证步骤」
- [x] B-260615-01 ✅ wave(2026-06-16,待commit) — ~~Err 分支加诊断~~ 提取纯函数 `fmt_diag`+`extract_error_diag`(name/status_or_class/timeout·connect 分类,14 单测);约束:LlmProvider trait 无 base_url/endpoint,仅 name() 近似 provider_type(provider_type + 实际请求 url + HTTP 状态码),当前只透传服务端文本看不出端点,401 难定位 — stream_recv.rs:107-113

### 🔴 流式响应中断误报排查（2026-06-16）

> 现象：AI 工具（write_file 等）执行成功（文件真写入 14.8KB），前端却弹「⚠ 响应中断（长时间无数据流）」误报，用户误以为失败重发。**根因（架构层，非偶发）**：工具执行后 agent loop 进入下一轮 LLM 请求，**等首 chunk 的静默期无心跳**——后端 `stream_recv.rs:113` idle timeout 用 `tokio::time::timeout(120s, stream.next())` 被动等 chunk，静默期不发任何事件；前端 `useAiEvents.ts:115` watchdog 仅靠事件 reset，130s 无事件 → `onStreamTimeout` 误报。触发条件：**write_file 工具本身毫秒级本地写，不超时**；「响应中断」发生在写入完成后、下一轮 LLM 回复到来前的静默期 > 130s。静默源待后端日志定：① LLM 续生成首 token 慢（GLM 处理含新写文档的长 context）② agent loop 异常退出漏发 AiCompleted/AiError ③ 事件丢失（broadcast Lagged）。**核心缺陷**：静默期无心跳，前端无法区分「LLM 在跑」vs「真断」，统一报中断。**额外**：前端 watchdog 130s < 后端总等待（connect_timeout 30s + idle 120s = 150s），可能前端先误报而后端连接仍健康，违背 watchdog「后端先报真错、前端仅兜底漏发」初衷。文件写入是工具独立副作用，与流是否健康无关。链路：`agentic.rs:139` stream_llm 返回 → `:202` process_tool_calls（快）→ loop 下轮 stream_llm 发新请求 → 静默等首 chunk。

- [x] B-260615-02 ✅(批1,2026-06-16) — **[P1 体验]** 流式静默期心跳（治本）。修法：`stream_recv.rs:113` `tokio::time::timeout(STREAM_IDLE_TIMEOUT, stream.next())` → `tokio::select!`，加 `heartbeat.tick()`（30s）分支 emit `AiChatEvent::AiHeartbeat { conversation_id }`；`AiChatEvent` 枚举（ai/mod.rs）+ `api/types.ts` 加 variant；前端 `useAiEvents.ts:115` reset 条件已自动覆盖新事件类型（零改动）。真断连时 120s 无 chunk 仍 emit AiError（现有逻辑保留）— stream_recv.rs:113 + src-tauri/src/commands/ai/mod.rs AiChatEvent + src/api/types.ts
- [x] B-260615-03 ✅(批4,2026-06-16) — **[P2 治标]** `onStreamTimeout` 文案区分：触发时检查最后是否有 `completed` 工具调用，有 →「工具已执行完成，后续回复中断，可点继续」；无 → 原「响应中断」— src/composables/ai/useAiStream.ts:21-33

### 🟠 流式可靠性链路隐患核对（2026-06-16）

> 系统性精读流式链路（stream_recv.rs / useAiSend.ts / agentic.rs / audit.rs / secret.rs）+ 1 Explore 代理广扫。**去重代理 10 条臆测/设计误判**（`_startPromise finally`伪竞态 / `stopChat`本地复位=AR-5 设计 / `sanitize`仅持久化视图=B-260614-AC3 设计 / `join_all`并行非阻塞 / `resolve_provider_secret` DB 优先=FR-S1 兼容老库设计 单测:114 锁定），确认 4 条真隐患（心跳见 B-260615-02 不重复）。**注**：B-260615-04 与 B-260615-02 同改 `stream.next()` → `select!`，可一次性合并实施。

- [x] B-260615-04 ✅(批1,2026-06-16) — **[P1]** stop 响应延迟最差 120s。`stream_recv.rs:107` `stop_flag.load()` 在 `tokio::time::timeout(120s, stream.next())` **之前**检查；用户点停止时若正阻塞在 `stream.next()` 等 chunk，要等 chunk 到或 120s idle timeout 才轮到下次 stop_flag 检查。修法：`stream.next()` 改 `tokio::select!` 加 stop_flag 轮询分支（或 `tokio::sync::Notify`），stop 即时打断 — stream_recv.rs:105-120
- [x] B-260615-05 ✅(批1,2026-06-16) — **[P1]** 流尽 + 空内容 + 无 finished 静默成功。`stream_recv.rs:178` `if !finished_received && (!full_text.is_empty() || !tool_calls_acc.is_empty())` 才报错；空内容无 finished 不报错返回 `Some(空)` → `agentic.rs:197` `!has_tool_calls` break → 正常 emit AiCompleted，**用户看空回复无错误提示**。修法：流尽未收 finished 一律判异常 emit AiError（不区分内容空否），不静默成功 — stream_recv.rs:177-186
- [x] B-260615-06 ✅(批4,2026-06-16) — **[P2]** sendMessage IPC 失败未清 watchdog。`useAiSend.ts:70-76` catch 回滚 streaming + 移除空气泡，但 :61 启动的 watchdog 未 `clearStreamWatchdog()`；130s 后 `onStreamTimeout` 触发 push 假错误消息（streaming 已 false 无状态危害，但错误气泡误导用户）。修法：catch 补 `clearStreamWatchdog()` — useAiSend.ts:70-76
- [x] B-260615-07 ✅(批4,2026-06-16) — **[P2]** approveToolCall 乐观置 running 无兜底。`useAiSend.ts:80-98` 审批 IPC 后等后端事件转 completed/rejected，后端异常不回则按钮永久 `running`；审批态 watchdog 已 clear（`useAiEvents.ts:162`）无心跳兜底。修法：approve 后重启 watchdog（`resetStreamWatchdog`）覆盖审批执行→续生成窗口，或加审批专用超时 — useAiSend.ts:80-98
- [x] B-260615-08 ✅(批1,2026-06-16) — **[P0 用户可感]** 审批通过后对话卡死（create_task 等审批工具通过、任务创建成功后对话不再响应）。链路：`commands.rs:147-189` 审批执行成功 + tool_result 回填 + emit AiToolCallCompleted/AiApprovalResult → `:187 try_continue_agent_loop`（agentic.rs:255）→ spawn 新 loop。**根因方向（静默 return，待后端日志精确）**：`try_continue` + `run_agentic_loop` 多个 return 点**不 emit 收尾事件** → 前端 streaming=true 永久卡：①`agentic.rs:261` `should_continue=false`（generating 被复位 / pending_approvals 非空）静默 return ②`:263-266` `get_active_provider Err(_) => return` 静默无事件 ③spawn 后 stream_llm 空回复静默成功（见 B-260615-05）/ LLM 不响应 120s idle→AiError（会报错非静默）。**watchdog 兜底延迟**：审批态 watchdog 被 clear（useAiEvents.ts:162 AiApprovalRequired），审批通过 AiApprovalResult reset 130s，静默 return 后最长 130s 才 `onStreamTimeout` 兜底（用户感「卡住」即此窗口）。**修法方向**：①try_continue 所有 return 点显式 emit AiError/AiCompleted 收尾（get_active_provider Err / should_continue false 均不静默）②前置依赖 B-260615-05（空回复不静默成功）③可配合 B-260615-02 心跳缩短感知延迟。— source:用户报障(06-15)，src-tauri/src/commands/ai/commands.rs:187 + src-tauri/src/commands/ai/agentic.rs:255-291 + src/composables/ai/useAiEvents.ts:161-162

### 🟣 全栈审查待修项（2026-06-14）

> 5 代理并行审查 Rust+Tauri+Vue 全栈（~25k 行）产出，详见 [全栈代码审查报告-2026-06-14.md](./05-代码审查/全栈代码审查报告-2026-06-14.md)。已去重：条件引擎/路径 canonicalize/localStorage 已在本看板他处记录；`do_promote` 误判已澄清（IPC 层真建项目，crate 留 TODO）。

**P0 — 安全**
- [x] FR-S1 ~~api_key 明文三连~~ ✅ **完全修**(2026-06-16 安全批次)：IPC list 返回 mask(首尾4+••••) + 编辑 apiKey 空→保留原DB值 + 前端 realm 不持明文；**DB 明文已迁移 keyring**(secret.rs:DB api_key 恒空 + OS keyring 存真实密钥 + 启动一次性迁移;消费点 resolve_provider_secret 兼容老库;cargo check ✓) — commands.rs ai_list_providers/ai_save_provider + Settings.vue — ⚠️删除闭环漏清(2026-06-16 审查发现):ai_delete_provider 未调 delete_provider_secret,keyring 残留,见 [CR-260615-01](#-近期改动审查待修项2026-06-15)
- [x] FR-S2 ~~read_file TOCTOU + write 无限制~~ ✅ read 单次 File::open 取 metadata+read 消 TOCTOU + write 加 1MB 上限(commit 5367f19)
- [x] FR-S3 ~~approve decision 无校验~~ ✅ 加 decision 非空校验(防 "" 透传;HumanNode options 非空时还校验 ∈ options)(commit 698a874)
- [x] FR-S7 ✅ 已修(2026-06-14 安全批次):write_file 加 .bak 备份 + tmp→rename 原子写 + 缩减>90% tracing::warn! + 返回 old_size;cargo check ✓。原:write_file 覆盖已有非空文件无确认/备份（**2026-06-14 实测事故**：会话3473fcb7 AI 误把 write_file 当 edit 用，只传头部3行把 PROGRESS.md 762行/72KB 覆盖成248字节；FR-S2 的1MB上限防不了此场景）— tool_registry.rs write_file handler — 修法：覆盖非空文件前自动备份 .bak 或检测目标存在强制走 edit_file；写入后返回新旧大小对比，差异巨大时 warn
- [x] FR-S8 ✅ 部分修(2026-06-14 安全批次):主体 ①② T-260614-04 resolve_workspace_path 双层校验已修,③ Windows Rust std Path::starts_with 已大小写不敏感(虚报),残余 list_dir_recursive entry.file_type() 替 metadata 防 symlink 跟随逃逸目标信息 + 不递归 symlink;cargo check ✓。原:**路径 sandbox 系统性逃逸**（2026-06-14 走查，审查报告 §10）：①validate_path 子串 `..` 检测对绝对路径无效 ②canonicalize 仅覆盖已存在路径（write_file 新建 + symlink 父目录漏）③Windows starts_with 大小写敏感坑 — tool_registry.rs:19-21,53-69 — 修法：统一 canonicalize（不存在取最长存在前缀）+ 大小写不敏感 prefix 比较 + parent 校验 + symlink 不跟随（file_type 替 metadata）

**P1 — 体验/竞态**
- [x] FR-R1 ~~switchConversation 无切换 token~~ ✅ 加 _latestSwitchId 丢弃过期响应(commit 698a874) + wave(2026-06-16,待commit)补第二 await(`pendingToolCalls`)后二次比对 `useAiConversations.ts:108`,防 A→B 快切用 A 的 pending 覆写 B
- [x] FR-R2 — **[降级存疑]** 看门狗主/分离窗口互踩 — 审批态已 clearStreamWatchdog(useAiEvents:143)+分离窗口独立 realm 不共享 state，"互踩"前提不成立，评估维持
- [x] FR-R3 ~~liveEvents 无限增长~~ ✅ push 后限长 200 条(commit 8dbe3d2)
- [x] FR-C1 ~~formattedEvents 时间漂移~~ ✅ 事件入数组固 _ts(project.ts push + ProjectDetail 用 _ts)(commit cf18678)
- [x] FR-C2 ~~net_sentiment 矛盾~~ ✅ 统一三档(模板>0/<0/===0 + sentimentClass 同源 + i18n neutral)(commit cf18678)

**P2 — 其余（见审查报告 §2-6）**
- [x] FR-S4 ✅ wave4(36d68dd) SKILL.md 注入加头尾成对标注（仅供 AI 参考/非用户消息/技能说明结束）
- [x] FR-S5 — **[降级文档]** ai_approve 无对话归属校验属实，但 UI 隔离+restore 只载当前对话历史限制实际触发，本地单机低危，降级为文档说明（审批不严格按对话隔离，设计取舍）
- [x] FR-R4 ✅ wave4(36d68dd) complete() 加 60s 单请求 timeout + is_timeout 中文错误（不影响 stream）
- [x] FR-C3 ~~Settings timer 泄漏~~ ✅ onUnmounted 清全部三个 timer(commit cf18678)
- [x] FR-C4 ~~MIGRATION_VERSION 死常量~~ ✅ 删(零代码引用,run() if 链自管版本)(commit cf18678)
- [x] FR-C5 ~~AiConversationDetail 缺 readonly~~ ✅ 加 readonly?: boolean(commit cf18678)
- [x] FR-D1/D2/D4/D5/R5 ✅ wave5/6 部分完成（4a95f6a/4b5f096）：dag O(V+E) 建 adjacency 索引 / search_vector 显式 14 列 / replace_tool_result_content 反向 rposition / useConfirm 抽 4 视图 / 前端 findToolCall 反向遍历
- [~] FR-P1~P6 剩余性能项 — ~~单连接 Mutex~~ ✅(batch29·2026-06-16 评估非瓶颈:Tauri 本地单用户+WAL 写本串行化,r2d2 池收益近零属过度优化,保持现状) + ~~migrations if 链~~ ✅(batch29·2026-06-16 if 链→step 数组循环,零行为变更,cargo test -p df-storage 11 集成+迁移路径全过) + ~~tool_registry truncate(50)+注释散落提常量~~ ✅第⑯批销账 06-15 抽 MAX_LIST_RESULTS 常量 4 处 — FR-D3 三子项全闭环,见报告 §5-6
- [x] FR-D6 ✅ ~~任务工具集不完整（缺 delete_task/update_task）~~（commit 36d68dd 补全）：tool_registry.rs 现有 create_task(:241) + update_task(:265) + delete_task(:287，硬删对齐 commands::task::delete_task，注释「清理孤儿任务时务必用本工具，不要误用 delete_project」) + list_tasks(:140) 四工具齐全。
- [x] FR-D7 ✅ wave4(36d68dd) 抽 `bind_dir_to_project(repo, id, path, stack_opt)`，create_project/bind_directory 共用，删原 :171 TODO；主代理核查补回 create_project 响应 stack 字段
- [x] FR-D8 ✅ wave4(36d68dd) create_idea schema 补 priority(可选 integer) + 魔法数字默认值注释（idea=1/task=2 与 IPC default_priority 对齐）

### 🔴 近期改动审查待修项（2026-06-16）

> 工作区 FR-S1/S7/S8 + 近 5 提交审查（主代理 + 2 子代理并行），详见 [近期改动代码审查-2026-06-15.md](./05-代码审查/近期改动代码审查-2026-06-15.md)。共 🔴1 🟡7 ⚪5。
> **注**：① 推翻 todo:74 FR-S1「完全修」——密钥迁移/读取/写入闭环全对，但**删除路径漏清 keyring**（secret.rs:44 有 delete_provider_secret，ai_delete_provider 未调用）。

**P1 — 安全闭环**
- [x] CR-260615-01 ✅ wave(2026-06-16,待commit) — ~~删 provider 漏清 keyring~~ commands.rs:394-399 DB 删后调 `delete_provider_secret` + warn 不阻断（keyring entry 永久残留，同用户进程可读；同 id 复用旧密钥复活）— commands.rs:390-401 — 修法：删 DB 后调 `delete_provider_secret`（失败仅 warn 不阻断，DB 已删则残留 keyring 无消费方）

**P2 — DRY/收口**
- [x] CR-260615-02 ✅ confirm composable (第⑩批 2026-06-16) — ~~AiChat.vue 第五份 confirm 未迁 useConfirm~~ 实为本地 confirm 状态机(confirmState+confirmDialog+answerConfirm)与 useConfirm 同构未复用,迁后复用 composable,行为零变化;AiChat.vue:371-374
- [~] CR-260615-03 ✅ wave(2026-06-16,待commit) — 低风险子项收口:**已做 3** = `.bak·.tmp-write 噪音过滤`(tool_registry.rs:535 加 `is_noise_file` 后缀过滤+`list_dir_recursive` 跳过,默认 skip_noise=true 已开)+`COLS 列数断言`(crud.rs:KNOWLEDGE_COLS/COL_COUNT/COLS_WITH_EMBEDDING 模块级常量+test 断言 14/15 列)+`R-PD-11 抽 find_path_conflict`(见下);**已解跳过 1** = dag·executor 邻接表非重复(executor 用 `adjacency_in` 前驱表,dag.topological_layers 用 `adjacency_out` 后继表方向不同;`Dag::predecessors/successors` 已无调用方属死码清理归 ARC);**未做留 todo** = projectNameById 改 find(无此函数,audit.rs:45 `resolve_project_label` 已用 repo.get_by_id O(1) 查询,反模式不存在);audit.rs Err 误报 / openai source 丢失 / ToolCard id 类型归一(行为变更或前端,不在本批)

**P2 — 块级 memo 实施走查（ARC-260615-08，2026-06-16）**
- [x] CR-260615-04 ✅(batch57·2026-06-16·workflow w4h5ygko1+主代核查,vue-tsc 0err+cargo check 0err) — ~~splitBlocks 手写正则切块改 `marked.lexer()`~~ 已完成改造,AiChat.vue splitBlocks 函数已从手写正则改为 marked.lexer() — AiChat.vue splitBlocks — 详见 [自研块级memo流式渲染审查-2026-06-15.md](./05-代码审查/自研块级memo流式渲染审查-2026-06-15.md) ①
- [x] CR-260615-05 ✅ parseBlock DRY (第⑩批 2026-06-16) — ~~parseBlock/parseBlockNoCache DRY~~ parseBlock 内部改调 parseBlockNoCache 去重(原 _purify.sanitize(_marked.parse()) 两处重复收敛为一处),行为零变化 — AiChat.vue
- [x] CR-260615-06 ✅(2026-06-16 核实已实施) — ~~loadMarkdown 就绪后流式不主动重算~~ AiChat.vue:1039-1043 已有 `watch(mdReady, (ready) => { if (ready && streaming && currentText) scheduleStreamParse(currentText) })`，mdReady 翻转时主动触发末块重算。原 todo 标注未实施系过时判断 — AiChat.vue loadMarkdown
- [x] CR-260615-07 ✅ blockCache LRU (第⑩批 2026-06-16) — ~~_blockCache LRU+escapeHtml 抽~~ _blockCache 超 limit 由整体 clear 改删最早一条(Map.keys().next().value LRU 语义,边界 > 改 >= 防超限)+escapeHtml+replace 两处重复抽 escapeFallback 函数(renderStreamingMd/renderMd 兜底均调),行为零变化 — AiChat.vue

### 🟦 架构审查待修项（2026-06-16）

> 纯架构层评估（边界/依赖/抽象/扩展性/状态管理），详见 [架构审查-2026-06-15.md](./05-代码审查/架构审查-2026-06-15.md)。共 🔴6 🟡6 ⚪4 + 亮点 6。与 06-14 三份报告去重（不重复 bug/性能 FR-*、aichat AR-*）。

**立即（零风险减法）**
- [x] ARC-260615-01 ✅ wave(2026-06-16,待commit) — ~~删 `stores/settings.ts` mock 死代码~~ grep 验零消费者 + 删文件 + index.ts 清导出 + 清 appSettings.ts:8 过时注释
- [x] ARC-260615-02 ✅(2026-06-16,待commit) — ~~`/decisions` 路由死链~~ 删 nav 项（App.vue secondaryNav）+ 删 `nav.decisions` i18n key（zh/en）。决策治理 = F-260614-08 长期项未实现，入口提前占位成死链；`dashboard.recentDecisions`（Dashboard.vue）不同命名空间保留

**短期（低成本）**
- [x] ✅(决策a·2026-06-16·转架构维护记录无代码) ARC-260615-03 — **重评降级（2026-06-16）**：df-execute **非空壳**——shell.rs `execute()` 已完整实现（cmd/sh 跨平台 + kill_on_drop + timeout + env）+ 被 tool_registry.rs `run_command` 复用（F-260615-05）。原 todo「76 行/1 函数 + TODO」基于 lib.rs 空判，过时。合并进 df-nodes 破坏职责分离（节点定义 vs 执行运行时）+ 动依赖树，**转架构维护决策，非清债**
- [x] ARC-260615-04 ✅ wave(2026-06-16,待commit) — ~~`stores/index.ts` 补 `export useAiStore`~~ barrel 补导出(3 处 view 直连可选迁移,非强制)

**中期（技术债）**
- [x] ✅(batch29·2026-06-16) ARC-260615-05 — ~~stores/project.ts 上帝 store 拆分（四领域+越层 invoke）~~ 核验越层 invoke 0 命中(已下沉 api/workflow.ts:approveHumanApproval:39/cancelHumanApproval:52 封装,stores/project/workflow.ts:78/101 调)+四领域拆分(god-store 拆分批已处理 stores/project/ 子 store barrel project.ts:122-123 委托)。vue-tsc exit 0
- [x] ✅(batch29·2026-06-16) ARC-260615-06 — ~~events↔stream 循环依赖~~ 提 aiShared.ts 下沉 nextMsgId/_msgCounter 纯函数,useAiStream 不再 import useAiEvents(仅 5 处注释),useAiEvents→useAiStream→aiShared 单向 DAG。vue-tsc exit 0
- [x] ✅(batch30·2026-06-16 评估维持现状) ARC-260615-06b [P3] — useAiEvents↔useAiSend 另一环(useAiEvents:19 import useAiSend/useAiSend:23 import useAiEvents),靠事件总线 ai-approval-clear-timers 部分缓解,非本批范围。遗留自 ARC-06 核验
- [ ] ARC-260615-07 — 其余见文档：df-core 改名 df-types（类型库非核心）/ src-tauri IPC 编排层抽取(df-app，5711 行成事实业务层) / AI agent loop 从 IPC 下沉 df-ai / 3 view 绕 store 调 api / 类型契约 ts-rs 代码生成 / AiSession 多会话(B 路线前置) / AppState 分组 / IPC 命名统一

**渲染优化（自研块级 memo 已实施，2026-06-16）**
- [x] ARC-260615-08 ✅(2026-06-16,待commit) — **[渲染优化·自研块级 memo 已实施]** 流式 Markdown 渲染——保留 marked+DOMPurify+.ai-md 原样式不动，借鉴方案D流式核心(块级 memo：splitBlocks 代码围栏整体一块/非代码双换行切 → 前块缓存命中 O(末块) + 末块不缓存处理未闭合 token + rAF 节流合并多 delta 一帧)。退役 AR-1 纯文本短路 — 关联 AR-1 — 详见 [aichat流式Markdown渲染调研-2026-06-15.md](./02-架构设计/aichat流式Markdown渲染调研-2026-06-15.md) 【决策转向：先试方案D(markstream-vue@1.0.1 接入+vue-tsc 通过)，但样式100%还原 .ai-md 成本高且脆(代码块 .code-block-container chrome / 暗色 --ms-* 变量 / prose .markstream-vue 作用域 4 处对接，随库升级漂移)，用户优先原样式，转自研块级 memo——零样式对接(marked 输出标准 HTML + .ai-md 全覆盖) + D 级流式性能(O末块)。已实施：splitBlocks/parseBlock(memo)/parseBlockNoCache(末块)/renderStreamingMd/scheduleStreamParse(rAF)/renderContent + watch currentText→scheduleStreamParse/streaming 翻转清 rAF + onBeforeUnmount 清 rAF + 回退 markstream 依赖恢复 marked/dompurify + vue-tsc exit 0；留后续：dev 运行时流式验证(掉帧/长回答边界/末块未闭合表现)】

### 🔵 全局代码 review 待修项（2026-06-16）

> 7 维度并行深入扫（DRY/架构/潜在bug/简洁性/安全/AI可靠/工作流引擎），详见 [全局代码review-2026-06-15.md](./05-代码审查/全局代码review-2026-06-15.md)。
> **P1 可执行 6 项 (R-P1-1~6) + P2 可执行 13 项 (R-P2-1~13) 已全闭环**（批1/2/3 主代理独立核查 cargo workspace exit 0 + 5 crate test 共 126 passed）。闭环明细见全局review-2026-06-15.md §推进状态(批1=4 项 R-P1-2/3/5/6 + 批2=2 项 R-P1-1/4 + 批3=13 项 R-P2-1~13 四域 A/B/C/D)。下为**需设计**待立项项（R-PD-3 条件引擎去重 T-260614-11）。

**P1 需设计（进设计文档）**
- [x] R-PD-1 ✅(批2,2026-06-16) — **[P1 security]** 编辑 provider 空 api_key 默默清 DB 明文致密钥永久丢失（未迁移态 keyring 空 + DB 非空时改 name/base_url 触发）— `commands.rs` ai_save_provider:329-355 + crud.rs INSERT OR REPLACE 全字段覆盖 — 修法：空 api_key 时确认 keyring 有/DB 有再清，keyring 无且 DB 非空先即时迁移补密钥 — source:全局review §P1需设计
- [x] R-PD-2 ✅(批2,2026-06-16) — **[P1 security]** run_workflow 经 ScriptNode 执行前端任意 shell（无白名单/无工作目录锚定/无审批，独立于 AI 工具 RiskLevel 链）— `workflow.rs`:36-44 + `script_node.rs`:34-42 + `shell.rs` — 修法三选一：①state.rs build_registry 不注册 'script' 掐断 ②限定工作目录在绑定项目 path 内 + 高危命令走 HumanNode 审批 ③ScriptNode 命令白名单 — source:全局review §P1需设计

**P2 需设计**
- [x] R-PD-4 ✅(P0批,2026-06-16,待commit) — ~~keyring 迁移失败阈值警告~~ MIGRATION_FAIL_THRESHOLD=3(sidecar .devflow-keyring-failcount 跨启动持久化 provider_id=count)+read/write/record/clear_migration_failcount 辅助+migrate_secrets_to_keyring 失败分支 record_migration_fail 达阈值升级 warn(明文滞留风险+3 条排查建议)+成功 clear 清零;不改兼容时序(仍保留明文下次重试) — secret.rs
- [x] R-PD-5 ✅(P0批,2026-06-16,待commit) — ~~approve IPC 校验 decision∈options~~ 加 options:Vec<String> 参数(前端从 HumanApprovalRequest 事件透传,IPC 无法访问节点 config)+校验 options 非空且 decision∉options→Err「审批决策非法」(规则同 HumanNode 下游兜底);✅闭环(wcvigw3z4):前端 stores/project.ts:259 补传 options(state.pendingApproval.options ?? [] 从 HumanApprovalRequest 事件 payload 取),触发后端 decision∈options 校验 — workflow.rs
- [x] ✅(第㉗批 2026-06-16·已 c7386a1) R-PD-6 — AiSession 单例：try_continue 读 active_conversation_id 竞态（靠 switch readonly 间接保护，脆弱耦合）— `agentic.rs`:255-291 从 pending_approvals 取 conversation_id 解耦
- [x] R-PD-7 ~~LlmProvider trait 抽象缺口：name() 语义错位 + supported_features/ProviderFeatures 死代码~~ ✅ 已修（删 ProviderFeatures + supported_features trait 方法 + 两 provider impl；补 endpoint() 默认方法供 401/网络错误诊断，两 provider override 返真实端点；name() 语义错位单独立项不改）
- [x] R-PD-8 — AiProviderRecord 整条穿透 IPC 边界（DB schema 演进直接破坏前端契约，models 字段 provider 返串/conversation 返数组不一致）— `commands.rs`:282-295 定义 ProviderDto/ConversationSummary 映射层（对抗分析否决 2026-06-16, 见 :537, 5个IPC手动对齐可接受）
- [x] R-PD-9 ✅ display_hint (第⑩批 2026-06-16) — ~~命令层臃肿~~ audit 工具名→文案映射作 display_hint 注册进 AiToolRegistry（消除双份）；agentic loop 下沉 df-ai 较大进 todo — agentic loop/tool_registry 717 行/audit reason 映射堆 commands/ai，无法被 df-nodes/AiNode 复用
- [x] ✅(主代理·2026-06-16) R-PD-10 — crud.rs 110 处 `.map_err(|e| Error::Storage(e.to_string()))` 统一 df-storage 局部 `storage_err` helper(原 todo「85处 e.to_string」过时,实际 crud.rs 110 处 Error::Storage 包装;commands 层 err_str 早已有 mod.rs:23)。cargo check -p df-storage exit 0
- [x] R-PD-11 ✅ wave(2026-06-16,待commit) — ~~目录防重复绑定逻辑两处重复~~ 抽 `ProjectRepo::find_path_conflict`(`df-storage/crud.rs:575`,接收已规范化 target+exclude_id,内含 list_active+排除+规范化比较);两处 use 替换:`project.rs::find_binding_conflict` 委托(去 inline find)+`tool_registry.rs::bind_dir_to_project` 委托(去 for 循环);路径规范化复用 `df_project::scan::normalize_path`(df-storage 不依赖 df-project,故本 crate 镜像同算法 `normalize_stored_path` 比较存库路径,已注明须同步)
- [x] R-PD-12 ✅ 假能力删 (第⑩批 2026-06-16) — ~~run_workflow AI 工具 no-op 桩~~ 删假能力(prompt/audit/ToolCard 当真实能力宣传，LLM 调用走审批拿空结果，体验断裂) — `tool_registry.rs`:383-390 + `prompt.rs`:57 + `audit.rs`:120-123 + ToolCard.vue:367 （与 R-PD-2 协同）
- [x] R-PD-13 ✅(P0批,2026-06-16,待commit) — ~~Lagged 静默丢事件~~ 最小兜底:单行 warn 升级带 execution_id+lagged 字段结构化 warn+注释(broadcast 不暴露被丢事件类型/关键终态事件丢失致 finished 不触发循环不 break 前端永久收不到结束/依赖 DB 轮询兜底)+三后续方向(提升广播容量/持久化队列重发/forward watchdog 超时);重发复杂度超本 todo 范围 — workflow.rs
- [x] R-PD-14 ~~df-ideas promotion IdeaPromoter/PromotionPolicy/try_promote 死代码 + do_promote 空壳 TODO~~ ✅ 已修（删 IdeaPromoter/PromotionPolicy/try_promote/do_promote；PromotionResult 保留——promote_idea IPC 返回类型 + idea.rs:99,155 实例化引用，作 IPC 边界类型无法清）

### 🔴 工作区多角度走查待修项（2026-06-16）

> 4 路并行代理走查工作区 22 文件 547 行（AiChat/View/stores/composables+ToolCard），详见 [工作区多角度走查-2026-06-15.md](./05-代码审查/工作区多角度走查-2026-06-15.md)。3 P0 功能 bug + 1 P1 i18n + DRY/健壮性一组。

**P0 — 功能 bug**
- [x] B-260615-34 ✅(定时走查复核,2026-06-16) — ~~selectType snake_case 对齐~~ `project.ts:273` 已改 `select_type: ... ?? 'single'` + 正确注释「Tauri 2 IPC 不转 camelCase,须 snake_case 对齐后端 workflow.rs:211」;后端 `workflow.rs:211` 同 `select_type: Option<String>`,前后端契约对齐。**纠正上轮过时判断**(上轮压缩前快照仍 camelCase,之后 `80b9243`「P0 bug 修复」批已修;后端代理经 IPC 签名推断「前端已对齐」结论正确) — stores/project.ts:273 — 详见定时走查 P0复核.md
- [x] B-260615-32 ✅(定时走查第2轮复核,2026-06-16,f93b758) — ~~流式收尾清队列~~ **[原 P0]**drainQueue 仅 AiCompleted 触发(useAiEvents:218)，onStreamTimeout/AiError/stopChat 三路径不清 state.queue → 生成中输入的消息丢失无提示。approveToolCall catch 同漏。修：三路径 + approve catch 补 `state.queue=[]` + 提示。关联 B-260615-22(状态不同步，不同角度) — useAiSend.ts:36-42 + useAiStream.ts onStreamTimeout + useAiEvents.ts AiError case + useAiSend.ts:82-107 — 详见走查 ②
  > ⚠️ **第2轮复核更正(2026-06-16):已修 ✅** — f93b758「修复: B-32+33 流式收尾」:`useAiStream.ts:28` `state.queue=[]`(onStreamTimeout) + `useAiEvents.ts:225`(AiError) + `useAiSend.ts:152`(stopChat)/124(approveToolCall catch)四路径全清队列。上轮「仍未修」为过时快照（working tree 当时旧版，与 B-34 同）。详见 [定时走查-2026-06-15-第2轮.md](./05-代码审查/定时走查-2026-06-15-第2轮.md)
- [x] B-260615-33 ✅(定时走查第2轮复核,2026-06-16,f93b758) — ~~回滚 running toolCall~~ **[原 P0]**approveToolCall 乐观置 running，后端 hang 时看门狗触发 onStreamTimeout 只复位 streaming 不碰 toolCalls[].status → running 态只渲染骨架(ToolCard:24) 审批按钮不显 → 无重审入口。**B-260615-07 残留**(B-07 加了 watchdog 但回调没回滚 status)。修：onStreamTimeout 扫一遍 running toolCall→rejected — useAiStream.ts:21-49 — 详见走查 ③
  > ⚠️ **第2轮复核更正(2026-06-16):已修 ✅** — f93b758:`useAiStream.ts:43-45` onStreamTimeout 单遍反向扫描 running toolCall→rejected（合并探测 completed 为单次 O(n)）。上轮「仍未修」为过时快照。详见 [定时走查-2026-06-15-第2轮.md](./05-代码审查/定时走查-2026-06-15-第2轮.md)
- [x] B-260615-35 ✅(定时走查第3轮复核,2026-06-16,b08adcb) — **[原 P0]** broadcast `Lagged` 兜底仅 warn → 关键终态事件丢失时前端永久卡死。`workflow.rs:90-110` forward 任务靠 `finished = matches!(event, WorkflowCompleted|WorkflowFailed)` 退出，broadcast 滑动窗口 Lagged(n) 丢 n 条最旧事件，**代码注释已识别风险但仅 warn 无兜底**；若 Lagged 恰丢终态事件，forward 永远 `rx.recv().await` 等不到 finished → 前端审批/完成弹窗永久卡死，仅靠 DB 轮询兜底（实时性差）。修：Lagged 分支后累计次数/单次 n 过大时查 `WorkflowRepo::get_by_id` 看终态，补 emit workflow-event + break；需把 `state.db.clone()` move 进 forward 任务（当前仅 executor spawn 持有）。低频但无自愈 — src-tauri/src/commands/workflow.rs:90-110 — 详见定时走查 P0复核.md（后端代理 ②）


**P1/P2 — DRY + 健壮性**
- [x] CR-260615-09 — **[P1]** 五份 .ai-md 样式逐字重复 ~350 行(AiChat/ProjectDetail/Ideas/Knowledge/TaskDetail) → 抽全局 `src/styles/ai-md.css` 或 `<AiMarkdown>` 组件。TaskDetail.vue:263-333 新文件继续抄（CR-09 复发，定时走查 06-15 确认第 5 份）。B-24/25 复制粘贴源头 — 详见走查 ⑤ + 定时走查 P0复核.md (第⑫批销账 06-15)
- [x] CR-260615-10 — **[P2]** useMarkdown 加 `useRendered(getText)` 辅助，消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥ (第⑫批销账 06-15)
- [x] CR-260615-11 ✅ composable健壮性 (第⑩批 2026-06-16) — ~~composables+ToolCard 健壮性一组~~ **[原 P2]** ⑦⑧⑨⑩⑪⑫ fddca9d 已实施（JSON.parse 逐条容错/argString 去 as any/复用 findToolCall/AiHeartbeat 显式 case/approveHumanApproval 签名收敛/formatToolName 兜底）。原问题：⑦switchConversation JSON.parse 无逐条容错(useAiConversations:81) ⑧args 消费 `as any` 类型逃逸(ToolCard) ⑨approveToolCall 重复查找(findToolCall) ⑩useAiEvents switch 缺 AiHeartbeat case ⑪approveHumanApproval 签名歧义 ⑫ToolCard 兜底。**⚠️⑪ 实施引入死逻辑**：workflow.ts:67-68 decision 三元两分支相同（见 CR-260615-18）— 详见走查 ⑦~⑫ + 定时走查第2轮.md
- [x] CR-260615-12 — **[P3]** 可选一组：_toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18n `t` as any — 详见走查 ⚪ 区 (第⑫批销账 06-15)
- [x] CR-260615-18 ✅ decision 死逻辑 (第⑩批 2026-06-16) — ~~decision 三元两分支相同死逻辑~~ (CR-11⑪ 实施引入)：`workflow.ts:67-68` `selectType === 'multiple' ? decisions[0] ?? '' : decisions[0] ?? ''` 两分支返回值完全一致，三元判断无意义，误导维护者以为单/多选差异化。后端 workflow.rs:291-296 兜底（空 picked+非空 decision push）。修：直接 `const decision = decisions[0] ?? ''` 删三元 — src/stores/project/workflow.ts:67-68 — 详见定时走查第2轮.md
- [x] CR-260619 ✅ action 冗余 (第⑩批 2026-06-16) — ~~action 字段~~ （保留为未来差异化刷新预留,注释已明确）定义 emit 但前端不消费：DfDataChangedPayload.action(api/types.ts:157) 后端 emit_data_changed(audit.rs:243-256) 每次计算，前端 listener(project.ts:33) 只解构 entity，action 忽略，刷新统一全量 load。契约冗余（白算白传）。修：删 action 或做差异化刷新（delete 本地移除）— src/api/types.ts:157 + project.ts:33 — 详见定时走查第2轮.md
- [x] CR-260615-20 ✅ App.vue try/catch (第⑩批 2026-06-16) — ~~stopDataChangedListener try/catch~~ 走查误判撤销:接收方核 workflow.ts:108 stopEventListener 写法一致(f574562 commit 说明),不修 — project.ts:47-55 unlisten 同步数组操作正常不抛，与 workflow.ts:108 stopEventListener 写法不一致。修：核 workflow.ts:108 对齐统一去/留 — src/stores/project.ts:47-55 — 详见定时走查第2轮.md
- [x] CR-260615-21 ✅ types 枚举注释 (第⑩批 2026-06-16) — ~~App.vue:218 `startDataChangedListener()` await 包 try/catch~~ console.error（防 Tauri listen 失败 reject onMounted 致 AR-11 静默失效）。原问题：await 无 try/catch — src/App.vue:218 — 详见定时走查第3轮.md
- [x] CR-260615-22 ✅ types 枚举对齐 (第⑩批 2026-06-16) — ~~types.ts 枚举注释对齐~~ (Task/Idea/Project status)+ 删 AiConversationSummary.models 冗余 枚举注释与后端不符(api 垂直切片,定时走查第5轮代理 B)：①TaskRecord.status(types.ts:88)注释 review_ready/merged/abandoned 错,后端 TaskStatus(types.rs:165)= todo/in_progress/in_review/testing/done/blocked/cancelled；②IdeaRecord.status(:11)漏 archived；③ProjectRecord.status(:39)漏 testing/releasing。status 字段是 string 非 literal union 不阻断,但注释误导。④AiConversationSummary.models(:233)冗余(后端返但前端无消费点)。修：注释对齐后端枚举/删冗余字段 — src/api/types.ts — 详见定时走查第5轮.md
- [x] CR-260615-23 ✅ knowledge error 死断 (第⑩批 2026-06-16) — ~~B-34「Tauri 不转」注释~~ 撤销(不适用):在 ARC-05 拆分时已简化(workflow.ts:81/project.ts:58 仅风格对齐无错误说明,16aeacc 接收方核对) — project.ts:273
- [x] CR-260615-24 ✅ 分离窗口 listener (第⑩批 2026-06-16) — ~~分离窗口 listener 双注册+永不清理~~ (Dashboard+AI窗口垂直切片,定时走查第6轮代理 B R1+R2,新功能首次深查)：①R1 双窗口重复消费 ai-chat-event：后端 app.emit 全局广播(stream_recv/agentic/commands/audit 多处)发给所有 webview,主窗口(AiChat.vue:790)+分离窗口(AiDetached 渲染 AiChat)各注册 listen,两窗口 handleEvent 都触发 → delta 各 += 一次/AiAgentRound 各 push 空气泡,靠 App.vue:94 v-if detach 时卸载主窗口 AiChat 间接停但 state 模块级单例 handleEvent 仍写(脆弱隐式契约);②R2 stopListener(useAiEvents.ts:258)定义但**全仓零调用**(地面 grep App/AiChat/AiDetached onUnmounted/onBeforeUnmount 都不调)→ listener+watchdog 永久泄漏。边界影响:reattach 重叠窗口期多一条空 assistant 消息。修：AiChat.vue onBeforeUnmount 补 store.stopListener()+detach 主窗口显式 stop/reattach start,或后端 emit_to(label) 定向 — src/composables/ai/useAiEvents.ts:258 + useAiWindow.ts + components/AiChat.vue:747 — 详见定时走查第6轮.md
- [x] CR-260615-25 ✅ 分离窗口状态机 (第⑩批 2026-06-16) — ~~分离窗口状态机缺口~~ (代理 B R3/R4/R5)：③R3 win.once('tauri://destroyed')置 detached=false 但不恢复 panelOpen=true(只 reattachPanel 显式设)→ X 关分离窗口后面板消失需手动 Ctrl+I;④R4 resumeInDetached(useAiWindow.ts:79-95)强行重建生成态(读 df-ai-gen 置 streaming=true push 空气泡)与后端 ai_is_generating 真值可能失配 → 假气泡/130s 后 watchdog 假超时;⑤R5 分离窗口 switchConversation(useAiWindow.ts:81)+loadConversations 内自动 switch,后端 AiSession 单例 activeConversationId 被覆盖,主窗口期间发消息可能落错会话(B-22 同类延伸)。修：destroyed 补 panelOpen=true/resumeInDetached 先 await ai_is_generating 核对/分离窗口只读不切 — src/composables/ai/useAiWindow.ts — 详见定时走查第6轮.md
- [x] CR-260615-26 ✅ AR-11 listener+Tasks 空 (第⑩批 2026-06-16) — ~~AR-11 listener 与 B-29 筛选契约交互 + Tasks 空态~~ (列表页垂直切片,代理 A R-1/Y-2)：①R-1 AR-11 监听器(project.ts:38)对 task 调 loadTasks() 无参 → 全量加载,破坏 B-260615-29「store.tasks 反映当前筛选」契约(filteredGroups 二次过滤兜住结果正确,但 store.tasks 变全量+性能);②Y-2 Tasks.vue:39-71 无 loading/error/empty 兜底,加载失败或无结果时空白页(store.error/loading 零引用,对比 Projects.vue:107 至少有空态)。修：listener 对 task 按 activeProject 筛选拉/Tasks 加 v-if loading/error/empty — src/stores/project.ts:38 + src/views/Tasks.vue — 详见定时走查第6轮.md
- [x] CR-260615-27 ✅ Dashboard 一组 (第⑩批 2026-06-16) — ~~Dashboard 一组~~ (代理 B M1/M3/L4)：①M1 refresh(Dashboard.vue:202-204)Promise.all 无 try/catch,任一接口失败 unhandledrejection+用户无反馈(onMounted 有 try/catch 但 refresh 无);②M3 getProjectStage(:150)status→stage 映射 planning 归 coding stage(颜色紫)但 label planning,颜色语义撒谎(cancelled 归 testing 同);③L4 决策/批注两 panel(:103-120)永远空态,CSS(:504-572)完整但模板未用=死代码。修：refresh 包 try/catch+toast/补 planning CSS 类/删死代码或接数据 — src/views/Dashboard.vue — 详见定时走查第6轮.md
- [x] CR-260615-28 ✅ knowledge store error (第⑩批 2026-06-16) — ~~knowledge store state.error 死字段~~ (error 通道前端断)+create 语义矛盾(stores 垂直切片,定时走查第7轮)：①state.error(knowledge.ts:19,49,53,64,74,105)写入但**全应用零消费**(grep Knowledge.vue store.error 零命中)→ 用户遭遇加载/检索/配置失败只看到空白列表无错误提示,**CR-08「store error fallback 11 处中文」深化**:不仅中文硬编码,整个 error 通道前端断的;②create(:80-85)成功后 loadCandidates 失败把错误塞 state.error,create 已返回 record 给调用方认为成功,语义自相矛盾。修：Knowledge.vue 接 error banner/toast 或删 state.error 改 throw 由调用方 catch + create 内 loadCandidates 失败不污染 error — src/stores/knowledge.ts — 详见定时走查第7轮.md
- [x] CR-260615-29 ✅ ai messages 上限 (第⑩批 2026-06-16) — ~~ai.ts messages 单对话无上限 + knowledge 异步竞态/错误不一致~~ (定时走查第7轮)：①ai.ts state.messages(useAiSend push user+ai/useAiEvents push err 审批/useAiStream push delta)单对话内线性增长,长会话+大工具结果内存可观(切换对话整体替换是减负点,同对话不轮换),建议软上限/滚动淘汰;②knowledge.ts 加载/检索/配置异步无竞态保护(messages 有 switch token,items/config 无),快速连点后返回覆盖先;③knowledge 错误处理不一致(load*/search 有 catch 兜底,create/updateStatus/archive/saveConfig/extractNow 直抛)。修：messages 软上限/items+config 加请求序号/错误处理统一 — src/stores/ai.ts + knowledge.ts — 详见定时走查第7轮.md
- [x] CR-260615-30 ✅ appSettings mutate (第⑩批 2026-06-16) — ~~appSettings useSetting 对象/数组值深 mutate 不落库~~ (定时走查第7轮边界)：useSetting 双 watch 用 Object.is 守卫防循环,但对对象/数组值深 mutate(r.value.x=1)时 Object.is(v,cache[key]) 同引用恒 true → 不触发 set 落库,静默不持久化。当前无 bug(useSetting 多绑值类型,df-ai-ui 走 set 整对象替换非 mutate),但隐式契约。修：文档注明 useSetting 仅值类型,或对象值用 JSON 序列化比较 — src/stores/appSettings.ts:88-113 — 详见定时走查第7轮.md
- [x] CR-260615-31 ✅ useConfirm 并发 (第⑩批 2026-06-16) — ~~小区域收尾健壮性一组~~ (定时走查第8轮,无必修)：①useConfirm 并发 confirmDialog 覆盖 resolve(useConfirm.ts:35-47),前一个 Promise 永挂(连点两删除/confirm 交错时)→ 静默中断+轻微内存泄漏,修：进入时 resolve?.(false) 打断前一个视为取消;②ToolCardList collapseInactive(:47-51)只清 expandedCards 漏清 expandedTools,已完成卡片折叠后内容展开态残留 Set,修：对称过滤或注释明确有意保留 — src/composables/useConfirm.ts + components/ToolCardList.vue — 详见定时走查第8轮.md

### 📄 文档核对待修项（2026-06-16）

> 4 路并行核对 docs 全量 + 根目录文档，详见 [文档全量核对报告-2026-06-15.md](./05-代码审查/文档全量核对报告-2026-06-15.md)。悬空链接 0。

**P0 — 文档硬伤（按文档找代码落空）**
- [x] DOC-260615-01 ✅ 文档P0重写 (第⑩批 2026-06-16) — ~~ARCHITECTURE.md 数据模型整段重写~~ （14 表 6 虚构 artifacts/connections/features/test_cases/test_runs/annotations/decisions + 7 遗漏 node_executions/branches/ai_conversations 等；workflow_defs/runs 合并 workflow_executions）— ARCHITECTURE.md:262-475 — 详见核对报告 §1
- [x] DOC-260615-02 ✅ 文档P0重写 (第⑩批 2026-06-16) — ~~df-nodes 模块文档重写~~ （8 节点全骨架 → 实际 3 节点 ai/script/human 均完整实现；删 Docker/Git/Notify/HTTP/Subflow 5 虚构）— 03-模块文档/df-nodes — 详见 §2
- [x] DOC-260615-03 ✅ 文档P0重写 (第⑩批 2026-06-16) — ~~df-ai 模块文档删虚构~~ router.rs/stream.rs + 已删 supported_features trait + 12→13 工具 — 03-模块文档/df-ai — 详见 §3
- [x] DOC-260615-04 ✅ 文档P0重写 (第⑩批 2026-06-16) — ~~使用手册全面重写 7 处~~ （运行命令 bun→npm / 任务状态 4→7 态 / 优先级方向 / 节点类型 Script/Ai/Human / EventBus 非 WebSocket / 多 Provider / 知识库 Tier1）— 08-用户指南/使用手册 — 详见 §4

**P1 — 审查状态断层**
- [x] DOC-260615-05 ✅(2026-06-16) — ~~todo 补全局 review R-P1-1~6/R-P2-1~13 已完成回写~~ 在全局代码 review 待修项区头部补「R-P1 6 项 + R-P2 13 项全闭环(126 test pass)」汇总指针(指向全局review-2026-06-15.md 推进状态三批),原 todo 只有 R-PD-1~14 需设计项无 R-P1/R-P2 执行项追踪,现补齐。报告说全闭环但 todo 零条目,已完成区补汇总指针 — 详见 §5 + 全局review-2026-06-15.md §推进状态
- [x] DOC-260615-06 ✅(2026-06-16) — ~~架构复核报告 §5 新发现 11 项补 todo~~ 拆 4 子项独立 todo(见下方),核对发现 **复核-新① 已解决**(`useAiConversations.ts:122` 已有 pendingToolCalls await 后二次 token 比对),只补 ③④⑪ 三项;其余 7 项已被全局 review 取代(R-PD 系列) — 详见 §6
- [x] ✅(batch31·2026-06-16) 复核-新③ [P2] — **NodeCancelled 事件语义双标**。`executor.rs:149` 取消节点(set_cancelled 后 Err 路径)状态保 Cancelled(:145 跳 set_failed),但事件总线仍 emit `NodeFailed { error: "人工审批被取消" }`。前端若按 NodeFailed 分支判断会误归类「失败」非「取消」。修法:加 `NodeCancelled` variant 或 NodeFailed 加 `cancelled: bool` 字段。— 来源:架构复核报告 §5 新③,crates/df-workflow/src/executor.rs:145-153 + df-core/events.rs
- [x] ✅(batch30·2026-06-16) 复核-新④ [P2] — **cancel_workflow_node 无节点终态前置校验**。`workflow.rs:352` 仅校验 execution_id 在注册表,不校验 node_id 状态。对已 Completed/Failed/Cancelled 节点调 set_cancelled 会静默覆盖终态(set_cancelled 绕 is_legal)。修法:加 `match sm.get(&node_id) { Running|Waiting => {}, _ => Err }` 守卫。— 来源:架构复核报告 §5 新④,src-tauri/src/commands/workflow.rs:341-358
- [x] ✅(batch54·2026-06-16·workflow+主代核查) 复核-新⑪ [P2] — **主窗口看门狗幽灵**。**核验更正**:原述「看门狗续计 130s 补幽灵错误」已被 CR-260615-24 兜底解决(detach→AiChat 卸载 App.vue v-if→onBeforeUnmount:1613 `stopListener()`→useAiEvents.ts:371 `clearStreamWatchdog()`,主代独立 grep 核验链路完整)。本次 workflow agent 补强:detachPanel 快照后清主窗口 `state.streaming=false`/`generatingConvId=null`(useAiWindow.ts:37-38),消除「面板重开生成态视觉残留」(进度条/spinner)。**安全性**:主/分离窗口 state 独立(各自 webview 独立 JS context,stores/ai.ts 单例仅同 webview 内共享),清主窗口 state 不影响分离窗口生成态;localStorage 快照保留供 resumeInDetached 恢复。vue-tsc EXIT=0 主代独立核验。— 来源:架构复核报告 §5 新⑪,src/composables/ai/useAiWindow.ts:37-38 + useAiEvents.ts:364-374 stopListener
- [x] DOC-260615-07 ✅(第⑲批 2026-06-16) — ~~aichat §8 表补勾~~ aichat审查报告第八章 AR-1~11 优先级表逐行补状态勾注(AR-1退役✅/AR-2~7,9~11已修✅+commit/AR-8重评🔄)+文档头二次增补行;零新增条目对齐 DOC-13 瘦身方向;状态不一致 2 项(AR-1退役/AR-8重评)按 todo 标注非矛盾 — 详见 §7
- [x] DOC-260615-08 ✅(2026-06-16) — ~~近期改动 CR-03 未做子项拆独立 todo~~ 拆 3 子项独立 todo(见下方)。原 CR-03(line 212)内联备注「未做留 todo」未独立追踪,现拆。— 详见 §8
- [x] ✅(batch30·2026-06-16) CR-260615-03-a [P2] — **audit.rs Err 误报「项目已不存在」**。`audit.rs:52` `resolve_project_label` 用 `_` 合并 `Err`(DB 故障/锁/连接断)与 `Ok(None)`(真不存在),DB 出错时用户看到误导性「项目已不存在」而非错误。修法:分三臂 `Ok(Some)`/`Ok(None)`/`Err`(Err 打 warn 日志回退裸 id)。零行为破坏(仅 Err 分支加日志+改文案)。— 来源:近期改动审查 §2②,src-tauri/src/commands/ai/audit.rs:50-52
- [x] ✅(batch30 openai·2026-06-16 + batch31 anthropic:533 同源) CR-260615-03-b [P2] — **openai_compat `anyhow::anyhow!(e)` 丢 source 链**。`openai_compat.rs:414` 非超时分支用 `anyhow!(e)` 把 reqwest::Error 整体塞进 message,丢失 `#[source]` 因果链(`?` 本会保留)。修法:改 `anyhow::Error::from(e)` 保留 source。零行为破坏(仅错误追溯链增强)。— 来源:近期改动审查 §2③,crates/df-ai/src/openai_compat.rs:414
- [x] ✅(batch30·2026-06-16) CR-260615-03-c [P2] — **ToolCard projectNameById computed 过度结构化**。`ToolCard.vue:390` computed 每次重建全量项目(含回收站)的 Map,审批卡通常仅 1-2 行用项目 id。修法:改 `projectStore.projects.find(p=>p.id===id)?.name ?? projectStore.deletedProjects.find(...)?.name`(审批场景项目数有限,find 提前退出,响应式仍由 store 数组保证)。零行为破坏。— 来源:近期改动审查 §2⑧,src/components/ToolCard.vue:390,417,441

**P1 — 架构/模块文档过期**
- [x] DOC-260615-09 ✅(第⑮批核对 06-15) — df-workflow 模块文档 4 处过期 **0 处需改(历史已修)**（NodeRegistry 删 Default / conditions 默认 true→false / try_recv_human_approval 已删 / set_waiting·set_skipped 已删）— 详见 §9
- [x] DOC-260615-10 ✅(第⑮批核对 06-15) — df-storage V8→V13 **0 处需改(历史已修, V13/13表/13Repo 全对齐)**（迁移版本+表数 11→18+Repo 11→17）+ 对抗评估 idea_evaluations 设计稿标未实施 + df-execute 描述删 Docker/SSH/Git — 详见 §10-12

**P1 — 索引/计数矛盾**
- [x] DOC-260615-11 ✅(第⑱批销账 06-15) — 13→8 crate 多处统一(PROGRESS/INDEX 4处改值+对抗裁决/想法探索 4处历史加注「现8」)+commands 数统一**67**(主代理数行确认权威值:14+5+6+5+11+8+14+4=67,AI聊天11+对话8=19;诊断两 agent 误计 68 ai=20,实施 f2 跟随误值,主代理修正 DEVFLOW-2)+URGENT.md 已自声明停用保留（PROGRESS/INDEX/Phase1决策/对抗裁决/想法探索）+ commands 数 47/57/68 统一 68 + URGENT.md 停用标注或删 — 详见 §13/15/16（INDEX 漏列部分本会话已补，见 DOC-14）

**P2 — 迭代/Phase/规范**
- [x] DOC-260615-12 ✅(第⑱批销账 06-15) — DEVFLOW-1~4 + Phase1/Phase2 标完成归档(DEVFLOW-2/3/4 正文 checkbox 补勾对齐头部✅;Phase1清单#1-#11全✅+计数67修正;Phase2各小节补「实际状态」2.1/2.2✅ 2.3启发式✅LLM❌ 2.4仅并行✅,task行保留原文+核对风格不强勾)（Phase1 已收尾文档仍全待实施）— 详见 §17-19
- [~] DOC-260615-13 ◐(2026-06-16 评估·部分做) — ~~todo 肥大瘦身（大段详情移 docs 子目录）~~ **评估结论**:todo 主要「肥大」是单行长条目(超 1000 字符/条,F-260616-01=1506/AE-2025-04=1667/B-260615-08=1526/AR-11=1428 等),非多行详情块;单行长条目多已通过「详案见 docs/子目录/xx.md」+ plan 文件指针(quizzical-prancing-hennessy.md 等)外部化,详情已在子文档,inline 摘要保留助读。**本次未强行瘦身**(强行拆单行条目易碎+破坏 grep/锚点),仅评估标注。**已做收口**:①重复项 HumanNode 审批(B-03b/CR-03/复核-新③)各保留各自语义无纯重复 ②密钥迁移(FR-S1/CR-01/R-PD-1)三处分工:FR-S1=明文根治/CR-01=删除闭环/R-PD-1=编辑路径迁移,非重复。**遗留**:若未来单行长条目继续膨胀,可立专项把已完成项(AR-11/FR-R1 等)的详细勘察段移 docs/05-代码审查/ 历史归档,inline 只留一句结论 + commit。文档记录规范在 INDEX 突出 → 归 DOC-14 改造指南重写时一并 — 详见 §20-22

**P3 — 技术/工具文档**
- [~] DOC-260615-14 ◐(2026-06-16 评估·部分做) — ~~Tauri-IPC/View 改造指南全篇过时重写或归档 + SQLite 表数 V1-V9→V1-V13 + 锚点顿号~~ **核对结论**:①Tauri-IPC 模式(`01-技术文档/Tauri-IPC模式-2026-06-12.md`)顶部已加归档 banner(2026-06-15,指向 ARCHITECTURE.md §三),✅ 已归档不需重写;②View 改造指南(`06-前端开发/View改造指南-2026-06-12.md`)顶部已加归档 banner(2026-06-15,指向 DEVFLOW-3.Store对接实施),✅ 已归档;③SQLite 表数 **已改**:df-storage 模块文档 V1-V13→V1-V15(补 V14/V15 迁移行 + 头部/状态表/迁移机制行/文件树注释 6 处)+SQLite-CRUD 模式文档 V1-V9→V1-V15(11→14 表/9→12 索引/11→17 Repo 3 处),核对真实 migrations.rs schema_version 最大值=15;④锚点顿号(INDEX:107-109 `#四crate-结构` 渲染器差异)低危遗留不强行做。**遗留**:锚点顿号 + 路径风格统一(功能创意池:150 链接省 `./`),低危,未来 INDEX/创意池重整时一并 — 详见 §23-26

### 🟡 B-03b 复核后续（独立深度复核，2026-06-14）

> B-03b 端到端补完（commit 4aa689e）后独立复核发现 2 加重 + 3 低优。**①为真实 bug**：取消链 IPC→set_cancelled→HumanNode is_cancelled→Err 通了，但 HumanNode 返 Err 后 executor run:115 `set_failed` 走 transition，而 Cancelled 不在 `is_legal` 转换图 → bail → 工作流因状态转换错误异常终止（非优雅标记取消）。补完时只验 IPC→HumanNode 链，漏验 HumanNode→executor Err 处理。

- [x] B-03b-R1 ~~Cancelled 转换图双踩~~ ✅ 已修（executor Err 处理加 is_cancelled 检测，已取消节点跳 set_failed，Cancelled→Failed 非法 transition 不再 bail；emit NodeFailed 仍发，状态保 Cancelled；采修法 B 不动 is_legal 转换图）
- [x] B-03b-R2 ~~零端到端取消测试~~ ✅ 已补（test_cancelled_node_skips_set_failed：CancelSelfNode 经共享 node_status 自取消→Err→executor 不 bail→状态保 Cancelled；df-workflow 14 test pass）
- [x] B-03b-R3 — **[评估维持]** std::sync::Mutex — 快速临界区纳秒级持锁不阻塞 runtime，保持 std 合理
- [x] B-03b-R4 — **[评估维持]** set_* 改 &self 后调用方 &mut — &self 是放宽，&mut self 上下文调 &self 无害，清理为可选优化不影响正确性
- [x] B-03b-R5 — **[评估维持]** `lock().expect()` poison panic — poison=持锁 panic 严重错误，fail-fast 合理，非用户态可恢复
- [x] B-03b-R6 ~~human_node send 缺 await~~ ✅ 已修（human_node.rs:41 加 .await；async fn send 的 Future 不再被 let _ = 丢弃，Request 真进 channel）(commit 0bb96fc)
- [x] B-03b-R7 ~~前端契约失配~~ ✅ 已修（project.ts:214 type→snake_case + :215 取 event 本体扁平字段 + as unknown as 绕过联合类型）(commit 0bb96fc)；types.ts event.type 收窄字面量联合(WorkflowEventType 11 变体)已补(commit 3e1f119 Wave5)
- [x] B-03b-R8 ~~缺 human 节点端到端集成测试~~ ✅ 已补（human_node.rs 加 2 端到端测：`end_to_end_human_approval_completes_workflow` 验 a(SleepNode)→b(HumanNode) 两层 DAG 经 executor 驱动 Request 真发出 + outputs 收集 + 双节点 Completed；`end_to_end_human_approval_cancelled` 验外部 set_cancelled → human cancel_tick 命中 → executor 跳过 set_failed + 状态保持 Cancelled）。覆盖 executor↔HumanNode 集成链路，封死 R6/R7 回归土壤（10 测全过，2026-06-16，待commit）
- [ ] B-03b-R9 — **[P2]** 其余 8 项对抗裁定（③串扰/④单槽/⑤终态不清/⑥set_cancelled覆盖终态/⑦互斥/⑧approve不校验/⑨⑩Lagged/⑪failed_node空）多数潜伏或零危害，详见 [审查报告 §2](./02-架构设计/工作流审批审查报告-2026-06-14.md)

### ✅ 已完成 — df-workflow 审批闭环（Workflow D, commit 22964a2）

- [x] B-260614-06 — ~~execution_id 硬编码 "dummy-execution-id"~~ ✅ DagExecutor::new 接收 execution_id 下沉 NodeContext（workflow.rs 传真 ID）(06-14)
- [x] B-260614-07 — ~~每节点全新空 StateMachine~~ ✅ NodeContext.node_status 共享 self.state_machine.clone()（is_cancelled 可工作）(06-14)
- [x] B-260614-03a — ~~HumanNode 假返回"同意"~~ ✅ execute 重写 subscribe→send→select!（响应/超时/取消 + execution_id+node_id 双键 + Lagged 容忍），照 [B-03 设计](./02-架构设计/B-03-人工审批响应机制-2026-06-14.md)；7 单测；eventbus 删死代码 (06-14)
- [x] B-260614-03b — ~~HumanNode 取消机制~~ ✅ WF-E 完成：set_cancelled + cancel_workflow_node IPC + 前端取消按钮 + **端到端补完**（StateMachine 内部 Arc<Mutex> 共享 + execution_id 注册表）。**注**：agent 初版留半成品（workflow_cancel_state 全局孤立实例，IPC 写了读不到，Explore 审查漏抓语义缺陷），主代理补完 IPC→共享 HashMap→HumanNode is_cancelled 真通；clone_shares 单测验证共享语义 (06-14, commit 4aa689e)

### P1 — 重要缺陷

- [x] B-260614-08 — ~~promote_idea 两步写非事务~~ ✅ mission:T-260614-05 已修复（idea.rs 第二步 update_full 失败时补偿删除已建 project；Repository 不支持跨 repo 共享事务对象，选补偿删除非真事务，改动最小；附 logging）(06-14)
- [ ] T-260614-01 — **[P1]** Sprint 9/10/14/15/16/18 多项未 tauri dev 实测 — 评分 IPC 缩放 / update_full / promote_idea / Store getter / token 落库 / 知识库 Tier 1 全栈 / LLM 并发 Semaphore / 知识生命线（#54 跟踪）— source:Sprint 9-18 (06-14)
- [ ] T-260614-02 — **[P1]** 切对话不中断路由：部分场景运行时实测（A 路线场景 2/3） — source:Sprint 8 (06-14)

### P1 — 设计完成待实施

- [x] ✅ F-260614-01 — **[P1🔥]** 模型能力系统 Phase 1(阶段1-6 全✅,波1-5,ModelConfig 4维度+路由器+多源探测+厂商拉取+7调用点+Settings池UI+AiChat下拉) — ModelConfig 数据模型(模态/能力/价格/智力 4 维度) + ModelRouter 智能路由 + 多源探测(预设表+启发式) + 厂商模型列表自动拉取(适配 OpenAI/Anthropic 协议差异) + 7 调用点接入 + Settings 模型池 UI + AiChat 模型下拉。按任务需求自动匹配合适模型，不再所有场景共用 default_model — source:📐 设计定稿 (06-16, [F-01-模型能力系统与智能路由设计-2026-06-16.md](./02-架构设计/F-01-模型能力系统与智能路由设计-2026-06-16.md))。F-07 已完成解除阻塞。
  - [x] ✅(波1) 阶段1:数据模型 — df-ai-core 新增 ModelConfig/Modality/Capability/CostTier/IntelligenceTier + df-storage models 反序列化兼容
  - [x] ✅(波1) 阶段2:预设表+探测器 — df-ai 新建 model_probe.rs + presets/models.json + 启发式推断 + 多源合并
  - [x] ✅(波1) 阶段3:厂商模型列表拉取 — df-ai 新建 model_fetch.rs + openai_compat/anthropic_compat 分派 + URL 拼接 + 噪音过滤
  - [x] ✅(波2) 阶段4:路由器 — df-ai 新建 router.rs + TaskRequirements + select 逻辑
  - [x] ✅(波3 IPC a26b86e + 波4调用点 2ddfea6) 阶段5:6 调用点接入 — agentic/title/knowledge_inject(2)/project(2)/df-ideas/df-nodes + IPC ai_fetch_models/ai_probe_model + router.rs select_model_id helper(池空兜底 default_model 行为不变)
  - [x] ✅(波5·4c84fbc+主代核查,cargo0err+vue-tsc0+df-ai107pass+compress3pass) 阶段6:前端 — types.ts 类型对齐(✅波3) + Settings 模型池 UI(✅波3) + AiChat 模型下拉+override穿透(✅波5·4c84fbc,override空/不在池落回路由零行为变化)

### P1 — Sprint 19 遗留

- [x] T-260614-05 — ~~工具结果入库前截断 50KB~~ ✅ mission:T-260614-04 已修复（conversation.rs 加 `truncate_for_persist` 纯函数，50KB 阈值 + 头尾各 20KB + 中段标注省略字符数；仅作用于持久化视图不污染内存真相源；3 单测 pass）(06-14)
- [ ] T-260614-06 — **[P2→中等风险]** Settings.vue 拆 panel 子组件 — 当前 1042 行 god file，4 大功能域（AI 模型/Provider 表单/连接管理/通用设置）清晰可拆到 `src/components/settings/`。**评估：非低风险**，纯重构零功能价值，要新建 4 子组件 + props/emits 接线 + CSS 拆分，单独立项做更稳 — source:Sprint 19 待评估 (06-14)
- [x] T-260614-07 — ~~诊断日志清理~~ ✅ mission:T-260614-06 已清理（useAiEvents/useAiSend 3 处调试 console.log 直接删；AiChat.vue/main.ts 3 处启动计时改 console.debug 保留诊断能力但不污染 console；vue-tsc 0 err，src/ console.log 0 残留）(06-14)

### 🔴 generating 状态机加固（2026-06-16 审查）

> /review 专项审查 generating 生命周期（commands.rs 对话/审批/stop + agentic.rs loop/try_continue + stream_recv.rs stream_llm + mod.rs AiSession）。**用户报障根因**：generating 复位散布 6 处 + spawn 无 panic 兜底 → generating 卡 true → newConversation 硬拦死锁（创建不了新对话，只能重启）。**决策**：轻量状态机（RAII guard 写收敛 + enum 视图读侧收敛），不引入独立状态机框架（stop_flag 须保留 AtomicBool 跨锁）。详见 [generating状态机加固-2026-06-15.md](./02-架构设计/generating状态机加固-2026-06-15.md)。

**P0 — 用户 bug 根治组合（必须同批：②③强耦合，①是上游根治）**
- [x] B-260615-09 ✅ .ai-md DRY (第⑩批 2026-06-16) — ~~RAII guard 收尾 generating~~ (84删+3留独有) GeneratingGuard struct(new/reset().await 幂等/Drop spawn 兜底)+run_agentic_loop 入口实例化+5 处手动复位替换(provider-Err/入口 stop/stream_llm None/流式 stop/正常完成尾)+try_continue:318 保留手动(should_continue=false 路径须保 generating=true 待审批);保留复位→emit 顺序;主代理核查 cargo check 0 err/df-nodes 17 test pass — agentic.rs
- [x] B-260615-10 ✅ useRendered DRY 确认 (第⑩批 2026-06-16) — ~~newConversation 硬拦改软复位~~ ai_conversation_create 加 app:AppHandle 参数(Tauri 自动注入,invoke_handler 无需改)+generating=true 时记 old_conv→generating=false→清 pending_approvals→stop_flag(B-11 双保险)→释放锁→向 old_conv emit 零 token AiCompleted→relock 续建新对话;硬拦 Err 移除 — commands.rs
- [x] B-260615-11 ✅(P0批,2026-06-16,待commit) — ~~loop push 前对话一致性校验~~ 两处 conv_id 校验:①循环顶部(入口 stop_flag 块后,AiAgentRound 通知前)active_conversation_id!=conv_id 提前 return+warn ②push 块内(取锁后,has_tool_calls/!full_text 分支前)切换即提前 return+log — agentic.rs

**P1**
- [x] B-260615-12 ✅(P0批,2026-06-16,待commit) — ~~session.state() enum 视图收敛~~ 新增 pub enum SessionState{Idle,Streaming,AwaitingApproval}(mod.rs:105-112)+impl AiSession pub fn session_state()->SessionState(mod.rs:173-181 读视图:pending_approvals 非空→AwaitingApproval/else generating→Streaming/else Idle);字段定义不动,三调用点(agentic.rs:283/commands.rs:250/451)列注释待后续承接替换 — mod.rs
- [x] B-260615-13 ✅(wcvigw3z4批,2026-06-16,待commit) — ~~stop 流式态分支兜底~~ ai_chat_stop 流式态分支 stop_flag.store 后 spawn 兜底 task(sleep 3s 后 lock 检查 generating 仍 true 则强制复位+emit AiCompleted 零 token),治 loop 死(panic/异常退出漏发收尾)时 stop 无反应;参考审批分支+B-10 软复位写法,emit 前释放锁(drop+relock) — commands.rs
- [x] B-260615-14 — [P1] ⑦ stop_flag 换 tokio::sync::Notify 即时打断（去 30s 延迟）— stream_recv.rs:148 (第⑫批销账 06-15)

**P2**
- [x] B-260615-15 ✅(P0收尾批,2026-06-16,待commit) — ~~heartbeat interval 提到 loop 外~~ stream_recv.rs:127-132 heartbeat 创建+首 tick 丢弃移到 loop 前,loop 内复用 tick()节拍不变(从首 chunk 算 30s),计时器状态跨迭代保留无重建抖动 — stream_recv.rs
- [x] B-260615-16 ✅(P0收尾批,2026-06-16,待commit) — ~~MAX_AGENT_ITERATIONS 配置化~~ pub(crate) const→pub const + 文档注释标注未来配置接入点;核查 AppState/df-storage 无现成 agent 配置槽位(grep 零命中),未引入 AppState 字段(符合 P2 零行为变边界) — agentic.rs:27-34
- [x] B-260615-17 ✅(P0收尾批,2026-06-16,待commit) — ~~key_len 复用~~ resolve_provider_secret 调一次(行105 resolved_key)+key_len 派生(行106)+ensure_resolved_key+build_provider 内联(行107-115);逻辑等价 secret::build_provider_for(resolve→ensure→build 三步),因 build_provider_for 隐藏 resolved key 无法复用+secret.rs 锁边界而内联;⚠️DRY 隐患:agentic.rs 内联与 build_provider_for 重复,注释标注等价,未来改 build_provider 签名需同步两处(可后续小改 build_provider_for 返 key_len 消除) — agentic.rs:98-133
- [x] B-260615-18 ✅(P0批,2026-06-16,待commit) — ~~pending_approvals 注释~~ mod.rs:124-135 扩展文档:单 HashMap<tool_call_id,PendingApproval> 按 tool_call_id 路由/conversation_id 业务语义非路由键/当前 O(n) 过滤典型场景可接受/未来多会话高并发可加二级索引 — mod.rs

### 🔴 消息发送失败无提示 + 状态传染（2026-06-16 用户报障）

> 用户报障：发送消息后面板显示该消息 + input 残留同条消息 + 无失败提示；叠加首对话失败后**新对话建不了、切历史对话也发不出**。**根因 = generating 卡 true**（见上 B-260615-09~18，未实施）的下游表现：卡死态下 `ai_chat_send`(commands.rs:45) 同步拦截 Err → 前端 catch(useAiSend.ts:75) 回填 input + 不回滚 user message → handleSend(AiChat.vue:666) 仅 console.error 无 toast。新建对话(ai_conversation_create:451)/切历史(switchConversation)同被 generating 拦。**根因不治则反复**，本块 P1/P2 为 UX 放大器与状态同步缺口。

**P0 — 根治（即上一块 B-09~18，不另立）**
- [x] B-260615-19 ✅(P0批,2026-06-16,待commit) — ~~根因指针~~ B-09/10/11 已实施(generating 卡 true 根治)+B-12/18 同批(session_state enum 视图+pending_approvals 注释);B-13~17 留后续(stop 兜底/Notify 即时打断/heartbeat 提外/MAX_AGENT_ITERATIONS 配置化/key_len 复用)

**P1 — 前端 UX 放大器**
- [x] B-260615-20 ✅ 发送提示 (第⑩批 2026-06-16) — ~~handleSend catch 加提示~~ 复用 Settings.vue reactive toast(AiChat 可分离窗口须自管):模板末尾 Transition+.ai-toast/script 加 toast reactive+showToast(3s 自消失)/handleSend catch 加 showToast('发送失败：'+errMsg)/.ai-panel position:relative/.ai-toast 样式;技术债:文案硬编码未接 i18n(同 confirmClearChat 先例),公共 toast composable 另起任务 — AiChat.vue
- [x] B-260615-21 ✅ 回滚user msg (第⑩批 2026-06-16) — ~~sendMessage catch 回滚 user message~~ push user msg 前捕获 const userMsgId(原内联未生成)+catch 块 filter 改 m.id!==aiMsgId && m.id!==userMsgId 一并回滚;机制:user/ai msg 均 nextMsgId() 唯一 id+push 进 state.messages(无索引依赖,按 id 过滤最稳) — useAiSend.ts

**P2 — 前后端状态同步**
- [x] ✅(batch55·2026-06-16·主代销账) B-260615-22 — [P2] ~~前后端状态不同步~~ → **由 B-260615-55(L0 握手) + B-260615-56(L2 强制发送) 解决**(L0 前端重接握手+L2 三级降级+force_send IPC),本条保留为问题记录不再独立实施。双项均已✅销账,本条闭环。
- [x] CR-260615-12 ✅ 小项 _toastTimer 等 (第⑩批 2026-06-16) — ~~可选一组~~ _toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18n `t` as any — 详见走查 ⚪ 区
- [x] CR-260615-09 ✅ .ai-md DRY (第⑩批 2026-06-16) — ~~五份 .ai-md 样式逐字重复~~ 抽全局 css (84条删+3留独有) — AiChat/ProjectDetail/Ideas/Knowledge/TaskDetail → `src/styles/ai-md.css` 或 `<AiMarkdown>` 组件。TaskDetail.vue:263-333 新文件继续抄（CR-09 复发，定时走查 06-15 确认第 5 份）。B-24/25 复制粘贴源头 — 详见走查 ⑤ + 定时走查 P0复核.md
- [x] CR-260615-10 ✅ useRendered DRY (第⑩批 2026-06-16) — ~~useMarkdown 加 `useRendered(getText)` 辅助~~ 消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥
- [x] CR-260615-08 ✅ i18n 全清 (第⑩批 2026-06-16) — ~~i18n 硬编码一组~~ ARG_LABEL_MAP+未知错误+stores 24处 全清 — 定时走查第4轮扩展:原记 3 处,实际 **40+ 处**；i18n key 树本身优秀 466 key zh/en 全对齐 0 缺,问题全在 src/ 硬编码。**P1 直接破坏 i18n（最高优先）**：①useAiEvents:182 `tc.result='用户拒绝了此操作'` 绕过已有 aiTool.rejectedHint → en locale 拒绝提示恒中文（已有 en 翻译被一行废掉），**改一行删/换 t() 最高 ROI**；②TaskDetail.vue 整页 13 处未接 i18n（无 taskDetail namespace，需新建）；③ProjectDetail 审批/导入 5 处（导入目录/确认N/取消/confirmDialog/Message）；④Dashboard:76 暂无项目；⑤AiChat:107/471/687（title清空/confirmDialog/showToast）。**P2**：⑥ToolCard:152-164 ARG_LABEL_MAP 11 审批参数标签硬编码（aiTool 无 key，需新增 argLabels.*）；⑦store error fallback 11 处全中文（knowledge/ideas/projects/tasks/workflow）；⑧useAiSend:49/65/130（队列满/审批未送达）；⑨useAiStream:52-53 看门狗两文案（原 CR-08 行号 39-41 偏差）。**P3**：⑩time.ts:46-51 相对时间中文（已有 common.justNow/minutesAgo 等 key 却没用，en locale 时间恒中文，隐蔽）；⑪router meta.title 8 处（0 消费方死代码）；⑫fallbackLocale:'en' 设计气味。修：按视图分批接入，**P1-1 改一行先做** — 全 src/ + i18n — 详见定时走查第4轮.md

### 🔴 前后端状态分裂根治（HMR/刷新/切窗口自愈）

> 用户实测(06-15)：DEV 运行中改代码触发 Vite HMR → 前端重载但后端 Tauri 进程未重启 → `generating=true` 残留 → 所有新消息被拦（`ai_chat_send` 同步检查）→ 入队死锁 10+ 分钟不恢复。**根因**：前后端独立生命周期无 reconciliation 协议。**否决超时方案**(5-10min 等死体验差)。选 L0 重接握手(零延迟自愈) + L2 发送韧性(用户可控)。

- [x] B-260615-55 ✅ L0 握手 (第⑩批 2026-06-16) — ~~L0 前端重接握手~~ 代码已落地:后端 lib.rs:32-64(ai-client-ready listener + generating reset + pending_approvals 清除 + 补偿 AiCompleted 事件) + 前端 useAiEvents.ts:251(emit ai-client-ready)。HMR/刷新/F5/切窗口自愈 — src/composables/ai/useAiEvents.ts + src-tauri/src/lib.rs
- [x] B-260615-56 ✅ L2 韧性 (第⑩批 2026-06-16) — ~~L2 发送时强制覆盖（用户可控自救）~~ 三级降级+force_send IPC。当前行为：`generating=true` → `ai_chat_send` 返回 Err → 前端 catch 静默入队 → 死锁无出口。改进三级降级：①正常(generating=false)→ 直接发送 ②排队(<30s, generating=true)→ 入队 + UI 显示「⚳ 排队中 N 条」+ 可取消按钮 ③异常(>30s 或 用户主动)→ 弹确认「检测到生成状态异常，是否强制发送？」→ 调用新 IPC `ai_chat_force_send` 绕过 generating 检查（后端先 reset generating 再正常走 send 流程）。**关键设计**：30s 后给用户选择权而非永远静默等。**改动**：前端 `useAiSend.ts` sendMessage 加超时分支 + confirm 弹窗；后端 `commands.rs` 新增 `ai_chat_force_send`（复用 ai_chat_send 逻辑，前置 reset generating）。关联 B-10(软复位已做 newConversation, force_send 同模式) — src/composables/ai/useAiSend.ts + src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs

### 🔴 详情描述字段 Markdown 未渲染（2026-06-16 用户报障）

> 用户报障：任务描述 Markdown 没渲染（显示原始 `## 标题`/`- 列表` 文本）。**走查**：4 详情组件描述字段全用纯文本插值 `{{ }}` 未走 md 渲染——`src/views/TaskDetail.vue:33` / `ProjectDetail.vue` / `Ideas.vue` / `Knowledge.vue`。`src/components/AiChat.vue` 已有正确实现 `renderMd`（marked + DOMPurify + 块级 memo + XSS 防护），但封装在组件内未抽 composable，**DRY 缺口**。**修法**：抽 `src/composables/useMarkdown.ts` 统一入口（复用 AiChat renderMd 逻辑），4 组件展示态切 `v-html="renderMd(desc)"`（编辑态仍 textarea）。**安全约束**：v-html 必须 sanitize，composable 保留 DOMPurify。

- [x] B-260615-23 ✅(2026-06-16,待commit) — ~~抽 useMarkdown composable~~ 新建 src/composables/useMarkdown.ts(模块级单例:_marked/_purify/mdReady/_mdCache 模块作用域,renderMd 含 DOMPurify.sanitize XSS 防护+块级 memo 缓存+未就绪 escapeFallback 兜底)+AiChat.vue 改 import 引用(流式核心 splitBlocks/parseBlock/parseBlockNoCache/renderStreamingMd 逐字节未动,仅 _marked/_purify→getMarked()/getPurify() 经单例 getter),行为零变化 — useMarkdown.ts + AiChat.vue
- [x] B-260615-24 ✅(2026-06-16,待commit) — ~~TaskDetail 描述 v-html=renderMd~~ TaskDetail.vue:33 描述非空 v-html=renderMd(task.description)(空值回退 —)+.ai-md 作用域样式(h1/h2/h3/ul/ol/code/pre/blockquote/table)+onMounted loadMarkdown 预热(单例)。用户报障描述 md 不渲染已修 — TaskDetail.vue
- [x] B-260615-25 ✅(2026-06-16,待commit) — ~~同类 3 组件描述 renderMd~~ ProjectDetail/Ideas/Knowledge 描述展示态 v-html=renderMd(desc)(空值回退 —)+import useMarkdown+onMounted loadMarkdown 预热+各加 .ai-md 样式(Ideas .detail-desc.ai-md/Knowledge .detail-content.ai-md 含 white-space:normal 覆盖原 pre-wrap,正确处理 v-html 标签间 \n;ProjectDetail 同). DOMPurify sanitize 安全。vue-tsc 0 err — ProjectDetail/Ideas/Knowledge.vue

### 🔴 审批执行后中断（2026-06-16 用户报障）

> 用户报障：AI Chat 审批卡片点批准 → 工具执行 → 对话就结束，没续生成下一轮（"不知原因"）。**走查根因**：B-260615-09 的 `GeneratingGuard`(agentic.rs:48-75) Drop 兜底复位 generating，但 `run_agentic_loop:292-300` 审批等待 return（pending>0）路径**既没 `guard.reset()` 也没 disarm** → `guard.done=false` → task 结束 guard Drop(agentic.rs:67-75) spawn `generating=false` → **审批态 generating 被误复位**（与 :82/:299 注释"generating 保持 true"设计意图直接矛盾）。之后 `ai_approve`(commands.rs:187) 调 `try_continue_agent_loop`(agentic.rs:370) 读 `is_generating=false` → `should_continue=false` → emit AiCompleted 不续生成。**竞态**：Drop 的 spawn 复位是异步，与用户点批准的时间差，致时续时断（用户感"不知原因"）。**属 B-260615-09 实施引入的回归**——guard Drop 兜底未区分"审批等待(应保 generating)"vs"异常退出(应复位)"。

- [x] B-260615-26 ✅ GeneratingGuard disarm (第⑩批 2026-06-16) — ~~GeneratingGuard disarm~~ 加 disarm()(行69-71 置 done=true 不 reset generating)+run_agentic_loop 审批等待 return 前(pending_count>0,:308)调 guard.disarm()——保 generating=true 留 try_continue 续,Drop 因 done=true 跳过复位 spawn。修正 B-09 引入回归(审批执行后对话不续生成)。主代理核查:disarm 逻辑正确/cargo check 0 err/df-nodes 21 test pass — agentic.rs
- [x] B-260615-27 ✅ 数据实证 (第⑩批 2026-06-16) — ~~数据侧实证~~ conv 6c2e11f4（U-Ask 概览）消息序列显示 **Low 工具(list_directory/read_file)执行后 loop 正常续生成，Medium/High 审批工具(create_project/create_task/run_command)审批执行后 loop 断**——对话多次断在 tool_result（[10][34][42][70] 后均无 assistant 续），用户被迫反复发"继续"([11][35][43][57][65][68])。坐实 B-26 根因：审批路径 loop return 触发 guard Drop 误复位 generating → try_continue 不续；自动执行路径 loop 不退出故正常。

### 🔴 Task 缺失（2026-06-16 用户报障）

> 用户报障：任务缺失。**DB 实证**（devflow-dev.db）：tasks 表 61 条，**无悬挂引用**（LEFT JOIN projects 验证所有 task.project_id 都在 projects 表），数据完整非丢失。根因在前端：`Tasks.vue:214` onMounted 全量 `loadTasks()` 无参 + 前端 filter（:159-164 activeProject 过滤），**切换项目不重新加载**（仅对已加载全量做前端 filter），新建 task 后其他项目视图不刷新。属 AR-11 数据变更联动同类。另发现：projects 表**重名 meta-kit×2**（f0fa88bf-a4bd / 32de9175-870d），疑重复导入/绑定，可能致用户认知"缺失"。

- [x] B-260615-29 ✅ Task 联动 (第⑩批 2026-06-16) — ~~Task 列表项目切换联动~~ store.loadTasks(projectId?) 已支持可选参(project.ts:124 无需改 store)+Tasks.vue 加 watch(activeProject):非 all 时 loadTasks(projectId) 按项目重载,all 时 loadTasks() 全量。切项目筛选触发后端重载,新建任务跨项目视图同步刷新。只改 Tasks.vue — src/views/Tasks.vue
- [x] B-260615-30 ✅(2026-06-16) — ~~projects 重名核查~~ meta-kit 两条(f0fa88bf/32de9175)结论: **建议不删,保留两条记录**。可能为不同目录绑定或不同导入批次,P2 低优先级,无数据冲突风险。如需去重待用户确认具体哪条 — projects 表

### 🔴 详情页字段布局紧凑化（2026-06-16 用户需求）

> 用户需求：任务详情中除「描述」外的字段（标题/状态/优先级/关联项目/分支/负责人/基础分支/工作流定义/时间），名称与值应**同行展示**（当前分行垂直堆叠，占空间）。**走查**：TaskDetail.vue:27-84 全部 `.info-item` 用 `flex-direction: column`（CSS :221-225）分行。ProjectDetail.vue:69-121 同款布局需一并改。Ideas.vue 用卡片式不同布局不改。

- [x] B-260615-31 ✅ 详情布局同行 (第⑩批 2026-06-16) — ~~TaskDetail + ProjectDetail 字段同行布局~~ `.info-item` flex-direction column→row + align-items baseline + gap 12px + `.label` min-width 88px flex-shrink:0(TaskDetail `.value` 加 flex:1 min-width:0 占余);描述字段 info-item 加 `info-block` class + CSS `.info-item.info-block{flex-direction:column}` 保块状(长文本独占整行);ProjectDetail `.path-row`/`.info-tags` 已 flex-wrap 无溢出风险。vue-tsc 0 err — src/views/TaskDetail.vue + src/views/ProjectDetail.vue

### 🔴 run_command stdout/stderr 恒空（2026-06-16 用户实测）

> 用户实测 F-05 run_command：命令能跑（副作用如写文件发生）+ exit_code 正常，但 **stdout 始终返回空字符串**，echo/Write-Output 等均如此 → "写→跑→**看**→改"闭环断在「看」，跑测试/构建无法获取输出。**根因(铁证)**：`crates/df-execute/src/shell.rs:37-58` 构造 `tokio::process::Command` 时**漏设 `cmd.stdout(Stdio::piped()).stderr(Stdio::piped())`**。tokio 默认 `Stdio::inherit()`（输出继承父进程 Tauri app → 控制台/devnull），`wait_with_output()` 读不到 pipe → `output.stdout/stderr` 空 Vec → `from_utf8_lossy` → 空串。F-05 plan doc 也未提 Stdio 设置，实现遗漏。**PS 静默失败（.Replace()/-replace/Set-Content exit_code=0 文件未变）疑非独立 bug**：修了 piped 后 stderr/stdout 可观测，方能定位是「PS Replace 源不匹配」（AI 用法）还是「cmd /C 调 PS 引号问题」（shell.rs:38 Windows 用 cmd /C，PS 语法引号嵌套易错）。**B-38 待 B-37 修复后据观测重评**。

- [x] B-260615-37 ✅(2026-06-16) — ~~shell.rs execute 补 Stdio::piped()~~ 已确认在代码中(shell.rs:41,46 Windows/Linux 分支均有 .stdout(Stdio::piped()).stderr(Stdio::piped()))。stdout/stderr 正常捕获 — crates/df-execute/src/shell.rs
- [x] B-260615-38 ✅ PS shell_type (第⑩批 2026-06-16) — ~~PS 静默失败根因~~ ShellRequest 新增 shell_type 枚举(ShellType: Cmd/PowerShell/Sh,默认 Windows→Cmd 非Windows→Sh)+ execute 函数改 match 分支(PowerShell→`powershell -NoProfile -Command`,Cmd→`cmd /C`,Sh→`sh -c`)。B-37(Stdio::piped())已确认在代码中。LLM 可通过 shell_type 参数选 PS — crates/df-execute/src/shell.rs

### 🔴 查看灵感来源跳空白页（2026-06-16 用户报障）

> 用户报障：项目详情点「来源灵感」(i18n key `projectDetail.sourceIdea`) 跳转后页面空白。**根因双锁**：① `ProjectDetail.vue:71` `router-link :to="/ideas/${idea_id}"` 指向 `/ideas/:id`，但**路由表(router/index.ts:21-25)只有 `/ideas` 精确路由，无 `:id` 子路由** → hash 路由无匹配 → 渲染空白（路由表无 404 catch-all 兜底）。② 即便补路由，`Ideas.vue` 不读 route params——`:209` import 无 `useRoute` / `:221` 只 `useRouter` / `:448-450` onMounted 只 `store.loadIdeas()` 不接 params → `selectedId`(:230) 纯内部 ref，无法 deep link 选中灵感。**数据层 OK**：`Project.idea_id` 字段(models.rs:38)+晋升写入(idea.rs:124)，ProjectDetail 拿得到 idea_id。**对照**：`/projects/:id` `/tasks/:id` 路由已建(独立详情页)，Ideas 是双栏列表+详情同页故未建 `:id`，但 ProjectDetail 反向溯源需 deep link。

- [x] B-260615-36 ✅ Ideas route.params (第⑩批 2026-06-16) — ~~查看灵感来源跳空白页~~ 修复两层：① `router/index.ts` 加 `/ideas/:id` 路由(name `IdeasDetail` 或复用 Ideas 组件)；② `Ideas.vue` 加 `useRoute` + `onMounted` 读 `route.params.id`→`selectedId.value=id`（loadIdeas 后校验 id 在 `store.ideas` 中，不在则提示「灵感不存在/已删」+清 selectedId）；可选 watch `route.params.id` 支持从详情再点别的灵感切换。— src/router/index.ts:21-25 + src/views/Ideas.vue:209,221,448-450 — source:用户报障(06-15)

### 🟡 AiChat 气泡拷贝能力（2026-06-16 用户需求）

> 用户需求：AiChat 消息气泡支持一键拷贝——**文本拷贝**(content 原文→剪贴板) + **截图拷贝**(气泡 DOM 渲染→图片→剪贴板,粘贴出图片)。**现状**：全项目零 clipboard 实现（grep `navigator.clipboard`/`copy`/`clipboard`/`拷贝`/`复制` 零命中）。气泡结构：用户消息 `.ai-msg-bubble--user`(纯文本 `{{msg.content}}`, :185)；AI 消息 `.ai-msg-bubble--ai.ai-md`(v-html 渲染 MD, :197)。**Tauri WebView2(Edge)**：Clipboard API 完整支持(`writeText`/`write([ClipboardItem])`)，无需额外 plugin（做减法，不用 `@tauri-apps/plugin-clipboard-manager`）。**安全约束**：必须在用户点击事件(手势)内调用剪贴板 API。

- [x] F-260615-13 [P2] — **AiChat 气泡文本拷贝**。每条消息气泡右上角加拷贝按钮(📋 icon,hover 显示)。点击 → `navigator.clipboard.writeText(msg.content)` → 短暂 toast「已复制」。AI 气泡拷原始 content(非 v-html 渲染后 HTML,防 HTML 标签污染);用户气泡同。按钮位置：`.ai-msg-bubble` 内右下角或 hover 时右上角浮现(不占空间,信息密度优先)。i18n 加 `aiChat.copied` / `aiChat.copyMsg`。— src/components/AiChat.vue(:185 用户/:197 AI) + src/i18n (第⑫批销账 06-15)
- [x] ✅(待决策.md已决c不做·2026-06-16) F-260615-14 [P2] — **AiChat 气泡截图拷贝(图片)**。同位置第二按钮(🖼️ 或与文本拷贝合并为下拉)。技术链：`html2canvas(bubbleElement)` → canvas → `canvas.toBlob('image/png')` → `new ClipboardItem({ 'image/png': blob })` → `navigator.clipboard.write([item])`。需加依赖 `html2canvas`(~90KB,或轻量替代 `dom-to-image-more` ~3KB,待选型)。**注意**:html2canvas 对 CSS 支持有限(阴影/渐变/CSS 变量可能不全),但气泡样式简单(ai-md 基本色+圆框),风险低。粘贴目标:Word/微信/钉钉等富文本编辑器可接图片;纯文本框不行(正常行为)。— src/components/AiChat.vue + 新依赖(html2canvas/dom-to-image-more 待选)

### 🟡 AI Chat 交互体验改进（2026-06-14 方案 → 待办化）

> 来源：[aichat交互体验改进方案-2026-06-14.md](./02-架构设计/aichat交互体验改进方案-2026-06-14.md)。7 大类 20 个改进项，按原文优先级分 4 批。**去重**：§1.3 复制 ⊂ F-260615-13/14（已写）、§3.4 新建中断 ⊂ B-260615-10（已做软复位，本文档加 confirm 增强）、§6.2 分离窗口同步 ⊂ CR-24/25（已写）。**状态: 待讨论→看板跟踪**。

**第一批 — 最高频痛点（每次对话都遇到）**

- [x] UX-2025-01 ✅ 选文字(文本偏移量方案) (第⑩批销账 06-15) — **流式渲染中选中文字保持**（§2.1）。现象：AI 流式输出时选文字被 delta 打断丢失选区。方案 A(推荐)：`selectionchange` + 选区在消息容器内 → 暂停 rAF 流式 parse → 选区结束恢复；方案 B：已完成块独立 DOM 节点不参与 v-html 替换。关联 ARC-08 块级 memo（splitBlocks 已按块切分，末块单独更新天然支持方案 B）— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-02 [P1] — **消息操作栏（复制 / 重新生成）**（§1.2+§1.3）。AI 气泡 hover 显示操作栏：「📋 复制」+「🔄 重新生成」。复制 = F-260615-13 合并实现；重新生成：后端删最后一条 AI 消息 + 用倒数第二条 user 消息重跑 agentic loop（新增 `ai_regenerate` IPC）。代码块额外 hover 浮出「📋 复制代码」按钮（§2.2 子项）。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-03 [P1] — **错误气泡操作入口**(error_type 枚举本身 batch34 B-42 已加,本批前端消费)（§4.1）。错误气泡底部增加按钮区：「🔁 重试」（取上一条 user 消息重发）+「⚙ 去设置」（仅 auth/provider 错误时显示，跳 Settings→AI Tab）。后端 `AiError` 加 `error_type: Option<ErrorType>` 枚举（auth/network/timeout/provider_config/unknown），前端据此显隐按钮。— src-tauri/src/commands/ai/mod.rs(AiChatEvent) + src/api/types.ts + src/components/AiChat.vue
- [x] ✅(batch59·2026-06-16·workflow wdlt0xfl6) UX-2025-04 [P1] — **断线保文**（§4.2）。保文核心落地：stream_recv.rs `StreamResult` enum(Complete/Partial/InitFailed 三分支)，MidStream 失败(流中途 chunk error/idle timeout/有 partial_text 未 finished)返 `Partial{partial_text}` **不重试保文**；agentic.rs 收 Partial → 入库 assistant 消息 + emit AiCompleted(incomplete=true，非 AiError) + 追加系统提示「⚠ 响应因网络中断不完整」；useAiEvents.ts:177 补 `AiStreamRetry` case(消除死事件穿透) + AiCompleted case 处理 incomplete(系统提示气泡镜像后端)；types.ts:204 `incomplete?:boolean`。对齐决策 a1(Init 可重试/MidStream 保文不重试) + UX-04 决策 a。**「继续生成」按钮暂未做**(保文核心硬需求已满足，按钮可复用 UX-2025-03 重试能力或按需补)。— src-tauri/src/commands/ai/stream_recv.rs + agentic.rs + src/composables/ai/useAiEvents.ts

**第二批 — 显著提升日常效率**

- [x] ✅(第⑳批 2026-06-16) UX-2025-05 [P2] — **代码块语法高亮 + 复制按钮**（§2.2）。**高亮部分完成**:highlight.js(core+16 语言按需注册,~50-60KB gzip)+ marked.use code renderer→highlightCode(已注册语言按 lang 高亮,否则 highlightAuto)+ github-dark 主题(贴合 #0c0e1a 深底)+ DOMPurify 默认保留 class。**复制按钮留 TODO**(需 AiChat.vue hover 浮层+事件委托,待 UX-02/F-260615-13 合并)。— src/composables/useMarkdown.ts + package.json(highlight.js ^11.11.1)
- [x] ✅(batch41 store + batch43 UI·2026-06-16·workflow wcy478m3y) UX-2025-06 [P2] — **对话搜索**（§3.1）。全闭环:store(batch41 searchQuery+filteredConversations+i18n)+ UI(batch43 AiChat 侧栏 header 搜索框+Ctrl+K 聚焦+平铺渲染+空结果态)。注:无独立 Sidebar.vue,侧栏内联 AiChat.vue
- [x] UX-2025-07 ✅(第⑲批 2026-06-16) — ~~键盘快捷键~~ onGlobalKeydown(AiChat.vue)+onMounted/onBeforeUnmount window listener(对齐 _unlistenToolSlow 生命周期);实现 Ctrl+L(confirmClearChat,streaming 守卫)/Ctrl+B(toggleSidebar)/Esc(confirmState.visible→answerConfirm(false)>props.detached→closeDetachedWindow 优先级);Ctrl+N/K/R 留 TODO(WebView2 浏览器级 preventDefault 不可靠待实测);Esc 冒泡与 textarea handleKeydown 不冲突(skill 浮层 Esc 由 textarea handler 先消费)。vue-tsc 0 err。原设计:Ctrl+N 新建/Ctrl+K 搜索/Ctrl+L 清空/Ctrl+Shift+C 复制/Ctrl+R 重新生成/Esc 关闭/Ctrl+B 切侧栏 — src/components/AiChat.vue
- [x] ✅(batch47·2026-06-16·workflow wjkywbhff) UX-2025-08 [P2] — **新建对话 confirm 弹窗**（§3.4 增强）。已落地(confirmNewConversation handler + 两处新建按钮改造 + 复用 useConfirm/ConfirmDialog + i18n)。注:生成态判定用 isViewingGenerating(非 state.generating 实际是 streaming),commands.rs 零改(复用 store.newConversation 软复位 B-260615-10)

**第三批 — 锦上添花**

- [x] ✅(batch51·2026-06-16·workflow wdfffj1ti) UX-2025-09 [P2] — **编辑已发送消息并重新生成**（§1.1）。全栈落地:**架构发现** messages 是 ai_conversations JSON 列非表行,status 加 ChatMessage struct(向前兼容老 JSON)非 V17 迁移。df-ai provider.rs status+is_active / context.rs sanitize 过滤 truncated+replace+truncate / commands.rs ai_chat_edit IPC(复用 ai_regenerate spawn)/ api+useAiSend editMessage / AiChat 末条 user 编辑按钮 / i18n 4 key。只末条 active user 可编辑+generating 拦截+truncated 软删保留 DB 不进上下文/视图
- [x] ✅(batch52·2026-06-16·workflow w91svrokz) UX-2025-10 [P2] — **@ 实体引用**（§1.4）。全栈落地:AiChat @ popover(复用技能架构 .ai-mention-* + detectMentionTrigger 排除邮箱 + selectMention [类型:名] 插入 + 键盘导航互斥技能 + 项目/任务各 20 分组)+ prompt.rs 任务段注入(state.tasks.list_active,**签名零变化**降级避碰 ai_chat_send 领地)+ i18n 5 key。降级:实体 id 未透传(凭名称+状态对齐),最近文件联想无 store 略
- [x] UX-2025-11 [P2] — **输入框高度提升**（§1.5）。textarea 最大高度 120px→200px（~10 行），超过后再内部滚动。或可拖拽 resize handle。— src/components/AiChat.vue (第⑬批销账 06-15)
- [ ] UX-2025-12 [P2] — **历史消息分页懒加载**（§2.3）。⏸️ **暂缓**：当前典型对话 <100 条消息，v-for 全量渲染无卡顿；等用户反馈长对话卡顿再做。switchConversation 首次加载最近 50 条，滚顶加载更多（需后端 offset/limit 支持 messages 查询）。或前端虚拟滚动替代。关联 B-260629(messages 无上限)。— src/stores/ai.ts + src/composables/ai/useAiConversations.ts + 后端 conversation.rs
- [x] UX-2025-13 [P2] — **消息时间戳**（§2.4）。气泡下方极小字号(dim 色)展示相对时间（复用 time.ts 已有 common.justNow 等 key）；hover tooltip 展示完整时间。— src/components/AiChat.vue (第⑬批销账 06-15)
- [x] ✅(第㉒批 2026-06-16) UX-2025-14 [P2] — **Provider 切换反馈**（§5.1）。cycleProvider(L739-748)主体切换逻辑保留,末尾加 showProviderSwitched:复用 AiChat 自管 showToast(info 型,model 名插值 aiChat.providerSwitched)+ 临时 bar(providerBarVisible + Transition ai-provider-switch 0.15s 淡入/淡出 + 2000ms 自动淡出,展示 aiChat.providerSwitchHint model 名小字+accent 脉动圆点)。toast 调研:AiChat 已有自管 reactive showToast(分离窗口无根 toast 故自管),复用无降级。_providerBarTimer onBeforeUnmount 清(L1067)。stores/ai.ts 未改(cycleProvider 在 AiChat.vue 内)。— src/components/AiChat.vue + i18n(aiChat.providerSwitched/providerSwitchHint zh/en)
- [x] ✅(第㉓批 2026-06-16) UX-2025-15 [P2] — **技能参数用法展示**（§5.2）。浮层 .ai-skill-item 改 column:.ai-skill-item-row(name/desc/src 单行)+ code.ai-skill-item-hint(v-if s.argument_hint,等宽 dim 11px,L390)。chip(L354-361):.ai-skill-chip-main(name+desc+code.ai-skill-chip-hint v-if pendingSkill.argument_hint L358)+ x 关闭(@click clearSkill,补 aria-label=aiChat.clearSkill 无障碍)。复用 var(--df-font-mono)+ui-monospace fallback。**JS 零改动**(仅 template 结构+CSS,/ 联想/selectSkill/clearSkill/Escape/Enter 逻辑全保留)。i18n 无新增(argument_hint 是 skill 数据自带,复用 aiChat.clearSkill)。— src/components/AiChat.vue
- [x] ✅(第㉑批 2026-06-16) UX-2025-16 [P2] — **侧栏宽度可拖拽**（§6.1）。右边缘 4px 拖拽条(col-resize,hover accent 高亮),mousedown→document mousemove(startWidth+delta)→mouseup;clamp 120-280(setSidebarWidth 内兜底 NaN/负数/缺省)。**持久化**:move 高频只改 state 不落盘,up 一次性 persistUiState 写 df-ai-ui(复用现有机制,App.vue key 已注册)。**宽度绑定 CSS 变量** `--sidebar-width`(非内联 width,保 sidebar-slide 折叠过渡不被压过)。**卸载兜底** onBeforeUnmount 摘 listener 防泄漏。body.ai-sidebar-resizing 锁 cursor+user-select 防抖动/误选。— src/components/AiChat.vue + src/composables/ai/useAiPanel.ts + src/stores/ai.ts(sidebarWidth:200) + i18n(aiChat.sidebarResizeHint zh/en)
- [x] ✅(batch49·2026-06-16·workflow wkan76fon) UX-2025-17 [P3] — **对话置顶**（§3.2）。全栈落地(df-storage pinned 字段+V16迁移+set_pinned + IPC ai_conversation_set_pinned + stores/AiChat 排序 pinned DESC,updated_at DESC + 3 处图钉按钮)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue,conversation.rs:141 补 pinned:false 必填(跨 batch44 领地 proactive-bug-fix),filteredConversations 搜索态未动(降级)
- [x] ✅(batch46 后端 + batch48 前端·2026-06-16) UX-2025-18 [P3] — **对话导出**（§3.3）。全闭环:后端(batch46 ai_conversation_export IPC commands.rs:843-911)+ 前端(batch48 exportConversation 封装 + AiChat 3 处导出按钮 markdown/json/txt 菜单 + Blob 下载 + sanitizeFilename + 外部点击关闭)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue
- [ ] UX-2025-19 [P3] — **消息列表虚拟滚动**（§7.2）。⏸️ **暂缓**：同 UX-12，当前数据量无需虚拟滚动；与 UX-12 合并为一条观察项，等用户反馈长对话卡顿再启动。长对话几百条消息全量渲染卡顿；集成 vue-virtual-scroller 或自研 IntersectionObserver 懒渲染；流式最后一条始终保持挂载。— src/components/AiChat.vue + 新依赖
- [x] ✅(第㉒批 2026-06-16) UX-2025-20 [P3] — **空状态引导 + 标题过渡**（§7.3+§7.4）。空态:messages 空+非流式时 .ai-empty-prompts 2 列网格 4 示例卡片(点击 sendExamplePrompt 填 inputText+复用 handleSend 发送)。无 provider 显「去设置」按钮(goToSettings:嵌入 router.push('/settings'),分离窗口降级 showToast 提示去主窗口)。标题淡入:titleFlash ref + watch(activeConv.title)空→非空(loadConversations 回填触发)置 flash 驱动 0.3s keyframes(opacity+transform)+ AI 生成标识图标(success 对勾,仅 flash 期显,绑定 conv.id===activeConversationId)。_titleFlashTimer 320ms 清 + onBeforeUnmount 兜底(L1065)。— src/components/AiChat.vue + i18n(aiChat.examplePrompt1~4/goToSettings/goToSettingsHint zh/en)

### 🟡 AI Chat 授权体验改进（2026-06-14 方案 → 待办化）

> 来源：[aichat授权体验改进方案-2026-06-14.md](./02-架构设计/aichat授权体验改进方案-2026-06-14.md)。6 痛点 + 8 改进项，按原文优先级转待办。**状态: 待讨论→看板跟踪**。

**P0 — 快速改善体感（第一批建议落地，~2 天工作量）**

- [x] AE-2025-01 [P0] — **批量审批**。同一轮多 pending 时，ToolCardList 顶部显示「全部批准(N) / 全部拒绝」按钮。改动：①`ToolCardList.vue` 加批量操作栏（监听 pending_approval 数量）②`useAiSend.ts` 新增 `approveAll(rejectAll)` 循环调 `ai_approve`。预估 0.5 天 — src/components/ToolCardList.vue + src/composables/ai/useAiSend.ts (第⑫批销账 06-15)
- [x] AE-2025-02 [P0] — **审批计数器 + 跳转**。输入框上方/header 显示 `⏳ N 项待审批` 徽标，点击跳转到第一个 pending 卡片。改动：①`AiChat.vue` header 区域加审批徽标 ②`ToolCardList.vue` 暴露 `scrollToFirstPending` 方法。预估 0.5 天 — src/components/AiChat.vue + src/components/ToolCardList.vue (第⑬批销账 06-15)
- [x] ✅(batch60·2026-06-16·workflow whae812z5+主代核查·路径B,cargo+vue-tsc 0err) AE-2025-03 [P0] — **write_file diff 预览** ✅**已落地(batch60 路径B)**:后端 write_file **未返 diff**(`tool_registry.rs:694` 结果 payload `{path,bytes_written,old_size,encoding}` 无 diff 字段,`generate_diff` 仅 patch_file 调用),前端 ToolCard 无 diff 可渲染,审批 payload `AiApprovalRequired`/`PendingApproval` 亦无 diff(只有 `args={path,content,encoding}` 纯新内容,拿不到旧文件)。需**后端先补**再追加前端渲染。**两条路径**(均 src-tauri 改动,跨 mod.rs/audit.rs/types.ts/AiChat.vue 领地):路径 B(推荐,满足审批预览诉求)审批 payload 加 `diff` 字段 + audit.rs:555 挂起审批前预读旧文件 + `generate_diff` 注入事件;路径 A 仅 write_file handler 补 diff 但结果阶段才有(审批时看不到,不满足)。前端部分待后端 diff 返回后追加 `<pre>` 红删绿增渲染。原诉求:write_file 审批时若文件已存在，展示前后对比 diff 而非裸 content（content 可能几百行代码截断到 300 字符）。改动：①后端 `tool_registry.rs` write_file handler 执行前读旧文件返回 diff ②`ToolCard.vue` pending_approval + name=write_file 时渲染 diff 视图（红删绿增）。关联 F-260615-10(diff 工具)可复用。预估 1 天 — src-tauri/src/commands/ai/tool_registry.rs + src/components/ToolCard.vue

**P1 — 增强控制力**

- [ ] AE-2025-04 [P1] — **会话级授权（Session Trust）**。替代原"全局宽松模式"。核心：信任是上下文相关的——同一会话内用户批准过某类操作后，后续同类操作自动放行；换会话清空重审。模型：AiSession 加 `session_trust: HashSet<TrustKey>`（内存态，随会话销毁）；TrustKey 按操作+目标范围粒度（如 `Write { dir: "src/" }`、`Execute { dir: workspace }`）。流程：首次操作→pending_approval 用户批准→记录 TrustKey → 同会话后续同类操作命中则自动放行（消息流显示轻量提示 `🔓 自动放行: write_file(xxx)`）。**白名单收紧决策**：纯读取(list_*/read_*/list_directory)始终自动放行；创建/修改/绑定/覆盖写入/高危全部需审批或会话级授权放行（bind_directory 归类为修改操作；write_file 覆盖写入已出事故不放行）。**首批实现**：write_file + run_command（写→跑→看→改闭环最高频连续操作）；create/update 后续按需加。改动：①`mod.rs` AiSession 加 session_trust 字段 + TrustKey enum ②`audit.rs` process_tool_calls 审批前查 session_trust 命中则跳过 pending ③审批通过时写 session_trust ④前端自动放行操作显示轻量 toast 提示。关联 AE-05(High 二次确认: 会话级授权的操作是否也需二次确认? 建议: 首次审批走二次确认,后续自动放行跳过)。预估 1.5 天 — src-tauri/src/commands/ai/(mod.rs|audit.rs) + src/components/AiChat.vue — **设计决策(2026-06-16)**: 目录级粒度(非文件级)、仅写+执行首批、Webhook 未来走独立 execution_token 链路不影响本设计
- [x] AE-2025-05 ✅(第⑲批 2026-06-16) — ~~High 二次确认~~ onApprove async:approved&&HIGH_RISK_TOOLS.has(tc.name)(delete_task/delete_project/restore_project/purge_project/delete_file/run_command 6 工具白名单,后端不回传 risk 故按名判定)→await confirmDialog(highRiskConfirmMsg)在 approving 置 true 前,!ok return 不进 loading 态可重试;拒绝路径不加确认(无害);approving loading+130s 兜底(B-260616-08)原样保留。每卡独立 useConfirm 实例不串台。i18n confirmHighDelete/Exec/Generic zh/en 对称。vue-tsc 0 err。TODO:后端回传 risk_level 后改读字段免维护白名单 — src/components/ToolCard.vue
- [x] AE-2025-06 ✅(第⑲批 2026-06-16) — ~~审批超时自动 reject~~ 纯前端定时器 APPROVAL_TIMEOUT_MS=5min(useAiSend.ts Map<id,timer>+start/clear/clearAll 三函数);启动挂 AiApprovalRequired(非 approveToolCall,agent 修正正确:审批开始是事件点非点击末态);Completed/ApprovalResult(!approved) clear 单条;onStreamTimeout 经 Tauri 事件总线 emit ai-approval-clear-timers→useAiEvents listen clearAll(破循环依赖 useAiStream 不能 import useAiSend);stopListener 卸载清;超时调 ai_approve(id,false)+push 系统错误消息(⚰ i18n ai.approvalTimeout)。vue-tsc 0 err。TODO:Settings 可配(toast 跨层接线改 push 消息参照 onStreamTimeout 模式,未碰 Settings god file)。行为变更但决策已定。原:pending_approval 超时自动 reject（默认 5 分钟），防对话永久卡死。**决策：纯前端定时器**（审批依赖页面交互，页面关闭 auto-reject 合理；不改后端）。超时后 toast 提示「⚰ 审批超时,已自动拒绝: {tool_name}({reason})」+ 调 ai_approve(id, false)。Settings 可配超时常量(3/5/10min)。**范围限定**：仅覆盖人工审批场景。未来 Webhook/外部集成走独立 `execution_token` 预鉴权链路（不经过 pending_approvals），不影响本设计。预估 0.5 天 — src/composables/ai/useAiSend.ts + src/views/Settings.vue

**P2 — 信息透明度**

- [x] ✅(第⑳批 2026-06-16) AE-2025-07 [P2] — **Agentic 进度条**。AiChat 输入区上方加 `.ai-agentic-progress`(v-if streaming&&agentRound>0),useAiEvents AiAgentRound(round>0 写,round==0 占位不覆盖避免闪烁)+ AiCompleted/AiError 复位清零。**实际显**:round(后端 AiAgentRound.round)+ pending(state.pendingApprovals.length 条件附加)。**降级省略**:max(后端 MAX_AGENT_ITERATIONS=10 常量未透传)+ completed(无累计计数器),均留 TODO。stores/ai.ts 加 agentRound 字段。单轮问答不收 AiAgentRound 故恒隐藏 ✓。— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts + src/stores/ai.ts + i18n(ai.agenticProgress/agenticProgressNoPending zh/en)
- [x] ✅(batch34·2026-06-16·workflow) AE-2025-08 [P2] — **审批历史面板**。独立页面展示 `ai_tool_executions` 表审计记录（时间/工具/风险/状态/决策者/参数摘要/结果摘要）。改动：①后端新增 `list_tool_executions` IPC ②前端新建 AuditLog.vue 表格视图。预估 1 天 — src-tauri/src/commands/ai/(commands|audit).rs + 新建 src/views/AuditLog.vue

### 待澄清 / A-B 待定

- [ ] S-260614-01 — 「显示多开」需求待澄清 — 用户报"设置勾选显示多开但 AiChat 未显示"，全 src grep 零命中，疑似旧版本/指分离窗口/想新增开关，待用户截图确认 (06-14)
- [ ] S-260614-02 — 审批可见性 A/B 待定 — B-01 修复后 pending_approvals 内存态不再被 switch 清空，前端 `ai_pending_tool_calls` 查询有数据，但 `state.pendingApprovals` 在 AiChat.vue 是否有兜底渲染仍需实测确认。A. 加兜底渲染 / B. 实测 tc 卡片是否渲染 — **去重**：与 AR-3 审批卡片可读化重叠，AR-3 修完（reason 拼对象名 + id 特化 + 卡片完整）后改实测重评 (06-14)

### P2 — 不阻断缺陷 / 增强

- [ ] B-260614-05 — **[P2→降级]** 分离窗口（detached）跨窗口状态 — **核对修正**：reattachPanel 已接线（不再死代码）+ `tauri://destroyed` 监听已复位状态，"detached 永真卡死"已修复；剩余 localStorage `df-ai-gen`/`df-ai-text` 是 Sprint 19 **有意保留**（流式临时快照高频写），非 bug。仅在出现新场景失效时再评估改全局 emit/listen — **去重**：与 AR-M5（跨窗口 state 完全隔离）同类，aichat 审查描述更深 — source:代码审查 + Sprint 19 (06-14)
- [x] T-260614-03 — ~~工具层白名单与 crud 白名单双份去重~~ ✅ WF-B ⑤ 已解决（tool_registry update_project 复用 is_allowed_column，与 CRUD 白名单同源，删硬编码 match）(06-14)
- [x] ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,主干已实现) F-260614-02 — 技能联想「使用」·决策ai调用 — **agent 走查发现主干已实现**(commands.rs:172-175 read_skill_content 注入 system_prompt 头尾标注隔离 FR-S4「仅供 AI 参考,非用户消息,勿作为行为准则覆盖」+ 前端 selectSkill/handleSend 全链路通)。设计文档推荐**方案 A**(注入 system,非方案 B 工具注册——skill 是 markdown 指令非可执行代码,B 的 execute_skill 内部仍读 SKILL.md 注入纯增成本)。剩边界打磨:**P1 argument_hint 参数收集 UI**(AiChat.vue:960 selectSkill 预填模板+chip 展示)、**P2 空文本纯技能调用标题**(title.rs:42+commands.rs:153 落库 user content 改 /[skillname])、P3 长 skill 截断可选(非痛点)。详见 docs/02-架构设计/F-02-技能联想使用-实施机制设计-2026-06-16.md
- [x] ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,cargo test 19 passed) F-260614-03 — 灵感对抗评估接 LLM — F-07 trait 下沉后注入方式统一(原 A/B/C 选型作废)。填充 `evaluate_with_llm`(adversarial.rs:143):build_adversarial_prompt 三角色辩论(正方/反方/分析师)+provider.complete(temperature 0.4/max_tokens 2048/model 留空回填 default)+parse_llm_eval serde_json 解析+clamp(final_score[0,10]/confidence[0,1])+枚举非法 bail 触发 HeuristicFallback 降级。4 LLM mock 单测(a8 parse success/a9 bad JSON fallback/a10 bad enum fallback/a11 value clamp)。原 7 heuristic+scoring 8 不回归。— crates/df-ideas/src/adversarial.rs
- [x] T-260614-04 — ~~路径校验根治~~ ✅ 已完成（resolve_workspace_path 加 canonicalize 防 symlink 逃逸 + 词法 starts_with 兜底；仅校验、返回词法路径保持前端友好；cargo check 0 err / 22 test pass）(06-14)
- [ ] F-260614-04 — 多 Provider 负载均衡池 — 备用模型/多账号聚合，全局容量=min(各 provider 上限之和, global_cap) (06-14)
- [ ] F-260614-05 — 模型能力系统 Phase 2 — 多模态消息支持：ChatMessage.content: String → Vec<ContentPart>(Text/Image)；前端粘贴/拖拽图片；vision 模型自动路由 (06-14)
- [x] ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+vue-tsc 0err) F-260614-06 — 导入历史项目（scan 第二步） — 6 决策全落地:①description 走 LLM(scan.rs extract_description 复用 complete)②采样扩 `images:Vec<ImageRef{alt,src}>`(scan.rs:330/343)+readme 剥 frontmatter/TOC/纯徽章行截 8KB(SAMPLE_README_MAX 2000→8000:346)+徽章域黑名单 5 域(shields.io/badge.fury/badgen 等:397-401)+is_badge_image+is_pure_badge_line③image 多模态留接口待 F-260614-05④monorepo 一层(is_monorepo:112+discover_projects:156 展开 packages/*/apps/* detect_stack 空过滤)⑤批量(scan_directory_for_projects:343 纯规则发现标已绑定 + import_projects_batch:415 并发 LLM llm_concurrency permit 限流非原子逐项独立)⑥抽 create_with_binding(:59 create/import 共用 缓解 :211 TODO)。前端 Projects.vue 导入 modal+api/project.ts 两 API+i18n 双语+scan.rs 单测全。— crates/df-project/scan.rs + src-tauri/commands/project.rs + lib.rs + api/project.ts + Projects.vue + i18n。详见功能决策记录(06-14)
- [x] T-260614-09 — ~~idea.rs 物理删不级联~~ ✅ WF-E 完成（idea.rs:149 delete→purge_with_descendants，1 行，签名兼容）(06-14, commit 89da9fa)
- [x] T-260614-10 — ~~findBinding canonicalize~~ ✅ WF-E 判定已解决（normalize_path 已含 canonicalize 优先 + 词法回退，find_binding_conflict 两端对称已用，无需重复加；零改动）(06-14, commit 89da9fa)
- [x] ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+7单测) F-260614-07 — **[架构前置]** df-ai-core trait 下沉拆 crate — 4 决策全落地:①df-ai-core 新 crate(仅 trait+数据结构 LlmProvider+ChatMessage 等,ContextManager/TokenEstimator 留 df-ai)②构造注入 Engine::new(Arc<dyn LlmProvider>)+Engine::heuristic()(idea.rs Some/None 分支,语义等价决策② Option 参数)③LLM 失败降级 EvaluatedBy 三态(Llm/Heuristic/HeuristicFallback:28)+warn④provider 应用层装配 idea.rs build_default_provider(DB is_default→build_provider→Option<Box>→Arc::from:192)。df-ai provider.rs:15 `pub use df_ai_core::provider::*` re-export(外部 use 路径不变,df-nodes 零改动)+lib.rs:17 `pub use df_ai_core`。**解锁 F-03 注入**(其他 crate 依赖 df-ai-core trait 非 df-ai impl)。— crates/df-ai-core(新)+crates/df-ai+crates/df-ideas+src-tauri/commands/idea.rs。详见设计文档 F-07-df-ai-core-trait下沉设计
- [x] ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-08 — 决策治理产品化 — 活契约/规格契约自检机制产品化为可操作功能（当前散落文档机制） — source:功能决策记录缺口
- [x] ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-09 — 项目 status 字段治理 — status 状态机规范化（planning/active/archived 等枚举约束） — source:功能决策记录缺口
- [x] ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-10 — 知识库 MCP Server + Tier 2/3 — 对外 MCP 暴露 + 分层存储（当前仅 Tier 1 全栈） — source:PROGRESS Sprint15
- [x] ⏸️(待决策.md已决暂缓·2026-06-16) T-260614-11 — 全局#6 条件表达式引擎升级 — df-workflow conditions 仅支持 true/false 字面量，升级为真表达式求值 — source:PROGRESS 全局问题
- [x] T-260614-12 — ~~df-ideas 死代码~~ ✅ WF-E 部分完成（capture.rs 删 CaptureInput/IdeaCapture 死码，保留 Idea/IdeaScores 共享实体；promotion/scoring/adversarial 内"两套 Recommendation/PromotionPolicy 死枚举"嫌疑 agent 未确认存在/保留为对外契约，本次未动，待复查）(06-14, commit 89da9fa)
- [x] F-260615-01 ✅(2026-06-16,待commit a3cccb070fe9c8821) — **[P1 功能增强]** HumanNode 审批节点支持自定义选项 + 单选/多选类型。现状：config `options: Vec<String>` 已支持任意数量（2/3/…数量扩展已通），但 `decision` 是单 `String` 仅单选语义，`options` 空=自由文本。增强目标：不止「同意/拒绝」二选一，可配置 N 个候选项 + 单选(single)/多选(multiple)两种类型。**改动面**：①`df-core/events.rs` `WorkflowEvent::HumanApprovalRequest` 加 `select_type`、`HumanApprovalResponse` decision 单值→多值（`decisions: Vec<String>` 或保留 decision 兼容 + 加 decisions）②`human_node.rs` config 解析 `select_type` + 校验（多选时每项 ∈ options，可加 min/max 选中数约束）③IPC `approve_human_approval` 签名 ④前端 `stores/project.ts` approve + `api/types.ts` 事件类型 + 审批弹窗 UI（单选 radio / 多选 checkbox）⑤单测改断言 + 新增多选/超限测。**注意**：向后兼容现 single 调用方，`select_type` 缺省 = single — source:用户需求(06-15)，crates/df-nodes/src/human_node.rs + src-tauri/src/commands/workflow.rs + src/stores/project.ts — **✅已实施(a3cccb070fe9c8821,2026-06-16,待commit)**:6 文件契约向后兼容(events.rs SelectType 枚举 Single/Multiple 缺省 Single+HumanApprovalResponse decision+decisions 双字段/human_node.rs 校验 single len==1·multiple len≥1·∈options/workflow.rs IPC 加 decisions+select_type Option 缺省兼容/types.ts/project.ts approve/ProjectDetail.vue checkbox 多选 UI)。cargo check 0 err/df-nodes 21 test(含 4 新增多选)/vue-tsc 0 err。主代理核查契约向后兼容 + 6 文件边界
- [x] F-260615-02 ✅(2026-06-16,待commit) — **[P1 功能]** task 详情查看。✅已实施:get_task_by_id IPC(复用 TaskRepo::get_by_id ok_or_else 转 Result)+lib.rs 注册+taskApi.get+/tasks/:id 路由+TaskDetail.vue(11 字段:标题/描述/状态/优先级/关联项目 router-link 解析名/分支标签/负责人/基础分支/工作流定义/创建更新时间;复用 constants/project 标签+formatDate+watch route.params.id 重载)+Tasks.vue 列表项 @click router.push;TaskRecord TS 类型已存在无需新增;主代理核查 cargo check 0 err/vue-tsc 0 err/git diff 6 文件边界干净。现状：`src/views/Tasks.vue` 仅列表，无独立 TaskDetail 视图/路由（grep 仅 `ProjectDetail.vue` 嵌套任务命中，无独立详情页）。需求：点击 task 查看详情。**数据模型已就绪**（`df-storage/src/models.rs:53` `TaskRecord` 12 字段：id / project_id / title / description / status / priority / branch_name / assignee / workflow_def_id / base_branch / created_at / updated_at）。**改动面**：①新建 `src/views/TaskDetail.vue` 视图 + 路由（`router/index.ts` `/tasks/:id`）②task 详情 IPC（`get_task_by_id`，核对 `commands/task.rs` 现有 IPC 是否已有，无则补）③`Tasks.vue` 列表项点击 → 跳详情 ④详情页字段展示（title/description 渲染、status/priority 标签、关联项目名解析 project_id→name、branch/assignee 信息、时间戳）⑤可选：详情页内编辑（`update_task` IPC 已存在，FR-D6）。— source:用户需求(06-15)，src/views/Tasks.vue + src/router/index.ts + src-tauri/src/commands/task.rs
- [x] F-260615-03 ✅ truncated 标志 (第⑩批 2026-06-16) — ~~list 工具截断标志~~ (list_trash补漏) 原完整分页方案，对抗分析降级。现状：4 工具 `truncate(50)` 硬截断，AI 不知被截断会回复「不存在」。**对抗分析裁决**:数据量未到 50 条线(projects=5, tasks=61)，完整分页 breaking change 风险 > 收益 → **降级为最小修**:不改返回结构(non breaking),只在 truncate 时加 `truncated: true` 标志 + 工具描述注明"最多返回 N 条"。未来数据量到时再升完整分页(F-03-full)。**改动面**:tool_registry.rs 4 handler 截断处加 truncated bool 返回 — src-tauri/src/commands/ai/tool_registry.rs:127-165,377-379 — source:用户需求(06-15)+对抗分析裁决(06-15)
- [x] F-260615-04 ✅ 卡片分组增强 (第⑩批 2026-06-16) — ~~read_dir / read_file 工具卡片连续时折叠/收起~~ 提高信息密度。现状：AI 探查目录常连续调多个 read_dir + read_file（先列目录再读多个文件），每个结果独立卡片平铺，长列表/大文件内容占满屏幕，信息密度低。需求：相邻同类读取卡片支持折叠——默认收起只显摘要（如「read_dir: 12 项」「read_file: src/main.rs (234 行)」），点击展开看详情；或连续 N 个同类卡片归组折叠。**改动面**：①`ToolCard.vue` 加折叠态（`collapsed` ref + 摘要/详情双视图 + chevron 图标 + 高度过渡）②连续同类检测/归组（`ToolCardList.vue` 按 `tool.name` 分组，已有列表容器适合放分组逻辑）③摘要提取（read_dir 数项数 / read_file 文件名 + 行数，解析 result）④折叠态持久化（可选，localStorage 按 conv）。关联信息密度构想（memory: devflow-info-density-concept，卡片折叠是其中一环）。— source:用户需求(06-15)，src/components/ToolCard.vue + src/components/ToolCardList.vue — **勘察完成(2026-06-16,wxflofhf2)**:feasible/plan 11 步跨 5 文件(ToolCardList/ToolCard/useAiSend/stores/ai/global.css)。risk 标低但实为 UX 新行为+改核心 ai 状态文件(useAiSend.ts/stores/ai.ts)+plan 细节有误(useAiSend composable 无 emit 方法)。**拆小或留待**:先做 ToolCardList 分组+单卡折叠摘要(限定不碰 useAiSend/stores),完整折叠交互归信息密度构想单独立项
- [x] F-260615-07 ✅ search_in_file (第⑩批销账 06-15) — ~~read_file 加 search 参数~~ 已落地（见 :493 search_in_file 同条）。**[P2 功能]** read_file 加 search 参数（单文件内关键词定位）。现状：read_file 仅 offset/limit 分页（tool_registry.rs:458-501），无原生搜索——AI 找关键词需多次分页扫描定位，token 浪费 + 多次 IPC。用户实测确认 offset 0基 + limit=1 单行精确读取可用，但定位靠 AI 多轮分页。**方案**：① schema 加 `search`（string，可选）参数 ② handler 内若有 search：读全文（1MB 限内）→ 按行过滤含 search 的 → 返回 `matches: [{line, content}]`（line 为 1基行号，须在描述注明与 offset 0基的差异）③ 匹配超量：默认返前 50 匹配 + total + has_more。**改动面**：tool_registry.rs read_file handler（:458-501）schema 加参数 + 返回结构扩展（有 search 返 matches，无 search 走原 content 路径，两路径并存）。**权衡**：vs 独立 grep 工具——read_file search 满足单文件定位主场景（最小改动，复用现有工具）；跨文件搜索待 B-260615-37（run_command stdout）修后用 grep/findstr 验证够不够用，不够再立 grep 工具（对标 Claude Code Grep）。**大小写**：默认区分（确定性优先），case_sensitive 参数先不加（做减法）。— source:用户建议(06-15)，src-tauri/src/commands/ai/tool_registry.rs:458-501

### 🟡 文件操作工具集补全（2026-06-16 能力梳理·设计定稿 V2）

> 用户给出 5 个工具具体 API 设计。**核心原则不变**：专用工具补 run_command 做不好/不安全的；系统命令类交给 run_command（B-37 修后）。**修正**:search_files 从"run_command 覆盖"升级为"值得做"(结构化 JSON > parse find 输出)。

**5 工具设计(用户方案)**:

- [x] F-260615-06 ✅ patch_file (第⑩批 2026-06-16) — ~~patch_file(edit_file)~~ ~50行 handler → 见下方对抗分析区 :503 完整条目（含设计文档指针）
- [x] F-260615-07 ✅ search_in_file (第⑩批销账 06-15) — **[P1] search_in_file**（内容搜索）— `path, keyword, (可选)regex, case_sensitive` → `[{line, content, column}]`。纯子串搜索先做(regex/case 后加,做减法)。替代 AI 多轮分页扫描 — tool_registry.rs read_file handler 扩展
- [x] F-260615-08 ✅ file_info (第⑩批 2026-06-16) — ~~file_info（元信息）~~ `path` → `{exists, size, lines?, modified, is_binary, is_dir}`。**不读内容**,快速元数据。lines 用 `\n` 计数(不全文读);二进制不报 lines — tool_registry.rs 新增
- [x] F-260615-09 ✅ append_file (第⑩批 2026-06-16) — ~~append_file（追加写入）~~ `path, content` → `{bytes_written, new_size}`。日志追加/增量写入,避 read-merge-write 竞态 — tool_registry.rs 新增
- [x] F-260615-12 ✅ search_files (第⑩批 2026-06-16) — ~~search_files（文件名/glob 搜索）~~ `path, pattern: "*.ts", recursive?` → `[{path, size}]`。结构化 JSON > parse find 输出(AI token 效率+可靠)。从"run_command 覆盖"升级(原归类为不做的系统命令类) — tool_registry.rs 新增

**后续(P3/合并)**:

- [x] F-260615-10 [P2] **diff**（修改前对比,防误覆盖）— 并入 patch_file 增强(edit 前 return diff 给审批卡看清改动),或独立工具 — 关联 F-06 (第⑬批销账 06-15, 后端diff已返回, 前端审批卡diff渲染待续)
- [x] F-260615-11 [P3] **write_file base64**（二进制写入）— 图片/PDF/Excel,场景窄 — tool_registry.rs:519 (第⑯批销账 06-15, encoding 参数+base64 decode+字节写入)

**新增(根治 AI rename 静默失败)**:

- [x] F-260615-15 [P1] ✅(第⑮批销账 06-15) — **rename_file（重命名/移动,一个工具覆盖 rename+move）**。文件系统层两者同一操作(改路径),DRY 合一。API: `from, to, overwrite?=false` → `{bytes_moved, cross_volume}`。实现:同卷 `tokio::fs::rename`(atomic,Windows 走 `MoveFileExW` UTF-16 wide API,**中文路径零字符集问题**);跨卷 `tokio::fs::copy`+`remove_file`(非原子,失败回滚删 to 保 from 完整)。**安全**:①from/to 双向 `validate_path`(黑名单+`..`防遍历) ②overwrite 默认 false(目标存在拒绝,防误覆盖) ③RiskLevel::Medium(改文件系统结构,类 write_file,需审批)。**为什么不用 run_command**:run_command 走 shell.rs 的 PS/cmd 链(B-37/B-38 中文路径 GBK 解码 UTF-8 → mojibake → exit 0 静默失败,老毛病);本工具 Rust 原生 std::fs 绕开整个 shell 层,根治 AI rename 的静默失败类。关联 [[patch_file 工具设计]] 同源(文件操作原生工具化,不依赖 shell)。— src-tauri/src/commands/ai/tool_registry.rs 新增 handler(~40 行)
- [x] F-260615-16 [P2] — **delete_file（删除文件,可选）**。`path, permanent?=false` → `{deleted, backed_up}`。默认非真删(移 `.trash` 或 `.bak`,对齐 WF-A soft_delete 哲学防误删);permanent=true 走 `remove_file`。RiskLevel::High(不可逆,对齐 delete_project)。**为什么独立于 run_command**:rm/del 无备份无审计;本工具接 ai_tool_executions + 可选 trash。— src-tauri/src/commands/ai/tool_registry.rs

**run_command 覆盖(不单独做)**:压缩/权限/软链/校验/批量
**不做**:文件监听(app 层)/文件锁定(并入 F-06 Mutex)

— source:用户 API 设计 V2(06-15),F-06 设计已定稿见 [patch_file工具设计-2026-06-15.md](./02-架构设计/patch_file工具设计-2026-06-15.md)

### 🟡 对抗分析裁决（2026-06-16 十域十八项交叉验证）

> 来源：用户对抗分析报告（10 域聚类 + 正反双向验证 + 依赖链提取）。**核心价值：6 个否决/暂缓裁决**（"不做"比"做什么"更值钱，避免浪费投入）。完整报告见 docs/02-架构设计/对抗分析裁决-2026-06-15.md（若已落盘）。

**✅ 推进（第一批独立可并行）**：

- [x] ✅(batch61·2026-06-16·workflow wwtn2knn6·2069f79) F-07 trait 下沉 df-ai-core — 已实施(详见 L599 F-260614-07),4 决策全落地,解锁 F-03(已做 dfe0096)/F-01(待推)。本条为对抗分析裁决区过时重复条目,销账。
- [x] R-PD-2 ✅ ScriptNode 不注册 (第⑩批 2026-06-16) — ~~ScriptNode 不注册 script~~ 3 行删除封死攻击面，工作流当前纯演示无真实脚本需求
- [x] F-260615-06 ✅ patch_file (第⑩批 2026-06-16) — ~~patch_file(edit_file)~~ 局部文件更新工具，补齐 AI 文件操作闭环。完整设计见 [patch_file工具设计-2026-06-15.md](./02-架构设计/patch_file工具设计-2026-06-15.md)（API/三层防御/边界情况/替代方案否决/实施步骤）。**核心**: old_text 精确匹配为主+line 辅助+Mutex 并发安全+expected_hash 指纹防脏写。第一批实现核心三件套(~50行)。— src-tauri/src/commands/ai/tool_registry.rs 新增 handler
- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) ARC-06 composable 循环依赖 — **破环全闭环**:①`aiShared.ts` 下沉 findToolCall(反向扫描O(1))+4个审批计时器函数(startApprovalTimer/clearApprovalTimer/clearAllApprovalTimers+APPROVAL_TIMEOUT_MS常量) ②`useAiEvents` 删 findToolCall 导出+改 import 从 aiShared 取 ③`useAiSend` 删审批计时器实现(改从 aiShared re-export)+删 drainQueue 直接调用(改经 `ai-drain-queue` 事件总线桥接) ④`stores/ai.ts` 加 `initDrainQueueListener()` 启动事件监听。**B-260616-19 CSS 缺失**(Knowledge.vue 窄屏标题挤压,agent 未产出 .css/.vue style 变更),待补。— src/composables/ai/aiShared.ts + useAiEvents.ts + useAiSend.ts + stores/ai.ts
- [x] CR-09 .ai-md 样式 5 份→全局 css 抽取 — 零风险减法 (第⑬批销账 06-15)
- [x] F-09 status 对齐前后端 ✅ (第⑯批销账 06-15) — 前端 map 删 in_progress/paused/cancelled(project 维度无数据源/UI 入口/消费方),保留 planning/active/completed。constants/project.ts(三 map 同步删+兜底改字面量)+Dashboard.vue getProjectStage(switch 收敛)+i18n zh-CN/en projects.status & dashboard.stage。后端不动(df-core enum 7 值是独立大改,F-260614-09 专项)。**注**:DB 实际只产生 planning(4 个建项目入口硬编码),真实生命周期=planning+deleted_at 软删二态
- [x] F-260615-03 ✅ truncated (第⑩批 2026-06-16) — ~~最小修（截断标志）~~ **从完整分页降级**:不改返回结构(non breaking)，truncate 时加 `truncated: true`（→ 见上方 :469 完整条目，本条为对抗分析区引用，勿重复实施）
- [x] R-PD-9 ✅ display_hint (第⑩批 2026-06-16) — ~~display_hint 轻量抽取~~ audit 工具名→文案映射作 display_hint，消除双份映射

**🟡 暂缓 / 需重设计**：

- [x] T-11 条件引擎 Phase1+2 🟡 **暂缓** — 引擎设计好但**无前端 UI 消费方**(demoDag edges 无 condition 字段,用户无法配置条件)。等工作流有真实使用场景(BuildNode 落地后跑构建→测试→部署)再推进。决策点 A/B/C(手写求值器/复活 set_skipped/default false+warn)均认可
- [x] F-260615-03 完整分页 🟡 **暂缓** → 降为最小修(truncated 标志)，见上方
- [x] ARC-05 project.ts 上帝 store 拆分 🟡 **需重设计拆分策略** — 不能简单按领域拆(会重蹈 ai.ts 循环依赖覆辙)，需先把越层 invoke 沉到 api/workflow.ts。ARC-06 先做

**❌ 否决（人定"不做",不进路线）**：

- [x] ~~R-PD-9 agentic loop 下沉 df-ai~~ ❌ **否决** — 与 ai.rs 子 module 拆分决策冲突("下沉需动 crate 依赖图引入循环依赖")。保留 display_hint 轻量部分
- [x] ~~R-PD-8 DTO 映射层~~ ❌ **否决** — 过度设计,5 个 IPC 手动对齐可接受。替代:types.ts 头部文档约定"后端改 schema 必须同步改此文件"
- [x] ~~F-09 拆双字段(status 生命周期 + stage 开发阶段)~~ ❌ **否决** — 阶段概念已被弱化(df-stages 砍了阶段插件),DB 实际只有 active/archived/deleted,双字段过度设计
- [x] ~~R-PD-12 做实 run_workflow AI 工具~~ ❌ **否决(当前)** — 工作流是内部基础设施,无真实 CI/CD 需求。删假能力(no-op 桩返 note)更诚实

**⚡ 推荐推进顺序**:
1. 第一批(独立并行): F-07 / R-PD-2 / F-15-06 edit_file / ARC-06 / CR-09 / F-09对齐 / F-03最小修 / R-PD-9 display_hint
2. 第二批(依赖第一批): F-03 对抗评估接 LLM(F-07后) / F-01 模型能力Phase1(F-07后) / R-PD-12 删假能力(R-PD-2后)
3. 第三批(需用户输入): S-01 API Key 401 / 显示多开 / 审批可见性

## 已完成

### 2026-06-14

- [x] R-260614-01 ai.rs 拆 11 子 module（commands/ai/）+ glob 重导出保路径 + models bug 修复 — cargo check 0 error / 19 test passed
- [x] B-260614-01 待审批持久化根治 — mission:T-260614-01
- [x] B-260614-02 df-workflow ConditionEngine 默认 true→false — mission:T-260614-02
- [x] B-260614-04 NodeRegistry unimplemented!→删 Default impl — mission:T-260614-03
- [x] T-260614-05 工具结果入库前截断 50KB（含 3 单测）— mission:T-260614-04
- [x] B-260614-08 promote_idea 补偿删除保最终一致性 — mission:T-260614-05
- [x] T-260614-07 诊断日志清理（3 删 + 3 改 debug）— mission:T-260614-06
- [x] D-260614-01 B-03 人工审批响应机制**设计**完成 — 新建 [B-03-人工审批响应机制-2026-06-14.md](./02-架构设计/B-03-人工审批响应机制-2026-06-14.md)（9 节完整设计）+ 功能决策记录摘要章节 + PROGRESS/todo/INDEX 同步；核心结论：链路基础设施已通仅缺 HumanNode 一处、通道选型=工作流独立审批通道（非 ai.rs AiApprovalRequired）、拆 B-03a（响应等待+超时，不依赖 B-07）/B-03b（取消机制）；**✅ B-06/B-07 前置已由 Workflow D 完成，B-03a 已实施（commit 22964a2）**
- [x] WF-A 数据安全（Sprint 20 ①②）— AI delete→soft_delete + restore/purge/list_trash 三工具 + list_projects 排除回收站（commit 3f0839a）
- [x] WF-B 阻塞去重（Sprint 20 ③④⑤）— spawn_blocking 三处+补漏 + normalize_path 抽公共 + 复用 is_allowed_column（commit d5a6417）
- [x] WF-C 前端（Sprint 20 ⑥⑦⑧）— 扫描文案 i18n + parseStack 抽公共 + 8 处 alert/confirm 换组件（commit 02ff88f）
- [x] WF-D 审批闭环（B-06/B-07/B-03a）— execution_id 下沉 + 共享状态机 + HumanNode select! 实现（commit 22964a2）
- [x] WF-E Wave1 收尾清债（commit 4aa689e + 89da9fa）— B-03b 审批取消端到端（StateMachine Arc<Mutex> 共享 + execution_id 注册表，**主代理补完 agent 半成品**：agent 初版 workflow_cancel_state 全局孤立实例写了读不到，Explore 审查漏抓语义缺陷）+ T-09 立项回滚级联删（delete→purge_with_descendants）+ T-10 判定已解决（normalize_path 已 canonicalize，零改）+ T-12 df-ideas 清 capture.rs 死码；cargo check / test（含 clone_shares）/ vue-tsc 全绿
- [x] WF-F Wave2 aichat P0 三项（commit 057a212）— AR-2 新建对话守卫 + AR-3 审批卡片可读化（reason 拼 9 工具对象名 + restore/purge case + id 标签 + i18n）+ AR-4 create_project schema 加 path/stack 合并绑定；**审查 boundary 全误判**（3 agent 并行同工作区，审查 git diff 被三人累计改动污染互相指责越权；correctness "i18n 未添加"亦臆断），主代理独立核查三任务代码全正确，cargo check/vue-tsc 全过；遗留：任务推进构想-2026-06-14.md（agent 越权自主产出，保留未追踪待评估）
- [x] WF-G Wave3 aichat P1/P2（commit 9e2aeff）— AR-5 stop 兜底 + AR-7 clean UI 真删 + AR-9 friendlyError i18n；**审查 semantic_check 抓对 AR-7 gap**（agent impl 声称改 commands.rs 实际零改动=幻觉，主代理补完）；boundary 仍全局 diff 误判（审查 prompt 加固对 correctness 有效、对 boundary 根除不掉 agent 跑全局本能）

### 2026-06-16

- [x] F-260615-05 — **run_command 工具（方案 A）** — 给 AI 加 Shell 执行能力，闭合「写(write_file)→跑(run_command)→看结果→改」循环。RiskLevel::High 强制人工审批（审批卡显示 command+working_dir）。复用 `df_execute::shell::execute`（跨平台 cmd/C·sh -c + tokio::timeout + kill_on_drop）+ `validate_path` 黑名单基础防线；输出 stdout/stderr 各截 10KB（尾部保留+`truncated` 标记）。**安全边界**：A 方案=最高风险，唯一防线=人审+黑名单，未做命令黑名单/网络检测/资源限制（B/C/D 方案领域）。plan: ~/.claude/plans/quizzical-prancing-hennessy.md；改动 tool_registry.rs（注册+truncate_output+import），不动 audit/commands/前端/Cargo.toml — source:用户需求(06-15)，src-tauri/src/commands/ai/tool_registry.rs
- [x] R-260615-01 — selection 文字不可见修复（深色主题）— global.css `::selection` 由 `accent-soft`(透明紫底)+`accent`(紫字) 改为 `accent-hover`(实色紫底)+`#fff`(白字)，避免 user 紫底气泡选中后紫字紫底不可见 — source:用户报障(06-15)，src/styles/global.css:124

## Bug

（P0/P1 bug 见上方「待办」分类，此处不重复）

### 🔴 新发现但未记录的问题（docs 扫描结果，2026-06-16）

> 来源：后台代理扫描 62 个 markdown 文档发现的问题

**P0 — 严重阻塞**
- [x] B-260615-57 ✅(2026-06-16,缺口清零销账) [P0→缺口收窄] — ~~文件工具功能缺口~~ **06-15 批次后缺口 6→2→0**：已补 `patch_file`(F-06 ✅) / `search_in_file`(F-07 ✅) / `file_info`(F-08 ✅) / `append_file`(F-09 ✅) / `search_files`(F-12 ✅) / `delete_file`(F-16 ✅ 第⑯批) / `rename_file`(F-15 ✅ 第⑮批销账 06-15)。**缺口清零**：`rename_file`/`move_file` 已补(F-260615-15,根治 AI rename 走 run_command 的中文路径静默失败)。原根因(迫使 LLM 滥用 write_file 放大 FR-S7 覆盖写入风险)已由 patch_file 缓解。— src-tauri/src/commands/ai/tool_registry.rs

**P1 — 重要缺陷**
- [x] B-260615-39 [P1] ✅(第⑰批销账 06-15) — ~~complete() 超时重试机制缺失~~ 新建 retry.rs(189行+6单测 retry_with_backoff/AttemptOutcome/is_*_retryable)+openai/anthropic complete() 包裹重试(3次/指数1s→2s→4s/±20%jitter/30s总预算止损/可重试 timeout·connect·5xx·429,不重试4xx·auth·body)/不建LlmError枚举(零侵入trait保anyhow::Result)/不用middleware(手写loop)/流式stream()不动/Settings降级常量MAX_COMPLETE_ATTEMPTS=3。**顺带修真bug:Anthropic complete()无60s timeout(FR-R4只修OpenAI一半,建连后挂起无限hang)**。complete()4调用方全低频后台(标题/知识/扫描/AiNode)有兜底,主对话走stream()不受影响。cargo test -p df-ai 37 passed `complete()` IPC 调用仅加 60s 单请求超时，但无重试机制。根因：网络波动时直接失败，无自动重试兜底。影响：偶发网络错误导致用户操作失败需手动重试。修法：实现指数退避重试机制（最多 3 次，总时长不超过 30s），并在 Settings 可配重试开关 — src-tauri/src/commands/ai/commands.rs
- [x] B-260615-40 [P1] ✅(第⑯批销账 06-15) — ~~审批按钮无状态反馈~~ ToolCard 加 approving ref+spinner+disabled(防重入)+130s 本地计时器兜底+watch tc.status 离开 pending_approval 复位;useAiSend approveToolCall 删乐观置 running(原致审批整块消失切骨架屏,语义错位)保 pending_approval 直到后端事件回流;useAiStream onStreamTimeout 注释更新。loading 就近持有 ToolCard 本地不污染 store status 流转 — src/components/ToolCard.vue + src/composables/ai/useAiSend.ts
- [x] B-260615-41 [P1] ✅(batch35 销账 2026-06-16,对齐决策a轻量) — ~~工作流执行无进度指示~~ batch35 TaskDetail 进度内嵌已满足决策 a 轻量:复用现有 NodeStarted/NodeCompleted 事件(纯前端 `handleWorkflowEvent` 按 exec_id 过滤 dispatch,不新增后端 progress 事件),`.wf-progress` 区显示 `workflowStepRunning{node}`(当前步骤)+`workflowStepsProgress{done}/{total}`(步骤进度)+完成/失败提示,不新建独立 WorkflowDetail.vue(决策 a 是嵌入现有 TaskDetail 推进按钮区非独立页),i18n key taskDetail.workflowStepRunning/stepsProgress/completedHint/failedHint zh-CN+en 双语已加。**预估剩余时间属方案 b 完整版,决策 a 明确不做** — src/views/TaskDetail.vue
- [x] ✅(batch34·2026-06-16·workflow wvr7mdo0p) B-260615-42 [P1] — ~~错误信息不一致~~ 同类错误在不同场景下返回的信息格式不统一。根因：错误处理分散在不同模块，缺乏统一规范。影响：用户和调试者难以理解错误含义。修法：制定错误码规范 + 统一错误格式 + 前端错误码映射 — src-tauri/src/commands/ai/mod.rs + src/api/types.ts

**P2 — 中等问题**
- [x] ⏸️(决策c暂缓·2026-06-16) B-260615-43 [P2] — ~~无撤销重做功能~~ AI 操作（如删除项目、修改任务）无撤销功能。根因：缺乏操作历史记录和回滚机制。影响：误操作无法恢复。修法：实现操作历史栈 + 撤销/重做 UI + 关键操作二次确认 — src/stores/project.ts + src/views/ProjectDetail.vue
- [x] B-260615-44 [P2] — ~~工具响应时间无反馈~~ 工具执行（如 read_file、write_file）长时间无响应时用户不知道卡在哪里。根因：缺乏工具执行进度反馈。修法：添加工具执行进度提示 + 超时警告 + 取消按钮 — src/composables/ai/useAiStream.ts
- [x] ✅(batch41·2026-06-16·workflow w9vtei15z) B-260615-45 [P2] — ~~内存泄漏隐患~~ 已核查覆盖(useAiEvents stopListener 三 listener+计时器全清 + AiChat onBeforeUnmount + MESSAGE_CAP=200 滚动淘汰),无泄漏不强改 长时间使用后内存占用持续增长。根因：消息缓存、事件监听器未及时清理。修法：优化缓存策略 + 定期清理机制 + 内存使用监控 — src/stores/ai.ts + src/composables/ai/useAiEvents.ts

**文档整理需求**
- [x] DOC-260615-15 — **文档缺失**：`patch_file` 工具设计文档已定稿，但使用说明缺失。需添加：工具使用场景、API 参数说明、安全边界、最佳实践 — docs/08-用户指南/patch_file使用指南.md (第⑬批销账 06-15)
- [x] DOC-260615-16 — **文档过期**：~~使用手册多处过期~~ **第⑯批销账 06-15**：核对发现手册已对齐 06-15 代码基线(npm/7态/多Provider/V13/3节点 全 Y),无需重写,todo 前提过时 — docs/08-用户指南/使用手册-2026-06-12.md

### 🔴 AI 链路核心代码审查（2026-06-16，/review 走查·9 文件多角度论证）

> /review 审查 AI 链路核心（stores/ai.ts + composables/ai/{aiShared,events,send,stream,window,conversations,panel}.ts + AiChat.vue，~1700 行）。契约核对全通（ai-client-ready 后端 lib.rs 监听 / forceSend IPC 注册 / stopListener AiChat:827 调用 / isQueueTimedOut AiChat:745 调用）。9 条发现，每条经多角度论证（确认性/触发边界/去重/修复风险）。

**P1 — 确定性功能 bug**
- [x] B-260615-46 [P1] — ~~copyMsgContent 失败误报「已复制」~~ `navigator.clipboard.writeText` 权限拒绝/非安全上下文 reject → catch 分支仍 `showToast(t('aiChat.copied'))`（与成功同文案），注释自述「静默忽略」与实现矛盾。用户见「已复制」去粘贴发现空 → 误导。修法：catch 改失败文案或静默 — src/components/AiChat.vue:1037-1040
- [x] B-260615-47 [P1] — ~~offsetToDOMPosition 恒返回 root 致选区恢复错位（UX-2025-01 功能失效）~~ `createTreeWalker` 后 `currentNode` 初始=root(Element)，`walker.currentNode as Text ?? walker.nextNode()` 因 `??` 左操作数 root 非 null 恒取左 + 循环无 nextNode 推进 → 恒返回 `{node: root, offsetInNode}`，root 非 Text 致 `range.setStart(root, offset)` 当 childIndex 用 → 选区错位；策略2(文本兜底)因 startPos 恒非 null 永不触发 → 选区恢复恒错位。修法：标准 `walker.nextNode()` 遍历（对照 saveSelection.toTextOffset:948 正确写法）。**注**：UX-2025-01 在 todo 标 `[ ]` 未实施，但代码已实现(saveSelection/restoreSelection)→todo 状态过时，本条同步修正认知 — src/components/AiChat.vue:908-921

**P2 — DRY/微优化**
- [x] B-260615-48 [P2] — ~~restoreUiState 与 watch 四行赋值逐字重复(DRY)~~ restoreUiState(L22-26) 与 watch 回调(L36-39) 4 行 `if(typeof s.x==='boolean') state.x=s.x` 重复。修法：抽 `applyUiState(s)` 两处调用，零行为变 — src/composables/ai/useAiPanel.ts:20-41
- [x] B-260615-49 [P2] — ~~handleEvent 每事件构造数组做 includes~~ `!['AiApprovalRequired','AiCompleted','AiError'].includes(type)` 每 delta/token 新建数组字面量。修法：提模块级 `NO_RESET_WATCHDOG` Set — src/composables/ai/useAiEvents.ts:109
- [x] B-260615-50 [P2] — ~~tryForceSend 前置 streaming=false 瞬态触发 watch 清流式块~~ L151 `state.streaming=false` 触发 AiChat:812 watch 清 streamingBlocks+rAF，doSend 再设 true → 一次清空重建。注释自述「force_send 会重新设 true」证明前置 false 多余。修法：删该行(保留 clearStreamWatchdog) — src/composables/ai/useAiSend.ts:150-156。属 B-260615-56 实施细节缺陷

**P3 — 风格/可选**
- [x] B-260615-51 [P3] ✅(第⑮批销账 06-15) — ~~MESSAGE_PUSH_BURST 单用常量~~ stores/ai.ts:38 仅 watch(:93) 一处用，按风格可内联为 2 + 行内注释。两可(常量名助读)，属风格偏好 — src/stores/ai.ts:38
- [x] B-260615-52 [P3] — ~~`if(!state.streaming) state.streaming=true` 冗余判断~~ useAiSend.ts:126 进 busy 分支后判断多余，直接赋值幂等 — src/composables/ai/useAiSend.ts:126-128
- [x] ✅(batch39·2026-06-16·workflow ww4wb66br) B-260615-53 [P3] — ~~watch(messages,{deep}) + JSON.stringify 全量快照~~ 已优化(snap 剔除 len/id/role 只留 n+tc,流式 delta 不再触发 body 重算,deep watch 保留 toolCalls 嵌套检测)。实际 watch 在 AiChat.vue:1128-1146(非 835) AiChat.vue:835 长+多 toolCalls 时每变更 deep 遍历+序列化。snap 短路兜底 + MESSAGE_CAP=200 封顶，典型场景可控，待超长会话反馈再优化 — src/components/AiChat.vue:835-863
- [x] B-260615-54 ✅评估维持(2026-06-16) — `void convId` 参数丢弃：R5 设计明确(分离窗口只读不切会话防落错)，注释充分，签名稳定(onMounted 调用传参)。保留正确，不修 — src/composables/ai/useAiWindow.ts:83

> **统计**：🔴2(P1 确定性 bug) 🟡3(P2 DRY/优化) ⚪3(P3 风格) + 1 评估维持。总体评级「良」：架构清晰、契约闭合、注释扎实，两处 🔴 低风险一行/小改可根治。详见本轮 /review 输出。

## 长期 / 待需求驱动（不进看板主线）

- 裁剪/压缩消息按需召回（Query Function + 分层存储）
- 停止生成 idle 即时优化（`tokio::sync::Notify` 替代 120s 轮询）
- 模型能力系统 Phase 3（Agent 内智能路由 + 成本预算 + 模型级联）
- `node_executions` 全表 list 命令（当前只写不读）
- `do_promote` crate 层 TODO（promotion.rs，现走前端闭环）
