Files
DevFlow/docs/todo.md

806 lines
213 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 僵尸 cratedf-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 保其他对话 pendingai_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 排查会话新报 bug4 项·仅分析未实施)
> 排查性质会话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-17 实测发现 bug审批流程三连
> 用户实测AI 对话中让 AI 创建任务并推进状态 → AI 调 `run_workflow(task_id, target_status)` → 审批卡片渲染 → 用户点批准 → handler 返回 Err → LLM 重试循环。
- [x] ✅(波9·e23b316+主代核查,cargo0err+devflow79pass,方案a2后端分支) B-260617-01 [P1] — **run_workflow 作为 AI 工具无法执行 + 无意义重试 + 审批混淆**。三个关联问题:
- **问题 1**`run_workflow` handlertool_registry.rs:530-546仅持有 `db: Arc<Database>`,无法访问 `AppHandle/State`registry/event_bus/workflows Repo返回 `Err("run_workflow 工具需经 Tauri IPC 执行...")`。审批通过后 `ai_approve`commands.rs:329`ai_tools.execute()` → handler 必然 Err → `audit_status="failed"` → 错误信息作为 tool_result 回传 LLM。工作流实际未执行任务状态未推进。
- **问题 2**handler Err → tool_result=错误提示 → LLM 认为失败 → 重试(1/4) → 同结果循环。工具定义未标 `no_retry`,浪费 token + 用户反复审批。
- **问题 3**:前端渲染的是 AI 工具审批tool_call pending_approval非工作流 HumanNode 审批。用户批准后工作流引擎从未启动HumanNode 审批节点从未到达。
- **修复方向****方案 A推荐**:前端拦截 — `ai_approve` 中识别 `run_workflow` tool_call → 不走 `ai_tools.execute` → 直接 `invoke('run_workflow', {task_id, target_status})` → 结果回填 tool_result。**方案 B**:后端注入 State — `build_ai_tool_registry` 传入 `AppHandle` 使 handler 可直接执行。问题 2/3 随 A 自动消解。
- **代码定位**tool_registry.rs:530-546handler 返 Err/ audit.rs:590-630High risk 进 pending/ commands.rs:329-334ai_approve execute→Err→failed/ agentic.rs:503-508pending_count>0 暂停循环)
— src-tauri/src/commands/ai/{tool_registry,audit,commands,agentic}.rs + src/composables/ai/useAiSend.ts
### 🔧 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 一次 Startedread_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_reviewstore 层兜底);②**决策点pending_review 从 library 排除后该去哪显示**(a) 扩 inbox 收件箱含 candidate+pending_review语义=「待处理」收件箱)(b) 单纯从 library 消失、pending_review 暂无专门视图 (c) 加第三个 tab「待审」。**倾向 (a)**inbox 收 candidate+pending_reviewlibrary 纯 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:205messages/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) 逐条续发**(删清队列即可,零改 drainQueueAiCompleted 链式触发每条保各条上下文独立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 自动 ✅(波8·0202b51+主代核查,cargo0err+vue-tsc0+devflow79pass,loop顶部检测budget×0.6+压缩+降级原裁剪,set_compressing成对,延迟mutate失败零副作用)。**F-15 全阶段完成 ✅**。
**核心问题**:① `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 任务软删除****决策:① 加软删除对标 projectsUI 缓做)**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-16CR-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-16splitBlocks 块级 memo O(末块)+rAF 节流 取代全量 marked.parse+sanitize流式全程有格式不掉帧。详见 [流式渲染调研](./02-架构设计/aichat流式Markdown渲染调研-2026-06-15.md) §5renderMd 缓存 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_label9 工具 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_directoryTODO 标注可抽公共绑定函数)(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.rsemit 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 正确抓到 gapcommit 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 keyTS2589 用 as any 规避 vue-i18n 深度泛型)(commit 9e2aeff)
- [x] AR-10 ~~想法→灵感迁移残留~~ ✅ 已统一13 文件批量i18n zh-CN + 后端错误 + LLM 描述/提示词 + store toasten 待定 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 仅靠事件 reset130s 无事件 → `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 被 clearuseAiEvents.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/4b5f096dag 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_secretai_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-082026-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 keyzh/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-app5711 行成事实业务层) / 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流式核心(块级 memosplitBlocks 代码围栏整体一块/非代码双换行切 → 前块缓存命中 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_promotePromotionResult 保留——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) 只解构 entityaction 忽略,刷新统一全量 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/16INDEX 漏列部分本会话已补,见 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_failedCancelled→Failed 非法 transition 不再 bailemit NodeFailed 仍发,状态保 Cancelled采修法 B 不动 is_legal 转换图)
- [x] B-03b-R2 ~~零端到端取消测试~~ ✅ 已补test_cancelled_node_skips_set_failedCancelSelfNode 经共享 node_status 自取消→Err→executor 不 bail→状态保 Cancelleddf-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 加 .awaitasync 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 下沉 NodeContextworkflow.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 失败时补偿删除已建 projectRepository 不支持跨 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 file4 大功能域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 保留诊断能力但不污染 consolevue-tsc 0 errsrc/ 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/687title清空/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 必须 sanitizecomposable 保留 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` 审批等待 returnpending>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 6c2e11f4U-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.dbtasks 表 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 /CPS 语法引号嵌套易错)。**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 块级 memosplitBlocks 已按块切分,末块单独更新天然支持方案 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 等 keyhover 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 而非裸 contentcontent 可能几百行代码截断到 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 — 增强控制力**
- [x] ✅(波8·6ec191e+主代核查,cargo0err+vue-tsc0+devflow79pass) 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_descendants1 行,签名兼容)(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-501schema 加参数 + 返回结构扩展(有 search 返 matches无 search 走原 content 路径,两路径并存)。**权衡**vs 独立 grep 工具——read_file search 满足单文件定位主场景(最小改动,复用现有工具);跨文件搜索待 B-260615-37run_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_commandB-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 子 modulecommands/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_columncommit 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.mdagent 越权自主产出,保留未追踪待评估)
- [x] WF-G Wave3 aichat P1/P2commit 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+rAFdoSend 再设 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 3Agent 内智能路由 + 成本预算 + 模型级联)
- `node_executions` 全表 list 命令(当前只写不读)
- `do_promote` crate 层 TODOpromotion.rs现走前端闭环