新增: 文档(任务推进链实施路径+任务模块分析+审查报告+patch_file指南)

This commit is contained in:
2026-06-16 02:33:16 +08:00
parent 73ed4bd637
commit 38c7180365
24 changed files with 1644 additions and 485 deletions

2
Cargo.lock generated
View File

@@ -721,6 +721,7 @@ name = "devflow"
version = "0.1.0"
dependencies = [
"anyhow",
"base64 0.22.1",
"chrono",
"df-ai",
"df-core",
@@ -805,6 +806,7 @@ dependencies = [
"df-ai",
"df-core",
"df-execute",
"df-storage",
"df-workflow",
"serde",
"serde_json",

View File

@@ -9,7 +9,7 @@
| 项目 | 值 |
|------|-----|
| 定位 | AI 原生创作流程驾驶舱,从想法到创作成果的全流程管理 |
| 技术栈 | Tauri v2 + Vue 3 + TypeScript + Pinia / Rust Workspace (13 crate) / SQLite |
| 技术栈 | Tauri v2 + Vue 3 + TypeScript + Pinia / Rust Workspace (8 crate) / SQLite |
| 路径 | `E:/wk-lab/devflow/` |
| 架构文档 | `ARCHITECTURE.md` (22,745 字) |
| Git 状态 | 未首次 commit代码全在 untracked |
@@ -45,7 +45,7 @@
| 设计系统 | 1 | 105 | ✅ CSS 变量/动画/Arco 暗色覆盖 |
| **前端合计** | | **~8,812** | |
> Tauri Commands 已达 **57 个**project/task/idea/workflow + AI 对话/工具/审批/Provider/技能全功能 + 知识库 11 个),见后端 src-tauri/src/commands/。
> Tauri Commands 已达 **67 个**src-tauri/src/lib.rs invoke_handler 注册数project 14 / task 5 / idea 6 / workflow 5 / ai 19 / knowledge 14 / settings 4),见后端 src-tauri/src/commands/。
**前端关键问题**Sprint 4/7 已全部解决):
- ~~所有 View 组件数据硬编码~~ ✅ Sprint 4 接 Store 真数据
@@ -69,7 +69,7 @@
~~df-workflow → df-stages (11种阶段节点 execute() 待实现)~~ — df-stages 已移除2026-06-14 零引用清理)
~~df-ai → LlmProvider 实现~~ ✅ 已打通OpenAI/Anthropic 双协议真实 HTTPSprint 5/8
~~df-storage → 所有 crate 的 CRUD 操作~~ ✅ 已打通impl_repo! 宏 CRUD + 多 RepoSprint 2
~~Tauri commands → Rust 业务逻辑~~ ✅ 已打通47 commands 全接业务Sprint 3
~~Tauri commands → Rust 业务逻辑~~ ✅ 已打通(Sprint 3 时点 47 commands 全接业务;现 67
~~Vue Views → Pinia Store → Tauri IPC → Rust~~ ✅ 已打通Sprint 4
```
@@ -136,7 +136,7 @@
**工作内容**
- 创建项目骨架:`bun create vite` + Tauri v2 + Cargo Workspace
- 编写完整架构文档 `ARCHITECTURE.md` (22,745 字)
- 13 个 Rust crate 全部创建,类型系统和接口设计完整
- 13 个 Rust crate 全部创建,类型系统和接口设计完整2026-06-14 评审后删 5 僵尸 crate现 8 crate
- 9 个 Vue 页面 UI 全部编写(~3,372 行),设计系统完善
- 4 个 Pinia Store 定义state + computed
- i18n 中英文翻译

View File

@@ -13,8 +13,9 @@ Phase 1 是 DevFlow 的引擎骨架阶段,聚焦于核心数据流打通。本
### ADR-001: 引擎不绑定业务
- **决策**: Workflow Engine (df-workflow) 只做 DAG 执行,不感知具体业务语义
- **原因**: 保持引擎通用性,阶段逻辑通过 df-stages 插件化注入
- **影响**: df-workflow 的 Node trait 是纯接口,业务逻辑在 df-nodes / df-stages 实现
- **原因**: 保持引擎通用性
- **影响**: df-workflow 的 Node trait 是纯接口,业务逻辑在 df-nodes 实现
> ⚠️ 原文写"阶段逻辑通过 df-stages 插件化注入"、"业务逻辑在 df-nodes / df-stages 实现"。`df-stages` crate 已删除(零引用清理),实际无 stages 层。业务逻辑直接在 df-nodes 的 3 个节点实现。
### ADR-002: 本地优先架构
@@ -24,9 +25,10 @@ Phase 1 是 DevFlow 的引擎骨架阶段,聚焦于核心数据流打通。本
### ADR-003: 多 Crate Workspace
- **决策**: 拆分为 13 个独立 crate
- **决策**: 拆分为个独立 crate
- **原因**: 模块解耦、独立编译、按需引用
- **影响**: 依赖关系需严格管控,避免循环依赖
> ⚠️ 原文写"13 个独立 crate",属过时数字(原始设计值)。实际为 **8 个 crate**df-core / df-workflow / df-nodes / df-ai / df-execute / df-storage / df-ideas / df-project。详见 [业务系统设计](./业务系统设计-2026-06-12.md) §六。
### ADR-004: 无 panic 原则

View File

@@ -1,6 +1,6 @@
# AIChat 交互体验改进方案
> 创建: 2025-07-15 | 状态: 待讨论
> 创建: 2026-06-14 | 状态: 待讨论
> 范围: 消息发送、流式渲染、对话管理、错误恢复、技能/Provider、窗口布局等非授权类交互
---

View File

@@ -2,6 +2,7 @@
> 性质:只审查不改代码。本报告汇总本次会话对 devflow AI chat 全链路的核对发现。
> 增补:2026-06-14 追加「修复进度」表AC1/AC2、AR-3、FR-S4、FR-R4、FR-R5 对照 commit 36d68dd / 4b5f096 标已完成),并在 §8 优先级表 AR-3 行内联标注。
> 二次增补:2026-06-15 §8 优先级表全表对齐 todo.md AR 编号体系,逐行补 AR-1~AR-11 标签 + 状态勾注AR-1 退役 / AR-2~7/9~11 已修 / AR-8 重评降级)。
## 审查范围
@@ -216,17 +217,17 @@ handler 接上后 `bind_directory` 使用频率大降(只剩改绑),第二章 bi
| 优先 | 问题 | 方向 |
|------|------|------|
| P0 | H1 流式 Markdown 重解析 | 流式态纯文本/增量渲染,完成后再 markdown;rAF 合并 |
| P0 | H2 审批态新建对话卡死 | `ai_conversation_create` 加 generating 守卫 |
| P0 | 第二章 审批卡片裸 id + reason 模板已完成 commit 36d68dd / AR-3| 后端 reason 拼对象名;前端 id→name |
| P0 | 第四章 create_project 双审(⚠️ 半成品:schema 加 handler 没接) | handler 读 args 的 path/stack,复用 IPC `project.rs:43-83` 探测逻辑 |
| P1 | H3 审批态 stop 无兜底 | stopChat 本地先复位 streaming |
| P1 | M3 Low 工具失败语义 | 统一 AiError 后 loop 也退出,或不 emit AiError |
| P1 | 第三章 clean 无入口 | AiChat 加清空按钮 + 后端真删当前对话消息 |
| P2 | M1+M2 delta 节流 + 滚动 | 后端 50ms 合批 / 前端 rAF |
| P2 | M4 friendlyError i18n | 抽 i18n key |
| P2 | 第六章 灵感迁移残留 | i18n + 后端错误 + LLM 描述统一改 |
| P2 | 第五章 数据联动 | 方案 A 后端 emit + store 监听 |
| P0 | H1 流式 Markdown 重解析**退役**AR-12026-06-15— 自研块级 memo 取代splitBlocks O(末块)+rAF 节流),详见 [流式渲染调研 §5](./aichat流式Markdown渲染调研-2026-06-15.md) | 流式态纯文本/增量渲染,完成后再 markdown;rAF 合并 |
| P0 | H2 审批态新建对话卡死**已修**AR-2commit 057a212 | `ai_conversation_create` 加 generating 守卫 |
| P0 | 第二章 审批卡片裸 id + reason 模板 **已修**AR-3commit 36d68dd| 后端 reason 拼对象名;前端 id→name |
| P0 | 第四章 create_project 双审**已修**AR-4commit 057a212schema 加 path/stack + handler 合并绑定 | handler 读 args 的 path/stack,复用 IPC `project.rs:43-83` 探测逻辑 |
| P1 | H3 审批态 stop 无兜底**已修**AR-5commit 9e2aeff— stopChat 本地先复位 streaming + clearStreamWatchdog | stopChat 本地先复位 streaming |
| P1 | M3 Low 工具失败语义**已修**AR-6commit f82dd8b— Low 失败非 AiError错误回填 tool_result 让 LLM 自处理 | 统一 AiError 后 loop 也退出,或不 emit AiError |
| P1 | 第三章 clean 无入口**已修**AR-7commit 9e2aeff— clear_messages 真删 + 垃桶按钮二次确认 | AiChat 加清空按钮 + 后端真删当前对话消息 |
| P2 | M1+M2 delta 节流 + 滚动 🔄 **重评降级**AR-82026-06-15— 前端 rAF 节流已被 ARC-08 覆盖;剩后端 50ms 合批 + 滚动跟随 | 后端 50ms 合批 / 前端 rAF |
| P2 | M4 friendlyError i18n **已修**AR-9commit 9e2aeff— 全走 i18n.global.t + zh/en 双语补 key | 抽 i18n key |
| P2 | 第六章 灵感迁移残留**已修**AR-10commit 65c475b— 13 文件批量统一 | i18n + 后端错误 + LLM 描述统一改 |
| P2 | 第五章 数据联动**已修**AR-11commit dc27e79— 方案 A 后端 emit `df-data-changed` + store listen 已 attach | 方案 A 后端 emit + store 监听 |
---

View File

@@ -1,6 +1,6 @@
# AIChat 授权功能体验改进方案
> 创建: 2025-07-15 | 状态: 待讨论
> 创建: 2026-06-14 | 状态: 待讨论
## 一、当前授权机制概览
@@ -24,7 +24,7 @@
| 风险等级 | 工具 | 执行方式 |
|---------|------|---------|
| **Low** | `list_projects`, `list_tasks`, `list_ideas`, `list_trash`, `read_file`, `list_directory` | 自动执行,并行 `join_all` |
| **Medium** | `create_project`, `create_task`, `create_idea`, `update_project`, `update_task`, `bind_directory`, `write_file` | 需人工审批 |
| Medium | `create_project`, `create_task`, `create_idea`, `update_project`, `update_task`, `bind_directory`, `write_file` | 需人工审批 |
| **High** | `delete_project`, `restore_project`, `purge_project`, `delete_task`, `run_workflow`, `run_command` | 需人工审批 |
### 1.3 已实现的亮点
@@ -181,26 +181,28 @@
### P1 — 增强控制力
#### 3.4 自动审批策略(信任模式)
#### 3.4 会话级授权 Session Trust
**方案**Settings 中增加"自动审批"配置,用户可选择对特定风险等级或工具类型自动放行
**方案**引入会话级信任机制,替代全局宽松模式。用户在当前对话中一次性授权某目录的写/执行权限,后续该对话内同类操作自动放行。切换对话或新建对话时信任清空
**配置项**
```
Settings → AI → 自动审批策略
○ 严格模式(默认):所有 Medium/High 需人工审批
○ 宽松模式Medium 自动放行High 需人工审批
○ 自定义:按工具类型选择
☑ write_fileworkspace 内自动放行)
☑ create_task / create_idea自动放行
☐ delete_*(始终需审批)
☐ run_command始终需审批
当前会话信任目录:
✅ E:/wk-lab/devflow/src (Write + Execute)
✅ E:/wk-lab/devflow/docs (Write)
+ 添加目录...
```
**改动范围**
- `df-storage``app_settings` 表存储配置KV 已有 V13 表)
- `audit.rs``process_tool_calls` 读取配置,决定 Medium 工具是否进 pending 或直接执行
- `Settings.vue`:新增配置面板
- 前端Settings 或对话 header 新增「信任管理」入口
- 后端:`AiSession` 增加 `trusted_dirs: HashSet<(PathBuf, TrustLevel)>`
- `audit.rs``process_tool_calls` 先查 session trust命中则跳过 pending
**安全边界**
- 仅纯读取操作list_*/read_*/list_directory保持自动放行
- 所有 create/update/bind/write/delete 操作默认需审批或 session-trust
- bind_directory 归类为修改操作
- 信任仅限当前会话内存,不持久化
#### 3.5 High 二次确认
@@ -221,13 +223,13 @@ Settings → AI → 自动审批策略
→ 才真正执行 ai_approve
```
#### 3.6 审批超时
#### 3.6 审批超时(前端定时器)
**方案**可配置超时自动拒绝(默认 5 分钟),避免对话永久卡住
**方案**前端侧 5 分钟超时自动拒绝,避免对话永久卡住。超时策略独立于 Webhook 等外部动作路径
**改动范围**
- 端:`AiSession` 增加 pending 审批的 `created_at` 时间戳,定时检查超时
- 或前端:`useAiSend.ts` 在 pending 时启动定时器,超时自动调 `ai_approve(id, false)`
- 端:`useAiSend.ts` pending 时启动 5min 定时器,超时自动调 `ai_approve(id, false)`
- 不改后端 AiSession 结构(远期 Webhook 走独立路径)
---
@@ -252,7 +254,7 @@ Settings → AI → 自动审批策略
└──────────────────────────────────┘
```
#### 3.8 审历史面板
#### 3.8 审历史面板
**方案**:独立页面展示 `ai_tool_executions` 表的审计记录。
@@ -262,7 +264,7 @@ Settings → AI → 自动审批策略
**展示字段**
| 时间 | 工具 | 风险 | 状态 | 决策者 | 参数摘要 | 结果摘要 |
|------|------|------|------|--------|---------|---------|
|------|------|------|------|--------|---------|----------|
---
@@ -273,10 +275,10 @@ Settings → AI → 自动审批策略
| **P0** | 3.1 批量审批 | 0.5 天 | 🔥🔥🔥 |
| **P0** | 3.2 审批计数器 + 跳转 | 0.5 天 | 🔥🔥🔥 |
| **P0** | 3.3 write_file diff 预览 | 1 天 | 🔥🔥 |
| **P1** | 3.4 自动审批策略 | 1.5 天 | 🔥🔥🔥 |
| **P1** | 3.4 会话级授权 Session Trust | 1.5 天 | 🔥🔥🔥 |
| **P1** | 3.5 High 二次确认 | 0.5 天 | 🔥 |
| **P1** | 3.6 审批超时 | 0.5 天 | 🔥 |
| **P1** | 3.6 审批超时(前端5min) | 0.5 天 | 🔥 |
| **P2** | 3.7 Agentic 进度条 | 0.5 天 | 🔥🔥 |
| **P2** | 3.8 审历史面板 | 1 天 | 🔥 |
| **P2** | 3.8 审历史面板 | 1 天 | 🔥 |
**建议第一批落地**P0 三项(批量审批 + 计数器 + diff 预览),总计约 2 天工作量,覆盖最高频的体验痛点。

View File

@@ -1,6 +1,8 @@
# DevFlow 业务系统设计
> 创建: 2026-06-10 | 状态: 设计中 | 基于: 功能审查结论
> 创建: 2026-06-10 | 状态: 设计中 | 最后重写: 2026-06-15 (DOC-01 硬伤修复)
>
> **本文档为 ARCHITECTURE.md 的实质载体**(项目无独立 ARCHITECTURE.md 文件)。所有数据模型、设计决策均以源码为基准,已剔除虚构内容。
---
@@ -8,9 +10,9 @@
| 维度 | 定义 |
|------|------|
| **一句话** | AI 原生的产研操作系统,从想法到上线的全流程编排 |
| **目标用户** | 个人开发者优先,后续扩展到小团队 |
| **核心价值** | 全流程编排 — 想法池 → 项目 → 任务 → 工作流 → 发布AI 贯穿每个环节 |
| **一句话** | AI 原生的个人开发流程驾驶舱,从想法到任务到工作流的本地工具 |
| **目标用户** | 个人开发者 |
| **核心价值** | 想法池 → 项目 → 任务 → 工作流DAGAI 贯穿每个环节 |
| **差异化** | 想法第一公民 + AI 全程参与 + 本地优先(零运维) |
---
@@ -20,240 +22,176 @@
### 2.1 核心旅程
```
💡 想法池 📂 项目 🔀 任务 🚀 发布
─────────────────────────────────────────────────────────────────────────────────────
捕捉想法 ──→ AI评估评分 ──→ 晋升立项 ──→ 创建任务 ──→ 绑定分支 ──→ 执行工作流 ──→ 合并发布
│ │
└── 淘汰/归档 └── 多任务 └── DAG └── AI辅助 └── 自动化
并行推进 自动执行 冲突解决 发布流程
想法池 项目 任务 工作流
────────────────────────────────────────────────────────────────────────────
捕捉想法 评估评分 → 晋升立项 → 创建任务 → 绑定分支 → 执行 DAG 工作流
│ │ │
└── 淘汰/归档 └── 多任务 └── 自动执行 └── Script/Ai/Human
```
### 2.2 个阶段详细设计
### 2.2 个阶段详细设计
#### 阶段一:💡 想法池 (Idea Pool)
#### 阶段一:想法池 (Idea Pool)
**用户场景**开发者日常产生大量想法(看到新技术、遇到痛点、产生产品灵感),需要一个地方快速捕捉、评估、筛选。
**用户场景**:快速捕捉想法、评估、筛选。
| 操作 | 描述 | AI 参与 |
|------|------|---------|
| **捕捉** | 文本输入、快捷键快速记录、剪贴板导入 | 无 |
| **评估** | AI 分析可行性、市场潜力、技术难度 | ⭐ 核心场景LLM 评估报告 |
| **评分** | 多维打分 (可行性/影响力/紧迫性) | AI 给出建议分 |
| **关联** | 相似想法自动发现,可合并 | AI 语义相似度 |
| **捕捉** | 文本输入 | 无 |
| **评估** | 启发式评分(可行性/影响力/紧迫性) | 当前固定算法Phase 2 接 LLM |
| **晋升** | 高分想法晋升为项目 | AI 生成项目初始化建议 |
| **淘汰** | 低分想法归档或删除 | 无 |
**状态机**
**状态机**(对齐 `IdeaStatus` 枚举)
```
Draft → Evaluating → ScoredApproved → Promoted
Rejected → Archived
draft → pending_reviewapproved → promoted(正向)
rejected → archived(淘汰)
```
**关键问题**
- ✅ 想法是独立于项目的第一公民,不需要先有项目
- ✅ AI 评估是核心差异化功能
- ⚠️ 评分维度需要与实际对齐当前有3套不同的维度定义
#### 阶段二:📂 项目 (Project)
**用户场景**:从想法晋升或手动创建项目,管理项目全生命周期。
#### 阶段二:项目 (Project)
| 操作 | 描述 | AI 参与 |
|------|------|---------|
| **创建** | 从想法晋升 或 手动创建 | AI 生成项目描述/技术栈建议 |
| **阶段管理** | 5阶段管线想法→需求→编码→测试→发布 | 阶段推进时 AI 检查前置条件 |
| **上下文** | 项目代码结构、依赖、规范 | AI 自动分析项目结构 |
| **暂停/恢复** | 项目可暂停后恢复 | 无 |
| **创建** | 从想法晋升 或 手动创建 | AI 生成描述/技术栈建议 |
| **绑定目录** | 关联本地代码目录(自动探测技术栈) | 无 |
| **软删/恢复** | 回收站机制deleted_at | 无 |
**状态机**
**状态机**(对齐 `ProjectStatus` 枚举)
```
Planning → InProgress → Testing → Releasing → Completed
Paused → InProgress (恢复)
Cancelled
planning → in_progress → testing → releasing → completed
paused → in_progress恢复
cancelled
```
**阶段管线**current_stage独立于 status
```
Idea → Requirement → Coding → Testing → Release
```
**关键问题**
- ⚠️ 当前 `status`(项目生命周期)和 `current_stage`(当前阶段)是两个维度,前端混用了
- ⚠️ 数据库 projects 表缺少 `current_stage``repo_path``priority``tags` 字段
#### 阶段三:🔀 任务 (Task)
**用户场景**:项目内创建多个并行任务,每个任务绑定一个 Git 分支,独立工作流。
#### 阶段三:任务 (Task)
| 操作 | 描述 | AI 参与 |
|------|------|---------|
| **创建任务** | 标题+描述,自动创建分支 | AI 从需求拆解任务 |
| **绑定分支** | 每个任务一个独立分支 | 自动生成分支名 |
| **执行工作流** | 触发 DAG 工作流(编码→测试→审查) | AI 参与每个节点 |
| **审查** | 代码审查、质量检查 | AI 自动审查 |
| **合并** | 合并到主分支,冲突解决 | AI 辅助冲突解决 |
| **创建任务** | 标题+描述 | AI 从需求拆解任务 |
| **执行工作流** | 触发 DAG 工作流 | AI 参与每个 Ai 节点 |
**状态机**
**状态机**(对齐 `TaskStatus` 枚举7 态)
```
Todo → InProgress → InReview → Testing → Done
Blocked → InProgress (解除阻塞)
Cancelled
todo → in_progress → in_review → testing → done
blocked → in_progress解除阻塞
cancelled
```
**关键问题**
- ⚠️ 缺少 `branches` 表,分支信息无法持久化
- ⚠️ 任务到工作流的关联 (`workflow_def_id`) 缺失
- ⚠️ 前端 TaskStatus 有 4 套不同的值
#### 阶段四:工作流 (Workflow)
#### 阶段四:⚙️ 工作流 (Workflow)
**实际内置 3 种节点类型**(均在 `crates/df-nodes/src/` 完整实现):
**用户场景**DAG 驱动的工作流自动执行,支持条件分支、并行、人工审批。
| 节点 | 文件 | 作用 | 阻塞 |
|------|------|------|------|
| **Script** | `script_node.rs` | Shell 命令执行(经 `df-execute::shell` | 否 |
| **Ai** | `ai_node.rs` | LLM 文本生成/分析(非流式 complete | 否 |
| **Human** | `human_node.rs` | 人工审批/确认(单选/多选) | 是 |
| 组件 | 描述 |
|------|------|
| **DAG 定义** | 可序列化的节点+边定义,持久化到 SQLite |
| **节点类型** | Script / AI / Docker / Git / HTTP / Human / Notify / Subflow |
| **执行器** | 按拓扑层并行执行,支持暂停/恢复 |
| **事件总线** | 实时推送节点状态到前端 |
| **NodeRegistry** | 根据类型字符串动态创建节点实例 |
> 不存在 Condition / Parallel / Docker / Git / Notify / HTTP / Subflow 节点。
> 条件分支由工作流引擎层处理(条件表达式引擎见 `条件表达式引擎-2026-06-15.md`)。
**工作流执行生命周期**
```
Pending → Running → Completed
Paused → Running (恢复)
Failed → Running (重试)
Cancelled
pending → running → completed
paused → running恢复
failed → running重试
cancelled
```
**关键问题**
- ✅ DAG 拓扑排序算法正确
- ✅ DagDef/NodeRegistry 已实现
- ⚠️ Executor 同层节点尚未并行化
- ⚠️ 条件分支引擎未实现
- ⚠️ HumanNode人工审批暂停/恢复未连通
#### 阶段五:🚀 发布 (Release)
**用户场景**:选择多个已完成任务,编排发布流程。
| 操作 | 描述 | AI 参与 |
|------|------|---------|
| **选择任务** | 选择要发布的 Done 状态任务 | 无 |
| **创建发布** | 合并分支到 release 分支 | AI 生成 changelog |
| **集成测试** | 运行完整测试工作流 | 自动 |
| **发布** | 部署 + 健康检查 | 自动 |
| **回滚** | 发布失败回滚 | AI 分析失败原因 |
**状态机**
```
Planning → Integrating → Testing → Ready → Published
→ RolledBack
→ Cancelled
```
**关键问题**
- ⚠️ 前端完全缺少发布入口
- ⚠️ releases 表缺少 `branch_name``workflow_def_id`
---
## 三、跨领域功能设计
## 三、跨领域功能设计(已实现)
### 3.1 标注系统 (Annotation)
### 3.1 知识库 (Knowledge)
**设计理念**:任何内容(代码/文档/需求/测试报告)都可插入标注,统一收集后交给 AI 批量处理
| 标记 | 含义 | AI 处理方式 |
|------|------|-----------|
| FIXME | 需要修复 | AI 定位问题并生成修复建议 |
| TODO | 待办 | AI 拆解为任务 |
| QUESTION | 疑问 | AI 尝试回答 |
| RISK | 风险 | AI 评估风险等级 |
| DECISION | 决策 | 自动记录到决策日志 |
| OPTIMIZE | 优化 | AI 给出优化方案 |
**批量处理流程**
```
收集所有 Open 标注 → 按类型分组 → AI 逐条处理 → 标记为 Resolved
```
### 3.2 决策留痕 (Decision Journal)
**设计理念**:所有关键决策自动或半自动记录,全程可追溯。
**自动记录的决策场景**
- 想法评估结果(为什么批准/拒绝)
- 功能标记为"不做"时(为什么不做)
- AI 选择了方案 A 而非方案 B 时
- 代码审查中发现风险时的处理决策
- 发布前的检查点决策
### 3.3 经验进化 (Evolution)
**设计理念**:开发过程自动沉淀知识,越用越聪明。
Tier1 AI 提炼:从 AI 对话中自动提炼候选经验条目,附带 reasoning 判断依据
| 知识类型 | 来源 | 复用场景 |
|---------|------|---------|
| 审查规则 | 代码审查结论 | 后续审查自动应用 |
| 审查规则 | 代码审查结论 | 后续审查参考 |
| Prompt 模板 | 成功的 AI 对话 | 类似场景复用 |
| 踩坑经验 | 错误修复过程 | 遇到类似问题提醒 |
| 架构模式 | 项目结构分析 | 新项目初始化建议 |
| 踩坑经验 | 错误修复过程 | 类似问题提醒 |
### 3.4 AI 编排
**生命线**candidate → pending_review → published → archived带 reuse_count / verified 信号。
**多模型策略**
```
任务类型 → ModelRouter → 最优模型
代码生成 → Claude/GPT-4
代码审查 → Claude (长上下文)
文档生成 → GLM/DeepSeek (性价比)
快速问答 → DeepSeek (低成本)
```
### 3.2 AI 多 Provider
**Agent 协作模式**Phase 2+
```
Planner Agent → 拆解任务
Coder Agent → 编码实现
Reviewer Agent → 代码审查
Fixer Agent → 修复问题
```
支持配置多个 AI 提供商OpenAI 兼容 / GLM / DeepSeek / Anthropic 原生协议),可在设置中管理并指定默认。
详见 [df-ai AI集成模块](../03-模块文档/df-ai-AI集成模块-2026-06-12.md)。
### 3.3 EventBus 事件总线
进程内 `tokio::sync::broadcast` 发布/订阅,前端经 `@tauri-apps/api/event` 的 emit/listen 接收。**不是 WebSocket**。
---
## 四、数据模型设计(按阶段)
## ~~三、跨领域功能设计(已废弃规划)~~
### Phase 1 最小表集(当前 + 补全)
> 以下章节曾详述标注系统、决策留痕、经验进化、AI 编排ModelRouter/Agent 协作)等设计。
> 这些功能**从未实现**对应表annotations/decisions/features/test_cases也从未建表。
> 保留此节仅作历史存档参考,读者应视为"规划意图"而非"现有能力"。
| 表 | 用途 | 状态 |
|----|------|------|
| ideas | 想法池 | ✅ 已有,需补字段 |
| projects | 项目管理 | ✅ 已有,需补字段 |
| tasks | 任务管理 | ✅ 已有,需补字段 |
| releases | 发布管理 | ✅ 已有,需补字段 |
| workflow_defs | 工作流定义 | ❌ 缺失 |
| workflow_executions | 工作流执行 | ✅ 已有,需补字段 |
| node_executions | 节点执行记录 | ✅ 已有 |
| branches | 分支管理 | ❌ 缺失 |
### ~~3.1 标注系统 (Annotation)~~ — ❌ 未实现
### Phase 2 扩展表
### ~~3.2 决策留痕 (Decision Journal)~~ — ❌ 未实现
| 表 | 用途 |
|----|------|
| ai_providers | AI 模型配置 |
| connections | 连接配置 |
| artifacts | 产出物 |
### ~~3.3 经验进化 (Evolution)~~ — ⚠️ 部分落地为知识库knowledges 表),但远不及原规划规模
### Phase 3+ 完整表
### ~~3.4 AI 编排ModelRouter / Agent 协作)~~ — ❌ ModelRouter 从未存在Agent 协作属 Phase 2 规划B 路线)
| 表 | 用途 |
|----|------|
| annotations | 标注系统 |
| decisions | 决策留痕 |
| features | 需求功能清单 |
| test_cases | 测试用例 |
| test_runs | 测试执行记录 |
| knowledge | 经验知识库 |
| merge_requests | 合并请求 |
---
## 四、数据模型设计V1-V13 迁移实际表)
> 核对基准:`crates/df-storage/src/migrations.rs` 建表 SQL + `models.rs` Record 结构体。
### 全量表清单13 业务表 + 1 元表)
#### 活跃业务表11 张)— 有上层代码读写
| # | 表名 | 建表版本 | 用途 | 对应 Model | 活跃消费者 |
|---|------|---------|------|-----------|-----------|
| 1 | `ideas` | V1+V2 | 想法池 | IdeaRecord | df-ideas crate |
| 2 | `projects` | V1+V11+V12 | 项目管理 | ProjectRecord | df-project crate |
| 3 | `tasks` | V1+V2 | 任务管理 | TaskRecord | commands::taskIPC handler 直连 CRUD |
| 4 | `workflow_executions` | V1+V2 | 工作流执行实例 | WorkflowRecord | df-workflow crate |
| 5 | `node_executions` | V1 | 节点执行审计 | NodeExecutionRecord | df-workflow executor |
| 6 | `ai_conversations` | V3+V4/V5/V6 | AI 对话历史 | AiConversationRecord | commands::ai |
| 7 | `ai_providers` | V9 | AI 提供商配置 | AiProviderRecord | commands::ai::provider |
| 8 | `ai_tool_executions` | V9 | AI 工具调用审计 | AiToolExecutionRecord | commands::ai |
| 9 | `knowledges` | V7+V8/V10 | 知识库条目 | KnowledgeRecord | commands::knowledge |
| 10 | `knowledge_events` | V10 | 知识生命线事件 | KnowledgeEventRecord | commands::knowledge |
| 11 | `app_settings` | V13 | 通用 KV 设置 | (无独立 model) | commands::settings手写 Repo |
#### 遗留表2 张)— DDL 存在但无活跃业务消费者
> `df-task` crate 已于 2026-06-14 移除(零引用清理)。以下表仍在 migrations.rs 中创建、models.rs 有结构体、CRUD 可用,但当前**无上层业务代码写入或消费**。
| # | 表名 | 建表版本 | 原始用途 | 状态 |
|---|------|---------|---------|------|
| 12 | `branches` | V2 | Git 分支绑定 | ⚠️ 无消费者DDL 存在CRUD 可用但无人调用) |
| 13 | `releases` | V1 | 发布记录 | ⚠️ **功能性死表**DDL 存在且含 version/status/task_ids/changelog/released_at 完整 schema但全代码库零业务读写——无 ReleaseStatus 枚举、无 release 相关 IPC command、前端无发布管理页面。属"建了但从未使用"的空壳占位。 |
#### 内部元表
| # | 表名 | 建表版本 | 用途 |
|---|------|---------|------|
| - | `schema_version` | V0 | 迁移版本跟踪(仅存 version INTEGER无业务语义 |
### 不存在的表(曾出现在早期规划但从未建表)
| 表名 | 状态 | 说明 |
|------|------|------|
| `workflow_defs` | ❌ 从未建表 | 工作流定义以 dag_json 内嵌在 workflow_executions 中 |
| `connections` | ❌ 从未建表 | 连接配置使用 app_settings KV 表存储 |
| `artifacts` | ❌ 从未建表 | 产出物概念未落地 |
| `annotations` | ❌ 从未建表 | 标注系统属已废弃规划 |
| `decisions` | ❌ 从未建表 | 决策留痕属已废弃规划 |
| `features` | ❌ 从未建表 | 需求功能清单未落地 |
| `test_cases` / `test_runs` | ❌ 从未建表 | 测试模块未落地 |
| `knowledge`(单数)| ❌ 不存在的旧命名 | 实际表名为 `knowledges`复数V7 建表 |
| `merge_requests` | ❌ 从未建表 | 合并请求未落地 |
---
@@ -261,52 +199,39 @@ Fixer Agent → 修复问题
### D1: 想法是第一公民
- 想法池独立于项目,可以独立运转
- 想法不需要关联项目即可被评估和打分
- 晋升是单向操作(想法→项目),但保留追溯
### D2: 多任务/分支并行
- 同一项目内多个任务同时开发
- 每个任务绑定独立 Git 分支
- 任务间互不干扰,完成后合并
### D3: 引擎不绑定业务
- DAG 引擎纯粹做编排,不知道"想法"/"项目"等概念
- 阶段是 DAG 模板,可自定义
- 节点通过 Node trait 扩展
### D4: 本地优先
### D2: 本地优先
- SQLite 嵌入,不依赖云服务
- 所有数据存储在本地
- 零运维,安装即用
### D5: AI 贯穿全程
- 不是"加了 AI 功能",而是"AI 是系统的一部分"
- 每个阶段都有 AI 参与
- AI 输出作为决策依据,最终决策权在人
### D3: 引擎不绑定业务
- DAG 引擎纯粹做编排,不感知具体业务语义
- 业务逻辑在 df-nodes 实现Node trait 是纯接口)
### D6: 决策必留痕
- 所有关键决策自动记录
- 决策可追溯到具体上下文(哪个想法、哪个功能、哪次审查)
- 未来可回溯"为什么这么做"
### D4: AI 贯穿全程
- AI Chat 对话 + 工作流 AiNode 双路径
- AI 输出作为决策依据,最终决策权在人
---
## 六、审查发现的设计问题与决策
## 六、Crate 结构
| # | 问题 | 设计决策 | 优先级 |
|---|------|---------|--------|
| 1 | 状态枚举三套不一致 | **以 types.rs 为准**ARCHITECTURE.md 和 SQL 同步 | Phase 1 |
| 2 | projects 缺 status vs stage | **status 和 current_stage 分开**status 管生命周期stage 管进度 | Phase 1 |
| 3 | ideas.promoted_to 缺失 | **V2 补字段**,晋升时回写 | Phase 1 |
| 4 | branches 表不存在 | **V2 新增表**,分支管理需要持久化 | Phase 1 |
| 5 | workflow_executions 缺 project_id | **V2 补字段**,执行记录必须关联业务 | Phase 1 |
| 6 | DAG 不可序列化 | **DagDef/Dag 分离**(已完成) | Phase 1 |
| 7 | Executor 串行 | **同层并行化**(待实现) | Phase 1 |
| 8 | 前端 id 类型不对 | **统一为 string (UUID)** | Phase 1 |
| 9 | Store 未接入 View | **先建 API 层再接 Store** | Phase 1 |
| 10 | 标注/决策表缺失 | Phase 3 再建表,当前 UI 标注 "Coming Soon" | Phase 3 |
| 11 | 需求-测试追溯 | Phase 4 再建表 | Phase 4 |
| 12 | 经验进化 | Phase 5 实现 | Phase 5 |
实际 **8 个 crate**`crates/` 目录下):
| Crate | 职责 |
|-------|------|
| `df-core` | 公共类型types.rs、事件定义、工具函数 |
| `df-workflow` | DAG 引擎拓扑排序、执行器、Node trait |
| `df-nodes` | 内置节点Ai / Script / Human |
| `df-ai` | AI 集成层LlmProvider trait、OpenAI 兼容、Anthropic、ContextManager、工具注册基础设施 |
| `df-execute` | Shell 执行(跨平台封装) |
| `df-storage` | SQLite 存储层migrations、CRUD 宏、Repo |
| `df-ideas` | 想法池业务逻辑(评估、晋升) |
| `df-project` | 项目管理业务逻辑(目录绑定、技术栈探测) |
> 原始设计文档Phase1架构决策 ADR-003曾写 "13 个独立 crate",属过时数字,未随代码演进更新。实际为以上 8 个。
---
@@ -316,16 +241,15 @@ Fixer Agent → 修复问题
```
1. 用户在想法池输入"做一个 Markdown 编辑器"
2. AI 评估可行性,给出评分和建议
2. 启发式评估可行性,给出评分和建议
3. 用户点击"晋升为项目"
4. 系统创建项目,进入编码阶段
4. 系统创建项目
5. 用户创建任务"实现基础编辑功能"
6. 系统创建分支 task/abc123
7. 用户点击"运行工作流"
8. DAG 执行: [Shell: 环境检查] → [Shell: 运行测试] → [Shell: 构建产物]
9. 前端实时展示执行日志
10. 执行完成,结果持久化到 SQLite
11. 用户刷新页面,数据仍在
6. 用户点击"运行工作流"
7. DAG 执行: [Script: 环境检查] → [Ai: 代码生成] → [Human: 审批]
8. 前端经 EventBus 实时展示执行日志
9. 执行完成,结果持久化到 SQLite
10. 用户刷新页面,数据仍在
```
---
@@ -337,13 +261,22 @@ Fixer Agent → 修复问题
-`evaluator.rs` 已实现的 `EvalDimension` 对齐
- `IdeaScores { feasibility, impact, urgency, overall }` 保留
### Q2: 发布模块 Phase 1 范围 ✅ 已确认
**决策**Phase 1 简化 — 只做 Release 记录 + 手动标记任务
- releases 表保留,支持 CRUD
- 不做自动化发布流程(合并→测试→部署)
- 前端在 ProjectDetail 中添加简单 Release 面板
### Q2: 发布模块 ✅ 已确认(当前为死表状态)
**决策**Phase 1 不做发布功能。releases 表 DDL 存在但无业务逻辑,待后续激活。
- 不做自动化发布流程
- 前端无发布入口
### Q3: AI 评估 Phase 1 范围 ✅ 已确认
**决策**Phase 1 用固定算法评分,延后接入 AI
- `ScoringEngine` 当前返回固定 5.0,改为基于启发式规则的简单算法
**决策**Phase 1 用固定算法评分,延后接入 LLM
- `ScoringEngine` 当前返回基于启发式规则的分数
- Phase 2 接入 LLM 后替换为 AI 评分
---
## 相关文档
- [df-nodes 节点集合](../03-模块文档/df-nodes-节点集合-2026-06-12.md) — 3 节点详述
- [df-ai AI 集成模块](../03-模块文档/df-ai-AI集成模块-2026-06-12.md) — Provider / Context / 工具注册
- [df-storage 存储层](../03-模块文档/df-storage-存储层-2026-06-12.md) — 迁移 / CRUD / Repo
- [df-workflow 工作流引擎](../03-模块文档/df-workflow-工作流引擎-2026-06-12.md) — DAG / Executor
- [Phase1 架构决策](./Phase1架构决策-2026-06-12.md) — ADR 记录注意ADR-001/003 含过时信息,以本文档为准)

View File

@@ -0,0 +1,125 @@
# 任务推进链实施路径
> **日期**: 2026-06-16
> **来源**: [任务执行与推进能力分析-2026-06-16.md](../05-代码审查/任务执行与推进能力分析-2026-06-16.md) 第八章(已核对注入)
> **状态**: 规划定稿。**D-260616-01~04 已决策(2026-06-16)**:①前端对齐7态 ②任务软删(UI缓做) ③advance_task 走 **df-nodes Node** ④阶段1先行。**阶段1可启动(F-01~05)**。
> **关联决策**: D-260616-01~04决策结果见 todo.md 待决策区块)
---
## 〇、核对纠正(实施前必读)
经 Explore 代理核对原分析报告「AI 缺 update_task / run_command 工具」**核实为假**
| 工具 | 报告称 | 核实 | 证据 |
|------|--------|------|------|
| `update_task` | 缺失 | ❌ **存在** | tool_registry.rs:348AI 能改任务字段(含 status经裸 update_field 非状态机收口) |
| `run_command` | 缺失 | ❌ **存在** | tool_registry.rs:468完整 Shell 执行实现 |
| `run_workflow` | 空壳桩 | ⚠️ **未注册** | tool_registry.rs 无此工具(连空壳都没有) |
| `advance_task` | 缺失 | ✅ 确实缺失 | 全局搜零定义 |
**修正后结论**AI **能**更新任务状态、**能**运行命令,但仍**不能**:① 触发三闸门推进链(无 advance_task② 联动工作流task_id=None + 无完成回调)③ 在对话中触发工作流(无 run_workflow 工具)。
---
## 一、阶段 0 — 基础修复(前置,部分已立)
已在 todo.md 立项 **B-260616-12~18**(状态枚举/路由/try-catch/字段保护/DDL/priority/绕 store
**阻塞已解除2026-06-16 D-01/D-02 决策)**
- B-260616-12状态枚举→ D-260616-01 定**前端对齐后端 7 态**,可直接做
- B-260616-13软删除→ D-260616-02 定**加软删除对标 projectsUI 缓做)**,可直接做
---
## 二、阶段 1 — 推进骨架(手动闭环 ~200 行,报告建议先行)
目标:任务状态经「合法路径」推进,而非裸字段修改。
| 任务 | 内容 | 依赖 |
|------|------|------|
| **F-260616-01** [P1] | **状态机定义df-nodes 新模块 `task_state_machine.rs`**7 态合法转换枚举(`todo→in_progress→in_review→testing→done` 闸门链 + `blocked` 退回 + `cancelled`)。独立模块,非挂在 TaskStatus enum 上。 | D-01✅ 前端对齐7态 |
| **F-260616-02** [P1] | **advance_task 推进逻辑df-nodes `task_advance_node.rs` 实现 Node trait**:校验转换 + 原子写(下沉 SQL `WHERE status=:expected` 防 TOCTOU。df-nodes 需补 `df-storage` 依赖读 TaskRecord核实无循环。IPC 层 thin 入口调 df-nodes。 | D-03✅ df-nodes, F-01 |
| **F-260616-03** [P1] | `status` 移出 `update_task` 白名单(推进链唯一收口) | F-02关联 B-260616-16 |
| **F-260616-04** [P2] | `review_rounds` 字段(退回时 +1任务卡显示「第 N 轮 review」 | F-01 |
| **F-260616-05** [P1] | 前端 TaskDetail 推进按钮(手动推进,不接 AI | F-02, F-03 |
此阶段不接 AI/工作流,纯人工推进,但状态机保护和收口到位。
---
## 三、阶段 2 — 工作流联动(单向)
目标:工作流执行能回写任务状态。
**F-260616-06** [P1](聚合):
1. `run_workflow` IPC 支持 `task_id` 参数(去 workflow.rs:56 None 硬编码)
2. 工作流完成回调 → 检查 task_id → 推进任务状态
3. 定义任务推进 DAG 模板AiNode 执行 + AiNode 自审 + HumanNode 核对)
4. `advance_task` 触发对应闸门工作流
5. 前端展示工作流执行进度
**依赖**:阶段 1 完成。详见报告 §8 阶段 2。
---
## 四、阶段 3 — AI 执行闭环
目标AI 能真正执行任务内容。
**F-260616-07** [P2](聚合):
1. `advance_task` AI 工具(让 AI 经合法路径推进)
2. `run_workflow` AI 工具注册实装核对tool_registry.rs **无此工具**,需新建)
3. AiNode 接入任务上下文(读任务描述 + 项目目录)
4. AI 自审 verdict 结构化输出 + 解析
5. 失败路径完整处理(退回/重做/保持)
**依赖**:阶段 2 完成。详见报告 §8 阶段 3。
---
## 五、阶段 4 — Git 集成(增强)
目标:代码类任务支持 Git 工作流。
**F-260616-08** [P3](聚合):
1.`kind` 字段code/doc/design/generic
2. code kind 闸门接 git 命令worktree/commit/merge
3. BranchRecord 联动(加 worktree_path
4. `on_task_advanced` 钩子填充(分支联动 + 项目 completed
**依赖**:阶段 3 完成。详见报告 §8 阶段 4。
---
## 六、依赖关系图
```
D-01 枚举方向 ──▶ F-01 状态机 ──▶ F-02 advance_task ──▶ F-03 收口 ──▶ F-05 前端按钮
│ │
└──▶ F-04 rounds └──▶ 阶段2(F-06) ──▶ 阶段3(F-07) ──▶ 阶段4(F-08)
D-03 架构落点 ──▶ F-02
D-04 路径取舍 ──▶ 阶段1 是否先行
```
**✅ 阶段 1 可启动2026-06-16**D-01前端 7 态)/ D-03df-nodes Node/ D-04先行三决策已定。阶段 1 ~200 行,从 0% 推进能力到「手动推进闭环」。df-nodes 落点核实可行Node trait 纯接口 `df-workflow/src/node.rs:67`,现有 AiNode/HumanNode/ScriptNode需补 `df-storage` 依赖无循环)。
---
## 七、待合并到 `docs/todo.md` 的指针
> 主文件 todo.md 并发修改频繁后台代理以下指针待稍后合并。合并时在「待决策」区块D-260616-04 后)插入:
```
### 🗺️ 任务推进链实施路径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。
- [ ] F-260616-01~05 阶段1 推进骨架(状态机+advance_task+收口+rounds+前端按钮)
- [ ] F-260616-06 阶段2 工作流联动task_id+回调+DAG模板
- [ ] F-260616-07 阶段3 AI 执行闭环advance_task/run_workflow 工具+AiNode+自审)
- [ ] F-260616-08 阶段4 Git 集成kind+git闸门+worktree
```

View File

@@ -11,7 +11,7 @@
| 代理 | 立场 | 综合评分 |
|------|------|---------|
| 市场分析师 | 竞品全景 + 市场数据(带外部信源) | **4/10 — 不建议以当前形态推进** |
| 技术架构师 | 13 crate / Tauri / 引擎 / AI 可行性 | **2.7/5 — 可行但必须砍 scope** |
| 技术架构师 | 13 crate2026-06-12 评审时数2026-06-14 删 5 僵尸 crate现 8/ Tauri / 引擎 / AI 可行性 | **2.7/5 — 可行但必须砍 scope** |
| 恶魔代言人 | 逐功能质疑需求真实性 | **核心成立60% 功能该砍** |
---
@@ -21,7 +21,7 @@
### 共识 1🔴 Scope 失控是最大风险
- 8 个核心功能横跨 4-5 个产品类别PM + 工作流 + AI 编排 + 代码分析 + 知识库)
- 22,745 字架构文档、16 张表、13 crate —— **这是操作系统的野心,不是 MVP 的规划**
- 22,745 字架构文档、16 张表、13 crate评审时点数2026-06-14 裁定为 8 crate—— **这是操作系统的野心,不是 MVP 的规划**
- 现实工时v1.0 全功能需全职 8-12 个月 / 业余 1.5-2 年
- 历史教训Firebase/Heroku/全生命周期 API 平台都被"组件化组合"打败
@@ -112,7 +112,7 @@
### 5.3 架构调整
- **13 crate 保留目录结构**已建好,删除反而费工),但 Phase 1 只激活 6 个:
- **13 crate 保留目录结构**评审时点2026-06-14 裁定删除 5 个僵尸 crate df-evolve/df-plugin/df-stages/df-task/df-traceability现实际 8 crate),但 Phase 1 只激活 6 个:
`df-core / df-workflow / df-storage / df-execute / df-nodes / src-tauri`
- 其余 7 个 crate 标记为 `[预留]`,从 workspace 默认构建中保留但不再投入开发
- Git 操作走 Shell CLI不引入 libgit2技术报告建议省 1-2 周)

View File

@@ -173,13 +173,15 @@ GLM 端对 `tool_use_id` 为 `None`/空串的 `tool_result` 块会返 500 卡死
> 归属:`ai_tools.rs` 仅提供基础设施(`RiskLevel` / `AiTool` / `AiToolRegistry`);工具的具体定义与注册在 `src-tauri/src/commands/ai/tool_registry.rs::build_ai_tool_registry`handler 即唯一执行路径schema+risk+实现同源)。
19 个内置工具,按风险分级(核对 `build_ai_tool_registry`2026-06-15
22 个内置工具,按风险分级(核对 `build_ai_tool_registry`2026-06-15
| 风险 | 工具 |
|------|------|
| Low自动执行6| list_projects / list_tasks / list_ideas / list_trash / read_file / list_directory |
| Medium需审批7| update_project / create_project / bind_directory / create_task / update_task / create_idea / write_file |
| High需审批6| delete_task / delete_project / restore_project / purge_project / run_workflow / run_command |
| 风险 | 数量 | 工具 |
|------|------|------|
| Low自动执行8| 8 | list_projects / list_tasks / list_ideas / list_trash / read_file / list_directory / file_info / search_files |
| Medium需审批9| 9 | update_project / create_project / bind_directory / create_task / update_task / create_idea / write_file / patch_file / append_file |
| High需审批5| 5 | delete_task / delete_project / restore_project / purge_project / run_command |
> 原文档写 "21 个Low 8 + Medium 8 + High 5",遗漏了 **patch_file**Sprint 后增补的 Medium 风险局部文件编辑工具)。实际为 **22 个Low 8 + Medium 9 + High 5**。
工具执行结果写 `ai_tool_executions` 表(审计日志)。

View File

@@ -431,7 +431,7 @@ pub enum Recommendation {
│ ┌── 🔴 反方质疑 ─────── 📊 4.2/10 ──────┐ │
│ │ ⚠️ 致命: Claude Code 已在做全流程 │ │
│ │ ⚠️ 致命: 个人开发者工具付费意愿极低 │ │
│ │ ⚠️ 严重: 13 crate 维护成本高 │ │
│ │ ⚠️ 严重: 13 crate 维护成本高 (06-14裁至8)│ │
│ │ ⚠️ 严重: 桌面应用市场天花板明显 │ │
│ └────────────────────────────────────────┘ │
│ │

View File

@@ -2,7 +2,7 @@
> 创建: 2026-06-10 | 状态: ✅ 已完成2026-06-14 核对源码标注)
>
> **完成说明(核对源码 src-tauri/src/commands/ 实测)**IPC 命令层已全部落地。`src-tauri/src/lib.rs` 的 `tauri::generate_handler!` 共注册 **66 个** IPC 命令;`commands/` 目录下共 69 处 `#[tauri::command]` 标注(含少量未在入口注册的内部/辅助命令)。按 CRUD 模块组织目录结构、统一错误处理、AppState 注入、前端 invoke 封装均已实施。原文(含目标清单与待填写占位)保留如下,未删除。
> **完成说明(核对源码 src-tauri/src/commands/ 实测2026-06-15 复核**IPC 命令层已全部落地。`src-tauri/src/lib.rs` 的 `tauri::generate_handler!` 共注册 **67 个** IPC 命令;`commands/` 目录下共 70 处 `#[tauri::command]` 标注(差额 3 为已定义但未在入口注册的内部/辅助命令主代理数行确认ai/commands.rs 20 标注注册 19 + ai/mod.rs 2 标注未注册)。按 CRUD 模块组织目录结构、统一错误处理、AppState 注入、前端 invoke 封装均已实施。原文(含目标清单与待填写占位)保留如下,未删除。
---
@@ -12,13 +12,13 @@
## 目标
- [ ] 按 CRUD 模块组织 commands 目录结构
- [ ] 实现 projects 相关 commands
- [ ] 实现 tasks 相关 commands
- [ ] 实现 workflow 相关 commands
- [ ] 统一错误处理 (Rust Error → 前端字符串)
- [ ] 状态注入 (AppState)
- [ ] 前端 invoke 封装
- [x] 按 CRUD 模块组织 commands 目录结构
- [x] 实现 projects 相关 commands
- [x] 实现 tasks 相关 commands
- [x] 实现 workflow 相关 commands
- [x] 统一错误处理 (Rust Error → 前端字符串)
- [x] 状态注入 (AppState)
- [x] 前端 invoke 封装
## 实施记录
@@ -32,12 +32,12 @@
> | 任务 | `commands/task.rs` | 5 |
> | 灵感 | `commands/idea.rs` | 6 |
> | 工作流 | `commands/workflow.rs` | 5 |
> | AI 聊天/对话/技能/并发 | `commands/ai/`commands.rs、conversation.rs、skills.rs 等) | 18 |
> | AI 聊天/对话/技能/并发 | `commands/ai/`commands.rs、conversation.rs、skills.rs 等) | 19 |
> | 知识库 | `commands/knowledge.rs` | 14 |
> | 应用设置 KV | `commands/settings.rs` | 4 |
> | **合计** | | **66** |
> | **合计** | | **67** |
>
> 另:`commands/` 目录内共 69 处 `#[tauri::command]` 标注(差额 3 为已定义但未在 `generate_handler!` 注册的内部/辅助命令)。`commands/knowledge_timeline.rs` 不是 IPC 命令文件,是知识生命线记录器(内部便捷方法,无 `#[tauri::command]`),不计入。
> 另:`commands/` 目录内共 70 处 `#[tauri::command]` 标注(差额 3 为已定义但未在 `generate_handler!` 注册的内部/辅助命令)。`commands/knowledge_timeline.rs` 不是 IPC 命令文件,是知识生命线记录器(内部便捷方法,无 `#[tauri::command]`),不计入。
*(原文占位:待填写)*

View File

@@ -14,13 +14,13 @@
## 目标
- [ ] Store 添加 async actions (调用 invoke)
- [ ] ProjectsView 接入 projectStore
- [ ] TasksView 接入 taskStore
- [ ] WorkflowView 接入 workflowStore
- [ ] IdeasView 接入 ideaStore (如有)
- [ ] 加载状态 / 错误状态 UI 处理
- [ ] 按钮事件绑定真实操作
- [x] Store 添加 async actions (调用 invoke)
- [x] ProjectsView 接入 projectStore
- [x] TasksView 接入 taskStore
- [x] WorkflowView 接入 workflowStore
- [x] IdeasView 接入 ideaStore (如有)
- [x] 加载状态 / 错误状态 UI 处理
- [x] 按钮事件绑定真实操作
## 实施记录

View File

@@ -13,10 +13,10 @@
## 验证目标
- [ ] 创建项目 → SQLite 持久化 → 前端显示
- [ ] 创建任务 → 绑定工作流 → 前端显示
- [ ] 启动工作流 → 3 节点顺序执行 → 状态实时更新到前端
- [ ] 工作流完成 → 产出物记录 → 前端查看
- [x] 创建项目 → SQLite 持久化 → 前端显示
- [x] 创建任务 → 绑定工作流 → 前端显示
- [x] 启动工作流 → 3 节点顺序执行 → 状态实时更新到前端
- [x] 工作流完成 → 产出物记录 → 前端查看
## 验证工作流设计

View File

@@ -0,0 +1,516 @@
# 任务执行能力与推进能力分析
> **日期**: 2026-06-16
> **范围**: 任务实体在 DevFlow 系统中的定位、执行链路、推进能力全景分析
> **关联文档**: `任务推进构想-2026-06-14.md` / `业务系统设计-2026-06-12.md` / `AI对话引擎-2026-06-14.md` / `DAG引擎详解-2026-06-14.md` / `任务模块问题分析-2026-06-16.md`
---
## 、核对结论速览2026-06-16 · Explore 代理并行取证)
> 本报告系架构分析,含密集代码事实断言。经 2 个代理逐项取证12 项断言中 **10 真 / 1 部分真 / 1 假(重大)**。核心论点(任务能存不能推进 / nodes 未接入 / 无回调)成立,但**一处工具缺失论据错误**(见下方纠正)。
### 真伪矩阵
| # | 断言 | 核实 | 证据 |
|---|------|------|------|
| 1 | workflow.rs task_id 恒 None | ✅真 | `workflow.rs:56`,全局无写入点 |
| 2 | workflow_def_id 从未写入 | ✅真 | `task.rs:73` Nonecrud 有 UPDATE 语句但无调用 |
| 3 | AI 缺 update_task / run_command | ❌**假** | `update_task`(tool_registry.rs:348) + `run_command`(:468) **均存在**,仅缺 `advance_task` |
| 4 | df-task crate 已删除 | ✅真 | `ARCHITECTURE.md:89-90`crates/ 无 df-task |
| 5 | DAG 完成无任务回调 | ✅真 | `executor.rs:169-172` 仅 emit WorkflowCompleted无监听推进 task |
| 6 | 任务无独立业务层 | ✅真 | `task.rs` 直连 Repository无状态机/推进逻辑 |
| 7 | AiNode 未接入任务推进 | ✅真 | `ai_node.rs:331`~12.7KB)完整,无任务推进 DAG 模板 |
| 8 | HumanNode 未接入任务推进 | ✅真 | `human_node.rs:640`~27.7KB)完整,无任务审批 DAG 模板 |
| 9 | ProjectDetail 工作流入口下线 | ✅真 | `ProjectDetail.vue:289-292` 注释 R-PD-2script 节点不注册 |
| 10 | 前端无推进入口 | ✅真 | Tasks.vue 纯展示 / TaskDetail.vue 纯只读 |
| 11 | 状态枚举三方不一致 | ⚠️部分真 | 后端7态 / 前端5态(merged) / 构想5态(done);前端 vs 构想 `merged``done` 微差 |
| 12 | advance_task / can_transition_to / review_rounds 全未实现 | ✅真 | Rust+TS 全局搜零定义TaskRecord 无 review_rounds 字段 |
### ⚠️ 关键纠正(影响多处结论)
报告第二章 2.2「缺失关键工具」、第四章断裂点 2/3 称 **「AI 缺 update_task / run_command 工具」——核实为假**
- `update_task`tool_registry.rs:348**存在**AI 能改任务字段(含 status经裸 update_field 非状态机收口)
- `run_command`tool_registry.rs:468**存在且有完整 Shell 执行实现**AI 能写代码也能跑命令
- 真正缺失的仅 `advance_task`(推进链触发器)
**修正后结论**AI **能**更新任务状态、**能**运行命令,但仍**不能**:① 触发三闘门推进链(无 advance_task② 联动工作流task_id=None + 无完成回调)。报告核心论点「任务能存不能(自动)执行/推进」成立但「AI 缺 update_task/run_command」的具体论据错误断裂点 2/3 已在正文中纠正标注。
---
## 一、任务在 DevFlow 中的设计定位
### 1.1 产品旅程中的位置
```
想法池 ──晋升──▶ 项目 ──拆解──▶ 任务 ──执行──▶ 工作流(DAG)
(Idea) (Project) (Task) (Workflow)
第一公民 容器/上下文 执行单元 编排引擎
```
DevFlow 的核心价值链是 **「想法 → 项目 → 任务 → 工作流」**,任务是从「规划」到「执行」的转折点:
- **想法**是「做什么」的候选池(评估/筛选/晋升)
- **项目**是「在哪个上下文做」(目录绑定/技术栈/状态)
- **任务**是「具体做什么」(标题/描述/状态/优先级/分支)
- **工作流**是「怎么自动做」DAG 编排 AI/Script/Human 节点)
### 1.2 设计意图AI-First 推进链
`任务推进构想-2026-06-14.md` 定义了任务的终极形态:
```
todo ──AI执行──▶ in_progress ──AI自审──▶ review_ready ──人工核对──▶ done
AiNode AiNode HumanNode
AI 干活 AI 审 AI 的活 人最终把关 AI 产出
```
核心设计原则:
- **人从「操作者」转为「审批者」** — 任务由 AI 执行,人监督 AI
- **advance_task 默认 AI 触发** — AI 执行/自审完成自动推进
- **三闸门必需** — AI 执行 / AI 自审 / 人工核对各有关卡
- **拒绝 → 退回 AI 重做** — 不是退回给人干
### 1.3 实际现状:设计 vs 实现的巨大鸿沟
| 维度 | 设计意图 | 实际实现 |
|------|---------|---------|
| 状态推进 | advance_task 状态机 + 三闸门 DAG | ❌ 不存在 advance_taskstatus 可被任意修改 |
| AI 执行 | AiNode/agent 读任务→写代码→跑测试→产出 diff | ❌ AiNode 存在但未接入任务推进链 |
| AI 自审 | AiNode 结构化 verdict (pass/warn/block) | ❌ 未实现 |
| 人工核对 | HumanNode 审批闭环 | ⚠️ HumanNode 存在但未接入任务推进链 |
| 状态机收口 | status 白名单移除advance_task 唯一入口 | ❌ status 仍在白名单,任意可改 |
| 工作流联动 | task → workflow 双向关联 | ❌ workflow 的 task_id 恒为 None |
| 前端推进 UI | 推进按钮 + 环节可视化 + diff 展示 | ❌ 纯只读,无任何推进入口 |
**结论:任务模块目前是一个「数据容器」,不是「执行单元」。它能存、能查、能删,但不能推进、不能执行、不能联动工作流。**
---
## 二、执行能力分析
### 2.1 任务「执行」的定义
在 DevFlow 的 AI-First 愿景中,「执行任务」意味着:
```
1. 读取任务描述 + 项目上下文
2. AI 写代码/改文件/跑测试agent 多步)
3. 产出 diff / 文件变更 / 测试结果
4. AI 自审产出code review结构化结论
5. 人工最终核对
```
### 2.2 当前执行能力盘点
#### ❌ 任务 → 工作流:无连接
```rust
// commands/workflow.rs — run_workflow 中 task_id 恒为 None
task_id: None, // 唯一引用点,硬编码 None
```
工作流执行**完全不感知任务**。`WorkflowRecord``task_id` 字段V2 迁移加的),但没有任何代码写入它。工作流是独立运行的,不知道自己在为哪个任务工作。
#### ❌ AI 对话 → 任务执行:无闭环
AI 对话引擎有 12 个工具,其中任务相关:
- `list_tasks`Low 风险,自动执行)— 只读
- `create_task`Medium 风险,需审批)— 只创建
**缺失的关键工具**
- ❌ 无 `update_task` 工具 — AI 不能推进任务状态
- ❌ 无 `advance_task` 工具 — AI 不能触发推进链
- ❌ 无 `run_command` 工具 — AI 能写代码但不能跑("能写不能跑"
-`run_workflow` 工具是空壳 — AI 不能在对话中触发工作流
AI 可以 **创建任务**,但不能 **执行任务**、**推进任务**、**关联工作流**。
#### ❌ 工作流节点 → 任务状态:无联动
```
DAG 执行完成
WorkflowRecord.status = "completed"
(结束 — 不回调任务状态,不触发 advance_task
```
DAG 引擎有完善的执行能力(拓扑排序/并发/状态机/事件总线),但执行结果**不回写任务**。一个工作流跑完了,关联的任务状态纹丝不动。
#### ⚠️ AiNode有能力但没接入
```rust
// ai_node.rs — 12.7KB,完整实现
// 能力:调用 LLMOpenAI/Anthropicconfig 驱动,支持上游输入
// 但:只在 DAG 内可用,没有「为某个任务执行」的入口
```
AiNode 是通用的 LLM 调用节点,可以做分析/生成/审查。但当前没有任何 DAG 模板把 AiNode 接入任务推进链。
#### ⚠️ HumanNode有能力但没接入
```rust
// human_node.rs — 27.7KB,完整实现
// 能力阻塞等待人工审批subscribe→send→select!),支持单选/多选
// 但:只在 DAG 内可用,没有「为某个任务审批」的入口
```
### 2.3 执行能力总结
| 执行环节 | 需要的能力 | 现状 | 缺口 |
|---------|-----------|------|------|
| 读取任务上下文 | 任务描述 + 项目目录 + 相关文件 | ✅ AI 工具可读 | — |
| AI 写代码 | write_file 工具 | ✅ 有(需审批) | — |
| AI 跑测试 | run_command 工具 | ❌ 不存在 | AI "能写不能跑" |
| AI 自审 | AiNode verdict 结构化输出 | ❌ 未实现 | 需定义 prompt + 解析 |
| 触发工作流 | run_workflow 工具 | ❌ 空壳 | 需实装 |
| 工作流回写任务 | 完成回调 advance_task | ❌ 不存在 | 需实现回调链路 |
| 人工审批 | HumanNode 审批 | ⚠️ 存在但未接入 | 需 DAG 模板 + 路由 |
**核心断链**:任务 ←✕→ 工作流 ←✕→ AI 执行。三个系统各自独立运行,没有形成闭环。
---
## 三、推进能力分析
### 3.1 当前推进机制:裸 status 字段 + 无保护
```rust
// commands/task.rs — update_task
// status 在白名单中,任意调用方可直接修改
state.tasks.update_field(&id, "status", &value)
```
任何人AI/用户/脚本)可以一行代码把任务从 `todo` 直接改成 `done`,跳过所有闸门。这是 `任务推进构想` 文档中标注的 **P0 致命漏洞**
### 3.2 设计中的推进机制advance_task + 状态机
> ⚠️ **决策更新2026-06-16**:本节原设想 advance_task 落 IPC 层 / df-task 复活。**D-260616-03 已决策走 df-nodes Node**(对齐 D3「业务逻辑在 df-nodes 实现」):状态机落 `df-nodes/src/task_state_machine.rs`F-01advance_task 落 `df-nodes/src/task_advance_node.rs` 实现 Node traitF-02IPC 层 thin 入口调 df-nodes。又 **D-260616-01 已定前端对齐 7 态**,下文状态机示例的 5 态ReviewReady/Abandoned实施时按 7 态重设(激活 InReview/Testing/Blocked。详见 [任务推进链实施路径](../../02-架构设计/任务推进链实施路径-2026-06-16.md)。
构想文档设计了完整的推进链,但**全部未实现**
#### 状态机can_transition_to
```rust
// 设计中 — 未实现
(Todo, InProgress | Abandoned) => true,
(InProgress, ReviewReady | Abandoned) => true,
(ReviewReady, Done | Abandoned | InProgress) => true, // 可退回
_ => false,
```
#### 状态机下沉 SQL防 TOCTOU
```sql
-- 设计中 — 未实现
UPDATE tasks SET status=:new, updated_at=:now
WHERE id=:id AND status=:expected -- affected_rows==0 即状态已变,拒绝
```
#### advance_task 命令
```
设计中 — 未实现
1. 校验状态转换合法性can_transition_to
2. 原子写入(下沉 SQL WHERE 前置)
3. 触发对应闸门工作流start/ready/merge
4. 工作流完成回调再推进状态
5. 失败路径处理(退回/保持)
```
#### loop 管理
```
设计中 — 未实现
review_rounds: i32 -- 退回时 +1任务卡显示「第 N 轮 review」
```
### 3.3 推进能力总结
| 推进环节 | 设计方案 | 实现状态 |
|---------|---------|---------|
| 状态机定义 | 7 态 / 5 态(待统一) | ❌ 无 can_transition_to |
| 状态机收口 | 移除 status 白名单 | ❌ status 仍可任意改 |
| advance_task 命令 | 唯一 status 写入路径 | ❌ 不存在 |
| 状态机下沉 SQL | WHERE 前置防 TOCTOU | ❌ 不存在 |
| AI 执行闸门 | AiNode 最小形态 | ❌ 未接入 |
| AI 自审闸门 | AiNode verdict | ❌ 未实现 |
| 人工核对闸门 | HumanNode 审批 | ❌ 未接入 |
| 失败路径 | 退回/保持/重做 | ❌ 未实现 |
| loop 管理 | review_rounds | ❌ 字段不存在 |
| 并发护栏 | per-task 互斥锁 | ❌ 不存在 |
| 崩溃恢复 | 孤儿清理 + 审批持久化 | ⚠️ 部分存在(审批持久化有,孤儿清理无) |
| 前端推进 UI | 按钮 + 可视化 | ❌ 纯只读 |
**推进能力实现度0%。全部停留在构想文档阶段。**
---
## 四、任务与其他系统的断裂点
### 4.1 断裂全景图
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 想法池 │──✅晋升──│ 项目 │──✅拆解──│ 任务 │──✕✕✕──│ 工作流 │
│ (Idea) │ │(Project) │ │ (Task) │ │(Workflow)│
└──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘
│ │
┌────┴─────┐ ┌────┴─────┐
│ AI 对话 │ │ DAG 引擎 │
│ (Agentic)│ │(Executor)│
└──────────┘ └──────────┘
│ │
✕ 无 update_task ✕ task_id=None
✕ 无 advance_task ✕ 无完成回调
✕ 无 run_command ✕ 无状态回写
✕ run_workflow=空壳 ✕ 无任务路由
```
### 4.2 六大断裂点详解
#### 断裂点 1任务 ↔ 工作流task_id = None
```rust
// commands/workflow.rs:56
task_id: None, // 硬编码
```
工作流不知道为哪个任务执行,任务不知道被哪个工作流处理。`TaskRecord.workflow_def_id` 字段存在但从未被写入。
**影响**:工作流执行结果无法回写任务状态,无法实现「工作流完成 → 自动推进任务」。
#### 断裂点 2AI 对话 ↔ 任务推进(无 advance_task 工具)
> ⚠️ **核对纠正**:原报告称「无 update_task 工具」——**核实为假**`update_task`tool_registry.rs:348存在。AI 工具集实际仅缺 `advance_task`推进链触发器。AI 能经裸 `update_task` 改 status 字段无状态机收口B-260616-15/16 同源),但不能触发三闸门推进链。
AI 工具集有 `create_task` / `update_task` 但没有 `advance_task`。AI 能创建/改任务但不能触发推进链。
**影响**AI 在对话中分析了任务、写了代码、跑了测试,但无法经「合法状态机路径」把任务从 `todo` 推进到 `done`,只能裸改 status旁路闸门
#### 断裂点 3~~AI 对话 ↔ 命令执行(无 run_command~~ 【⚠️ 核对为假,本断裂点不成立】
> ⚠️ **核对纠正**`run_command`tool_registry.rs:468**实际存在且有完整 Shell 执行实现**。AI 有 `write_file` 也有 `run_command`,能写代码也能跑命令,「写→跑→改」闭环成立。(注:`run_command` 属高危需审批工具,见 AE-2025-04 会话级授权;其 stdout/stderr 恒空问题见 B-260616 系列另报。)
~~AI 有 `write_file` 但没有 `run_command`。AI 写了代码但无法运行验证。~~
**影响**~~AI 执行链断裂在「写→跑→改」的「跑」环节~~ **不成立**。AI 执行链在命令执行环节闭合。
#### 断裂点 4AI 对话 ↔ 工作流run_workflow 空壳)
```rust
// tool_registry.rs — run_workflow 工具是 no-op 桩
// 返回提示信息,不真正执行工作流
```
**影响**AI 不能在对话中触发工作流来自动化任务执行。对应已有任务 `R-PD-12`
#### 断裂点 5工作流完成 → 任务状态(无回调)
DAG 执行器有 `WorkflowCompleted` 事件,但没有回调机制把这个事件转化为任务状态推进。
**影响**:即使工作流成功执行了 AI 执行 + AI 自审,任务状态仍然是 `todo`
#### 断裂点 6前端 ↔ 推进操作(无 UI 入口)
Tasks.vue 是纯展示TaskDetail.vue 是纯只读。没有任何按钮/操作可以推进任务状态。
**影响**:用户只能通过 AI 对话(如果 AI 有工具的话)或直接 API 调用来推进任务,但前者缺工具、后者不暴露 UI。
---
## 五、核心矛盾分析
### 矛盾 1状态枚举三方不一致
| 层面 | 状态集 | 语义导向 |
|------|--------|---------|
| 后端 enum | todo/in_progress/in_review/testing/done/blocked/cancelled | 通用软件工程 |
| 前端常量 | todo/in_progress/review_ready/merged/abandoned | Git 工作流 |
| 推进构想 | todo/in_progress/review_ready/done/abandoned | AI-First 推进链 |
三方各执一词,且推进构想的 5 态与前端常量一致但与后端 enum 不一致。**在推进链实现前必须先统一状态集**,否则状态机无法定义。
### 矛盾 2df-task crate 已删除但任务无独立业务层
```
ARCHITECTURE.md: 5.3.1 ~~Task & Branch Manager (df-task)~~ — 已移除
> 2026-06-14 零引用清理df-task crate 已删除
```
对比其他实体:
- **Idea** → 有 `df-ideas` crate评估/晋升/对抗)
- **Project** → 有 `df-project` crate扫描/管理)
- **Task** → ❌ 无独立 crateIPC 层直连 CRUD
任务没有业务逻辑层,`commands/task.rs` 直接调 `state.tasks.insert/query/update_field/delete`。这意味着:
- 状态机逻辑无处安放(只能塞 IPC 层或重新建 crate
- 推进链编排无处安放
- 与其他系统的联动逻辑无处安放
### 矛盾 3工作流引擎完善但无业务消费
DAG 引擎功能完善(拓扑排序/并发执行/状态机/事件总线/审批闭环/取消机制),但**没有任何业务场景在使用它**
- ProjectDetail.vue 的工作流演示入口已下线R-PD-2script 节点不再注册)
- run_workflow AI 工具是空壳
- 任务推进链未接入
引擎是「准备好了但没有乘客的列车」。
### 矛盾 4AI 能力在增长但无法触达任务
AI 对话引擎是系统中最活跃的模块Agentic Loop / 12 工具 / 审批门控 / 知识库集成 / 多 Provider但它的能力**无法触达任务执行**
- AI 能读项目代码、能写文件、能创建任务/项目/灵感
- 但不能推进任务、不能触发工作流、不能运行命令
- AI 的「手」伸到了文件系统,但伸不到任务状态机和工作流引擎
---
## 六、能力成熟度评估
### 按维度评分(满分 5 分)
| 维度 | 评分 | 说明 |
|------|------|------|
| **数据存储** | ⭐⭐⭐⭐ | CRUD 完整SQLite 持久化,字段丰富 |
| **数据查询** | ⭐⭐⭐ | 基本查询可用,缺分页/搜索/排序 |
| **状态管理** | ⭐ | 裸字段无保护,无状态机,无收口 |
| **执行能力** | ⭐ | 完全断裂,任务无法被执行 |
| **推进能力** | ⭐ | 0% 实现,全停留在构想文档 |
| **工作流联动** | ⭐ | task_id=None无回调无路由 |
| **AI 集成** | ⭐⭐ | AI 能创建任务,但不能执行/推进 |
| **前端体验** | ⭐⭐ | 列表展示可用,详情只读,无操作入口 |
| **数据安全** | ⭐⭐ | 硬删除无恢复,字段保护不足 |
| **架构设计** | ⭐⭐⭐⭐ | 构想文档非常完整344 行),设计质量高 |
**综合评分2.1/5** — 数据层及格,执行/推进层空白。
### 与其他实体对比
| 实体 | 存储 | 业务逻辑 | AI 集成 | 工作流联动 | 前端体验 | 综合 |
|------|------|---------|---------|-----------|---------|------|
| **Idea** | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ (df-ideas) | ⭐⭐⭐ (评估/晋升) | ⚪ N/A | ⭐⭐⭐⭐ | **3.6** |
| **Project** | ⭐⭐⭐⭐ | ⭐⭐⭐ (df-project) | ⭐⭐⭐ (创建/描述) | ⚪ N/A | ⭐⭐⭐⭐ | **3.5** |
| **Task** | ⭐⭐⭐⭐ | ⭐ (无 crate) | ⭐ (仅创建) | ⭐ (断裂) | ⭐⭐ (只读) | **2.1** |
| **Workflow** | ⭐⭐⭐ | ⭐⭐⭐⭐ (df-workflow) | ⭐⭐ (AiNode 可用) | ⭐ (无消费) | ⭐⭐ (已下线) | **2.4** |
| **Knowledge** | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ (提炼/注入) | ⚪ N/A | ⭐⭐⭐ | **3.4** |
**任务是系统中成熟度最低的实体。**
---
## 七、根因分析
### 为什么任务模块「能存不能执行」?
```
根因链(从表层到深层):
表层:前端无推进入口,后端无 advance_task
中层:任务 ↔ 工作流断裂task_id=NoneAI 工具缺 update_task/run_command
深层df-task crate 被删除后,任务没有业务逻辑层
根因任务推进链涉及跨系统编排Task + Workflow + AI + Human
但系统设计是「引擎不绑定业务」D3 决策),
导致引擎和业务之间的「胶水层」始终没有建立
```
### 架构决策 D3 的双刃剑
```markdown
### D3: 引擎不绑定业务
- DAG 引擎纯粹做编排,不感知具体业务语义
- 业务逻辑在 df-nodes 实现Node trait 是纯接口)
```
这个决策本身是好的(关注点分离),但它留下了一个**架构空洞**
```
DAG 引擎(通用编排) ←——空洞——→ 任务业务(具体语义)
df-workflow ???
df-nodes
```
谁来把「任务推进」这个业务语义映射到「DAG 工作流执行」?答案应该是 **advance_task 编排层**(构想文档中设计了但未实现),或者一个新 cratedf-task 的复活)。
---
## 八、建议:从「数据容器」到「执行单元」的路径
### 阶段 0修复基础问题前置条件
参照 `任务模块问题分析-2026-06-16.md`
1. 统一状态枚举(前后端对齐)
2. 注册 `/tasks/:id` 路由
3. 补 updateTask store 错误处理
4. 不可变字段保护
### 阶段 1建立推进骨架最小闭环
```
目标:任务状态能通过「合法路径」推进,而非裸字段修改
1. 实现 TaskStatus::can_transition_to状态机定义
2. 实现 advance_task 推进逻辑(**df-nodes Node**D-260616-03 已决策IPC 层 thin 入口):校验+原子写入+状态机下沉 SQL
3. 从 update_task 白名单移除 status收口
4. 前端 TaskDetail 加推进按钮(手动推进,不接 AI
5. 补 review_rounds 字段
```
此阶段不接 AI/工作流,纯人工推进,但状态机保护和收口到位。
### 阶段 2接入工作流单向联动
```
目标:工作流执行能回写任务状态
1. run_workflow 支持 task_id 参数(不再硬编码 None
2. 工作流完成回调 → 检查 task_id → 推进任务状态
3. 定义任务推进 DAG 模板AiNode 执行 + AiNode 自审 + HumanNode 核对)
4. advance_task 触发对应闸门工作流
5. 前端展示工作流执行进度
```
### 阶段 3AI 执行能力(闭环)
```
目标AI 能真正执行任务内容
1. 补 run_command AI 工具(或等 patch_file + run_command 完善)
2. 补 update_task / advance_task AI 工具
3. 实装 run_workflow AI 工具(不再是空壳)
4. AiNode 接入任务上下文(读任务描述 + 项目目录)
5. AI 自审 verdict 结构化输出 + 解析
6. 失败路径完整处理(退回/重做/保持)
```
### 阶段 4Git 集成(增强)
```
目标:代码类任务支持 Git 工作流
1. 加 kind 字段code/doc/design/generic
2. code kind 闸门接 git 命令worktree/commit/merge
3. BranchRecord 联动(加 worktree_path
4. on_task_advanced 钩子填充(分支联动 + 项目 completed
```
---
## 九、总结
### 一句话诊断
**任务是 DevFlow 系统中设计最完善344 行构想文档但实现最空白0% 推进能力的模块。它目前是一个「能存能查不能做」的数据容器距离设计中的「AI-First 执行单元」还有阶段 1-3 的完整路径要走。**
### 最紧迫的事
不是写 AI 执行、不是接工作流,而是 **先建立 advance_task 状态机骨架 + 收口 status 字段**。因为:
- 状态机是所有后续工作的基础没有合法转换定义AI 推进无从谈起)
- status 旁路是 P0 安全漏洞AI 能直接改 status = done所有闸门形同虚设
- 这是投入最小(~200 行代码)但收益最大的改动(从 0% 推进能力到「手动推进闭环」)

View File

@@ -0,0 +1,351 @@
# 任务模块问题分析报告(核对版)
> **日期**: 2026-06-16
> **范围**: 任务模块全栈Rust 后端 + Tauri IPC + Vue 3 前端)
> **审查文件**: `task.rs` / `tasks.ts` / `Tasks.vue` / `TaskDetail.vue` / `crud.rs` / `models.rs` / `types.rs` / `project.ts` / `state.rs` / `migrations.rs` / `router/index.ts`
> **核对方法**: Explore 代理并行验源码,逐项 `file:line` 取证
> **核对结论**: 原清单 18 项中 **真 bug 8 项**P0×2 P1×6、增强 5 项、假/部分假 3 项、与现有 todo 去重 3 项
---
## 〇、核对结论速览
| # | 原清单结论 | 核实 | 处置 |
|---|-----------|------|------|
| 1 | 状态枚举前后端不一致5 vs 7 | ✅ 真 | 三方分裂,记 B-260616-12 |
| 2 | DDL priority 默认(1) vs 代码(2) | ✅ 真 | 潜在非现患,记 B-260616-14 |
| 3 | delete_task 硬删除无恢复 | ✅ 真 | 记 B-260616-13 |
| 4 | priority 无值域校验 | ✅ 真 | 记 B-260616-15 |
| 5 | id/created_at/project_id 可篡改 | ✅ 真 | 记 B-260616-16 |
| 6 | updateTask 无 try/catch | ✅ 真 | 记 B-260616-17 |
| 7 | TaskDetail 绕过 store | ✅ 真 | 记 B-260616-18 |
| 8 | /tasks/:id 路由未注册 | ✅ 真 | 🔄 去重 = B-260616-09 已存在 |
| 9 | 无分页全量加载 | 🔄 去重 | = F-260615-03 已立 |
| 10 | watch 重复 IPC | 🔄 去重 | = B-260615-29 设计决策 |
| 11 | 列表组件重复 | ❌ 假 | 两文件模板结构不同(.task-item vs .task-card排除 |
| 12 | project_id 无存在性校验 | ⚠️ 部分假 | `db.rs:22 PRAGMA foreign_keys=ON` 兜底拦截,降级 |
| 13 | description 无长度限制 | 🟡 增强 | 待产品决策 |
| 14 | branch_name 无格式校验 | 🟡 增强 | 待产品决策 |
| 15 | TaskDetail 只读无编辑 | 🟡 增强 | 待产品决策 |
| 16 | 无批量操作 | 🟡 增强 | 待产品决策 |
| 17 | 无排序选项 | 🟡 增强 | 待产品决策 |
| 18 | 无搜索能力 | 🔄 去重 | ≈ F-260615-07 思路 |
---
## 一、模块架构概览
```
┌─────────────────────────────────────────────────────────┐
│ 前端 (Vue 3 + TS) │
│ Views │ Tasks.vue (列表) → TaskDetail.vue │
│ Store │ stores/project/tasks.ts │
│ API │ api/task.ts │
│ Types │ api/types.ts → TaskRecord │
│ Constants │ constants/project.ts (状态/优先级映射) │
├─────────────────────────────────────────────────────────┤
│ IPC 层 (Tauri) │
│ Commands │ commands/task.rs (5 个命令) │
│ State │ state.rs → TaskRepo │
├─────────────────────────────────────────────────────────┤
│ 后端 (Rust Crates) │
│ df-core │ types.rs → TaskStatus 枚举 │
│ df-storage │ models.rs → TaskRecord 结构体 │
│ │ crud.rs → TaskRepo (CRUD 实现) │
│ │ migrations.rs → tasks 表 DDL │
└─────────────────────────────────────────────────────────┘
```
### IPC 命令5 个)
| 命令 | 签名 | 说明 |
|------|------|------|
| `list_tasks` | `(project_id?) → Vec<TaskRecord>` | 列出任务,可按项目筛选 |
| `get_task_by_id` | `(id) → TaskRecord` | 按 ID 查单个任务 |
| `create_task` | `(input) → TaskRecord` | 创建任务,默认 status=todo, priority=2 |
| `update_task` | `(id, field, value) → bool` | 更新单字段status 值有枚举校验 |
| `delete_task` | `(id) → bool` | 删除任务(硬删除) |
### TaskStatus 枚举(后端 7 种)
| 状态值 | 说明 |
|--------|------|
| `todo` | 待开始 |
| `in_progress` | 进行中 |
| `in_review` | 代码审查中 |
| `testing` | 测试中 |
| `done` | 已完成 |
| `blocked` | 已阻塞 |
| `cancelled` | 已取消 |
### 优先级映射
| 值 | 标签 | 样式 |
|----|------|------|
| 0 | P0 | critical紧急 |
| 1 | P1 | high |
| 2 | P2 | medium默认 |
| 3 | P3 | low |
---
## 二、问题清单(含核对标注)
### 🔴 P0 — 数据一致性 / 功能断裂
#### 1. 前后端状态枚举完全不一致 【✅ 真 · 核对确认】
| 层 | 状态值集合 | 证据 |
|----|-----------|------|
| 后端枚举 `TaskStatus` | `todo, in_progress, in_review, testing, done, blocked, cancelled`7 | `crates/df-core/src/types.rs:131-146` |
| 前端类型注释 | 同后端 7 种 | `src/api/types.ts:88` |
| 前端常量 `TASK_STATUS_LABELS` | `todo, in_progress, review_ready, merged, abandoned`5 | `src/constants/project.ts:56-62` |
| 前端 `taskStatusClass` | 同常量 5 种,未定义回退 `status-todo` | `src/constants/project.ts:76-78` |
| 前端 i18n `tasks.ts` | 同常量 5 种 | `src/i18n/{en,zh-CN}/tasks.ts:47-54` |
**影响链**
- 后端 `update_task``TaskStatus::is_valid()``task.rs:96`)只接受 7 种;前端筛选器的 `review_ready/merged/abandoned` 后端全部拒绝
- AI 工具或后端写入 `in_review/testing/blocked` 时,前端 `TASK_STATUS_LABELS` 查不到 → 回退显示原始 key用户见 `in_review` 而非中文)
- `taskStatusClass` 查不到 → 回退 `status-todo`,视觉无法区分
- Dashboard 统计 `activeTasks` 仅查 `in_progress``testing/blocked` 不计入
**根因**:前端状态集是早期 Git 工作流导向review_ready/merged/abandoned后端枚举后来规范化为通用状态机两者从未对齐。`types.ts` 注释随了后端,但常量/i18n/样式仍停在旧集。
**人定决策点**:前端对齐到后端 7 种纯状态机,还是保留 Git 工作流 5 种语义做映射?
---
#### 2. DDL 默认值与代码默认值矛盾 【✅ 真 · 潜在非现患】
| 来源 | `priority` 默认值 | 证据 |
|------|-------------------|------|
| DDL | `DEFAULT 1`high | `crates/df-storage/src/migrations.rs:304` |
| Rust `default_priority()` | `2`medium | `src-tauri/src/commands/task.rs:26-28` |
| 前端 `Tasks.vue` | `2`medium | `src/views/Tasks.vue:62` |
当前 `create_task` 显式传 `priority`(走代码默认 2DDL 默认值不生效。但未来若有直连 SQL 写入路径或手动改库,`priority=1` 与应用层 `2` 不一致。低危,可并入下次 migration 对齐。
---
#### 3. `delete_task` 硬删除 — 无恢复机制 【✅ 真 · 核对确认】
```rust
// src-tauri/src/commands/task.rs:113
state.tasks.delete(&id).await.map_err(err_str) // 物理删
// crates/df-storage/src/crud.rs:194 — DELETE FROM tasks WHERE id=?
```
`TaskRecord``crates/df-storage/src/models.rs:53-66`)无 `deleted_at` 字段。对比 `ProjectRecord` 有完整软删除(`deleted_at` + `list_deleted` + `restore`)。
**风险**:误删永久丢失;关联 `branches.task_id` 外键变悬空(无 `ON DELETE CASCADE/SET NULL`)。
**人定决策点**:任务是否需要软删除 + 恢复(对标 projects还是物理删除即可任务粒度小、误删可重建
---
### 🟠 P1 — 安全 / 竞态 / 架构
#### 4. `update_task` 缺少 `priority` 值域校验 【✅ 真 · 核对确认】
```rust
// src-tauri/src/commands/task.rs:96-102 — 仅 status 校验
if field == "status" && !TaskStatus::is_valid(&value) { ... }
// priority 直接透传 update_field无范围检查
```
`update_task(id, "priority", "999")``"abc"` 静默落库。前端 `PRIORITY_LABELS``constants/project.ts:82`key 0-3查不到 → 回退 `P999` + 样式 `priority-low``project.ts:95-97`)。
**修复**`field == "priority"` 时校验 `value.parse::<i32>()``0..=3`
---
#### 5. `update_task` 缺少 `id` / `created_at` 等不可变字段保护 【✅ 真 · 核对确认】
```rust
// crates/df-storage/src/crud.rs:324-327
"tasks" => &["id", "project_id", "title", "description", "status", "priority",
"branch_name", "assignee", "workflow_def_id", "base_branch",
"created_at", "updated_at"],
```
白名单含 `id` / `created_at` / `project_id` → AI 工具或恶意调用可改主键、篡改创建时间、把任务移到别的项目。前端未暴露,但 IPC 层无防护。
**修复**tasks 白名单移除 `id`/`created_at``project_id` 若允许跨项目移动则保留但加目标项目存在性校验。
---
#### 6. 前端 `updateTask` store 无错误处理 【✅ 真 · 核对确认】
```typescript
// src/stores/project/tasks.ts:29-35 — 无 try/catch
async function updateTask(id, field, value) {
await taskApi.update(id, field, value)
const idx = state.tasks.findIndex(t => t.id === id)
if (idx >= 0) (state.tasks[idx] as any)[field] = value
}
```
对比同文件 `loadTasks`(10-16) / `createTask`(18-27) / `deleteTask`(37-44) 都有 try/catch。IPC 失败(如非法 status 被后端拒)→ Promise reject 冒泡,错误不写 `state.error`,用户无提示。
**修复**:补 try/catch失败写 `state.error` + toast。
---
#### 7. `TaskDetail.vue` 绕过 store 直接调 API 【✅ 真 · 核对确认】
```typescript
// src/views/TaskDetail.vue:101 import { taskApi, projectApi } from '@/api'
// src/views/TaskDetail.vue:137-141 直接 taskApi.get() / projectApi.list()
```
其他视图走 `useProjectStore()`TaskDetail 直调 API
- 不享受 AR-11 `df-data-changed` 联动刷新(其他窗口改任务,本页不自动刷新)
- `projectApi.list()` 全量拉项目列表仅为解析 `project_id → name`
**修复**:改走 store.loadTasks/store.projects或单独监听 `df-data-changed` 重载当前 task。
---
#### 8. `/tasks/:id` 路由未注册 【✅ 真 · 导航断裂 · 🔄 去重 = B-260616-09 已存在】
```typescript
// src/router/index.ts:45-49 — 只有 /tasks
{ path: '/tasks', name: 'Tasks', component: () => import('../views/Tasks.vue') }
// src/views/Tasks.vue:57 — 任务卡点击跳转
router.push('/tasks/${task.id}') // 路由表无此路径 → 落空
```
对比 `/ideas/:id``/projects/:id` 均已注册,唯独 `/tasks/:id` 遗漏。点击任务卡片匹配不到路由(落入 catch-all 或空白页。1 行改动速赢。
---
### 🟡 P2 — 性能 / 体验 / 代码质量
#### 9. 无分页 — 全量加载所有任务 【🔄 去重 = F-260615-03】
```rust
// src-tauri/src/commands/task.rs:32-41
None => state.tasks.list_all().await, // 无 limit/offsetORDER BY created_at DESC
```
已有任务 **F-260615-03** 覆盖,不重复立项。
---
#### 10. `Tasks.vue` 筛选切换导致重复全量请求 【🔄 去重 = B-260615-29】
```typescript
// src/views/Tasks.vue:229-233
watch(activeProject, (key) => { store.loadTasks(key === 'all' ? undefined : key) })
// src/views/Tasks.vue:165-192 filteredGroups computed 已做纯前端 filter
```
这是 **B-260615-29** 的设计决策(避免跨项目视图不同步),代价是每次切换 IPC 往返。不重复立项。
---
#### 11. ~~`Tasks.vue` 和 `ProjectDetail.vue` 任务列表样式/逻辑重复~~ 【❌ 假 · 排除】
核对:`Tasks.vue:57-73``.task-item` 布局,`ProjectDetail.vue:137-151``.task-card` 布局,模板结构不同。两者引用相同常量函数(状态/优先级映射),但模板本身非重复。**排除**。
---
#### 12. `create_task` 无 `project_id` 存在性校验 【⚠️ 部分假 · 降级】
```rust
// src-tauri/src/commands/task.rs:59-84 — create_task 不校验 project_id
project_id: input.project_id, // 直接使用
```
`crates/df-storage/src/db.rs:22` 已开 `PRAGMA foreign_keys=ON`DB 层外键约束会拦截指向不存在 project 的 insert。代码层无显式校验但风险被 DB 兜住。**降级为非漏洞**,仅留注释说明依赖外键。
---
#### 13. `description` 字段无长度限制 【🟡 增强】
`TaskRecord.description``String`DDL 为 `TEXT NOT NULL DEFAULT ''`。无前后端长度校验,超大文本影响 Markdown 渲染性能。待产品决策加上限。
---
#### 14. `branch_name` / `base_branch` 无 Git 分支名格式校验 【🟡 增强】
允许任意字符串(空格/特殊字符/中文),可能与实际 Git 分支不兼容。待产品决策加格式校验。
---
### 🔵 P3 — 增强建议(均待产品决策)
| # | 项 | 说明 |
|---|----|------|
| 15 | TaskDetail 纯只读 | 无编辑/改状态/改优先级 UI列表页也无内联编辑 → 当前无任何 UI 改任务状态,只能靠 AI 工具/API |
| 16 | 无批量操作 | 无法批量改状态/删除/分配 |
| 17 | 无排序选项 | 固定 `ORDER BY created_at DESC`,无法按优先级/状态/更新时间排 |
| 18 | 无搜索能力 | ≈ F-260615-07 思路 |
---
## 三、问题汇总矩阵(含核对标注)
| # | 严重度 | 类型 | 问题 | 影响 | 核实 |
|---|--------|------|------|------|------|
| 1 | P0 | 数据一致性 | 前后端状态枚举不一致5 vs 7 种) | 全局 | ✅ 真 |
| 2 | P0 | 数据一致性 | DDL priority 默认值(1) vs 代码(2) | 数据层 | ✅ 真(潜在) |
| 3 | P0 | 数据安全 | delete_task 硬删除无恢复 | 数据丢失 | ✅ 真 |
| 4 | P1 | 安全校验 | priority 无值域校验 | 数据质量 | ✅ 真 |
| 5 | P1 | 安全校验 | id/created_at/project_id 可被篡改 | 数据完整性 | ✅ 真 |
| 6 | P1 | 健壮性 | updateTask store 无 try/catch | 用户体验 | ✅ 真 |
| 7 | P1 | 架构 | TaskDetail 绕过 store | 数据同步 | ✅ 真 |
| 8 | P1 | 功能缺陷 | /tasks/:id 路由未注册 | 导航断裂 | ✅ 真 |
| 9 | P2 | 性能 | 无分页全量加载 | 扩展性 | 🔄 F-260615-03 |
| 10 | P2 | 性能 | 筛选切换重复 IPC | 响应速度 | 🔄 B-260615-29 |
| 11 | P2 | 代码质量 | 任务列表组件重复 | 可维护性 | ❌ 假 |
| 12 | P2 | 安全校验 | project_id 无存在性校验 | 数据完整性 | ⚠️ 部分假(外键兜底) |
| 13 | P2 | 安全校验 | description 无长度限制 | 性能 | 🟡 增强 |
| 14 | P2 | 安全校验 | branch_name 无格式校验 | 数据质量 | 🟡 增强 |
| 15 | P3 | 功能缺失 | TaskDetail 无编辑能力 | 用户体验 | 🟡 增强 |
| 16 | P3 | 功能缺失 | 无批量操作 | 效率 | 🟡 增强 |
| 17 | P3 | 功能缺失 | 无排序选项 | 用户体验 | 🟡 增强 |
| 18 | P3 | 功能缺失 | 无搜索能力 | 用户体验 | 🔄 ≈F-260615-07 |
---
## 四、todo 编号映射
记入 `docs/todo.md`B-260615-58 起,沿用 6.15 编号保连续):
| todo 号 | 原清单 # | 等级 | 摘要 |
|---------|---------|------|------|
| B-260616-12 | 1 | P0 | 状态枚举前后端分裂7 vs 5需人定Git 工作流 vs 纯状态机 |
| B-260616-13 | 3 | P0 | delete_task 硬删除无恢复,需人定:是否要任务软删除 |
| B-260616-14 | 2 | P1 | DDL priority DEFAULT 1 vs 代码 2migration 对齐 |
| B-260616-15 | 4 | P1 | update_task priority 无值域校验0..=3 |
| B-260616-16 | 5 | P1 | allowed_columns 含 id/created_at/project_id 可篡改 |
| B-260616-17 | 6 | P1 | updateTask store 无 try/catch |
| B-260616-18 | 7 | P1 | TaskDetail 绕 store不享受联动刷新 |
| B-260616-09 | 8 | P1 | /tasks/:id 路由未注册 — **已存在去重**todo 排查会话区块) |
> 编号沿用 B-260616 批次(与 B-260616-08~11 同批,今日 06-16 发现)。
> 增强 #13-17 归 F- 类,待产品决策后立项。
---
## 五、建议修复顺序
1. **#8** 路由注册 `/tasks/:id`1 行速赢)
2. **#6** updateTask store 加 try/catch
3. **#4** priority 值域校验0..=3
4. **#5** 不可变字段保护id/created_at 移出白名单)
5. **#1** 状态枚举对齐(**需人定决策**
6. **#3** 软删除支持(**需人定决策**
7. **#2** DDL 默认值修正
8. **#7** TaskDetail 接入 store
9. 其余 P2/P3 按需排期
---
## 六、人定决策点(需用户拍板,非大模型推断)
1. **状态枚举方向**#1):前端对齐到后端 7 种纯状态机,还是保留 Git 工作流 5 种语义review_ready/merged/abandoned做映射影响筛选器/i18n/统计全链。
2. **任务软删除**#3):任务是否需要软删除 + 恢复(对标 projects还是物理删除即可任务粒度小、误删可重建
这两项是产品/架构取舍,与模型能力无关,决策后记入功能决策记录。

View File

@@ -30,10 +30,10 @@ Phase 1 目标:**引擎骨架**,打通 `df-core → df-workflow → df-stora
| # | 任务 | 优先级 | 依赖 | 文档 | 完成 |
|---|------|--------|------|------|------|
| 7 | df-storage CRUD 层 | P0 | 无 | [DEVFLOW-1](../04-功能迭代/DEVFLOW-1.CRUD层实施-2026-06-12.md) | [x] ✅ `crud.rs` `impl_repo!` 宏 12 处Project/Task/Idea/Branch/Release/WorkflowExecution/Knowledge/AiConversation 等 RepoSprint 2 |
| 8 | Tauri IPC 命令层 | P0 | #7 | [DEVFLOW-2](../04-功能迭代/DEVFLOW-2.IPC桥接实施-2026-06-12.md) | [x] ✅ `commands/` 共 20 文件 / 57+ commandproject/task/idea/workflow + AI + knowledge + settingsSprint 3 起 |
| 8 | Tauri IPC 命令层 | P0 | #7 | [DEVFLOW-2](../04-功能迭代/DEVFLOW-2.IPC桥接实施-2026-06-12.md) | [x] ✅ `commands/` 共 20 文件 / 67 个 command 注册src-tauri/src/lib.rs invoke_handlerproject 14 + task 5 + idea 6 + workflow 5 + ai 19 + knowledge 14 + settings 4Sprint 3 起 |
| 9 | Store 接入 View | P0 | #8 | [DEVFLOW-3](../04-功能迭代/DEVFLOW-3.Store对接实施-2026-06-12.md) | [x] ✅ `stores/` 10 文件composable 模式),各 View 接 Store 真数据Sprint 4 |
| 10 | 端到端验证 (3 节点工作流) | P0 | #7, #8, #9 | [DEVFLOW-4](../04-功能迭代/DEVFLOW-4.端到端验证-2026-06-12.md) | [x] ✅ `run_workflow` IPC + DAG `topological_layers` + `join_all` 并行ProjectDetail.vue 3 节点 Shell DAG 运行 + 实时事件日志Sprint 4 |
| 11 | 首次 Git Commit | P0 | 无 | 建立版本基线 | [x] ✅ git log 73 commit首提交 `98393b4 初始化 DevFlow 项目仓库`Sprint 2 |
| 11 | 首次 Git Commit | P0 | 无 | 建立版本基线 | [x] ✅ git log 82 commit首提交 `98393b4 初始化 DevFlow 项目仓库`Sprint 2 |
### 已知问题

View File

@@ -0,0 +1,107 @@
# patch_file 使用指南
> 创建: 2026-06-15
> 关联设计: docs/02-架构设计/patch_file工具设计-2026-06-15.md
## 使用场景
局部更新文件(替代 read-merge-write 多步流程),精确定位 `old_text` 并替换为 `new_text`
适用于只改文件中某几行的场景:大文件改 3 行无需重发全部内容,避免 `write_file` 全量覆盖的事故风险(参见 FR-S7 记录762 行文件被覆盖为 248 字节)。
## API 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `path` | 是 | 目标文件路径workspace 内,走 `validate_path` 校验 + 黑名单) |
| `old_text` | 是 | 要替换的精确文本(必须与文件内容完全匹配,含空格/缩进;充当乐观锁) |
| `new_text` | 是 | 替换后的新文本 |
| `line` | 否 | 行号辅助定位(快速跳转 + 去歧增强;有值时优先跳到该行检查 `old_text`,不匹配则降级全文扫描) |
| `expected_hash` | 否 | 文件指纹防脏写,格式 `"{unix_timestamp}_{size}"`(如 `"1718400000_12345"`),由 `read_file` 返回的 `file_hash` 字段携带 |
补充去歧参数(可选):`before_text` / `after_text` —— 当 `old_text` 多处匹配时,作为上下文锚精确锁定目标位置。
## 返回值
```json
{
"success": true,
"patches_applied": 1,
"total_matches": 1,
"lines_changed": 2,
"warnings": [],
"file_hash": "1718400000_12400"
}
```
| 字段 | 含义 |
|------|------|
| `success` | 是否成功 |
| `patches_applied` | 成功替换的 patch 数 |
| `total_matches` | 每个 patch 的总命中数(含未替换的) |
| `lines_changed` | 总行数变化(正=增加,负=减少) |
| `warnings` | 警告信息,如 `["匹配到 3 处,仅替换第 1 处"]` |
| `file_hash` | 操作后的新指纹(下次操作传入 `expected_hash` 用) |
## 安全边界
- **RiskLevel Medium**:修改已有文件,需人工审批
- **自动 `.bak` 备份**:复用 FR-S7 已有逻辑,误操作可恢复
- **old_text 不匹配 → 报错**(不修改文件,避免盲替换)
- **expected_hash 不匹配 → 报错**(防并发脏写,提示「文件已被外部修改,请重新读取」)
- **三层防御**L1 文件级 Mutex防时机冲突+ L2 old_text 精确匹配(防内容错配)+ L3 expected_hash 指纹校验(防版本漂移),底层兜底 `.bak` 备份
## 最佳实践
- **old_text 取足够上下文确保唯一**:避免短串多处匹配,收到「匹配到 N 处」warning 时用 `before_text`/`after_text` 锚定或加长 `old_text` 重试
- **大段修改用多个小 patch 而非一个巨大 patch**:每个 patch 独立校验,失败可定位
- **危险操作前先 `file_info` 确认**:核对路径、大小、是否二进制(含 `\0` 的文件会被拒绝)
- **read → patch 链路带上 hash**`read_file` 返回 `file_hash`,传入 `patch_file``expected_hash` 形成乐观锁闭环
- **多 patch 从文件末尾往前排**:避免行号偏移(工具内部已按此执行)
- **old_text 必须完全匹配**:含空格、缩进、换行,复制粘贴原文最稳妥
## 示例
### 基础替换
```json
{
"path": "src/main.rs",
"old_text": "fn old()",
"new_text": "fn new()"
}
```
### 带 hash 乐观锁
```json
{
"path": "src/main.rs",
"expected_hash": "1718400000_12345",
"patches": [
{ "old_text": "fn old()", "new_text": "fn new()" }
]
}
```
### 多处匹配用 line 锚定
```json
{
"path": "src/main.rs",
"old_text": "return Ok(())",
"new_text": "return Ok(value)",
"line": 42
}
```
## 边界情况
| 情况 | 行为 |
|------|------|
| 文件不存在 | 报错「文件不存在」 |
| 二进制文件(含 `\0` | 报错「不支持二进制文件」 |
| `old_text` 为空串 | 报错「old_text 不能为空」 |
| `new_text` == `old_text` | 成功 + warning「无实际更改」 |
| 路径含 `..` | `validate_path` 黑名单拦截 |
| 目标是 `.bak`/`.tmp` | 拒绝CR-03 噪声文件过滤) |

View File

@@ -123,8 +123,11 @@ npm run tauri build
#### 工具调用
- **生成代码**:根据描述生成完整实现
- **文件操作**:读取、编辑项目文件
- **Git 操作**:提交、推送、合并
- **文件操作**:读取、写入write_file、局部编辑patch_file、追加append_file、搜索search_files项目文件
- **项目管理**:创建/更新/删除项目、绑定目录
- **任务管理**:创建/更新/删除任务
- **Shell 执行**在项目目录运行命令run_command需人工审批
- **知识库**:自动提炼对话经验到知识库
## 💡 想法池功能
@@ -138,9 +141,19 @@ npm run tauri build
```
### 状态管理
- **草稿**:初始想法
- **活跃**:正在考虑
- **已完成**:已实现或放弃
想法共 6 个状态(对齐 `crates/df-core/src/types.rs``IdeaStatus` 枚举):
| 状态值 | 含义 | 说明 |
|--------|------|------|
| `draft` | 草稿 | 初始创建 |
| `pending_review` | 待评估 | 已提交,等待 AI/人工评估 |
| `approved` | 已批准 | 评估通过,可晋升为项目 |
| `rejected` | 已拒绝 | 评估未通过 |
| `promoted` | 已晋升 | 已转为项目(`promoted_to` 写入目标 project_id|
| `archived` | 已归档 | 历史归档 |
典型流转:`draft → pending_review → approved → promoted`(正向)/ `→ rejected → archived`(淘汰)。
### 未来升级
- **对抗式评估**:正方+反方+分析师
@@ -175,7 +188,6 @@ npm run tauri build
### 个人效能
- **任务完成率**:按时完成任务比例
- **分支管理**:活跃分支数量
- **工作流成功率**:自动执行成功率
### 项目进度
- **阶段分布**:规划/开发/测试/上线
@@ -241,7 +253,6 @@ npm run tauri build
- 问题反馈:创建 Issue
- 功能建议:想法池提交
- 使用交流Discord 社区
---

View File

@@ -124,7 +124,7 @@ docs/
|------|------|------|
| 架构设计 | `../ARCHITECTURE.md` | 22,745 字完整架构文档 |
| 项目进展 | `../PROGRESS.md` | 工作进展与交接 |
| Crate 结构 | `../ARCHITECTURE.md#四crate-结构` | 13 个 Crate 概览 |
| Crate 结构 | `../ARCHITECTURE.md#四crate-结构` | 8 个 Crate 概览 |
| 数据模型 | `../ARCHITECTURE.md#六数据模型` | SQLite 表结构定义 |
| Phase 规划 | `../ARCHITECTURE.md#八phase-规划` | 5 个 Phase 路线图 |
@@ -136,7 +136,7 @@ docs/
|----|------|------|
| Desktop | Tauri v2 | Rust 后端 + WebView 前端 |
| Frontend | Vue 3 + TypeScript + Pinia | Arco Design 组件库 |
| Engine | Rust Workspace (13 crate) | 多 crate 架构 |
| Engine | Rust Workspace (8 crate) | 多 crate 架构 |
| Storage | SQLite (rusqlite) | 本地优先,零运维 |
| AI | Multi-Provider | Claude/GLM/DeepSeek/OpenAI 兼容 |
| Build | Bun + Vite | 前端构建 |

View File

@@ -24,7 +24,7 @@
## 待办
### 📋 编排推进总览2026-06-15 汇总)
### 📋 编排推进总览2026-06-16 汇总)
未完成待办按可执行性分 8 组(详细条目见下方各分类,勿重复记录):
@@ -47,22 +47,70 @@
- [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-15核查闭环,待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 友好报错),卡死在其下游。
- [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-15splitBlocks 块级 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-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-15,待commit)补 `toolDisplayName` CRUD case 7 项(delete/restore/purge/update/create_task/create_project)+i18n 10 key 中英对称。
- [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**
@@ -71,49 +119,49 @@
- [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-15**:前端 rAF 节流已被 ARC-08 覆盖(每帧 ≤1 parse剩后端 50ms 合批B-260615-02 心跳已动 stream_recv.rs合批可并入同文件+ 滚动跟随。降优先级 — stream_recv.rs + AiChat.vue 滚动
- [ ] 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-15,dc27e79) — ~~数据变更联动刷新~~推荐方案A 后端 emit + store 监听)— 跨层 — 详见 [审查第五章](./02-架构设计/aichat审查报告-2026-06-14.md) — **勘察完成(2026-06-15,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-15):后端已实施 ✅,前端半接通 🔴** — 后端 `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-15):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)
- [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-15
### 🔴 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-15,待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
- [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-15
### 🔴 流式响应中断误报排查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-15) — **[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-15) — **[P2 治标]** `onStreamTimeout` 文案区分:触发时检查最后是否有 `completed` 工具调用,有 →「工具已执行完成,后续回复中断,可点继续」;无 → 原「响应中断」— src/composables/ai/useAiStream.ts:21-33
- [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-15
### 🟠 流式可靠性链路隐患核对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-15) — **[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-15) — **[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-15) — **[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-15) — **[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-15) — **[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
- [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-15 安全批次)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-15 审查发现):ai_delete_provider 未调 delete_provider_secret,keyring 残留,见 [CR-260615-01](#-近期改动审查待修项2026-06-15)
- [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-15,待commit)补第二 await(`pendingToolCalls`)后二次比对 `useAiConversations.ts:108`,防 A→B 快切用 A 的 pending 覆写 B
- [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)
@@ -127,131 +175,129 @@
- [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)+注释散落提常量)— 见报告 §5-6
- [ ] 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-15
### 🔴 近期改动审查待修项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-15,待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 无消费方)
- [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 ✅ wave(2026-06-15,待commit) — ~~AiChat.vue 第五份 confirm 未迁 useConfirm~~ 实为本地 confirm 状态机(confirmState+confirmDialog+answerConfirm)与 useConfirm 同构未复用,迁后复用 composable,行为零变化;AiChat.vue:371-374
- [~] CR-260615-03 ✅ wave(2026-06-15,待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 类型归一(行为变更或前端,不在本批)
- [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-15**
**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 ✅(wcvigw3z4批,2026-06-15,待commit) — ~~parseBlock/parseBlockNoCache DRY~~ parseBlock 内部改调 parseBlockNoCache 去重(原 _purify.sanitize(_marked.parse()) 两处重复收敛为一处),行为零变化 — AiChat.vue
- [ ] CR-260615-06 — loadMarkdown 就绪后流式不主动重算mdReady 翻转后 scheduleStreamParse 只在 currentText 变化触发;首屏 marked 慢 + 无新 delta → 末段停留纯文本)— AiChat.vue loadMarkdown — 同上 ③
- [x] CR-260615-07 ✅(wcvigw3z4批,2026-06-15,待commit) — ~~_blockCache LRU+escapeHtml 抽~~ _blockCache 超 limit 由整体 clear 改删最早一条(Map.keys().next().value LRU 语义,边界 > 改 >= 防超限)+escapeHtml+replace 两处重复抽 escapeFallback 函数(renderStreamingMd/renderMd 兜底均调),行为零变化 — AiChat.vue
- [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-15
### 🟦 架构审查待修项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-15,待commit) — ~~删 `stores/settings.ts` mock 死代码~~ grep 验零消费者 + 删文件 + index.ts 清导出 + 清 appSettings.ts:8 过时注释
- [x] ARC-260615-02 ✅(2026-06-15,待commit) — ~~`/decisions` 路由死链~~ 删 nav 项App.vue secondaryNav+ 删 `nav.decisions` i18n keyzh/en。决策治理 = F-260614-08 长期项未实现,入口提前占位成死链;`dashboard.recentDecisions`Dashboard.vue不同命名空间保留
- [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-15**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-15,待commit) — ~~`stores/index.ts` 补 `export useAiStore`~~ barrel 补导出(3 处 view 直连可选迁移,非强制)
- [ ] 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-15**
- [x] ARC-260615-08 ✅(2026-06-15,待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 运行时流式验证(掉帧/长回答边界/末块未闭合表现)】
**渲染优化(自研块级 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-15
### 🔵 全局代码 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-15) — **[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-15) — **[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需设计
- [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-15,待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-15,待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
- [ ] R-PD-6 — AiSession 单例try_continue 读 active_conversation_id 竞态(靠 switch readonly 间接保护,脆弱耦合)— `agentic.rs`:255-291 从 pending_approvals 取 conversation_id 解耦
- [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() 语义错位单独立项不改)
- [ ] R-PD-8 — AiProviderRecord 整条穿透 IPC 边界DB schema 演进直接破坏前端契约models 字段 provider 返串/conversation 返数组不一致)— `commands.rs`:282-295 定义 ProviderDto/ConversationSummary 映射层
- [ ] R-PD-9 — 命令层臃肿agentic loop/tool_registry 717 行/audit reason 映射堆 commands/ai无法被 df-nodes/AiNode 复用)— 最小起步 audit 工具名→文案映射作 display_hint 注册进 AiToolRegistry消除双份agentic loop 下沉 df-ai 较大进 todo
- [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-15,待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` 比较存库路径,已注明须同步)
- [ ] R-PD-12 run_workflow AI 工具 no-op 桩prompt/audit/ToolCard 当真实能力宣传LLM 调用走审批拿空结果,体验断裂`tool_registry.rs`:383-390 + `prompt.rs`:57 + `audit.rs`:120-123 + ToolCard.vue:367 删假能力 or 真接线(与 R-PD-2 协同)
- [x] R-PD-13 ✅(P0批,2026-06-15,待commit) — ~~Lagged 静默丢事件~~ 最小兜底:单行 warn 升级带 execution_id+lagged 字段结构化 warn+注释(broadcast 不暴露被丢事件类型/关键终态事件丢失致 finished 不触发循环不 break 前端永久收不到结束/依赖 DB 轮询兜底)+三后续方向(提升广播容量/持久化队列重发/forward watchdog 超时);重发复杂度超本 todo 范围 — workflow.rs
- [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-15
### 🔴 工作区多角度走查待修项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-15) — ~~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-15,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-15):已修 ✅** — 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-15,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-15):已修 ✅** — 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-15,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后端代理 ②)
- [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 — i18n**
- [ ] CR-260615-08 — **[P1]** i18n 硬编码一组定时走查第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
**P1/P2 — DRY + 健壮性**
- [ ] 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
- [ ] CR-260615-10 — **[P2]** useMarkdown 加 `useRendered(getText)` 辅助,消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥
- [x] CR-260615-11 ✅(定时走查第2轮复核,2026-06-15,fddca9d) — ~~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
- [ ] CR-260615-12 — **[P3]** 可选一组_toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18n `t` as any — 详见走查 ⚪ 区
- [x] CR-260615-18 ✅(定时走查第3轮,2026-06-15,f574562) — **[原 P2]** 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-260615-19 ✅(定时走查第3轮,2026-06-15,f574562) — **[原 P3]** 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 ✅撤销(定时走查第3轮,2026-06-15) — 走查误判:接收方核 workflow.ts:108 stopEventListener 写法一致(f574562 commit 说明),不修 — stopDataChangedListener try/catch 过度防御:project.ts:47-55 unlisten 同步数组操作正常不抛,与 workflow.ts:108 stopEventListener 写法不一致。修:核 workflow.ts:108 对齐统一去/留 — src/stores/project.ts:47-55 — 详见定时走查第2轮.md
- [x] CR-260615-21 ✅(定时走查第4轮,2026-06-15,94cdcdc) — **[原 P3]** 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 ✅(定时走查第6轮,2026-06-15,16aeacc) — **[原 P3]** 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 ✅撤销(定时走查第6轮,2026-06-15,不适用) — B-34「Tauri 不转」注释在 ARC-05 拆分时已简化(workflow.ts:81/project.ts:58 仅风格对齐无错误说明,16aeacc 接收方核对) — project.ts:273
- [x] CR-260615-24 ✅(定时走查第7轮,2026-06-15,ef2d033) — **[原 P1]** 分离窗口 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 ✅(定时走查第8轮,2026-06-15,ac8dab3) — **[原 P2]** 分离窗口状态机缺口(代理 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 ✅(定时走查第7轮,2026-06-15,7637f29) — **[原 P2]** 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 ✅(定时走查第7轮,2026-06-15,5e6aa79) — **[原 P2]** 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 ✅(定时走查第8轮,2026-06-15,40461b2) — **[原 P1]** 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 ✅(定时走查第9轮,2026-06-15,6116c9b) — **[原 P2]** 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 ✅(定时走查第8轮,2026-06-15,b300bb7) — **[原 P3]** 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 ✅(定时走查第9轮,2026-06-15,2ddd294) — **[原 P2]** 小区域收尾健壮性一组(定时走查第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
- [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-15
### 📄 文档核对待修项2026-06-16
> 4 路并行核对 docs 全量 + 根目录文档,详见 [文档全量核对报告-2026-06-15.md](./05-代码审查/文档全量核对报告-2026-06-15.md)。悬空链接 0。
**P0 — 文档硬伤(按文档找代码落空)**
- [ ] DOC-260615-01 **[P0]** 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
- [ ] DOC-260615-02 **[P0]** df-nodes 模块文档重写8 节点全骨架 → 实际 3 节点 ai/script/human 均完整实现;删 Docker/Git/Notify/HTTP/Subflow 5 虚构)— 03-模块文档/df-nodes — 详见 §2
- [ ] DOC-260615-03 **[P0]** df-ai 模块文档删虚构 router.rs/stream.rs + 已删 supported_features trait + 12→13 工具 — 03-模块文档/df-ai — 详见 §3
- [ ] DOC-260615-04 **[P0]** 使用手册全面重写 7 处(运行命令 bun→npm / 任务状态 4→7 态 / 优先级方向 / 节点类型 Script/Ai/Human / EventBus 非 WebSocket / 多 Provider / 知识库 Tier1— 08-用户指南/使用手册 — 详见 §4
- [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
- [ ] DOC-260615-07 — aichat §8 表 9 行 + 工作流审批 TL;DR 已修项补 ✅ 勾选H1/H2/create_project/AR-5~10 + human_node.rs:41 await B-03b-R6— 详见 §7
- [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 — 架构/模块文档过期**
- [ ] DOC-260615-09 — df-workflow 模块文档 4 处过期NodeRegistry 删 Default / conditions 默认 true→false / try_recv_human_approval 已删 / set_waiting·set_skipped 已删)— 详见 §9
- [ ] DOC-260615-10 — df-storage V8→V13迁移版本+表数 11→18+Repo 11→17+ 对抗评估 idea_evaluations 设计稿标未实施 + df-execute 描述删 Docker/SSH/Git — 详见 §10-12
- [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 — 索引/计数矛盾**
- [ ] DOC-260615-11 — 13→8 crate 多处统一PROGRESS/INDEX/Phase1决策/对抗裁决/想法探索)+ commands 数 47/57/68 统一 68 + URGENT.md 停用标注或删 — 详见 §13/15/16INDEX 漏列部分本会话已补,见 DOC-14
- [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/规范**
- [ ] DOC-260615-12 — DEVFLOW-1~4 + Phase1/Phase2 标完成归档Phase1 已收尾文档仍全待实施)— 详见 §17-19
- [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 — 技术/工具文档**
@@ -268,7 +314,7 @@
- [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-15待commit
- [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
@@ -294,151 +340,151 @@
- [ ] 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-15 审查)
### 🔴 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 ✅(P0批,2026-06-15,待commit) — ~~RAII guard 收尾 generating~~ 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 ✅(P0批,2026-06-15,待commit) — ~~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-15,待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
- [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-15,待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-15,待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
- [ ] B-260615-14 — [P1] ⑦ stop_flag 换 tokio::sync::Notify 即时打断(去 30s 延迟)— stream_recv.rs:148
- [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-15,待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-15,待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-15,待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-15,待commit) — ~~pending_approvals 注释~~ mod.rs:124-135 扩展文档:单 HashMap<tool_call_id,PendingApproval> 按 tool_call_id 路由/conversation_id 业务语义非路由键/当前 O(n) 过滤典型场景可接受/未来多会话高并发可加二级索引 — mod.rs
- [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-15 用户报障)
### 🔴 消息发送失败无提示 + 状态传染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-15,待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 复用)
- [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 ✅(P0批,2026-06-15,待commit) — ~~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 ✅(P0批,2026-06-15,待commit) — ~~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
- [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-260616-01(L0 握手) + B-260616-02(L2 强制发送) 解决**,本条保留为问题记录不再独立实施
- [ ] CR-260615-12 **[P3]** 可选一组:_toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18n `t` as any — 详见走查 ⚪ 区
- [ ] 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
- [ ] CR-260615-10 **[P2]** useMarkdown 加 `useRendered(getText)` 辅助消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥
- [ ] CR-260615-08 **[P1]** i18n 硬编码一组(定时走查第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
- [ ] 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 发送韧性(用户可控)。
- [ ] B-260616-01 [P0] — **L0 前端重握手(状态分裂根治)**。前端每次 startListener 完成后 emit `ai-client-ready` 事件(携带 client_id + last_conv_id后端注册 listener 收到后:①`generating=true`? → 强制 reset + emit AiCompleted补偿事件让前端收尾清 watchdog/续发队列)②`pending_approvals` 有残留? → 清除(审批态已无前端 UI 可操作)③记录 `last_heartbeat = now`。覆盖场景Vite HMR / 页面 F5 / 分离窗口重建 / 切换标签页后台回收。**改动**前端 `useAiEvents.ts:startListener()` 末尾加 emit~3 行);后端 `mod.rs` 或新建 `commands/ai/handshake.rs` 注册 listener + 状态清洗(~30 行)。**用户体验**HMR 后零延迟自动恢复,无需用户干预 — src/composables/ai/useAiEvents.ts + src-tauri/src/commands/ai/mod.rs
- [ ] B-260616-02 [P1] — **L2 发送时强制覆盖(用户可控自救)**。当前行为:`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
- [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-15 用户报障)
### 🔴 详情描述字段 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-15,待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-15,待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-15,待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
- [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-15 用户报障)
### 🔴 审批执行后中断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 ✅(P0回归修,2026-06-15,待commit) — ~~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-15conv 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 不退出故正常。
- [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-15 用户报障)
### 🔴 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 ✅(2026-06-15,待commit) — ~~Task 列表项目切换联动~~ store.loadTasks(projectId?) 已支持可选参(project.ts:124 无需改 store)+Tasks.vue 加 watch(activeProject):非 all 时 loadTasks(projectId) 按项目重载,all 时 loadTasks() 全量。切项目筛选触发后端重载,新建任务跨项目视图同步刷新。只改 Tasks.vue — src/views/Tasks.vue
- [ ] B-260615-30 — [P2] projects 重名核查meta-kit 两条f0fa88bf/32de9175)确认是否重复导入/目录绑定,去重或区分
- [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-15 用户需求)
### 🔴 详情页字段布局紧凑化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-15,待commit) — ~~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
- [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-15 用户实测)
### 🔴 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 修复后据观测重评**。
- [ ] B-260615-37 [P0] — **shell.rs execute 补 Stdio::piped()**`shell.rs:58` spawn 前 加 `cmd.stdout(Stdio::piped()).stderr(Stdio::piped())`(需 `use tokio::process::Stdio;`)。修后 stdout/stderr 正常捕获run_command 闭环恢复。最小改动 1 行 + import — crates/df-execute/src/shell.rs:37-58 — source:用户实测(06-15)
- [ ] B-260615-38 [P1] — **PS 静默失败根因**(待 B-37 修后据 stderr 观测重评。候选a) PS Replace/-replace 源字符串不匹配AI 用法,非 bugb) shell.rs:38 Windows `cmd /C` 不适配 PS 语法(引号嵌套/管道),需支持 shell 选择(powershell -NoProfile -Command / cmd /C / sh -c。B-37 修复前无法定位 — crates/df-execute/src/shell.rs:37-39 — source:用户实测(06-15)
- [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-15 用户报障)
### 🔴 查看灵感来源跳空白页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。
- [ ] B-260615-36 [P1] — **查看灵感来源跳空白页**修复两层:① `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)
- [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-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。
- [ ] 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
- [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 交互体验改进2025-07-15 方案 → 待办化)
### 🟡 AI Chat 交互体验改进2026-06-14 方案 → 待办化)
> 来源:[aichat交互体验改进方案-2025-07-15.md](./02-架构设计/aichat交互体验改进方案-2025-07-15.md)。7 大类 20 个改进项,按原文优先级分 4 批。**去重**§1.3 复制 ⊂ F-260615-13/14已写、§3.4 新建中断 ⊂ B-260615-10已做软复位本文档加 confirm 增强、§6.2 分离窗口同步 ⊂ CR-24/25已写。**状态: 待讨论→看板跟踪**。
> 来源:[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已写。**状态: 待讨论→看板跟踪**。
**第一批 — 最高频痛点(每次对话都遇到)**
- [ ] UX-2025-01 [P1]**流式渲染中选中文字保持**§2.1。现象AI 流式输出时选文字被 delta 打断丢失选区。方案 A(推荐)`selectionchange` + 选区在消息容器内 → 暂停 rAF 流式 parse → 选区结束恢复;方案 B已完成块独立 DOM 节点不参与 v-html 替换。关联 ARC-08 块级 memosplitBlocks 已按块切分,末块单独更新天然支持方案 B— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts
- [x] 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
**第二批 — 显著提升日常效率**
- [ ] UX-2025-05 [P2] — **代码块语法高亮 + 复制按钮**§2.2)。集成 highlight.js按需加载不阻塞首屏marked renderer code 回调调 `hljs.highlightAuto`;代码块右上角 hover 浮出「📋 复制代码」。依赖 ~30KBhighlight.js core + 常用语言包)。— src/composables/useMarkdown.ts + 新依赖 highlight.js
- [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
- [ ] UX-2025-07 [P2] — **键盘快捷键**§7.1)。Ctrl+N 新建 / Ctrl+K 搜索对话 / Ctrl+L 清空 / Ctrl+Shift+C 复制 AI 消息 / Ctrl+R 重新生成 / Esc 关闭面板 / Ctrl+B 切侧栏。全局监听 AiChat 聚焦时生效。— src/components/AiChat.vue
- [x] UX-2025-07 ✅(第⑲批 2026-06-16) — ~~键盘快捷键~~ onGlobalKeydown(AiChat.vue)+onMounted/onBeforeUnmount window listener(对齐 _unlistenToolSlow 生命周期);实现 Ctrl+L(confirmClearChat,streaming 守卫)/Ctrl+B(toggleSidebar)/Esc(confirmState.visible→answerConfirm(false)>props.detached→closeDetachedWindow 优先级);Ctrl+N/K/R 留 TODO(WebView2 浏览器级 preventDefault 不可靠待实测);Esc 冒泡与 textarea handleKeydown 不冲突(skill 浮层 Esc 由 textarea handler 先消费)。vue-tsc 0 err。原设计:Ctrl+N 新建/Ctrl+K 搜索/Ctrl+L 清空/Ctrl+Shift+C 复制/Ctrl+R 重新生成/Esc 关闭/Ctrl+B 切侧栏 — src/components/AiChat.vue
- [ ] 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
- [ ] UX-2025-11 [P2] — **输入框高度提升**§1.5。textarea 最大高度 120px→200px~10 行),超过后再内部滚动。或可拖拽 resize handle。— src/components/AiChat.vue
- [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
- [ ] UX-2025-13 [P2] — **消息时间戳**§2.4)。气泡下方极小字号(dim 色)展示相对时间(复用 time.ts 已有 common.justNow 等 keyhover tooltip 展示完整时间。— src/components/AiChat.vue
- [ ] UX-2025-14 [P2] — **Provider 切换反馈**§5.1。cycleProvider 切换后 toast 提示 + 0.15s 淡入动画 + bar 展示 model 名小字。— src/stores/ai.ts + src/components/AiChat.vue
- [ ] UX-2025-15 [P2] — **技能参数用法展示**§5.2)。联想浮层每项增加 argument_hint 等宽小字提示;选中技能后输入框上方 chip 展示完整参数格式说明。关联 F-260614-02(技能联想)。— src/components/AiChat.vue
- [ ] UX-2025-16 [P2] — **侧栏宽度可拖拽**§6.1)。侧栏右边缘 2px 拖拽条(cursor: col-resize),范围 120~280px宽度持久化 df-ai-ui 设置。— src/components/AiChat.vue (sidebar CSS)
- [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 + 新依赖
- [ ] UX-2025-20 [P3] — **空状态引导 + 标题过渡**§7.3+§7.4)。空态展示 3-4 示例问题卡片点击自动填入发送);无 provider 时引导配置;标题生成后 0.3s 淡入动画 + AI 生成标识图标。— 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 授权体验改进2025-07-15 方案 → 待办化)
### 🟡 AI Chat 授权体验改进2026-06-14 方案 → 待办化)
> 来源:[aichat授权体验改进方案-2025-07-15.md](./02-架构设计/aichat授权体验改进方案-2025-07-15.md)。6 痛点 + 8 改进项,按原文优先级转待办。**状态: 待讨论→看板跟踪**。
> 来源:[aichat授权体验改进方案-2026-06-14.md](./02-架构设计/aichat授权体验改进方案-2026-06-14.md)。6 痛点 + 8 改进项,按原文优先级转待办。**状态: 待讨论→看板跟踪**。
**P0 — 快速改善体感(第一批建议落地,~2 天工作量)**
- [ ] 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
- [ ] AE-2025-02 [P0] — **审批计数器 + 跳转**。输入框上方/header 显示 `⏳ N 项待审批` 徽标,点击跳转到第一个 pending 卡片。改动:①`AiChat.vue` header 区域加审批徽标 ②`ToolCardList.vue` 暴露 `scrollToFirstPending` 方法。预估 0.5 天 — src/components/AiChat.vue + src/components/ToolCardList.vue
- [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-15)**: 目录级粒度(非文件级)、仅写+执行首批、Webhook 未来走独立 execution_token 链路不影响本设计
- [ ] AE-2025-05 [P1] — **High 二次确认**。delete/purge/run_command 等 High 风险操作,批准后弹 inline 确认("确定要永久删除?此操作不可恢复"),二次确认才真正执行 ai_approve。可用现有 ConfirmDialog 组件。预估 0.5 天 — src/components/ToolCard.vue
- [ ] AE-2025-06 [P1] — **审批超时(人工审批场景)**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
- [ ] 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 — 信息透明度**
- [ ] AE-2025-07 [P2] — **Agentic 进度条**消息区域底部显示循环进度(`🔄 循环 3/10 · ⏳2待审批 · ✅5完成`)。改动:①`AiChat.vue` 底部加进度指示条 ②`useAiEvents.ts` 处理 AiAgentRound 时更新进度。预估 0.5 天 — src/components/AiChat.vue + src/composables/ai/useAiEvents.ts
- [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 待定
@@ -464,48 +510,53 @@
- [ ] 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-15,待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-15,待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
- [ ] F-260615-03 **[P2 最小修]** list 工具截断标志(原完整分页方案,对抗分析降级。现状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)
- [ ] F-260615-04 **[P1 UX]** 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-15,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),完整折叠交互归信息密度构想单独立项
- [ ] F-260615-07 **[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
- [ ] 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-15 能力梳理·设计定稿 V2
### 🟡 文件操作工具集补全2026-06-16 能力梳理·设计定稿 V2
> 用户给出 5 个工具具体 API 设计。**核心原则不变**:专用工具补 run_command 做不好/不安全的;系统命令类交给 run_commandB-37 修后)。**修正**:search_files 从"run_command 覆盖"升级为"值得做"(结构化 JSON > parse find 输出)。
**5 工具设计(用户方案)**:
- [ ] F-260615-06 [P0] **patch_file(edit_file)** → 见下方对抗分析区 :503 完整条目(含设计文档指针)
- [ ] F-260615-07 — **[P1] search_in_file**(内容搜索)— `path, keyword, (可选)regex, case_sensitive``[{line, content, column}]`。纯子串搜索先做(regex/case 后加,做减法)。替代 AI 多轮分页扫描 — tool_registry.rs read_file handler 扩展
- [ ] F-260615-08 [P1] **file_info**(元信息) `path``{exists, size, lines?, modified, is_binary, is_dir}`。**不读内容**,快速元数据。lines 用 `\n` 计数(不全文读);二进制不报 lines — tool_registry.rs 新增
- [ ] F-260615-09 [P1] **append_file**(追加写入) `path, content``{bytes_written, new_size}`。日志追加/增量写入,避 read-merge-write 竞态 — tool_registry.rs 新增
- [ ] F-260615-12 [P1] **search_files**(文件名/glob 搜索) `path, pattern: "*.ts", recursive?``[{path, size}]`。结构化 JSON > parse find 输出(AI token 效率+可靠)。从"run_command 覆盖"升级(原归类为不做的系统命令类) — tool_registry.rs 新增
- [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/合并)**:
- [ ] F-260615-10 [P2] **diff**(修改前对比,防误覆盖)— 并入 patch_file 增强(edit 前 return diff 给审批卡看清改动),或独立工具 — 关联 F-06
- [ ] F-260615-11 [P3] **write_file base64**(二进制写入)— 图片/PDF/Excel,场景窄 — tool_registry.rs:519
- [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-15 十域十八项交叉验证)
### 🟡 对抗分析裁决2026-06-16 十域十八项交叉验证)
> 来源用户对抗分析报告10 域聚类 + 正反双向验证 + 依赖链提取)。**核心价值6 个否决/暂缓裁决**"不做"比"做什么"更值钱,避免浪费投入)。完整报告见 docs/02-架构设计/对抗分析裁决-2026-06-15.md若已落盘
**✅ 推进(第一批独立可并行)**
- [ ] F-07 trait 下沉 df-ai-core — 设计完备 4 项决策全定稿,退路可放 df-core解锁 F-03/F-01
- [ ] R-PD-2 ScriptNode 不注册 script — 3 行删除封死攻击面,工作流当前纯演示无真实脚本需求
- [ ] F-260615-06 [P0] **patch_file(edit_file)** 局部文件更新工具,补齐 AI 文件操作闭环。完整设计见 [patch_file工具设计-2026-06-15.md](./02-架构设计/patch_file工具设计-2026-06-15.md)API/三层防御/边界情况/替代方案否决/实施步骤)。**核心**: old_text 精确匹配为主+line 辅助+Mutex 并发安全+expected_hash 指纹防脏写。第一批实现核心三件套(~50行)。— src-tauri/src/commands/ai/tool_registry.rs 新增 handler
- [x] 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 备选)
- [ ] CR-09 .ai-md 样式 5 份→全局 css 抽取 — 零风险减法
- [ ] F-09 status 对齐前后端 — 删从未产生的 planning/in_progress 等,前端 map 改 {active/archived}
- [ ] F-260615-03 最小修(截断标志) **从完整分页降级**:不改返回结构(non breaking)truncate 时加 `truncated: true`(→ 见上方 :469 完整条目,本条为对抗分析区引用,勿重复实施)
- [ ] R-PD-9 display_hint 轻量抽取 audit 工具名→文案映射作 display_hint消除双份映射
- [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消除双份映射
**🟡 暂缓 / 需重设计**
@@ -545,7 +596,7 @@
- [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-15
### 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
@@ -554,6 +605,49 @@
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 + 分层存储)

10
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.7.1",
"dompurify": "^3.4.10",
"highlight.js": "^11.11.1",
"marked": "^18.0.5",
"vue": "^3.5.13",
"vue-i18n": "9",
@@ -1597,6 +1598,15 @@
"he": "bin/he"
}
},
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/is-arrayish": {
"version": "0.3.4",
"resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz",

View File

@@ -16,6 +16,7 @@
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.7.1",
"dompurify": "^3.4.10",
"highlight.js": "^11.11.1",
"marked": "^18.0.5",
"vue": "^3.5.13",
"vue-i18n": "9",

View File

@@ -34,6 +34,8 @@ df-ai = { path = "../crates/df-ai" }
df-ideas = { path = "../crates/df-ideas" }
df-project = { path = "../crates/df-project" }
futures = "0.3"
# write_file base64 编码:写二进制/非 UTF-8 文件(图片/PDF/Excel 等)
base64 = "0.22"
# keyring v3 默认不带任何 platform 后端,必须显式启 feature——
# 否则 Entry 走 noop store:set_password 静默成功不持久化,get_password 永远空(=FR-S1 密钥存了等于没存,根因见 docs/09-问题排查/aichat-apikey-401排查-2026-06-15.md)
# Win+mac 启用原生后端;linux(secret-service)按需补