Files
DevFlow/docs/todo.md

658 lines
148 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 清死链。
### 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)
### 🔧 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 实际输出格式)。vue-tsc exit 0。
- [x] ✅(第㉗批 2026-06-16·workflow) B-260616-19 [P2] — **知识库详情窄屏标题挤压成竖线(一行一字)**。三因子叠加Knowledge.vue`.kn-layout` grid `grid-template-columns: 360px 1fr`L567——左列表死钉 360px、右详情 `1fr` 拿剩余,**无响应式断点**;窗口窄(如总宽 500px减 padding 40+gap详情列塌缩到 ~80px 甚至更小。②`.detail-title`L625`font-size:18px` + `word-break:break-word` —— 窄列里中文每字宽度 > 列宽,逐字换行 → 竖线化。③`.detail-actions`L626`flex-shrink:0` —— 编辑/发布/拒绝 3 按钮不缩,优先占位,把 `.detail-title-row`(`flex:1`) 挤到零头。`.kn-detail-panel`(L570-576) 无 `min-width` 兜底。改动待定①grid 列宽改 `minmax(240px,360px) 1fr` + `@media (max-width:760px)` 切单列(`grid-template-columns:1fr`,列表详情上下堆叠);②详情面板加 `min-width:0` 配合标题行 `min-width` 防塌缩③可选标题字号响应式降级18px→15px。— src/views/Knowledge.vue。**低风险纯 CSS**,无逻辑/i18n/数据变动。
### 🔧 任务模块全栈核对2026-06-16 · 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。
- [ ] F-260616-06 阶段2 工作流联动task_id+回调+DAG模板
- [ ] F-260616-07 阶段3 AI 执行闭环advance_task/run_workflow 工具+AiNode+自审)+ **F-03收口三件**(advance_task注册AI工具+update_task描述/schema移status+df-storage tasks白名单移status,同步落地防AI工具行为不一致)
- [ ] F-260616-08 阶段4 Git 集成kind+git闸门+worktree
- [ ] **CR-260616-01 代码审查 — 阶段1推进链(batch㉘)** 范围:df-nodes/{task_state_machine.rs,task_advance_node.rs,lib.rs,Cargo.toml}+crud.rs(advance_status_atomic)+models.rs+migrations.rs(V15)+task.rs(advance_task IPC)+lib.rs注册+TaskDetail.vue(ADVANCE_MAP)+api/task.ts(advance)+i18n zh/en taskDetail.ts+tool_registry.rs/tests project_soft_delete.rs/types.ts(补review_rounds字段)。内容:①状态机转换矩阵正确性(对照报告§8,退回/终态/非法跳过)②advance_task CAS原子写`WHERE status=expected`防TOCTOU ③advance_status_atomic是否唯一status/review_rounds写入路径(防旁路写)④review_rounds退回递增逻辑+V15迁移幂等(PRAGMA column_exists)⑤F-03停下决策正确性(AI update_task依赖status,收口延后阶段3三件)⑥前端ADVANCE_MAP与后端can_transition一致性(勿前端臆断合法性)⑦指派外补字段(tool_registry create_task/tests fixture/types.ts)最小性非超范围 ⑧TaskAdvanceNode Node trait实现正确性(阶段2 DAG预留,config读task_id/target_status)
### ⏸️ 待用户授权/确认巡检悬挂项·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 日期引用均正确,无需核对修改。
- [ ] **DEC-260616-03 CR-04/CR-06 流式核心改造授权**【风险确认】splitBlocks→marked.lexer(CR-04)/loadMarkdown 重算(CR-06)。流式渲染核心,中风险(回归可能)。选项:①授权巡检推进(主代理重点核查流式渲染/审批/输入不破坏) ②单独立项谨慎做(不混巡检快推) ③暂不碰。
### 🔴 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**
- [ ] AR-8 delta 节流+滚动 — **重评2026-06-16**:前端 rAF 节流已被 ARC-08 覆盖(每帧 ≤1 parse剩后端 50ms 合批B-260615-02 心跳已动 stream_recv.rs合批可并入同文件+ 滚动跟随。降优先级 — stream_recv.rs + AiChat.vue 滚动
- [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-D3 / FR-P1~P6 剩余性能项(单连接 Mutex、migrations if 链、~~tool_registry truncate(50)+注释散落提常量~~ ✅第⑯批销账 06-15 抽 MAX_LIST_RESULTS 常量 4 处)— 见报告 §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**
- [ ] CR-260615-04 — splitBlocks 手写正则切块改 `marked.lexer()``/```[^\n]*\n[\s\S]*?(?:```|$)/g` 不要求行首 + 固定 3 backtick与 marked 围栏规则不一致;行中裸 ``` / 4+ backtick 嵌套围栏切错,前块缓存固化错误 html机制2原生=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不同命名空间保留
**短期(低成本)**
- [ ] 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 直连可选迁移,非强制)
**中期(技术债)**
- [ ] ARC-260615-05 — `stores/project.ts` 上帝 store 拆分(四领域+越层 invoke`approve_human_approval`/`cancel_workflow_node` 应沉 `api/workflow.ts`,全项目仅此 store 越层)
- [ ] ARC-260615-06 — `composables/ai/` 6 文件为拆而拆events↔stream 循环依赖 useAiEvents.ts:18↔useAiStream.ts:12— 合回 `stores/ai.ts` 或提 `aiShared.ts` 破环
- [ ] 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 + P2 可执行 13 已全闭环**批1/2/3 主代理独立核查 cargo workspace exit 0 + 5 crate test 共 126 passed。下为**需设计**待立项项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 复用
- [ ] R-PD-10 — .map_err(\|e\| e.to_string()) 10 文件 85 处复制(强类型 Error 拍平成自由文本,分类信息丢弃)— 全 commands/ 加 err_str helper 统一日志点
- [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 — 审查状态断层**
- [ ] DOC-260615-05 — todo 补全局 review R-P1-1~6/R-P2-1~13 已完成回写(报告说全闭环 126 test passtodo 零条目)— 已完成区补汇总 — 详见 §5
- [ ] DOC-260615-06 — 架构复核报告 §5 新发现 11 项补 todo复核-新①③④⑪ 待补pendingToolCalls 二次 token / NodeCancelled 事件语义 / cancel 终态守卫 / 主窗口幽灵 watchdog余已被全局 review 取代)— 详见 §6
- [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
- [ ] DOC-260615-08 — 近期改动 CR-03 未做子项拆独立 todoaudit.rs 三臂 / openai Error::from / ToolCard projectNameById find— 详见 §8
**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 — todo 肥大瘦身(大段详情移 docs 子目录)+ 重复收口HumanNode 审批/密钥迁移/FR-S1+ 文档记录规范在 INDEX 突出 — 详见 §20-22
**P3 — 技术/工具文档**
- [ ] DOC-260615-14 — Tauri-IPC/View 改造指南全篇过时重写或归档 + SQLite 表数 V1-V9→V1-V13 + 锚点顿号 — 详见 §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 — 设计完成待实施
- [ ] F-260614-01 — **[P1]** 模型能力系统 Phase 1 — ModelCapability 数据模型 + ModelRouter 重写 + 7 调用点接入 + Settings 模型池编辑 UI + AiChat 模型下拉。按任务需求(模态/功能/成本)自动匹配合适模型,不再所有场景共用 default_model — source:📐 设计完成 (06-14)
### 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 — 前后端状态同步**
- [ ] B-260615-22 — [P2] ~~前后端状态不同步~~**由 B-260615-55(L0 握手) + B-260615-56(L2 强制发送) 解决**,本条保留为问题记录不再独立实施
- [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)
- [ ] 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
- [ ] 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
- [ ] UX-2025-03 [P1] — **错误气泡操作入口**§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
- [ ] UX-2025-04 [P1] — **断线保文**§4.2)。流式中断时已接收文本不丢弃:`stream_llm` mid-stream error 改返回 `Some(partial_text, ...)` + `incomplete: true` 标志agentic 收到 incomplete → 正常入库 + emit AiCompleted非 AiError+ 追加系统提示「⚠ 响应因网络中断不完整」;前端 AI 气泡底显示「继续生成」按钮。关联 B-260615-05(流尽空内容报错)同改 stream_recv.rs。— crates/df-ai/src/stream_recv.rs + src-tauri/src/commands/ai/agentic.rs + src/components/AiChat.vue
**第二批 — 显著提升日常效率**
- [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)
- [ ] UX-2025-06 [P2] — **对话搜索**§3.1)。侧栏 header 加搜索框(实时过滤 conversations 匹配 title搜索时取消分组平铺展示支持 `Ctrl+K` 快捷键聚焦。— src/views/(AiChat|Sidebar).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
- [ ] UX-2025-08 [P2] — **新建对话 confirm 弹窗**§3.4 增强)。生成中点新建对话时弹 ConfirmDialog「当前正在生成确定中断并新建确认后才执行 B-260615-10 的软复位逻辑。防误触中断。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
**第三批 — 锦上添花**
- [ ] UX-2025-09 [P2] — **编辑已发送消息并重新生成**§1.1)。用户气泡 hover 显示"✏️ 编辑",点击回填输入框;发送时后端:①替换目标 message content ②后续所有消息标记 `status='truncated'`(软删除,保留历史可追溯,与 WF-A soft_delete 模式一致)③用 status='active' 的历史重跑 agentic loop。约束只允许编辑最后一条 user 消息(中间编辑语义复杂+易乱上下文generating 中拦住。前端截断消息从视图移除loading 等新回复。后端新增 `ai_chat_edit(message_id, new_content)` IPC。— src/components/AiChat.vue + src-tauri/src/commands/ai/(commands.rs|conversation.rs) + df-storage(messages 表加 status 列)
- [ ] UX-2025-10 [P2] — **@ 实体引用**§1.4)。输入框支持 `@` 触发实体联想浮层(复用 `/` 技能联想 popover 架构);联想源:项目列表/任务列表/最近文件;选中展开为 `[项目: xxx]` 标记文本,后端 system prompt 注入上下文摘要。关联 F-260614-02(技能联想)。— src/components/AiChat.vue + src-tauri/src/commands/ai/prompt.rs
- [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)
- [ ] UX-2025-17 [P3] — **对话置顶**§3.2。ai_conversations 表加 pinned 字段;排序改为 pinned DESC, updated_at DESC侧栏 hover 图钉按钮。— df-storage + src/views/Sidebar.vue + src/stores/ai.ts
- [ ] UX-2025-18 [P3] — **对话导出**§3.3)。后端新增 ai_conversation_export(conv_id, format) 支持 markdown/json/txt前端侧栏 hover 导出按钮。Markdown 格式:## 用户 / ## 助手交替 + 代码块保留围栏。— src-tauri/src/commands/ai/conversation.rs + src/views/Sidebar.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)
- [ ] AE-2025-03 [P0] — **write_file diff 预览**。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 — 增强控制力**
- [ ] 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)
- [ ] 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)
- [ ] F-260614-02 — 技能联想「使用」 — 首批 3 类联想已做,联想后实际触发/执行技能未实现 (06-14)
- [ ] F-260614-03 — **[需设计]** 灵感对抗评估接 LLM — 核对 adversarial.rs`evaluate()` 纯启发式,接 LLM 需改签名注入 provider。3 种注入方式待定A.trait 解耦df-ideas 定义 IdeaAnalyzer trait推荐/ B.df-ideas 直接依赖 df-ai / C.closure。属架构决策非直接做 (06-14)
- [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)
- [ ] F-260614-06 — 导入历史项目scan 第二步) — [📐设计定稿 06-14] description 走 LLM复用 scan_project_with_ai非纯规则+ 采样保留内容图(待 F-260614-05 多模态)+ monorepo 一层 + 批量并发;抽 create_with_binding 缓解 :211 TODO详见功能决策记录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)
- [ ] F-260614-07 — **[架构前置]** df-ai-core trait 下沉拆 crate — F-03 注入方式前置(决策记录已收敛:原 F-03 的 A/B/C 选型作废,统一为全局 AI trait 下沉独立 cratedf-ideas/df-nodes 依赖 trait 而非 df-ai 具体 impl。解锁 F-03 — source:功能决策记录 (06-14)
- [ ] F-260614-08 — 决策治理产品化 — 活契约/规格契约自检机制产品化为可操作功能(当前散落文档机制) — source:功能决策记录缺口
- [ ] F-260614-09 — 项目 status 字段治理 — status 状态机规范化planning/active/archived 等枚举约束) — source:功能决策记录缺口
- [ ] F-260614-10 — 知识库 MCP Server + Tier 2/3 — 对外 MCP 暴露 + 分层存储(当前仅 Tier 1 全栈) — source:PROGRESS Sprint15
- [ ] 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)
- [ ] F-260615-01 — **[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若已落盘
**✅ 推进(第一批独立可并行)**
- [ ] F-07 trait 下沉 df-ai-core — 设计完备 4 项决策全定稿,退路可放 df-core解锁 F-03/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
- [ ] ARC-06 composable 循环依赖 — 提 aiShared.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 — 严重阻塞**
- [ ] B-260615-57 [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
- [ ] B-260615-41 [P1] — ~~工作流执行无进度指示~~ 长时间工作流执行时,用户不知道执行进度和剩余时间。根因:缺乏执行进度可视化。修法:在工作流详情页添加进度条 + 执行步骤提示 + 预估剩余时间 — src/views/WorkflowDetail.vue需新建
- [ ] B-260615-42 [P1] — ~~错误信息不一致~~ 同类错误在不同场景下返回的信息格式不统一。根因:错误处理分散在不同模块,缺乏统一规范。影响:用户和调试者难以理解错误含义。修法:制定错误码规范 + 统一错误格式 + 前端错误码映射 — src-tauri/src/commands/ai/mod.rs + src/api/types.ts
**P2 — 中等问题**
- [ ] 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
- [ ] B-260615-45 [P2] — ~~内存泄漏隐患~~ 长时间使用后内存占用持续增长。根因:消息缓存、事件监听器未及时清理。修法:优化缓存策略 + 定期清理机制 + 内存使用监控 — 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
- [ ] B-260615-53 [P3] — ~~watch(messages,{deep}) + JSON.stringify 全量快照~~ 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现走前端闭环