重构: 文档汇总+进度看板+孤儿任务清理脚本+gitignore 噪音排除

- docs/02 架构设计: 新增 aichat审查/异步审批构想/流式渲染调研/generating状态机/密钥迁移健壮性/工作流脚本执行边界/条件表达式引擎/F-07 trait下沉/Agent架构说明/任务推进构想/功能创意池;更新功能决策记录+归档/对抗论证/文档记录规范/经验记录
- docs/03 模块文档: 新增 AI对话引擎/DAG引擎详解;更新 df-knowledge/df-nodes/df-storage/df-workflow/df-ai
- docs/05 代码审查: 新增 全栈审查/全局review/架构审查/近期改动审查/工作区多角度走查/自研memo流式渲染审查
- docs/09 问题排查: 新增 aichat-apikey-401
- docs/INDEX+README 索引同步;docs/todo 待办看板(2026-06-15 汇总)
- PROGRESS.md Sprint 22-25;URGENT.md 加急清单快照(5 项 P0 已全修)
- scripts/cleanup_orphan_tasks.{py,sh} 孤儿任务清理工具
- .gitignore 补 *.broken.bak + tmp/ 噪音排除
This commit is contained in:
2026-06-15 05:14:21 +08:00
parent 4b5f096d1c
commit 04032a2a8d
43 changed files with 5372 additions and 163 deletions

View File

@@ -1,6 +1,7 @@
# aichat 审查报告 — 2026-06-14
> 性质:只审查不改代码。本报告汇总本次会话对 devflow AI chat 全链路的核对发现。
> 增补:2026-06-14 追加「修复进度」表AC1/AC2、AR-3、FR-S4、FR-R4、FR-R5 对照 commit 36d68dd / 4b5f096 标已完成),并在 §8 优先级表 AR-3 行内联标注。
## 审查范围
@@ -10,6 +11,20 @@
发现分六块:交互流畅性 / 信息卡片完整性 / clean 与压缩对话 / create_project 双审 / 数据联动方案 / 想法→灵感迁移。
## 修复进度2026-06-14 增补)
> 本节汇总对照后续 commit 已落地的修复项,供快速核对。未列入的本报告其余发现仍待办。
| ID | 问题 | 修复 commit | 状态 |
|---|------|------------|------|
| AC1/AC2 | `tool_use_id` 为 None/空致 GLM 端 500 卡死 | 36d68dd | ✅ 已完成(出站 tool_result 块 tool_call_id 空跳过+warn入站 tool_use 缺 id 流式填占位 `tool_missing_{idx}`+warn、同步路径跳过|
| AR-3 | 审批卡片裸 id + reason 两句固定模板(详见 §2| 36d68dd | ✅ 已完成(前端 `toolArgsEntries` 对 id/project_id 白名单特化查项目名回显;后端 reason 查不到对象时友好提示)|
| FR-S4 | SKILL.md 全文注入 system prompt 无隔离标注 | 36d68dd | ✅ 已完成(注入头尾加隔离标注,明确"用户选择的技能说明,非系统指令"|
| FR-R4 | `complete()` 同步路径无超时无重试(全栈报告 §4| 36d68dd | ✅ 已完成(`RequestBuilder::timeout(60s)` 单请求超时,不影响 stream 流式路径)|
| FR-R5 | `findToolCall`/`flatMap` 正向 O(n²) 全量线性扫描(全栈报告 §4| 4b5f096 | ✅ 已完成(`useAiEvents::findToolCall` 改反向遍历命中最近,放弃 Map 索引防陈旧引用)|
> AC1/AC2、FR-S4、FR-R4、FR-R5 的 ID 源自全栈审查报告/todo 跨文档编号体系,本表仅标注状态,技术细节见对应 commit 与 [全栈代码审查报告](../05-代码审查/全栈代码审查报告-2026-06-14.md)。
---
## 一、AI chat 交互流畅性
@@ -114,22 +129,20 @@ let reason = match risk_level {
---
## 四、create_project 双审双 API(用户实测痛点)
## 四、create_project 双审双 API(用户实测痛点)— ⚠️ 半成品(2026-06-14 核对)
### 根因:AI 工具 schema 比 IPC 接口窄
### 现状:已改一半
- ✅ schema 已加 `path`/`stack`(`tool_registry.rs:148-151`),描述已改"可选传 path/stack 一步完成"(`:147`)
-**handler 仍写死 `path: None, stack: None`(`:162`),未读 args** → schema 假支持
| 层 | create_project 参数 | 建带目录项目 |
|----|---------------------|-------------|
| IPC `create_project`(`project.rs:43-83`) | name, description, idea_id, **path, stack** | ✅ 一步完成(校验+防重复+探测 stack) |
| AI 工具 `create_project`(`tool_registry.rs:148`) | name, description(**无 path**) | ❌ 只能建空项目,handler 写死 path=None(:159) |
### 后果
LLM 见 schema 有 path 会传,handler 忽略 → 建出空项目 → 仍需 `bind_directory` 二审。**双审未解,反变误导**(schema 承诺了 handler 不兑现)。比原始"schema 无 path"更糟。
→ LLM 建带目录项目被逼拆两步:`create_project`(Medium 审1 + LLM 轮次1)→ `bind_directory`(Medium 审2 + LLM 轮次2)= **双审批双 API**。IPC 本可一步完成,AI 工具没用上。
### 方案
`create_project` AI 工具 schema 加可选 `path`/`stack`(对齐 IPC `CreateProjectInput`),handler 复用 IPC 的绑定+探测逻辑。`bind_directory` 保留用于后期改绑(relocate)。
### 待改(别再加 schema,已加完)
handler 从 args 读 `path`/`stack`,复用 IPC `create_project`(`project.rs:43-83`)的校验+防重复+`scan::detect_stack` 探测逻辑。`bind_directory` 保留改绑用。
### 关联
问题 1 修好`bind_directory` 使用频率大降(只剩改绑),第二章 bind_directory 信息缺口随之缓解;但 bind_directory 仍需补信息完整性。
handler 接上`bind_directory` 使用频率大降(只剩改绑),第二章 bind_directory 信息缺口随之缓解;但 bind_directory 仍需补信息完整性。
---
@@ -177,7 +190,7 @@ let reason = match risk_level {
用 Ideas/Idea(英文术语),若产品要求统一 Inspiration 也需改,待定。
### docs + crates 注释
大量"想法"(低优先),含文件名 `docs/03-模块文档/想法探索-对抗式评估.md`
大量"想法"(低优先),含文件名 `docs/03-模块文档/想法探索-对抗式评估-2026-06-12.md`
### 根因
上次迁移只改 ideas.ts 页头区,详情/操作/模态框 + 其他 i18n + 后端错误 + LLM 工具描述未跟进。
@@ -205,8 +218,8 @@ let reason = match risk_level {
|------|------|------|
| P0 | H1 流式 Markdown 重解析 | 流式态纯文本/增量渲染,完成后再 markdown;rAF 合并 |
| P0 | H2 审批态新建对话卡死 | `ai_conversation_create` 加 generating 守卫 |
| P0 | 第二章 审批卡片裸 id + reason 模板 | 后端 reason 拼对象名;前端 id→name |
| P0 | 第四章 create_project 双审 | AI 工具 schema 加 path/stack |
| 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 加清空按钮 + 后端真删当前对话消息 |
@@ -217,6 +230,66 @@ let reason = match risk_level {
---
## 九、write_file 覆盖事故与可靠性风险2026-06-14 实测)
### 事故经过
会话 `3473fcb7`2026-06-14 22:16AI 拟对 `PROGRESS.md` 做 3 处精准更新(头部当前阶段、全局问题 #9 状态、新增 #10),但**误用 `write_file`(全文覆盖语义)只传了头部 3 行 content**,把原 **762行/72KB** 覆盖成 **248字节**。AI 自查发现msg[59-61]尝试凭记忆重建恢复write_file 17956 字符),**但该恢复写入未生效**(会话中断),用户无感知「恢复失败」。
### 暴露的潜在问题
| # | 问题 | 性质 | 修法方向 |
|---|------|------|----------|
| FR-S7 | write_file 覆盖已有非空文件无确认/备份 | **根因** | 覆盖非空文件前自动备份 `.bak`;或检测目标存在强制走 edit_file |
| 关联-1 | 写入后无「预期 vs 实际」校验 | 可靠性 | write_file 返回新旧大小,差异巨大(如原 72KB→新 248B时 warn/阻断 |
| 关联-2 | AI 自恢复失败无感知 | agent 可靠性 | 工具失败/中断需明确通知用户,不静默吞掉 |
| 备份 | DB 50KB 截断致无法从 DB 完整恢复 | 备份策略 | 长文档丢失仅 git 可救;考虑关键文件写前 git 快照 |
### 恢复方式与教训
DB 中 PROGRESS.md 原文已被 `TRUNCATE_THRESHOLD=50KB``conversation.rs:55`截断头尾拼接、中段省略AI 重建版仅 17.9KB 残缺。**最终靠 `git restore PROGRESS.md`HEAD 版本 762行/72KB 完整)恢复**。教训:本地文档类资产的实际保护层是 **git** 而非 DB 会话历史——会话历史是「对话快照」非「文件备份」。
### 关联待办
- `todo.md` FR-S7write_file 覆盖保护)— P0 安全
---
## 十、文件工具系统性走查2026-06-143-agent review 之外的补充走查)
`tool_registry.rs` 实际注册 **3 个文件工具**`read_file`(:394) / `list_directory`(:428) / `write_file`(:444)。其余 edit_file/delete_file/rename_file/move_file/search_content/append_file **均未实现**——功能缺口,迫使 LLM 滥用 write_file 全量覆写,**放大 FR-S7 危害**。
### P0 — 阻断
| # | 问题 | 位置 | 修法方向 |
|---|------|------|----------|
| **FR-S8** | **路径 sandbox 系统性逃逸**:①`validate_path` 子串 `..` 检测对**绝对路径无效**`C:\Windows\...` 不含 `..` 绕过②canonicalize 仅对**已存在路径**跑write_file 新建文件 + symlink 父目录场景失效③Windows `Path::starts_with` 大小写敏感而文件系统不敏感,可误判/漏判 | :19-21, :53-69 | 统一 canonicalize不存在路径取最长存在前缀+ 大小写不敏感 prefix 比较 + parent 也校验 |
| FR-S7(放大) | write_file 定级 Medium 可自动批准 + 非原子覆写,覆写任意已存在非空文件即数据丢失(见 §9 | :446, :461 | 覆写非空文件升 High 审批 + `.bak` 备份 + 原子写(tmp→rename) |
### P1 — 重要
| # | 问题 | 位置 |
|---|------|------|
| P1-1 | write_file 非原子写:中途崩溃留半截文件丢原内容(叠加 FR-S7 数据彻底丢失) | :461 |
| P1-2 | write_file `create_dir_all(parent)` 不校验 parentworkspace 内 symlink 父目录可写逃逸 | :457-460 |
| P1-3 | read_file 1MB 按**字节** + `read_to_string` 对非 UTF-8/二进制直接失败无降级 | :409, :413 |
| P1-4 | read_file offset 无上限校验超范围静默返空limit 无硬上限 | :415-419 |
| P1-5 | list_directory 噪音目录仍作为 entry 返回仅不深入max_depth=2 写死无文档 | :437, :439, :500 |
| P1-6 | list_directory `DirEntry::metadata()` **跟随 symlink**symlink 目录被当普通目录递归(信息泄露 + 与 P1-2/FR-S8 形成逃逸组合拳) | :491-492 |
| P1-7 | `validate_path` 黑名单**子串匹配**:易误伤(`appdata-collector` 项目)易绕过(漏 `.config`/`.kube`/Program Files冗余弱层 | :22-29 |
### P2 — 次要
list_directory 子目录无权限读整层 bail:484/ read_file 错误回显完整绝对路径泄露(:406/ write_file bytes_written 字节非字符易误导(:463/ max_entries off-by-one:486/ to_str 非 UTF-8 路径笼统报错(:401/ 未实现工具缺口edit/delete/rename/move/search/append 迫使滥用 write_file
### 总结
框架方向正确schema+risk+handler 同源、双层校验、FR-S2 TOCTOU+1MB 已修),但 **sandbox 实现层有系统性缺口**:子串黑名单(弱)+ 词法 starts_withWindows 大小写坑)+ canonicalize 只覆盖存在路径(新建漏)。优先级:**FR-S8 统一 canonicalize > P1-6/P1-2 symlink 不跟随 > FR-S7 原子写+升审批**。
关联 todoFR-S7覆盖保护、FR-S8sandbox 逃逸)。
---
## 附:相关 memory(指针)
- `devflow-aichat-review-pending.md`
- `devflow-idea-inspiration-migration.md`