squash合并: - 意图识别层论证(8维度+10业界佐证) - 多主题上下文管理愿景+并存论证+补充论证(多轮agentic) - 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化) - 前端架构技术债清单归档
199 KiB
todo 已完成项归档(2026-06)
从 docs/todo.md 拆出: 已完成
[x]条目段 + 纯历史分析段。 维护: 随开发追加; 主看板只留未完成[ ]与导航/总览。 拆分日期: 2026-06-18 来源: docs/todo.md
P0 — 阻断性 bug
- ✅(主代修06-17·撤销误判) 🚨 BUG-260617-01 [P0] 已修 —
|| true致 400/参数错误无效重试 4 次【已修:stream_recv.rs:433 删|| true+ 注释 + 单测 classify_unknown_retryable→classify_unknown_not_retryable 断言改 !;cargo test 20 passed】 — 用户实测证实(2026-06-17)。Anthropic[1214][messages 参数非法]无 HTTP 前缀 → 落入stream_recv.rs:433\|\| true→ retryable=true → 空耗 4 次重试浪费 token。前次审查误判为"有意设计"。一行改动:删|| true+ 改单测断言。— src-tauri/src/commands/ai/stream_recv.rs:433 - ✅(wf wf_b0fa5730-9e9·主代适配+commit 74003c) 🚨 BUG-260617-12 [P0] 已修 — DeepSeek reasoning_content 缺失致必 400【全链路透传:df-ai-core(provider.rs CompletionRequest/ChatMessage/CompletionResponse/StreamChunk 加字段+5单测) → df-ai(openai_compat.rs OpenAiRequest/OpenAiMessage/OpenAiMessageResp/OpenAiStreamDelta 映射+流式解析) → anthropic_compat.rs 11处 StreamChunk/CompletionResponse 兼底) → agentic.rs(last_reasoning_content 初始化→stream_one_provider 注入请求→outcome 提取→push 时回填 ChatMessage) → stream_recv.rs(reasoning_content 累积+StreamResult Complete/Partial 携带) → title.rs 字面量补齐 + df-nodes/ai_node.rs/compress/knowledge_inject 非agentic 路径 reasoning_content: None。cargo check --workspace EXIT 0 / df-ai-core 24 test pass / df-ai+df-nodes 0 test(无单测)】。调研确认 DeepSeek API 规范:reasoning_content 在 choices[].message.reasoning_content(响应)+多轮 assistant 消息必须回传(有 tool_calls 时),否则 400 "must be passed back"。】 — 14 文件 / +255 行 (commit 74003c)
⚠ 2026-06-17 走查独立核验:闭环逻辑逐行验明正确(流式累积→StreamResult→StreamOutcome→agentic loop
last_reasoning_content跨轮缓存→写回 ChatMessage 三路径→openai_compat 透传回 API;前端 src/ 无 reasoning_content 引用,落库不展示无 UX 泄露)。B-260617-14 提交遗漏已修(commitf6e4e82):补提 adversarial.rs + project.rs 的reasoning_content: None,HEAD 干净 checkout 编译通过。
- B-260614-01 —
待审批持久化根治(重启恢复)未生效✅ mission:T-260614-01 已修复(commands.rs:444 clear→retain 保其他对话 pending;ai_approve 两处 if !recovered 守卫移除;cargo check 0 err / 19 test pass)(06-14) - B-260614-02 —
df-workflow ConditionEngine 默认 true✅ mission:T-260614-02 已修复(conditions.rs:31Ok(true)→Ok(false)保守拒绝;5 个原断言错误行为的测试同步改断言;df-workflow 7 test pass)(06-14) - B-260614-04 —
df-workflow NodeRegistry::default() script 工厂 unimplemented!✅ mission:T-260614-03 已修复(删除整个 Default impl——零调用方 + 违反铁律;state.rs build_registry 已用 new() + 手动注册真实 ScriptNode)(06-14) - B-260616-20 [P0] — df-nodes 测试回归:
advance_succeeds_after_prior_status_change红(CR-260616-41 status 收口副作用) ✅ 已修(主代 task_advance_node.rs:331-332 改advance_status_atomic("t1","todo","in_progress",false)走 CAS 合法路径模拟并发推进,亲跑cargo test -p df-nodes67 passed/0 failed/1 ignored)。原委:commitb94e74a把 status 移出 tasks 白名单(D-260616-04 收口),crates/df-nodes/src/task_advance_node.rs:332测试用repo.update_field("t1","status","in_progress")模拟「旁路并发改 status」前置,现被白名单拒 → unwrap panic。生产代码无回归,仅测试 fixture 同步。— crates/df-nodes/src/task_advance_node.rs:331(审查 CR-260616-41 复现)
🔧 2026-06-16 排查会话新报 bug(4 项·仅分析未实施)
排查性质会话(session-role-diagnose-only),仅走查定位根因 + 记录待办,未改代码。
- ✅(第㉖批 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 - ✅(第㉖批 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
- ✅(ca9b318·wrapNakedDiff落地+第㉖批diff语言注册) B-260616-11 [P2] — AI 消息
-/+列表/diff 无视觉效果。两项子项均已落地:①裸 diff 预处理wrapNakedDiff(ca9b318useMarkdown.ts:+96 守卫4:纯-列表不触发/≥2行连续±/已在围栏不动/段落级处理+短路优化+缓存key用wrapped) ②diff 围栏高亮(第㉖批 useMarkdown.ts import diff+registerLanguage('diff')+highlightCode getLanguage('diff')→hljs addition/deletion token+github-dark.css 红绿)。— src/composables/useMarkdown.ts(:183-282 renderMd+wrapNakedDiff 全函数) - ✅(第㉗批 2026-06-16·workflow) B-260616-19 [P2] — 知识库详情窄屏标题挤压成竖线(一行一字)。三因子叠加(Knowledge.vue):①
.kn-layoutgridgrid-template-columns: 360px 1fr(L567)——左列表死钉 360px、右详情1fr拿剩余,无响应式断点;窗口窄(如总宽 500px,减 padding 40+gap)详情列塌缩到 ~80px 甚至更小。②.detail-title(L625)font-size:18px+word-break:break-word—— 窄列里中文每字宽度 > 列宽,逐字换行 → 竖线化。③.detail-actions(L626)flex-shrink:0—— 编辑/发布/拒绝 3 按钮不缩,优先占位,把.detail-title-row(flex:1) 挤到零头。.kn-detail-panel(L570-576) 无min-width兜底。改动(待定):①grid 列宽改minmax(240px,360px) 1fr+@media (max-width:760px)切单列(grid-template-columns:1fr,列表详情上下堆叠);②详情面板加min-width:0配合标题行min-width防塌缩;③可选标题字号响应式降级(18px→15px)。— src/views/Knowledge.vue。低风险纯 CSS,无逻辑/i18n/数据变动。
🔧 2026-06-17 实测发现 bug(审批流程三连)
用户实测:AI 对话中让 AI 创建任务并推进状态 → AI 调
run_workflow(task_id, target_status)→ 审批卡片渲染 → 用户点批准 → handler 返回 Err → LLM 重试循环。
- ✅(波9·e23b316+主代核查,cargo0err+devflow79pass,方案a2后端分支) B-260617-01 [P1] — run_workflow 作为 AI 工具无法执行 + 无意义重试 + 审批混淆。三个关联问题:
- 问题 1:
run_workflowhandler(tool_registry.rs:530-546)仅持有db: Arc<Database>,无法访问AppHandle/State(registry/event_bus/workflows Repo),返回Err("run_workflow 工具需经 Tauri IPC 执行...")。审批通过后ai_approve(commands.rs:329)调ai_tools.execute()→ handler 必然 Err →audit_status="failed"→ 错误信息作为 tool_result 回传 LLM。工作流实际未执行,任务状态未推进。 - 问题 2:handler Err → tool_result=错误提示 → LLM 认为失败 → 重试(1/4) → 同结果循环。工具定义未标
no_retry,浪费 token + 用户反复审批。 - 问题 3:前端渲染的是 AI 工具审批(tool_call pending_approval),非工作流 HumanNode 审批。用户批准后工作流引擎从未启动,HumanNode 审批节点从未到达。
- 修复方向:方案 A(推荐):前端拦截 —
ai_approve中识别run_workflowtool_call → 不走ai_tools.execute→ 直接invoke('run_workflow', {task_id, target_status})→ 结果回填 tool_result。方案 B:后端注入 State —build_ai_tool_registry传入AppHandle使 handler 可直接执行。问题 2/3 随 A 自动消解。 - 代码定位:tool_registry.rs:530-546(handler 返 Err)/ audit.rs:590-630(High risk 进 pending)/ commands.rs:329-334(ai_approve execute→Err→failed)/ agentic.rs:503-508(pending_count>0 暂停循环) — src-tauri/src/commands/ai/{tool_registry,audit,commands,agentic}.rs + src/composables/ai/useAiSend.ts
- 问题 1:
🔧 2026-06-16 aichat 工具卡片重复渲染排查(仅分析·未实施)
排查性质(session-role-diagnose-only),走查定位根因 + 记录待办,未改代码。
- ✅(247dc5d后端audit按id去重治本+useAiEvents.ts:205前端findToolCall幂等守卫双保险·候选A根因闭环) B-260616-21 [P1] — aichat 工具卡片「重复读取同一文件」表象(read_file 出现两条卡:一 0 行·7.1KB running、一 183 行·7.1KB completed)。用户报对话记录里
读取 .../api/ai.ts出现两次。根因分析(已走查前端流转 + 后端 emit 点):①前端useAiEvents.ts:202-206AiToolCallStarted分支直接lastMsg.toolCalls.push(info),无 id 幂等守卫(对比同文件startToolSlowTimer:58有_toolTimers.has(callId)守卫,Started 漏了同款判重)②findToolCall(aiShared.ts:41-49) 按id命中首个返回,AiToolCallCompleted(:213) 只 update 第一张卡 → 同 id 若被 push 两次,第二张卡永远 running,ToolCard.vue:72parsed?.lines||0无 result 时显示0 行③后端audit.rs:538-549process_tool_calls对 tc_list 每 draft emit 一次 Started;read_file=Low risk(:561) 无去重(去重仅 High,:553-556注释明确 Low 不进)。两候选根因:(A 最贴合现象) 同一tool_call_id被重复 emit Started(后端process_tool_calls重入 /stream_recv.rs生成重复 id /agentic.rsloop 重 process 同轮)→ push 两张卡 → Completed 只更首张(183 行)→ 次张残留 running(0 行);(B) agent loop 多轮 LLM 真读两次(不同 id)→ 但应两卡皆 183 行,与「0 行 + 183 行」现象不符,排除为主因。待查:后端同 id 重复 emit 的确切路径(audit.rs:532tc_list 来源tool_calls_acc+stream_recv.rsid 生成 +agentic.rs是否对同轮 tool_calls 重复 process)。修复方向(待定):①前端 Started push 前加findToolCall(event.id)幂等守卫(确定性低风险,对齐 startToolSlowTimer 守卫,治标兜底) ②后端查重复 emit 源治本(定位 stream_recv/process/loop 哪环生重复)。关联 F-260616-05(agentic loop 工具去重缺失,batch53 仅 High 去重,read_file/search_files 等 Low 未去重;本现象若实为候选 A 则与 F-05 不同维度——F-05 是不同 id 重复调用,本条是同 id 重复 emit)。— src/composables/ai/useAiEvents.ts(:195-209 Started 分支) + src/composables/ai/aiShared.ts(:41 findToolCall) + src/components/ToolCard.vue(:72 lines 展示) + src-tauri/src/commands/ai/audit.rs(:538-549 emit Started)。
🔧 2026-06-17 定期全量走查(第1轮·仅走查·未实施)
周期性自检(session-role-diagnose-only:不碰代码,文档不对改文档 + 代码问题记 todo)。本轮范围:secret.rs 全文 + 近 6 提交 vs todo 状态核对 + 工作区未提交改动(tauri.conf.json)。
文档状态校正(治本/实现已落地,原 [ ] 更新为 [x]):
- B-260616-21 → ✅:后端 247dc5d(audit.rs:534-540
seen_ids: HashSet按 id 去重保首个)+ 前端 useAiEvents.ts:205findToolCall幂等守卫双保险,候选 A 根因(LLM 复用同 tool_use.id 致 emit 两次)闭环。 - F-260614-04 → ✅ 后端三段(79b6a43 数据层+池选择器 /
b3684f4池 IPC+set_caps /80c0955stream_one_provider+候选链 fallback),前端 api/ai.ts:142 IPC 封装已接;UI 配置面板待核验。
新登记代码问题:
- ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-06 [P3] — secret.rs:104 注释与实现相反。
resolve_provider_secret注释写「keyring 优先,fallback DB.api_key」,但代码(:105-110if !record.api_key.is_empty() { return DB }+get_provider_secret().unwrap_or_default())实际「DB 优先,fallback keyring」。与顶层设计(line 15「DB 优先,fallback keyring」)矛盾,line 104 注释为笔误。实际无功能影响(迁移态 DB 与 keyring 不会同时有值,set keyring→clear DB 原子序列),纯文档错误。修法:line 104 注释改为「DB 优先,fallback keyring」。— crates/df-storage/src/secret.rs:104
工作区未提交改动观察(非 bug,记录供判断):
- src-tauri/tauri.conf.json:
bundle.targets"all"→["nsis"](打包目标从全平台收窄到仅 NSIS Windows 安装包)。正式收窄(devflow 仅 Win 桌面)则合理;若 dev 调试临时收窄忘还原,后续 mac/linux 打包会漏。待用户确认意图。
🔧 2026-06-17 定期全量走查(第2轮·仅走查·未实施)
增量范围:F-05 多模态三提交(Phase2a 后端
e3cd448/ Phase2b 前端e1d396d/ Phase2c 闭环 4317d02)+ 核对src/components/settings/(未跟踪)。UX-19 虚拟滚动已被 CR-62 复审 PASS(待审查.md 已回填),本轮不重审。
F-05 多模态审查结论:质量优
- 设计周全:
content:String主载荷不变 +parts:Option<Vec<ContentPart>>挂载,老 JSON 无 parts → None,向后兼容零回归(audit/title/commands 读 content 当字符串全不受影响)。ContentPart#[serde(tag="type",rename_all="snake_case")]↔ 前端 TS discriminated uniontype:'text'|'image',wire 格式严格对齐。 - 转换层协议正确:Anthropic image
source.type=base64+ media_type + data;OpenAIimage_urldata URI / http url 双形态;OpenAIcontent:String→serde_json::Value双形态,String 简写保纯文本端点兼容。 - 所有权处理到位:openai_compat 注释「须在 move m.tool_calls 之前算,借用 m」——Rust 借用易错点作者注意到了。
- truncate_parts 防撑爆:Image base64 → 占位 Text 片,url 模式原样;落库副本不污染内存真相源(ContextManager 保留原图,重发仍带图)。
- 测试覆盖:ContentPart round-trip / legacy JSON / truncate / 转换层双形态各 2+ 测试。
- FR-S1 安全边界注释到位:commands.rs 明确 ContentPart Image base64 是图片数据非 api_key,不入敏感面。
新登记代码问题(均 P3,无 P0/P1/P2):
- ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-07 [P3] — F-05 变量名误导 + 兜底注释矛盾。①conversation.rs:109
bytes_approx = b.len()实为 base64 字符数(文案「约 N 字符」对,变量名bytes_approx错,注释「base64 字符数 ≈ 字节数*4/3」也混淆)。②openai_compat.rs Image 缺数据兜底分支_ => String::new()注释「避免发空 url 触发 400」自相矛盾——空串本身即空 url,仍触发 400。当前不可达(image_base64构造器强制media_type:Some),纯注释/命名瑕疵。修法:变量改名base64_len/文案统一;兜底分支改为明确报错或移除误导注释。— crates/df-ai/src/openai_compat.rs:336 / src-tauri/src/commands/ai/conversation.rs:109 - ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-08 [P3] — F-05 Anthropic url 模式发空 data 预留路径缺口。anthropic_compat.rs:355 Image(url,base64:None) 时兜底发空 data + warn「commands 层应预拉 base64」,但 Phase2c commands.rs:177 透传 parts 时无预拉 url 字节逻辑。当前前端 Phase2b 只产 base64 模式(AiChat.vue:999
FileReader.readAsDataURL),路径不可达;但注释误导(声称的预拉不存在),未来加 URL 图片输入 / 历史 url 图重发会静默 Anthropic 400。修法:commands 层补 url→base64 预拉,或 provider 层 url 模式明确报错而非发空 data。— crates/df-ai/src/anthropic_compat.rs:355
文档状态校正:
- F-260614-04 UI 面板核验通过:第1轮标 [x] 时备注「UI 面板待核验」,本轮确认
src/components/settings/ProviderPanel.vue(enabled toggle + weight 0-100 input +onPoolToggle即时调 IPC,落库后后端reload_provider_caps重建)已实现 = F-04c 负载均衡池 UI。注:该文件当前未跟踪未提交(git status?? src/components/settings/),含 GeneralPanel/KnowledgePanel/ConnectionPanel/ProviderPanel 四组件(设置面板拆分重构),待提交纳入版本控制。
已审文件清单(本轮):crates/df-ai-core/src/provider.rs · crates/df-ai/src/anthropic_compat.rs · crates/df-ai/src/openai_compat.rs · src-tauri/src/commands/ai/conversation.rs · src-tauri/src/commands/ai/commands.rs · src/api/ai.ts · src/api/types.ts · src/composables/ai/useAiSend.ts · src/composables/ai/useAiConversations.ts · src/stores/ai.ts · src/components/AiChat.vue(图片构建+渲染) · src/components/settings/ProviderPanel.vue 新登记 todo:B-260617-07 · B-260617-08
🔧 2026-06-17 Tasks.vue i18n 缺失(仅分析·未实施)
Tasks.vue:72 报
Not found 'completed' key in 'zh' locale messages。session-role-diagnose-only,仅分析+记todo。
现象:Tasks.vue:72 $t(statusLabel(task.status)) → taskStatusLabel() → TASK_STATUS_LABELS[status] 返回 'tasks.status.done' → $t('tasks.status.done') 在 en/zh-CN 均未定义 → intlify fallback 到 en 仍缺失。
根因:src/constants/project.ts:61-68 定义了 TASK_STATUS_LABELS(值如 'tasks.status.done'/'tasks.status.todo' 等 7 个 status + 'tasks.statusFilter.done' 等 5 个 filter + priority 相关),但 en/zh-CN 的 tasks 相关 i18n 文件完全不存在。6b67214 大量新增功能时未同步补 tasks i18n key。
影响范围(需补全的 key):
| 组件 | 用到的 key 前缀 | 缺失的 key |
|---|---|---|
| Tasks.vue:72 | tasks.status.* (7个) |
全部 |
| Tasks.vue:148-155 | tasks.statusFilter.* (5个) |
全部 |
| Tasks.vue:96-103 | tasks.priority.* / P0/P1/P2/P3 (5+4=9) |
全部 |
- ✅(误判销账·核验 tasks.ts 齐全) B-260617-10 [P2] — Tasks.vue i18n 缺失【独立核验:tasks.ts status 7 key(todo/in_progress/in_review/testing/done/blocked/cancelled)+ statusFilter 8 key 全有(zh/en 对称)。真实根因=老 DB status='completed' 残留 + taskStatusLabel
?? status兜底裸字符串 → $t() not found,已 commit1cd7652LEGACY_STATUS_MAP 防御修复】
2026-06-17 进展注记(1cd7652):提交
1cd7652已做数据层映射兜底(project.ts 新增LEGACY_STATUS_MAP:completed→done / review_ready→in_review / merged→done / abandoned→cancelled),taskStatusLabel/taskStatusClass经映射后老 DB 脏态不再直接走$t()报 not found。但 i18n key 补全(显示层)仍未做——映射后$t('tasks.status.done')仍 not found(tasks.ts 不存在),用户从看到 "completed" 变成看到 "tasks.status.done" 原始 key。B-260617-10 主问题(补全 21 个 i18n key)仍在,1cd7652 是其前置的数据归一,待补 key 后老态自动正确显示。映射目标态(done/in_review/cancelled)均已核验在 TASK_STATUS_LABELS 7 态内 ✓。
🔧 2026-06-18 走查·虚拟滚动修复链(B-260617-09 销账核对)
核验 8abcd56/0ca5d98/02bee38 三 commit(B-260617-09 重叠修复链)。session-role-diagnose-only,独立核验代码形态非信 commit msg。
8abcd56(方案 A·治本)✓:卸载分支去 height>0 守卫,height>0 用实测 / 否则 offsetHeight / 再否则 40px 兜底(useAiVirtualScroll.ts:72-76);额外修 registerSentinel 首帧占位(:107-109 h≤0 设 40px 防 list 首渲染塌 0)。实施正确,比我原建议完善。
0ca5d98(🔴 临时禁用未回退·P1 遗留):shouldRender 恒 return true(useAiVirtualScroll.ts:138),虚拟滚动裁剪完全关闭。注释标"临时禁用·定位后回退"但仍在 HEAD。三重问题:①8abcd56 已修根因,禁用多余;②裁剪关闭致 8abcd56 卸载 fallback 被绕过(不走卸载分支);③长对话所有消息恒渲染,虚拟滚动形同关闭性能下降。应回退:恢复 return renderedKeys.value.has(key) || pinnedKeys.value.has(key)。
02bee38 销账 B-260617-09:根因 8abcd56 修了,0ca5d98 诊断未回退销账略早。→ 2026-06-18 最终决策:彻底移除虚拟滚动(见下工作区进展),非回退 0ca5d98 恢复裁剪。B-260617-09 真闭环。
- ✅(2026-06-18·转向彻底移除·工作区待提交) B-260618-01 — 原"回退
0ca5d98恢复虚拟滚动裁剪"→ 转向彻底移除虚拟滚动(删 useAiVirtualScroll.ts + AiChat.vue 移除全链路,见下进展段)。理由:IO/RO 时序竞态致重叠(shouldRender 恒 true 时 IO 仍设/清 sentinel minHeight 竞态源未除)+ 消息量级不需要。B-260617-09 真闭环。
🔥 2026-06-18 工作区进展(方案变更·虚拟滚动整体删除):wf agent 未回退 0ca5d98,而是彻底删除 useAiVirtualScroll.ts(工作区
D,-175 行)+ AiChat.vue 移除全部 6 处引用(模板 ref / import / 解构 / onMounted setupVirtualScroll / lastStreamingRenderKey+watch),grep确认无残留引用(编译安全)。重叠由此根治(移除 IO/RO 时序复杂度源)。B-260618-01 以"删除文件"方式解决(非回退临时禁用)。代价:长对话所有消息恒渲染(无虚拟化),性能下降——正确性优先权衡。均工作区未提交(HEAD 仍 0ca5d98),待提交入库。同期改动:ToolCard run_command 增强 + ToolCardList userCollapsedCards 修复首卡折叠 bug(见 B-260618-03)+ U-260618 global-toggle(全部展开/收起)从 topbar 下沉到首个可折叠分组标题行尾巴(firstCollapsibleGi computed,与 run_command 分组标题"run command N"同行,chevron 移除 margin-left:auto)+ 分组标题行加 max-width:var(--df-msg-max-width)(90%) 对齐气泡/卡片宽度(原无 max-width 拉满 100% > 气泡/卡片 90%)+ global-toggle 移除独立按钮 padding/背景高度=文字行高不撑高标题行(见 B-260618-04)。
-
✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) B-260618-03 — ToolCardList 首卡折叠 bug。
isCardExpanded(ToolCardList.vue)ci===0兜底return true覆盖用户折叠——多卡分组首卡默认展开,用户点折叠后 ci===0 仍 true 重新展开。修复:加userCollapsedCards记忆态,isCardExpanded最优先判断用户折叠(覆盖 ci===0 兜底 + userExpandedCards 记忆);toggleCardExpand改用 isCardExpanded 判当前真实态(避免首卡 toggle 误判反向展开);template@toggle传 tc/gi/ci。— src/components/ToolCardList.vue(isCardExpanded + toggleCardExpand + userCollapsedCards) -
✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) B-260618-04 — ToolCardList 全部展开/收起下沉 + 宽度对齐(U-260618)。①global-toggle 从 topbar 下沉到首个可折叠分组标题行尾巴(firstCollapsibleGi computed,与 run_command 分组标题同行,chevron 移除 margin-left:auto);②分组标题行加 max-width:var(--df-msg-max-width)(90%) 对齐气泡/卡片宽度(原无 max-width 拉满 100%);③global-toggle 移除独立按钮 padding/背景高度=文字行高不撑高标题行。— src/components/ToolCardList.vue(firstCollapsibleGi + template global-toggle 下沉 + CSS)
-
✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) B-260618-05 — AI 独立窗口吸附挤出屏幕 + 宽度偏窄。①吸附挤出:
syncToMain把 AI 窗口放主窗口右(pos.x+size.width+GAP),主窗口最大化时 AI x=屏幕宽+4 挤出屏幕。修复:syncToMain 增强——主窗口 isMaximized 时 unmaximize + 主+AI 宽度超屏幕时缩小主窗口(newMainWidth=screenWidth-GAP-AI_WIDTH-pos.x,最小 400px 保护)+ _isSyncing flag 防 syncToMain 改主窗口 size 触发 onResized 递归;屏幕宽度用顶层函数currentMonitor()(@tauri-apps/api/window,非 Window 实例方法)。②独立窗口窄:detachPanel width 520→600 对齐吸附态 AI_DOCK_WIDTH。— src/composables/ai/useAiWindow.ts(syncToMain + detachPanel)
🔧 2026-06-17 aichat 消息全量重叠(5角度深入分析·未实施)
用户报 aichat 对话「大量重叠」「不单间距问题」。session-role-diagnose-only,5 角度并行论证后记录。
🔥 2026-06-17 补充触发场景(展开全部·同根因):用户报"展开全部"折叠段(archived/compressed)后,展开内容未把后续消息往下挤压,而是与未展开消息重叠。机制同主根因:展开段 →
renderItemscomputed 新增 msg item(key='e-'+seg.key+'-'+m.id,AiChat.vue:2586)→ 新 item 注册 sentinel,itemMetaheight 初始 0(useAiVirtualScroll.ts:104)→ 高度竞态 → slot 塌 0 → 内容 overflow 显示 + 后续上移 = 重叠。与"第二次发送"触发的重叠同根(虚拟滚动 slot 高度塌陷),B-260617-09 方案 A(卸载 minHeight fallback)/ C(禁虚拟滚动验证)可一并解决。
🔥 2026-06-17 用户精确场景收敛(根因定位·待 DevTools 终验):用户给出精确触发模式——①user 发 msg1→AI 回 reply1(一或多卡片) ②user 再次发送 msg2 时,msg2 重叠在 reply1 之上 ③reply2 紧跟重叠 msg2 后 ④后续连续重叠。此场景锁定触发时机=第二次发送(reply1 从 pinned 移出的瞬间),根因链收敛:
- CSS 兜底缺失(确定):
.ai-msg-slot(AiChat.vue:3174)仅display:flex;flex-direction:column,无 min-height/height/position。slot 高度完全靠内容撑;内容卸载时高度唯一依赖虚拟滚动 IO 回调设的 inlineminHeight(useAiVirtualScroll.ts:74),无 CSS 静态兜底。- minHeight 守卫跳过(核心嫌疑):useAiVirtualScroll.ts:73 卸载分支
if (meta && meta.height > 0 && meta.el.isConnected)——height=0 时跳过设 minHeight → slot 塌 0 → 后续 flex 项上移 → 叠在 reply1 内容(overflow 显示)上 = 用户所见"msg2 重叠在 reply1 上"+后续连续重叠。- height=0 高发窗口=第二次发送:reply2 push →
lastStreamingRenderKey(:2596)切到 reply2 → watch(:2604)setPinnedMsgsreply1 移出 pinned;同时isLastAi(reply1)(:1561)变 false → bubble key(:463)从_mdRenderKey变0→ Vue 重建 reply1 bubble(流式 streamingBlocks→renderContent 切换)。pinned 移出 + bubble 重建叠加,RO/IO 在此窗口易捕获 height=0(首次挂载初始化 0 :104 / 重建中间态 / RO 异步未跟上最终值)。- 确诊待 DevTools:第二次发送后查 ①reply1 的
itemMeta.height(useAiVirtualScroll itemMeta Map)②reply1.ai-msg-slot的 inlineminHeight+offsetHeight③shouldRenderMsg(reply1)是否变 false。若 height=0 且 minHeight 未设 → 确诊。- 修法选项:
- A 治本:useAiVirtualScroll.ts:73 卸载分支去掉
height>0跳过,height=0 时 fallback 测meta.el.offsetHeight(卸载前内容还在,offsetHeight 即真实高度)作 minHeight。- B 兜底:
.ai-msg-slot加静态min-height(如 60px),防塌 0(治标,虚拟滚动 height=0 隐患仍在)。- C 隔离验证(推荐先做):临时禁用虚拟滚动(
shouldRenderMsg恒 true),立判重叠是否消失 → 确诊是否虚拟滚动所致(5 分钟验证)。
现象确认(基于截图):
| 现象 | 判定 |
|---|---|
顶部出现 <transition name="sidebar-slide"> 等模板源码文本 |
✅ 正常 — AI 回复中 read_file 读取 AiChat.vue 源码后 v-html 展示,非渲染泄漏 |
| 消息/工具卡大量重叠堆叠 | 🔴 异常 — 布局层叠问题 |
| 整体挤压、间距消失 | 🔴 异常 |
已排除项:
| 检查点 | 结论 |
|---|---|
| CSS 声明链路 | ✅ .ai-messages 有 display:flex;flex-direction:column;gap:14px(:3109-3116),无外部覆盖 |
| scoped 样式冲突 | ✅ 单 <style scoped> 块,global.css 无 ai-messages 覆盖 |
| markdown 渲染安全 | ✅ DOMPurify sanitize + escapeFallback 兜底(useMarkdown.ts:181-192) |
| DOM 结构正确性 | ✅ .ai-msg-slot 是 .ai-messages 直接子元素,gap 应生效 |
角度①:虚拟滚动 IntersectionObserver 时序与渲染逻辑
Vue 3 ref 时序修正:.ai-messages(ref=messagesContainer) 是父元素,其 ref 先于 v-for 子元素 .ai-msg-slot 的 ref 回调赋值。所以 registerSentinel 执行时 options.root.value 理论上已就绪,IO/RO 应能正常创建和 observe。**假设「root 未就绪导致漏 observe」概率下调但仍不能排除——因为 Vue 3 的 ref 赋值时机是微任务队列中的异步操作,v-for 子元素的 ref 回调可能在同一次微任务中但顺序不确定。
关键执行路径核验:
registerSentinel(key, el) 执行流程:
① el instanceof HTMLElement? ✓ (DOM 已创建)
② el.dataset.vscrollKey = key
③ h = prev?.height ?? 0 → 首次 h=0
④ h>0? → 否,跳过 minHeight 设置
⑤ itemMeta.set(key, {el, height:0})
⑥ ensureObserver() → rootEl=options.root.value
- 若 rootEl 存在且 io 为空 → 创建 IO+RO → io.observe(el)+ro.observe(el) ✓
- 若 rootEl 为 null → 静默返回,io/ro 仍为 null → observe 全部跳过 ✗
⑦ renderedKeys 不含 key → add(key)
如果路径 ⑥ 走了 ✓ 分支(IO 正常创建):
- IO 以 rootMargin:600px 观察 sentinel → 首屏所有 item 都在可见区 → 全部 add 到 renderedKeys → 所有消息渲染 → 此时不应有重叠
- RO 测量每个 sentinel 的真实高度 → itemMeta.height 被回填
- 滚动后不可见 item → IO 回调触发删除 → 设置 minHeight 锁高度 → 内容卸载(v-else-if=false) → slot 占位
如果路径 ⑥ 走了 ✗ 分支(rootEl 为 null):
- 所有 sentinel 的 io/ro observe 被跳过
- onMounted → setupVirtualScroll() → ensureObserver() → 此时 rootEl 就绪 → 创建 IO+RO
- 但已注册的元素未被重新 observe! setupOnMount 注释明确写"不主动全量 observe"
- IO 永远不触发回调 → renderedKeys 只增不减 → 所有消息始终渲染 → 单纯"虚拟化失效"不应导致重叠
- 除非后续操作(消息新增/对话切换)触发 registerSentinel 重跑 → 此时 IO 已存在 → 新 item 被 observe → IO 开始工作 → 但旧 item 仍从未被 observe
角度①结论:IO 漏 observe 导致纯虚拟化失效(全部渲染)不会直接造成重叠。但如果 IO 在某个时刻开始部分工作(如新增消息触发的 registerSentinel 让 IO 活跃起来),可能出现不一致状态:部分 item 被 IO 管理(可卸载)、部分不被管理(永远渲染)。这种半激活态下若发生卸载而 height=0(RO 从未测过)→ minHeight 不设置 → slot 塌 0 → 可能重叠。概率:中(需特定时序条件触发)。
角度②:CSS 布局嵌套链路分析
完整 flex 嵌套层级(6 层):
.ai-panel display:flex; flex-direction:row; height:100%
└─ .ai-chat-area position:relative; flex:1; display:flex; flex-direction:column; height:100%; min-width:0
├─ .ai-header (固定高度 ~44px)
├─ .provider-bar (~32px)
└─ .ai-messages flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:14px; padding:14px
└─ .ai-msg-slot display:flex; flex-direction:column ← UX-19 新增 wrapper
└─ .ai-msg (role-based class)
├─ .ai-msg-user display:flex; gap:8px; align-items:flex-start; flex-direction:row-reverse
└─ .ai-msg-ai display:flex; gap:8px; align-items:flex-start
└─ .ai-msg-content flex:1; min-width:0; display:flex; flex-direction:column; gap:8px
├─ .ai-msg-bubble (内容)
├─ .ai-msg-actions (按钮组)
├─ ToolCardList (工具卡列表)
└─ .ai-msg-time (时间戳)
全局规则影响:
* { margin:0; padding:0; box-sizing:border-box }(global.css:100)→ 消除浏览器默认 margin/padding,对 flex gap 计算无负面影响html,body,#app { overflow:hidden }(:105)→ 根级不滚动,.ai-messages的overflow-y:auto独立滚动,这是正确的- 无冲突点
UX-19 改前 vs 改后对比:
| 改前 | 改后 | |
|---|---|---|
.ai-messages 直接子元素 |
.ai-msg / .ai-msg-segment |
.ai-msg-slot(新 wrapper) |
| gap 作用对象 | 消息/分隔条本身 | slot 容器 |
| slot 内部布局 | N/A | display:flex;flex-direction:column |
slot 作为额外 flex 容器的影响:.ai-msg-slot { display:flex;flex-direction:column } 将 .ai-msg 变为 flex item。由于 .ai-msg 自身也是 flex 容器(user/ai 方向),这增加了一层 flex 格式化上下文。在标准浏览器中这不应导致问题,但增加了嵌套深度到 7 层。
角度②结论:CSS 声明链路干净,无全局覆盖。UX-19 多一层 slot wrapper 增加嵌套深度但在标准 Chromium 中不导致重叠。概率:低(非根因)。
角度③:消息数据→渲染一致性分析
renderItems 生成逻辑(:2511-2527):
// messageSegments computed → 按 status 分段(normal/archived/compressed)
// renderItems computed → 扁平化:
// normal 段 → { kind:'msg', key:'m-'+msg.id, msg }
// 折叠段 → { kind:'sep', key:seg.key, seg } + 展开时追加 { kind:'msg', key:'e-'+seg.key+'-'+msg.id, msg }
key 唯一性:
- normal 段 key =
'm-' + msg.id→ msg.id 全局唯一 → key 唯一 ✅ - 展开折叠段 key =
'e-' + seg.key + '-' + msg.id→ seg.key 含 msg.id 前缀 → 组合唯一 ✅ - sep key = `'seg-' + startMsgId → 按 status 连续分组 → key 唯一 ✅
- 无重复 key 风险 ✅
切换对话时的清理:
switchConversation替换store.state.messages→ messageSegments 重新计算 → renderItems 全部重建- Vue 的 v-for + :key 机制:旧 key 对应的 DOM 元素被销毁 → 触发旧 sentinel 的
registerSentinel(key, null)卸载分支 → io.unobserve + ro.unobserve + itemMeta 保留 height - 清理链路完整 ✅
流式生成中新增消息:
- 新消息 push 到 store.state.messages → messageSegments 更新 → renderItems 追加新 item → 模板 v-for 挂载新
.ai-msg-slot→ registerSentinel(newKey, newEl) - 此时 IO/RO 已存在(onMounted 早已执行)→ ensureObserver 直接返回(io 非 null)→ io.observe(newEl) + ro.observe(newEl) ✅ 正常
- 新增消息路径无问题 ✅
角度③结论:数据→渲染链路 key 唯一、切换清理完整、流式新增正常。概率:极低(非根因)。
角度④:ToolCard 内部布局对父级影响
ToolCard 绝对定位元素清单:
| 选择器 | 位置 | 用途 | 影响 |
|---|---|---|---|
.ai-tool-file-pre--collapsed::after |
:1080-1089 | 文件预览渐变遮罩(48px高) | position:absolute; bottom:0 — 伪元素脱离文档流 |
.ai-sidebar-resize-handle |
AiChat.vue:2641-2649 | 侧栏拖拽条 | position:absolute; height:100% — 在 sidebar 上,不影响 messages |
关键分析:.ai-tool-file-pre--collapsed::after:
- 父元素
.ai-tool-file-pre--collapsed设了position:relative(:1078)→ absolute 伪元素相对于它定位 - 伪元素
height:48px覆盖底部,但不撑高父元素(absolute 元素不参与父级高度计算) - 然而:
.ai-tool-file-pre--collapsed本身有max-height:180px; overflow-y:auto(:1074-1077)→ 内容超出 180px 时内部滚动 → 父元素高度锁定 180px → 不受 absolute 伪元素影响 - 结论:此 absolute 伪元素不会导致父级高度塌陷 ✅
ToolCardList 布局(ToolCardList.vue:282-284):
/* ToolCardList 容器 */
display: flex;
flex-direction: column;
gap: 8px;
- 每个 ToolCard 是 flex item,高度由内容决定
- 折叠态 ToolCard 用
v-show(非 v-if)→display:none彻底移出流 → 不占空间 ✅ - running/pending_approval 强制展开 → ToolCard 可见 → 占据实际高度
极端场景模拟:单条 AI 消息含 N 个 ToolCard(截图场景看起来有 10+ 个工具调用):
- 每个 ToolCard 含 header(~36px) + body(文件预览/参数/结果, 可能 200-500px) + footer
- 单条 AI 消息总高度可能达 2000-5000px
.ai-msg-content(flex-direction:column) 正确包含全部高度 ✅.ai-msg-ai(flex) 正确包含 content ✅.ai-msg-slot(flex-direction:column) 正确包含.ai-msg✅
角度④结论:ToolCard 内部 absolute 定位有 position:relative 父容器约束,不会导致高度塌陷。工具卡密集导致的单条消息极高在 flex 嵌套中能正确传播高度。概率:低(非根因)。
角度⑤:Tauri/WebView2 特有问题
WebView2 版本:Tauri v2 使用系统安装的 WebView2 Runtime(非捆绑)。Windows 11 默认 WebView2 版本 ≥ 91,flex gap 在 84+ 完整支持。
已知 WebView2 与 flex 相关问题:
- Chromium bug #1143747:深层嵌套 flex + gap 在某些情况下 gap 不生效 → 但我们的 gap:14px 在
.ai-messages上仅 1 层嵌套,不太可能命中 - DPI 缩放:Windows 125%/150% 缩放下 flex 布局的像素计算可能有亚像素舍入误差 → 导致 1px 级别的偏移,不足以解释大规模重叠
- IntersectionObserver rootMargin:WebView2 的 IO 实现与标准 Chrome 一致(共享 Blink 引擎),rootMargin:600px 应正常工作
- ResizeObserver + IO 同时观测同一元素:规范允许,Chromium 正确处理,无竞态问题
Tauri 特殊配置:检查 tauri.conf.json 中是否有 webview 相关配置可能影响渲染。
角度⑤结论:WebView2 在 Windows 11 上与标准 Chrome 行为一致,无已知 bug 能解释此现象。概率:极低(非根因)。
综合判定与新假设
5 角度逐一排除后,原假设(IO 漏 observe / ToolCard absolute / CSS 嵌套 / 数据一致 / WebView2)均无法充分解释「全量重叠」现象。
需要补充排查的方向:
🔴 新假设 A [最高概率]:虚拟滚动 shouldRenderMsg 返回 false 导致内容卸载,但 slot 的 minHeight 未正确设置 → 0 高 slot 堆叠
具体路径:
- IO 正常工作(角度①修正:rootEl 就绪时 IO 成功创建)
- 某些 sentinel 在 IO 首次回调时被判为不可见(如页面尚未完成 layout,或 rootMargin 计算偏差)
- IO 回调进入卸载分支(:68-76):
meta.height > 0检查 - 此时 RO 尚未触发(RO 的首次回调通常比 IO 晚,因为 RO 需要等内容渲染完成后才测得尺寸)→
meta.height仍为初始值 0 meta.height > 0为 false → 不设置 minHeight → renderedKeys 中删除该 keyshouldRenderMsg(key)返回 false →v-else-if不渲染内容.ai-msg-slot内部无内容 → 高度塌为 0(padding/margin 均为 0,仅靠 gap 撑开)- 下一条消息的 slot "坐落" 在上一条的 0 高 slot 上 → 视觉重叠
关键证据支持:
- useAiVirtualScroll.ts:73
if (meta && meta.height > 0 && meta.el.isConnected)— height=0 时跳过 minHeight 保护,这正是漏洞 - RO 首次回调 timing 不保证在 IO 之前 → 存在 IO 先于 RO 触发 的竞态窗口
- 一旦某个 item 的 height 被错误地记为 0(或从未被 RO 测量),后续即使重新可见(:62-66 清 minHeight),如果再次不可见而 height 仍为 0 → 再次不设 minHeight → 持续塌陷
🟡 新假设 B [中等概率]:v-else-if 条件渲染导致 Vue 复用 DOM 节点错乱
模板结构:
<div class="ai-msg-slot">
<div v-if="item.kind === 'sep'" ...> <!-- 分隔条 -->
<div v-else-if="shouldRenderMsg(item.key)" ...> <!-- 消息内容 -->
<!-- 无 v-else → 两者都不满足时 slot 内为空 -->
</div>
当 shouldRenderMsg 在 true/false 间快速切换(IO 回调频繁触发时),Vue 的 v-if/v-else-if DOM 复用机制可能在极端情况下产生短暂的 DOM 结构不一致(如旧节点未完全销毁即被复用)。但这通常是瞬态的,不应导致持久重叠。
🟡 新假设 C [中等概率]:markdown v-html 输出含未闭合标签破坏 DOM 结构
虽然 DOMPurify sanitize 应防止此问题,但如果 AI 回复的内容(经 marked 解析后)产生了异常 HTML(如 <div> 未闭合被 Purify 截断),可能导致:
- v-html 注入的 HTML 破坏
.ai-msg-bubble的闭合标签 - 后续兄弟元素被"吞入" bubble 内部
- 多条消息的 DOM 树结构错乱 → 视觉重叠
这与截图现象吻合(代码块内容溢出到相邻消息区域)。
最终优先级排序:
| 优先级 | 假设 | 概率 | 验证方法 |
|---|---|---|---|
| P0 | A: IO 先于 RO 触发 → height=0 → minHeight 跳过 → slot 塌陷 | 高 | DevTools 断点 IO 回调检查 meta.height;或在 :73 前加 console.log |
| P1 | C: v-html DOM 破坏 | 中 | DevTools 检查 .ai-msg-bubble 的 innerHTML 是否有未闭合标签 |
| P2 | B: v-else-if 快速切换 DOM 复用异常 | 低 | 临时改 v-else-if 为 v-else(始终渲染)验证 |
| P3 | 原①: IO 漏 observe | 低→中 | 已修正为"IO 正常但时序竞态" |
- ✅(wf wf_9e13176f+主代核验·commit 8abcd56·防御修复已落地) B-260617-09 [P1] — aichat 消息全量重叠【根因嫌疑=虚拟滚动 IO 与 RO 时序竞态:IO 先于 RO 首次回调触发卸载分支时 meta.height===0 → 不设 minHeight → slot 塌 0 高 → 下条消息上浮重叠。防御修复已落地:useAiVirtualScroll.ts IO 卸载分支(line 77-79)三层 fallback(height>0 实测→offsetHeight→40px)+ registerSentinel 首次挂载(line 110-111)40px 兜底。sentinel 永不塌 0。vue-tsc EXIT 0。待用户实测确认:若重叠消除则根因确认;若仍重叠则 DevTools 验证次 probable(v-html DOM 破坏)或其他根因】 — src/composables/ai/useAiVirtualScroll.ts(:68-82 IO卸载分支 + :104-112 registerSentinel)
🔧 2026-06-17 useAiSend.ts 代码审查(仅走查·未实施)
排查性质(session-role-diagnose-only),对照代码走查
src/composables/ai/useAiSend.ts(456 行)。波2 已实施 UX-05/06/07 + F-01 modelOverride,本次基于当前代码核验(不信 todo 声明:UX-05/06/07 经代码确认一致 ✅)。仅记录待办,未改代码。
- ✅(workflow wfryptv2t·代理D核验并修·2026-06-17·forceMode=true) B-260617-01 [P2] — sendQueuedNow「立即发送」可能退化为入队。代理D独立核验:stopChat 仅 await stop IPC 发出(stop_flag.store),不等后端 loop 跑到 agentic.rs:444/:819 检测点+guard.reset()(generating 才复位),紧接 sendMessage(forceMode=false) 命中 L1 ai_is_generating(后端真值仍 true)→ 入队而非立即发。修:sendQueuedNow forceMode false→true(useAiSend.ts:472),跳 L1 走 doSend→ai_chat_force_send(commands.rs:742-748)原子复位 generating=false 再发,无竞态窗口;stop_flag 让旧 loop 下个检测点退出,force_send stop_flag 同向不冲突;parts 透传保留。vue-tsc EXIT 0。审查发现:sendQueuedNow(:406-411)
await stopChat()仅等 stop IPC 发出,不等后端 loop 退出 /ai_is_generating复位。紧接await sendMessage(spliced)时,后端 stop_flag 已置但 generating 可能仍 true(loop 未跑到 agentic.rs:178 检测点 / guard.reset():188 未执行),sendMessage 命中 L1(:281backendGenerating || streaming) → spliced 被入队而非立即发,与「立即」语义不符。UX-07 条目原标「stop_flag 复位降级可接受」,此审查质疑该结论——降级实为功能不达预期。待核验:ai_is_generating复位时机(stop_flag→loop:178→guard.reset:188 链路延迟);若滞后,sendQueuedNow 应走 forceMode 跳过预检或等 AiCompleted 再发。— src/composables/ai/useAiSend.ts:406-411 - ✅(小修批·2026-06-17·workflow wdqxhw4x1+主代修回填/文案) B-260617-02 [P2] — drainQueue 单条失败致队列卡死 + 错误吞没。drainQueue(:247-251)
void sendMessage(...)fire-and-forget;doSend IPC 失败 throw(:113) 被 void 忽略 → 无 AiCompleted 触发下次 drain → 剩余队列永久卡住 + 用户无错误反馈。修法:drainQueue catch 失败,emit 错误提示 + 决定续发下一条或终止。— src/composables/ai/useAiSend.ts:247-251 - ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-03 [P3] — lang 解析三处重复(DRY)。doSend(:94-97) / regenerate(:151-154) / editMessage(:224-227) 各一份相同
df-ai-language→auto 回落df-language解析。修法:提取resolveLang(): string辅助函数,三处复用。— src/composables/ai/useAiSend.ts - ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-04 [P2] — tryForceSend 失败后消息丢失无提示。tryForceSend(:301-318) force_send 失败 catch 返 false(:316),但队首已 shift(:307) → 消息丢失,调用方是否提示用户未核验。修法:失败回填输入框或 toast(对齐 doSend 失败回填:108-113 模式)。— src/composables/ai/useAiSend.ts:301-318
- ✅(误判销账·2026-06-17·主代独立核验推翻 agent A) B-260617-05 [P2] — modelOverride 切换对话清逻辑缺失(误判)。agent A 只看 useAiConversations.ts 漏 AiChat.vue watch 兜底。主代核验:AiChat.vue:1236-1239 watch activeConversationId 变 → store.modelOverride.value = null 响应式清;newConversation(useAiConversations:33)+ switchConversation(:60)都改 activeConversationId → watch 触发清 store.modelOverride(= useAiSend 模块 modelOverride,composable return 共享);切对话入口全在 AiChat.vue(L46/89/155/1163/2600/2607)→ watch 全覆盖。F-01 阶段6 已实施,非 bug。注释(:50-51)明确「切换对话前端应同步清 modelOverride.value=null」,但本文件无清逻辑(切换在 useAiConversations.ts)。若未清,切对话后 modelOverride 残留 → 新对话误用旧模型。待核验 useAiConversations.ts switchConversation/newConversation 是否清;未清则补。— src/composables/ai/useAiSend.ts:53 + src/composables/ai/useAiConversations.ts
⚪ 可选优化(未单列 todo):① cancelQueued(:372)/editQueued(:390)/sendQueuedNow(:407) 越界检查不一致——按「确定性>防御」风格 UI 调用方必合法,三处可统一(都删或都留)② doSend/regenerate/editMessage 的 streaming+watchdog+占位前置模式重复,可提取
beginStream(),但回滚差异大收益中等 ③_pendingApprovalIds(:62) 无超时清理——approveToolCall 卡在 await 永不返回时 id 永驻 Set(边缘态,watchdog 不清此 Set)。
🔧 任务模块全栈核对(2026-06-16 · Explore 代理并行取证)
来源:任务模块问题分析-2026-06-16.md。原清单 18 项核对:真 bug 7 项 + #8 路由漏注册去重(B-260616-09 已存在)、增强 5 项、假/部分假 3 项、去重 3 项。详见文档速览矩阵。
- ✅(第㉗批 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
- ✅(第㉗批 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
- ✅(第㉔批 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
- ✅(第㉔批 2026-06-16) B-260616-15 [P1] — update_task priority 无值域校验。task.rs:105-115 加 priority 校验(parse::() ∈ 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
- ✅(第㉔批 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
- ✅(第㉔批 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
- ✅(第㉕批 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)暴露的架构决策点。定了才能实施推进链,与模型能力无关。标注关联 todo。
- D-260616-01 状态枚举方向 ✅ 决策:① 前端对齐后端 7 态(2026-06-16 用户拍板)。后端 7 态不动,前端 constants/i18n/Tasks 筛选器/Dashboard 扩展至 7 态。理由:推进链阶段 2/3 需 InReview(AI 自审)/Testing(测试闸门)/Blocked(退回)三态,删则自断推进链。【关联 B-260616-12】
- D-260616-02 任务软删除 ✅ 决策:① 加软删除对标 projects(UI 缓做)(2026-06-16 用户拍板)。先加
deleted_at列 +list_active过滤 +restoreAPI,恢复 UI 后续按需补。抄 projects 全套模板(V11 迁移 + ProjectRepo 方法)。【关联 B-260616-13】 - 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 层完成。
- D-260616-04 推进路径取舍 ✅ 决策:阶段1先行(2026-06-16 用户拍板)。先做阶段1骨架(advance_task 状态机 + status 收口 + 手动推进闭环),不等工作流联动/AI 执行。基础 + P0 安全优先,后续阶段按需迭代叠加。
🗺️ 任务推进链实施路径(2026-06-16 规划·供其他会话读取)
详见 任务推进链实施路径-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。
-
✅(第㉘批 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。 -
✅(batch32-35·2026-06-16·全部子项落地) F-260616-06 阶段2 工作流联动(task_id+回调+DAG模板)— 全部落地(④-1/②-1/②-5 batch32 + ②-2/②-3/②-4/②-6 batch33 + ①-1/①-3/B-41 batch35),实施路径见 推进链阶段2实施路径-2026-06-16.md:
- ✅(batch32·2026-06-16·workflow wn4gudnz8) ④-1 [架构前置·阻塞] DagExecutor config 下沉语义修复 —
executor.rs:99-107NodeContext.config 用 initial_config 覆盖 NodeDef.config(节点级配置被忽略,TaskAdvanceNode 读不到 ctx.config.task_id)。修法:NodeContext.config = deep_merge(node_def_config, initial_config)节点级覆盖全局级。文件锁:crates/df-workflow/src/{dag.rs,executor.rs,registry.rs}+src-tauri/src/commands/workflow.rs - ✅(batch32·2026-06-16·workflow) ②-1 build_registry 注入 Arc + 注册 TaskAdvanceNode —
build_registry(db)改签名 + move 闭包 + init 调用传 db.clone()。文件锁:src-tauri/src/state.rs - ✅(batch33·2026-06-16·workflow w98anf4y5) ②-2 run_workflow IPC 加 task_id + target_status 参数 — 签名扩展 + WorkflowRecord.task_id 填入 + spawn move 捕获。文件锁:
src-tauri/src/commands/workflow.rs+src/api/workflow.ts+src/stores/project/workflow.ts - ✅(batch33·2026-06-16·workflow) ②-3 完成回调 WorkflowCompleted→advance_task — spawn 闭包内 executor.run Ok 后,task_id+target_status 都 Some 时调 advance_task_atomic,失败降级 warn 不回滚。文件锁:
src-tauri/src/commands/workflow.rs - ✅(batch33·2026-06-16·workflow) ②-4 失败回调(退回语义) — 工作流 failed 时按 target_status 推算退回态调 advance。文件锁:
src-tauri/src/commands/workflow.rs - ✅(batch32·2026-06-16·workflow) ②-5 HumanNode reject 语义化 — options 含 reject/block 时返 Err(非 Ok),使工作流 failed 触发退回。文件锁:
crates/df-nodes/src/human_node.rs - ✅(batch33·2026-06-16·workflow) ②-6 DAG 模板(task_workflow_templates) — df-nodes 新模块导出 template_for(target_status)->DagDef,5 前向边+退回。文件锁:
crates/df-nodes/src/task_workflow_templates.rs(新) - ✅(batch35·2026-06-16·workflow w17duic3f) ①-1 前端 TaskDetail 工作流推进按钮 — 与手动 advance 并存,调 workflowApi.run+监听进度。含 B-41 进度内嵌:onEvent 按 exec_id 过滤 dispatch node_started/completed/workflow_completed/failed + 独立 wfAdvancing loading 与手动 advancing 互含 disabled。后端补丁:workflow.rs run_workflow 空dag选模板(dag.nodes 非空→原 / 空+target→template_for / 空+无target→原路径,向后兼容)。文件锁:
src/views/TaskDetail.vue+src/api/workflow.ts+src/stores/project/workflow.ts+src-tauri/src/commands/workflow.rs - ✅(batch35·2026-06-16·workflow) ①-3 i18n 文案 —
src/i18n/{zh-CN,en}/taskDetail.ts(taskDetail.* 命名空间,8 key 对称:workflowAdvanceTitle/Advancing/AdvanceFailed{msg}/StepRunning{node}/StepsProgress{done}{total}/CompletedHint/FailedHint)。注:todo 原路径locales/tasks.ts不准,实为i18n/taskDetail.ts - 实施顺序:④-1→②-1→②-5→②-2→②-3→②-4→②-6→①-1/①-3。最小里程碑(④-1+②-1+②-2+②-3):run_workflow 单 task_advance 节点 DAG 端到端推进 todo→in_progress
- ✅(batch32·2026-06-16·workflow wn4gudnz8) ④-1 [架构前置·阻塞] DagExecutor config 下沉语义修复 —
-
✅ F-260616-07 阶段3 AI 执行闭环 — F-03 收口三件 ✅ batch64 + AiNode 自审闸门 ✅ batch65(本批)
- ✅(batch64·2026-06-16·workflow wii1u1lnm) F-03 收口三件 — ①advance_task 注册 AI 工具(tool_registry.rs:395,handler L407 调
df_nodes::task_advance_node::advance_task_atomic与 IPCcommands::task::advance_task:165同源) ②run_workflow 注册 AI 工具(:428,handler 架构约束无 AppHandle/State 报错引导走 IPC,ToolDefinition+审批文案 L1260-1261/1286-1287 注册让 LLM 可产出 tool_call) ③update_task handler guard 拒 status(L374field=="status"bail,schema 通用 field/value 故 guard 拦非 schema 改)+ df-storage tasks 白名单移 status+review_rounds(crud.rs:331-347,advance_status_atomic CAS L848 独立路径不经白名单)。同步落地防 AI 工具行为不一致。主代独立核查全过:cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试(含新update_field_rejects_tasks_status)。文件锁:tool_registry.rs + crud.rs + project_soft_delete.rs。审查登记 CR-260616-41(待审查.md 当前队列) - ✅ AiNode 自审闸门(阶段3 核心,决策a) — 阶段1迁移+方案✅(539b5ed+25409e5) + 阶段2②③④⑤✅(c10adaf+741b0b9) + P0 secret下沉+注入链✅(766f235+84a6fc4)。⑥联调代码层解锁(待实测 tauri dev)。cargo df-nodes 79 passed + workspace EXIT 0 + vue-tsc EXIT 0
- ✅(
539b5ed) ①df-storage: TaskRecord 加output_json: Option<String>+ V17 迁移(幂等补列) + crud 白名单+SELECT/INSERT/UPDATE 全链路 - ✅(
c10adaf) ②df-nodes ai_node.rs: AiNode 持 db(Arc)+execute 后 config[task_id] 存在则 update_field 落 output_json - ✅(
c10adaf) ③df-nodes ai_node.rs: AiSelfReviewNode 独立节点(prompt 四维度+parse_review_json 兜底 verdict=unknown+写回 review 子字段)+ state.rs 注册 + testing 模板 ai→ai_self_review - ✅(
84a6fc4) ④df-nodes ai_node.rs: resolve_provider 注入链(provider_id 优先/老明文兼容 warn/空兜底 is_default)+ execute 经 df_storage::secret 解析 api_key 不进 config(FR-S1)+ schema required [provider_id]。run_workflow 注入 task_id 到 NodeContext(④-1 deep_merge 就绪),模板 config 空走 AiNode 兜底 - ✅(
c10adaf) human_review: 经 DAG inputs["ai_self_review"].data 透传 verdict/summary(HumanNode 零改动,方案④更优路径) - ✅(
741b0b9) ⑤前端: TaskDetail.vue 展示 output_json(parsedOutput+review 红绿标+产出 markdown)+ types.ts+i18n 双语 - ⑥端到端联调 — 代码层完成(P0 secret 下沉+注入链✅ 766f235+84a6fc4),实测类待用户(tauri dev 验证 testing 模板 ai_self_review→human_review 闭环)
- ✅(
- ✅(batch65·2026-06-17·workflow) AiNode 自审闸门(阶段3 核心) —
verdict=fail作 DAG 节点闸门阻断下游,对齐工作流失败语义。方案 A(AiNode 内部门控)胜出:新增纯函数gate_should_block(gate, verdict)(ai_node.rs:426gate && verdict=="fail") + execute 末尾 gate_enabled(config["gate"]==true,默认 false 阶段2 兼容)&& fail →return Err("AI 自审闸门未通过(verdict=fail): ...")复用 executor first_err(:147-178)中止后续层 → 工作流 failed → ②-4 回调退回 in_review。verdict=unknown/pass 放行(unknown 保人定权,阶段2 保守语义不变)。落库在前(update_field output_json),故 fail 时审查痕迹不丢。选型对比(方案 B DAG edges 条件 + ConditionEngine:依赖 T-260614-11 暂缓 + executor 当前完全不评估 edge.condition(topological_layers 无条件收录所有边)→ 拆波;方案 C executor 闸门检查钩子:改 DagExecutor 核心循环牵动所有节点,不符「最简不破坏」)。testing 模板 ai_self_review 节点 config 加"gate": true(task_workflow_templates.rs:67)启用。schema 加 gate 布尔字段。自验:cargo check -p df-nodes -p df-workflow EXIT 0 + cargo test df-nodes 82 passed(79→82,+3 gate 单测)+ df-workflow 23 passed。文件锁:crates/df-nodes/src/{ai_node.rs,task_workflow_templates.rs}。审查登记 CR-260616-44(待审查.md 当前队列) - run_workflow handler 注入 AppState(当前报错引导走 IPC) — 后续批扩展 build_ai_tool_registry 签名注入 AppState 句柄让 AI 直驱
- ✅(batch64·2026-06-16·workflow wii1u1lnm) F-03 收口三件 — ①advance_task 注册 AI 工具(tool_registry.rs:395,handler L407 调
-
⏸️(待决策.md已决b暂缓·2026-06-16) F-260616-08 阶段4 Git 集成(kind+git闸门+worktree)
-
✅ CR-260616-01 代码审查完成 → 审查登记已迁 待审查.md(职责分离:审查队列独立,不进 todo)。结论 🔴0 🟡6 ⚪4 质量优,8 维度全过。待修项 CR-01-A~I 见下方推进区。
审查发现待修项(2026-06-16 · CR-260616-01):
- ✅(cr01-fixes·wu4r8ejcp) CR-01-A [P1] TaskDetail.vue 补
.status-testing(橙)/.status-blocked(红 danger 边框)CSS;Tasks.vue 已有/Dashboard 不用此体系无需补 - ✅(cr01-fixes·wu4r8ejcp) CR-01-B [P1] crud.rs advance_status_atomic 两分支 CAS SQL 加
AND deleted_at IS NULL,回收站任务 CAS 必败返 None(D-02 软删收口) - ✅(cr01-fixes·wu4r8ejcp) CR-01-C [P2] task_advance_node.rs:50 错误信息改
ALL_STATES.join("/")DRY(运行时文案不变) - ✅(batch30·2026-06-16) CR-01-D [P2] task_advance_node.rs:62 同态拒绝(from==to)/非法转换(can_transition false)错误区分(现合并
InvalidState,前端无法分辨)— ②类留(需改错误枚举判断) - ✅(cr01-fixes·wu4r8ejcp) CR-01-E [P2] src-tauri/src/state.rs:240 build_registry 补注释「TaskAdvanceNode 未注册(阶段2联动时加),勿删」
- ✅(cr01-fixes·wu4r8ejcp) CR-01-F [P2] crud.rs 白名单注释补 review_rounds 专用路径(仅 advance_status_atomic,防误补破坏收口)
- ✅(batch30·2026-06-16) CR-01-G [P3] TaskDetail.vue:218 advance 失败错误走 i18n 分级(
e.toString()非空直显后端中文,en locale 也中文)— ②类留(需 i18n 分级改造) - ✅(cr01-fixes·wu4r8ejcp) CR-01-H [P3] task_advance_node.rs 测试改名
concurrent_cas_change_detected→advance_succeeds_after_prior_status_change(反映实际测正常推进) - ✅(cr01-fixes·wu4r8ejcp) CR-01-I [P3] i18n
taskDetail.advancing接文案(推进按钮 advancing 时显示「推进中...」,冗余 key 转实引用)
修复核查(主代理独立·2026-06-16):7 项真实落地(Read+git diff+cargo check --workspace exit 0.83s+vue-tsc 0 核实)。注意:验证 agent CR-01-E 证据张冠李戴(报 df-workflow/state.rs,实改 src-tauri/state.rs),改动正确但证据幻觉——验证 agent 可信度打折,主代理核查兜底。D/G ②类留。
审查发现待修项(2026-06-16 · CR-08/10/13,均 med 非阻断,详见 待审查.md 对应归档):
- ✅(batch50·主代理决策:接受现状降级 P2) CR-10-A [med] ai_regenerate 并发竞态窗口 → 统一并发模型(锁内变更+锁外 spawn,ai_regenerate/ai_chat_send/ai_chat_force_send 同款),窗口有兜底(stop_flag 置位 loop 退出 + generating 拦截 send),med 非阻断。统一并发改造复杂(原子标记预占+3 入口),ROI 不明非优先,降级观察/架构改进候选
- ✅(batch50·主代理核查:无效) CR-10-B [med] commands.rs:56/59 save 与 emit 时序倒置 → 不成立:ai_regenerate 当前代码 save L94 锁外 spawn L105 前(时序正确),无 emit(L56/59 是 pop 后错误处理),审查锚定行号漂移
- ✅(batch55·2026-06-16·workflow wem402ncv+主代核查) CR-13-O1 [产品决策→已决b实施] regression_target("in_progress")→None 对齐状态机。workflow.rs:51
in_progress => None(替原 Some("todo"))+task_advance_node.rs 镜像同步+4 测试断言更新(含集成测改 None-skip 场景)。主代核验:git diff 2 文件 26+/24- 双处一致+注释对齐决策b理由+cargo test -p df-nodes 67/67 pass。— src-tauri/src/commands/workflow.rs + crates/df-nodes/src/task_advance_node.rs - ✅(batch50·2026-06-16·workflow w6nqio4o5) CR-08-O1 [med·非阻断] blocked 态推进按钮语义偏差 → 已修复(WF_EXCLUDED_FROM={'blocked'} + wfAdvanceAction computed 守卫,blocked 不显示工作流推进按钮,其他态不受影响,不动 ADVANCE_MAP)
- ✅(batch50·2026-06-16·workflow w6nqio4o5) CR-15-1 [med]
crates/df-execute/src/shell.rs:60-79缺.kill_on_drop(true)→ 已修复(cmd.kill_on_drop(true) 三类型覆盖 + 注释配合 tokio::time::timeout 防孤儿,文案「进程已终止」名副其实) - ✅(主代理验证:不成立·workspace cargo check exit 0) 🔴 CR-23-1 [high·紧急·阻断构建]
crates/df-ai/src/context.rs:491E0594 → 不成立:审查代理跑 batch51 中间态 filter 版(EXIT 101),batch51 最终改 for 循环(EXIT 0,df-ai + workspace 双验证,代理自报属实)。context.rs:488 当前是for t in iter_mut()单层引用可写 status,无 filter 闭包。审查锚定滞后(跑 batch51 中间态)— CR-260616-23 归档 - ✅(主代理修·CSS :has item 左边线) CR-22-1 [med·UX]
AiChat.vue已置顶非 hover 不可见 → 已修复(.ai-conv-item:has(.ai-conv-item-act--pinned){ box-shadow: inset 2px 0 0 accent }左边线强调,不破坏 hover 浮出 toggle,原方案图钉常驻与 actions display:none 矛盾,改 :has item 左线最简)— CR-260616-22 归档 - ✅(CR-260616-35 复审 PASS·主线程预核验) CR-34-1 [med] useAiPanel.ts:102 loadProviders 守卫
if(!state.activeProvider)只查 null 不查有效性 — localStorage savedProvider 指向已删除 provider 时 activeProvider 残留无效 id → cycleProvider:1185 findIndex=-1 跳过 ps[0] + 发消息消费方用无效 id。修法:守卫加|| !list.some(p=>p.id===state.activeProvider)残留无效时回落 DB is_default — CR-260616-34 归档 - ✅(CR-260616-35 复审 PASS·agent cargo 6+20 passed) CR-30-1 [med] agentic.rs:298 退避
1<<retry_attempt纯指数无 jitter/无总预算/一律重试(含 MidStream),三处注释(agentic.rs:37/commands.rs:1106/state.rs:185)误称「对齐 retry.rs(1s→2s→4s+jitter)」。retry.rs:67-78 实有 ±20% jitter+30s 预算+4xx 分类(:102-104 Fatal 立即放弃)。修法:agentic 加 jitter+总预算+错误分类真对齐,或删注释改「独立简化策略」— CR-260616-30 归档 - ✅(CR-260616-35 复审 PASS·agent cargo 6+20 passed) CR-30-2 [med] AiStreamRetry 死事件 + MidStream 重试文本混乱 — 后端 emit(agentic.rs:310)+types.ts:214 variant 有,但 useAiEvents.ts switch 无 case 穿透忽略;MidStream 失败重试时已 emit AiTextDelta 累积 currentText,重试新 delta 追加→文本重复。修法:useAiEvents 补 case+重试前清 currentText;或后端 MidStream 不重试只重试 Init(对齐决策a)— CR-260616-30 归档
- ✅(cr01-fixes·wu4r8ejcp) CR-01-A [P1] TaskDetail.vue 补
⏸️ 待用户授权/确认(巡检悬挂项·2026-06-16)
巡检推进中浮现的、需用户拍板才能继续的操作授权/范围确认项(非架构方向决策,见上节 D-*)。巡检每轮不再重复问,看此处标注。
- DEC-260616-01 文件名 git mv 06-15→06-16 ❌ 否决(2026-06-16 用户确认):21 个 docs 文件名日期
-2026-06-15.md均正确(06-15 创建),无需修改。原前提"今天误标 06-15"不成立。 - 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 流式核心改造授权 ✅ 已授权并实施完成(2026-06-16):CR-04 splitBlocks→marked.lexer 已完成(batch57,见 CR-260615-04);CR-06 loadMarkdown 重算已确认实施(CR-260615-06 ✅)。两项流式核心改造均已落地并通过 vue-tsc + cargo check 零错误验证。
🔴 anthropic_compat 多轮工具调用(2026-06-14 排查·会话卡死根因)
来源:本排查会话定位另一 Claude Code 会话(经 GLM anthropic 端点)「卡死后再也对话不了、一直返回同一 500」现象,顺带暴露 devflow 同构缺陷。会话卡死机制:畸形 tool_result 写入 append-only 历史 → 后续每轮把毒历史原样重发 → 每次触发同一 500 → 死循环(救援只能清历史/新会话)。GLM 端报
[500]['ClaudeContentBlockToolResult' object has no attribute 'id']。
- 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。 - B-260614-AC2 ✅ wave4(
36d68dd) — [P2·防御](入站 tool_use 缺 id 同步跳过/流式占位 tool_missing_{idx}+warn) tool_use id 解析无兜底 —anthropic_compat.rs:167LLM 返回 tool_use 块缺id时 draft.id 为空 → 后续 tool_result 带空 id → 回传 500。修法:id 缺失时跳过该 tool_use 或生成占位 + warn。 - B-260614-AC3 ✅ wave(2026-06-16核查闭环,待commit) —
历史中毒无自愈context.rssanitize_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 第八章)
2026-06-14 aichat 模块代码审查产出,原仅留 memory 指针未回流看板,今补入。去重:S-02 审批可见性 ⊂ AR-3(修 AR-3 卡片可读性直接缓解"看不到审批批什么");B-05 detach = AR-M5 同类(跨窗口 state 隔离,aichat 审查描述更深)。
P0(用户可感硬伤)
- AR-1
流式 Markdown 全量重解析✅ 退役(ARC-260615-08 自研块级 memo 取代,2026-06-16):splitBlocks 块级 memo O(末块)+rAF 节流 取代全量 marked.parse+sanitize,流式全程有格式不掉帧。详见 流式渲染调研 §5(⬆️ 原:renderMd 缓存 key=完整文本,每 delta 全量 marked.parse+sanitize,长回复主线程阻塞掉帧,AiChat.vue:343-354) - AR-2
审批态新建对话卡死✅ WF-F 完成(ai_conversation_create 加 generating 守卫,位于 clear 前,对齐 switch:433 写法)(commit057a212) - AR-3 ✅
审批卡片信息不足(删除等操作只返回数据 ID)(commit36d68dd完整修复):后端audit.rs:45-127build_approval_reason + resolve_project_label(9 工具 reason 拼项目名,fallback「(项目已不存在, id=)」:52);前端ToolCard.vue:296-320PROJECT_ID_TOOL_ARG 映射 + toolArgsEntries 特化 id/project_id 回显项目名。原两个剩余问题(①前端裸显 id ②fallback 裸 id)均已修。wave(2026-06-16,待commit)补toolDisplayNameCRUD case 7 项(delete/restore/purge/update/create_task/create_project)+i18n 10 key 中英对称。 - AR-4
create_project 双审双 API✅ WF-F 完成(schema 加 path/stack + handler 有 path 时合并绑定 spawn_blocking 探测栈,消除二次 bind_directory;TODO 标注可抽公共绑定函数)(commit057a212)
P1
- AR-5
审批态 stop 无兜底✅ Wave3 完成(stopChat 本地先复位 streaming + clearStreamWatchdog,防审批态看门狗已 clear + AiCompleted 竞态丢失卡死)(commit9e2aeff) - AR-6 ✅ wave8(
f82dd8b)已落地 Low 失败语义(audit.rs:312 emit AiToolCallCompleted 非 AiError,错误回填 tool_result 让 LLM 自处理;todo 原引 agentic.rs:195 过时,df-ai 重构后 stream 在 provider.rs/anthropic_compat.rs)(emit AiError 置 streaming=false 但 agentic loop 续跑,残留文本 flush 又"完成",状态紊乱)— audit.rs + agentic.rs:195 - 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 正确抓到 gap(commit 9e2aeff)
P2
- ✅(batch39·2026-06-16·workflow ww4wb66br) AR-8 delta 节流+滚动 — 前端已存在(onContentChange/isNearBottom 80px 阈值等效 userScrolledUp + showBackToBottom 回底按钮),后端合批负 ROI 不改(rAF 节流链 ARC-08 已吸收重 parse,加合批破 heartbeat 30s 语义)。B-53 snap 优化同批落地
- AR-9
friendlyError 硬编码中文✅ Wave3 完成(friendlyError 全走 i18n.global.t + zh/en 双语补 4 key;TS2589 用 as any 规避 vue-i18n 深度泛型)(commit9e2aeff) - AR-10
想法→灵感迁移残留✅ 已统一(13 文件批量:i18n zh-CN + 后端错误 + LLM 描述/提示词 + store toast;en 待定 Ideas/Idea、docs 注释低优先略)(commit65c475b) - AR-11 ✅(定时走查第3轮,2026-06-16,dc27e79) —
数据变更联动刷新(推荐方案A 后端 emit + store 监听)— 跨层 — 详见 审查第五章 — 勘察完成(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第3轮(2026-06-16):listener 已 attach ✅ —
dc27e79App.vue:218onMountedawait startDataChangedListener()+ :258 onUnmounted stop。AR-11 功能闭环(后端 emit df-data-changed + 前端 listen 按 entity 调 load)。详见 定时走查-2026-06-15-第3轮.md
🔴 流式响应中断误报排查(2026-06-16)
现象:AI 工具(write_file 等)执行成功(文件真写入 14.8KB),前端却弹「⚠ 响应中断(长时间无数据流)」误报,用户误以为失败重发。根因(架构层,非偶发):工具执行后 agent loop 进入下一轮 LLM 请求,等首 chunk 的静默期无心跳——后端
stream_recv.rs:113idle timeout 用tokio::time::timeout(120s, stream.next())被动等 chunk,静默期不发任何事件;前端useAiEvents.ts:115watchdog 仅靠事件 reset,130s 无事件 →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:139stream_llm 返回 →:202process_tool_calls(快)→ loop 下轮 stream_llm 发新请求 → 静默等首 chunk。
- B-260615-02 ✅(批1,2026-06-16) — [P1 体验] 流式静默期心跳(治本)。修法:
stream_recv.rs:113tokio::time::timeout(STREAM_IDLE_TIMEOUT, stream.next())→tokio::select!,加heartbeat.tick()(30s)分支 emitAiChatEvent::AiHeartbeat { conversation_id };AiChatEvent枚举(ai/mod.rs)+api/types.ts加 variant;前端useAiEvents.ts:115reset 条件已自动覆盖新事件类型(零改动)。真断连时 120s 无 chunk 仍 emit AiError(现有逻辑保留)— stream_recv.rs:113 + src-tauri/src/commands/ai/mod.rs AiChatEvent + src/api/types.ts - B-260615-03 ✅(批4,2026-06-16) — [P2 治标]
onStreamTimeout文案区分:触发时检查最后是否有completed工具调用,有 →「工具已执行完成,后续回复中断,可点继续」;无 → 原「响应中断」— src/composables/ai/useAiStream.ts:21-33
🟠 流式可靠性链路隐患核对(2026-06-16)
系统性精读流式链路(stream_recv.rs / useAiSend.ts / agentic.rs / audit.rs / secret.rs)+ 1 Explore 代理广扫。去重代理 10 条臆测/设计误判(
_startPromise finally伪竞态 /stopChat本地复位=AR-5 设计 /sanitize仅持久化视图=B-260614-AC3 设计 /join_all并行非阻塞 /resolve_provider_secretDB 优先=FR-S1 兼容老库设计 单测:114 锁定),确认 4 条真隐患(心跳见 B-260615-02 不重复)。注:B-260615-04 与 B-260615-02 同改stream.next()→select!,可一次性合并实施。
- B-260615-04 ✅(批1,2026-06-16) — [P1] stop 响应延迟最差 120s。
stream_recv.rs:107stop_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 - B-260615-05 ✅(批1,2026-06-16) — [P1] 流尽 + 空内容 + 无 finished 静默成功。
stream_recv.rs:178if !finished_received && (!full_text.is_empty() || !tool_calls_acc.is_empty())才报错;空内容无 finished 不报错返回Some(空)→agentic.rs:197!has_tool_callsbreak → 正常 emit AiCompleted,用户看空回复无错误提示。修法:流尽未收 finished 一律判异常 emit AiError(不区分内容空否),不静默成功 — stream_recv.rs:177-186 - B-260615-06 ✅(批4,2026-06-16) — [P2] sendMessage IPC 失败未清 watchdog。
useAiSend.ts:70-76catch 回滚 streaming + 移除空气泡,但 :61 启动的 watchdog 未clearStreamWatchdog();130s 后onStreamTimeout触发 push 假错误消息(streaming 已 false 无状态危害,但错误气泡误导用户)。修法:catch 补clearStreamWatchdog()— useAiSend.ts:70-76 - 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 - 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:261should_continue=false(generating 被复位 / pending_approvals 非空)静默 return ②:263-266get_active_provider Err(_) => return静默无事件 ③spawn 后 stream_llm 空回复静默成功(见 B-260615-05)/ LLM 不响应 120s idle→AiError(会报错非静默)。watchdog 兜底延迟:审批态 watchdog 被 clear(useAiEvents.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。已去重:条件引擎/路径 canonicalize/localStorage 已在本看板他处记录;
do_promote误判已澄清(IPC 层真建项目,crate 留 TODO)。
P0 — 安全
- 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 - FR-S2
read_file TOCTOU + write 无限制✅ read 单次 File::open 取 metadata+read 消 TOCTOU + write 加 1MB 上限(commit5367f19) - FR-S3
approve decision 无校验✅ 加 decision 非空校验(防 "" 透传;HumanNode options 非空时还校验 ∈ options)(commit698a874) - 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
- 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 — 体验/竞态
- FR-R1
switchConversation 无切换 token✅ 加 _latestSwitchId 丢弃过期响应(commit698a874) + wave(2026-06-16,待commit)补第二 await(pendingToolCalls)后二次比对useAiConversations.ts:108,防 A→B 快切用 A 的 pending 覆写 B - FR-R2 — [降级存疑] 看门狗主/分离窗口互踩 — 审批态已 clearStreamWatchdog(useAiEvents:143)+分离窗口独立 realm 不共享 state,"互踩"前提不成立,评估维持
- FR-R3
liveEvents 无限增长✅ push 后限长 200 条(commit8dbe3d2) - FR-C1
formattedEvents 时间漂移✅ 事件入数组固 _ts(project.ts push + ProjectDetail 用 _ts)(commitcf18678) - FR-C2
net_sentiment 矛盾✅ 统一三档(模板>0/<0/===0 + sentimentClass 同源 + i18n neutral)(commitcf18678)
P2 — 其余(见审查报告 §2-6)
- FR-S4 ✅ wave4(
36d68dd) SKILL.md 注入加头尾成对标注(仅供 AI 参考/非用户消息/技能说明结束) - FR-S5 — [降级文档] ai_approve 无对话归属校验属实,但 UI 隔离+restore 只载当前对话历史限制实际触发,本地单机低危,降级为文档说明(审批不严格按对话隔离,设计取舍)
- FR-R4 ✅ wave4(
36d68dd) complete() 加 60s 单请求 timeout + is_timeout 中文错误(不影响 stream) - FR-C3
Settings timer 泄漏✅ onUnmounted 清全部三个 timer(commitcf18678) - FR-C4
MIGRATION_VERSION 死常量✅ 删(零代码引用,run() if 链自管版本)(commitcf18678) - FR-C5
AiConversationDetail 缺 readonly✅ 加 readonly?: boolean(commitcf18678) - FR-D1/D2/D4/D5/R5 ✅ wave5/6 部分完成(4a95f6a/4b5f096):dag O(V+E) 建 adjacency 索引 / search_vector 显式 14 列 / replace_tool_result_content 反向 rposition / useConfirm 抽 4 视图 / 前端 findToolCall 反向遍历
- [
] FR-P1P6 剩余性能项 —单连接 Mutex✅(batch29·2026-06-16 评估非瓶颈:Tauri 本地单用户+WAL 写本串行化,r2d2 池收益近零属过度优化,保持现状) +migrations if 链✅(batch29·2026-06-16 if 链→step 数组循环,零行为变更,cargo test -p df-storage 11 集成+迁移路径全过) +tool_registry truncate(50)+注释散落提常量✅第⑯批销账 06-15 抽 MAX_LIST_RESULTS 常量 4 处 — FR-D3 三子项全闭环,见报告 §5-6 - FR-D6 ✅
任务工具集不完整(缺 delete_task/update_task)(commit36d68dd补全):tool_registry.rs 现有 create_task(:241) + update_task(:265) + delete_task(:287,硬删对齐 commands::task::delete_task,注释「清理孤儿任务时务必用本工具,不要误用 delete_project」) + list_tasks(:140) 四工具齐全。 - FR-D7 ✅ wave4(
36d68dd) 抽bind_dir_to_project(repo, id, path, stack_opt),create_project/bind_directory 共用,删原 :171 TODO;主代理核查补回 create_project 响应 stack 字段 - FR-D8 ✅ wave4(
36d68dd) create_idea schema 补 priority(可选 integer) + 魔法数字默认值注释(idea=1/task=2 与 IPC default_priority 对齐)
🔴 近期改动审查待修项(2026-06-16)
工作区 FR-S1/S7/S8 + 近 5 提交审查(主代理 + 2 子代理并行),详见 近期改动代码审查-2026-06-15.md。共 🔴1 🟡7 ⚪5。 注:① 推翻 todo:74 FR-S1「完全修」——密钥迁移/读取/写入闭环全对,但删除路径漏清 keyring(secret.rs:44 有 delete_provider_secret,ai_delete_provider 未调用)。
P1 — 安全闭环
- CR-260615-01 ✅ wave(2026-06-16,待commit) —
删 provider 漏清 keyringcommands.rs:394-399 DB 删后调delete_provider_secret+ warn 不阻断(keyring entry 永久残留,同用户进程可读;同 id 复用旧密钥复活)— commands.rs:390-401 — 修法:删 DB 后调delete_provider_secret(失败仅 warn 不阻断,DB 已删则残留 keyring 无消费方)
P2 — DRY/收口
- 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:45resolve_project_label已用 repo.get_by_id O(1) 查询,反模式不存在);audit.rs Err 误报 / openai source 丢失 / ToolCard id 类型归一(行为变更或前端,不在本批)
P2 — 块级 memo 实施走查(ARC-260615-08,2026-06-16)
- CR-260615-04 ✅(batch57·2026-06-16·workflow w4h5ygko1+主代核查,vue-tsc 0err+cargo check 0err) —
splitBlocks 手写正则切块改已完成改造,AiChat.vue splitBlocks 函数已从手写正则改为 marked.lexer() — AiChat.vue splitBlocks — 详见 自研块级memo流式渲染审查-2026-06-15.md ①marked.lexer() - CR-260615-05 ✅ parseBlock DRY (第⑩批 2026-06-16) —
parseBlock/parseBlockNoCache DRYparseBlock 内部改调 parseBlockNoCache 去重(原 _purify.sanitize(_marked.parse()) 两处重复收敛为一处),行为零变化 — AiChat.vue - 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 - 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
🔵 全局代码 review 待修项(2026-06-16)
7 维度并行深入扫(DRY/架构/潜在bug/简洁性/安全/AI可靠/工作流引擎),详见 全局代码review-2026-06-15.md。 P1 可执行 6 项 (R-P1-1
6) + P2 可执行 13 项 (R-P2-113) 已全闭环(批1/2/3 主代理独立核查 cargo workspace exit 0 + 5 crate test 共 126 passed)。闭环明细见全局review-2026-06-15.md §推进状态(批1=4 项 R-P1-2/3/5/6 + 批2=2 项 R-P1-1/4 + 批3=13 项 R-P2-1~13 四域 A/B/C/D)。下为需设计待立项项(R-PD-3 条件引擎去重 T-260614-11)。
P1 需设计(进设计文档)
- R-PD-1 ✅(批2,2026-06-16) — [P1 security] 编辑 provider 空 api_key 默默清 DB 明文致密钥永久丢失(未迁移态 keyring 空 + DB 非空时改 name/base_url 触发)—
commands.rsai_save_provider:329-355 + crud.rs INSERT OR REPLACE 全字段覆盖 — 修法:空 api_key 时确认 keyring 有/DB 有再清,keyring 无且 DB 非空先即时迁移补密钥 — source:全局review §P1需设计 - 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 需设计
- 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 - R-PD-5 ✅(P0批,2026-06-16,待commit) —
approve IPC 校验 decision∈options加 options:Vec 参数(前端从 HumanApprovalRequest 事件透传,IPC 无法访问节点 config)+校验 options 非空且 decision∉options→Err「审批决策非法」(规则同 HumanNode 下游兜底);✅闭环(wcvigw3z4):前端 stores/project.ts:259 补传 options(state.pendingApproval.options ?? [] 从 HumanApprovalRequest 事件 payload 取),触发后端 decision∈options 校验 — workflow.rs - ✅(第㉗批 2026-06-16·已
c7386a1) R-PD-6 — AiSession 单例:try_continue 读 active_conversation_id 竞态(靠 switch readonly 间接保护,脆弱耦合)—agentic.rs:255-291 从 pending_approvals 取 conversation_id 解耦 - 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 映射层(对抗分析否决 2026-06-16, 见 :537, 5个IPC手动对齐可接受) - 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 复用 - ✅(主代理·2026-06-16) R-PD-10 — crud.rs 110 处
.map_err(|e| Error::Storage(e.to_string()))统一 df-storage 局部storage_errhelper(原 todo「85处 e.to_string」过时,实际 crud.rs 110 处 Error::Storage 包装;commands 层 err_str 早已有 mod.rs:23)。cargo check -p df-storage exit 0 - 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比较存库路径,已注明须同步) - 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 协同) - R-PD-13 ✅(P0批,2026-06-16,待commit) —
Lagged 静默丢事件最小兜底:单行 warn 升级带 execution_id+lagged 字段结构化 warn+注释(broadcast 不暴露被丢事件类型/关键终态事件丢失致 finished 不触发循环不 break 前端永久收不到结束/依赖 DB 轮询兜底)+三后续方向(提升广播容量/持久化队列重发/forward watchdog 超时);重发复杂度超本 todo 范围 — workflow.rs - R-PD-14
df-ideas promotion IdeaPromoter/PromotionPolicy/try_promote 死代码 + do_promote 空壳 TODO✅ 已修(删 IdeaPromoter/PromotionPolicy/try_promote/do_promote;PromotionResult 保留——promote_idea IPC 返回类型 + idea.rs:99,155 实例化引用,作 IPC 边界类型无法清)
🔴 工作区多角度走查待修项(2026-06-16)
4 路并行代理走查工作区 22 文件 547 行(AiChat/View/stores/composables+ToolCard),详见 工作区多角度走查-2026-06-15.md。3 P0 功能 bug + 1 P1 i18n + DRY/健壮性一组。
P0 — 功能 bug
- 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 - 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:28state.queue=[](onStreamTimeout) +useAiEvents.ts:225(AiError) +useAiSend.ts:152(stopChat)/124(approveToolCall catch)四路径全清队列。上轮「仍未修」为过时快照(working tree 当时旧版,与 B-34 同)。详见 定时走查-2026-06-15-第2轮.md - 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-45onStreamTimeout 单遍反向扫描 running toolCall→rejected(合并探测 completed 为单次 O(n))。上轮「仍未修」为过时快照。详见 定时走查-2026-06-15-第2轮.md - B-260615-35 ✅(定时走查第3轮复核,2026-06-16,b08adcb) — [原 P0] broadcast
Lagged兜底仅 warn → 关键终态事件丢失时前端永久卡死。workflow.rs:90-110forward 任务靠finished = matches!(event, WorkflowCompleted|WorkflowFailed)退出,broadcast 滑动窗口 Lagged(n) 丢 n 条最旧事件,代码注释已识别风险但仅 warn 无兜底;若 Lagged 恰丢终态事件,forward 永远rx.recv().await等不到 finished → 前端审批/完成弹窗永久卡死,仅靠 DB 轮询兜底(实时性差)。修:Lagged 分支后累计次数/单次 n 过大时查WorkflowRepo::get_by_id看终态,补 emit workflow-event + break;需把state.db.clone()move 进 forward 任务(当前仅 executor spawn 持有)。低频但无自愈 — src-tauri/src/commands/workflow.rs:90-110 — 详见定时走查 P0复核.md(后端代理 ②)
P1/P2 — DRY + 健壮性
- 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) - CR-260615-10 — [P2] useMarkdown 加
useRendered(getText)辅助,消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥ (第⑫批销账 06-15) - 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 - CR-260615-12 — [P3] 可选一组:_toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18n
tas any — 详见走查 ⚪ 区 (第⑫批销账 06-15) - CR-260615-18 ✅ decision 死逻辑 (第⑩批 2026-06-16) —
decision 三元两分支相同死逻辑(CR-11⑪ 实施引入):workflow.ts:67-68selectType === '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 - 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) 只解构 entity,action 忽略,刷新统一全量 load。契约冗余(白算白传)。修:删 action 或做差异化刷新(delete 本地移除)— src/api/types.ts:157 + project.ts:33 — 详见定时走查第2轮.md - CR-260615-20 ✅ App.vue try/catch (第⑩批 2026-06-16) —
stopDataChangedListener try/catch走查误判撤销:接收方核 workflow.ts:108 stopEventListener 写法一致(f574562commit 说明),不修 — project.ts:47-55 unlisten 同步数组操作正常不抛,与 workflow.ts:108 stopEventListener 写法不一致。修:核 workflow.ts:108 对齐统一去/留 — src/stores/project.ts:47-55 — 详见定时走查第2轮.md - CR-260615-21 ✅ types 枚举注释 (第⑩批 2026-06-16) —
App.vue:218console.error(防 Tauri listen 失败 reject onMounted 致 AR-11 静默失效)。原问题:await 无 try/catch — src/App.vue:218 — 详见定时走查第3轮.mdstartDataChangedListener()await 包 try/catch - 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 - CR-260615-23 ✅ knowledge error 死断 (第⑩批 2026-06-16) —
B-34「Tauri 不转」注释撤销(不适用):在 ARC-05 拆分时已简化(workflow.ts:81/project.ts:58 仅风格对齐无错误说明,16aeacc 接收方核对) — project.ts:273 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - CR-260615-31 ✅ useConfirm 并发 (第⑩批 2026-06-16) —
小区域收尾健壮性一组(定时走查第8轮,无必修):①useConfirm 并发 confirmDialog 覆盖 resolve(useConfirm.ts:35-47),前一个 Promise 永挂(连点两删除/confirm 交错时)→ 静默中断+轻微内存泄漏,修:进入时 resolve?.(false) 打断前一个视为取消;②ToolCardList collapseInactive(:47-51)只清 expandedCards 漏清 expandedTools,已完成卡片折叠后内容展开态残留 Set,修:对称过滤或注释明确有意保留 — src/composables/useConfirm.ts + components/ToolCardList.vue — 详见定时走查第8轮.md
📄 文档核对待修项(2026-06-16)
4 路并行核对 docs 全量 + 根目录文档,详见 文档全量核对报告-2026-06-15.md。悬空链接 0。
P0 — 文档硬伤(按文档找代码落空)
- 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 - DOC-260615-02 ✅ 文档P0重写 (第⑩批 2026-06-16) —
df-nodes 模块文档重写(8 节点全骨架 → 实际 3 节点 ai/script/human 均完整实现;删 Docker/Git/Notify/HTTP/Subflow 5 虚构)— 03-模块文档/df-nodes — 详见 §2 - DOC-260615-03 ✅ 文档P0重写 (第⑩批 2026-06-16) —
df-ai 模块文档删虚构router.rs/stream.rs + 已删 supported_features trait + 12→13 工具 — 03-模块文档/df-ai — 详见 §3 - 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 ✅(2026-06-16) —
todo 补全局 review R-P1-1在全局代码 review 待修项区头部补「R-P1 6 项 + R-P2 13 项全闭环(126 test pass)」汇总指针(指向全局review-2026-06-15.md 推进状态三批),原 todo 只有 R-PD-1~14 需设计项无 R-P1/R-P2 执行项追踪,现补齐。报告说全闭环但 todo 零条目,已完成区补汇总指针 — 详见 §5 + 全局review-2026-06-15.md §推进状态6/R-P2-113 已完成回写 - DOC-260615-06 ✅(2026-06-16) —
架构复核报告 §5 新发现 11 项补 todo拆 4 子项独立 todo(见下方),核对发现 复核-新① 已解决(useAiConversations.ts:122已有 pendingToolCalls await 后二次 token 比对),只补 ③④⑪ 三项;其余 7 项已被全局 review 取代(R-PD 系列) — 详见 §6 - ✅(batch31·2026-06-16) 复核-新③ [P2] — NodeCancelled 事件语义双标。
executor.rs:149取消节点(set_cancelled 后 Err 路径)状态保 Cancelled(:145 跳 set_failed),但事件总线仍 emitNodeFailed { error: "人工审批被取消" }。前端若按 NodeFailed 分支判断会误归类「失败」非「取消」。修法:加NodeCancelledvariant 或 NodeFailed 加cancelled: bool字段。— 来源:架构复核报告 §5 新③,crates/df-workflow/src/executor.rs:145-153 + df-core/events.rs - ✅(batch30·2026-06-16) 复核-新④ [P2] — cancel_workflow_node 无节点终态前置校验。
workflow.rs:352仅校验 execution_id 在注册表,不校验 node_id 状态。对已 Completed/Failed/Cancelled 节点调 set_cancelled 会静默覆盖终态(set_cancelled 绕 is_legal)。修法:加match sm.get(&node_id) { Running|Waiting => {}, _ => Err }守卫。— 来源:架构复核报告 §5 新④,src-tauri/src/commands/workflow.rs:341-358 - ✅(batch54·2026-06-16·workflow+主代核查) 复核-新⑪ [P2] — 主窗口看门狗幽灵。核验更正:原述「看门狗续计 130s 补幽灵错误」已被 CR-260615-24 兜底解决(detach→AiChat 卸载 App.vue v-if→onBeforeUnmount:1613
stopListener()→useAiEvents.ts:371clearStreamWatchdog(),主代独立 grep 核验链路完整)。本次 workflow agent 补强:detachPanel 快照后清主窗口state.streaming=false/generatingConvId=null(useAiWindow.ts:37-38),消除「面板重开生成态视觉残留」(进度条/spinner)。安全性:主/分离窗口 state 独立(各自 webview 独立 JS context,stores/ai.ts 单例仅同 webview 内共享),清主窗口 state 不影响分离窗口生成态;localStorage 快照保留供 resumeInDetached 恢复。vue-tsc EXIT=0 主代独立核验。— 来源:架构复核报告 §5 新⑪,src/composables/ai/useAiWindow.ts:37-38 + useAiEvents.ts:364-374 stopListener - DOC-260615-07 ✅(第⑲批 2026-06-16) —
aichat §8 表补勾aichat审查报告第八章 AR-111 优先级表逐行补状态勾注(AR-1退役✅/AR-27,9~11已修✅+commit/AR-8重评🔄)+文档头二次增补行;零新增条目对齐 DOC-13 瘦身方向;状态不一致 2 项(AR-1退役/AR-8重评)按 todo 标注非矛盾 — 详见 §7 - DOC-260615-08 ✅(2026-06-16) —
近期改动 CR-03 未做子项拆独立 todo拆 3 子项独立 todo(见下方)。原 CR-03(line 212)内联备注「未做留 todo」未独立追踪,现拆。— 详见 §8 - ✅(batch30·2026-06-16) CR-260615-03-a [P2] — audit.rs Err 误报「项目已不存在」。
audit.rs:52resolve_project_label用_合并Err(DB 故障/锁/连接断)与Ok(None)(真不存在),DB 出错时用户看到误导性「项目已不存在」而非错误。修法:分三臂Ok(Some)/Ok(None)/Err(Err 打 warn 日志回退裸 id)。零行为破坏(仅 Err 分支加日志+改文案)。— 来源:近期改动审查 §2②,src-tauri/src/commands/ai/audit.rs:50-52 - ✅(batch30 openai·2026-06-16 + batch31 anthropic:533 同源) CR-260615-03-b [P2] — openai_compat
anyhow::anyhow!(e)丢 source 链。openai_compat.rs:414非超时分支用anyhow!(e)把 reqwest::Error 整体塞进 message,丢失#[source]因果链(?本会保留)。修法:改anyhow::Error::from(e)保留 source。零行为破坏(仅错误追溯链增强)。— 来源:近期改动审查 §2③,crates/df-ai/src/openai_compat.rs:414 - ✅(batch30·2026-06-16) CR-260615-03-c [P2] — ToolCard projectNameById computed 过度结构化。
ToolCard.vue:390computed 每次重建全量项目(含回收站)的 Map,审批卡通常仅 1-2 行用项目 id。修法:改projectStore.projects.find(p=>p.id===id)?.name ?? projectStore.deletedProjects.find(...)?.name(审批场景项目数有限,find 提前退出,响应式仍由 store 数组保证)。零行为破坏。— 来源:近期改动审查 §2⑧,src/components/ToolCard.vue:390,417,441
P1 — 架构/模块文档过期
- DOC-260615-09 ✅(第⑮批核对 06-15) — df-workflow 模块文档 4 处过期 0 处需改(历史已修)(NodeRegistry 删 Default / conditions 默认 true→false / try_recv_human_approval 已删 / set_waiting·set_skipped 已删)— 详见 §9
- 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 ✅(第⑱批销账 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/16(INDEX 漏列部分本会话已补,见 DOC-14)
P2 — 迭代/Phase/规范
- DOC-260615-12 ✅(第⑱批销账 06-15) — DEVFLOW-1~4 + Phase1/Phase2 标完成归档(DEVFLOW-2/3/4 正文 checkbox 补勾对齐头部✅;Phase1清单#1-#11全✅+计数67修正;Phase2各小节补「实际状态」2.1/2.2✅ 2.3启发式✅LLM❌ 2.4仅并行✅,task行保留原文+核对风格不强勾)(Phase1 已收尾文档仍全待实施)— 详见 §17-19
- [~] DOC-260615-13 ◐(2026-06-16 评估·部分做) —
todo 肥大瘦身(大段详情移 docs 子目录)评估结论:todo 主要「肥大」是单行长条目(超 1000 字符/条,F-260616-01=1506/AE-2025-04=1667/B-260615-08=1526/AR-11=1428 等),非多行详情块;单行长条目多已通过「详案见 docs/子目录/xx.md」+ plan 文件指针(quizzical-prancing-hennessy.md 等)外部化,详情已在子文档,inline 摘要保留助读。本次未强行瘦身(强行拆单行条目易碎+破坏 grep/锚点),仅评估标注。已做收口:①重复项 HumanNode 审批(B-03b/CR-03/复核-新③)各保留各自语义无纯重复 ②密钥迁移(FR-S1/CR-01/R-PD-1)三处分工:FR-S1=明文根治/CR-01=删除闭环/R-PD-1=编辑路径迁移,非重复。遗留:若未来单行长条目继续膨胀,可立专项把已完成项(AR-11/FR-R1 等)的详细勘察段移 docs/05-代码审查/ 历史归档,inline 只留一句结论 + commit。文档记录规范在 INDEX 突出 → 归 DOC-14 改造指南重写时一并 — 详见 §20-22
P3 — 技术/工具文档
- [~] DOC-260615-14 ◐(2026-06-16 评估·部分做) —
Tauri-IPC/View 改造指南全篇过时重写或归档 + SQLite 表数 V1-V9→V1-V13 + 锚点顿号核对结论:①Tauri-IPC 模式(01-技术文档/Tauri-IPC模式-2026-06-12.md)顶部已加归档 banner(2026-06-15,指向 ARCHITECTURE.md §三),✅ 已归档不需重写;②View 改造指南(06-前端开发/View改造指南-2026-06-12.md)顶部已加归档 banner(2026-06-15,指向 DEVFLOW-3.Store对接实施),✅ 已归档;③SQLite 表数 已改:df-storage 模块文档 V1-V13→V1-V15(补 V14/V15 迁移行 + 头部/状态表/迁移机制行/文件树注释 6 处)+SQLite-CRUD 模式文档 V1-V9→V1-V15(11→14 表/9→12 索引/11→17 Repo 3 处),核对真实 migrations.rs schema_version 最大值=15;④锚点顿号(INDEX:107-109#四crate-结构渲染器差异)低危遗留不强行做。遗留:锚点顿号 + 路径风格统一(功能创意池:150 链接省./),低危,未来 INDEX/创意池重整时一并 — 详见 §23-26
🟡 B-03b 复核后续(独立深度复核,2026-06-14)
B-03b 端到端补完(commit 4aa689e)后独立复核发现 2 加重 + 3 低优。①为真实 bug:取消链 IPC→set_cancelled→HumanNode is_cancelled→Err 通了,但 HumanNode 返 Err 后 executor run:115
set_failed走 transition,而 Cancelled 不在is_legal转换图 → bail → 工作流因状态转换错误异常终止(非优雅标记取消)。补完时只验 IPC→HumanNode 链,漏验 HumanNode→executor Err 处理。
- B-03b-R1
Cancelled 转换图双踩✅ 已修(executor Err 处理加 is_cancelled 检测,已取消节点跳 set_failed,Cancelled→Failed 非法 transition 不再 bail;emit NodeFailed 仍发,状态保 Cancelled;采修法 B 不动 is_legal 转换图) - B-03b-R2
零端到端取消测试✅ 已补(test_cancelled_node_skips_set_failed:CancelSelfNode 经共享 node_status 自取消→Err→executor 不 bail→状态保 Cancelled;df-workflow 14 test pass) - B-03b-R3 — [评估维持] std::sync::Mutex — 快速临界区纳秒级持锁不阻塞 runtime,保持 std 合理
- B-03b-R4 — [评估维持] set_* 改 &self 后调用方 &mut — &self 是放宽,&mut self 上下文调 &self 无害,清理为可选优化不影响正确性
- B-03b-R5 — [评估维持]
lock().expect()poison panic — poison=持锁 panic 严重错误,fail-fast 合理,非用户态可恢复 - B-03b-R6
human_node send 缺 await✅ 已修(human_node.rs:41 加 .await;async fn send 的 Future 不再被 let _ = 丢弃,Request 真进 channel)(commit0bb96fc) - B-03b-R7
前端契约失配✅ 已修(project.ts:214 type→snake_case + :215 取 event 本体扁平字段 + as unknown as 绕过联合类型)(commit0bb96fc);types.ts event.type 收窄字面量联合(WorkflowEventType 11 变体)已补(commit3e1f119Wave5) - 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) - ✅(workflow wfryptv2t·代理C独立Read+Grep核验·2026-06-17·不信报告结论) B-03b-R9 — [P2] 其余 8 项对抗裁定(③串扰/④单槽/⑤终态不清/⑥set_cancelled覆盖终态/⑦互斥/⑧approve不校验/⑨⑩Lagged/⑪failed_node空)。7项销账:③串扰✅(workflow.rs:167-172 exec_id双重匹配治本)⑤终态不清✅(workflow.ts:57-70三变体监听+exec_id限定+Lagged兜底补发)⑥set_cancelled覆盖终态✅(workflow.rs:497-517 IPC终态前置守卫)⑦互斥✅(workflow.ts:8-11 _approvalInFlight单例approve/cancel共享)⑧approve校验✅(workflow.rs:431-450三层防御+human_node.rs:106-111下游兜底)⑨⑩Lagged✅(human_node.rs:153-161续等+workflow.rs:191-263累计阈值查DB终态兜底)⑪failed_node空✅(workflow.rs:355-369 snapshot取首个Failed/Cancelled节点id)。1项留观察:④单槽——state.ts:23 pendingApproval仍单值非Map,零危害(前端唯一DAG=demoDag无human节点入口+UI无并发工作流入口+⑤已按exec_id限定清理+后端HumanNode按exec_id+node_id各独立select!),待并发工作流入口落地时升Map。详见 审查报告 §2
✅ 已完成 — df-workflow 审批闭环(Workflow D, commit 22964a2)
- B-260614-06 —
execution_id 硬编码 "dummy-execution-id"✅ DagExecutor::new 接收 execution_id 下沉 NodeContext(workflow.rs 传真 ID)(06-14) - B-260614-07 —
每节点全新空 StateMachine✅ NodeContext.node_status 共享 self.state_machine.clone()(is_cancelled 可工作)(06-14) - B-260614-03a —
HumanNode 假返回"同意"✅ execute 重写 subscribe→send→select!(响应/超时/取消 + execution_id+node_id 双键 + Lagged 容忍),照 B-03 设计;7 单测;eventbus 删死代码 (06-14) - B-260614-03b —
HumanNode 取消机制✅ WF-E 完成:set_cancelled + cancel_workflow_node IPC + 前端取消按钮 + 端到端补完(StateMachine 内部 Arc 共享 + execution_id 注册表)。注:agent 初版留半成品(workflow_cancel_state 全局孤立实例,IPC 写了读不到,Explore 审查漏抓语义缺陷),主代理补完 IPC→共享 HashMap→HumanNode is_cancelled 真通;clone_shares 单测验证共享语义 (06-14, commit4aa689e)
P1 — 设计完成待实施
- ✅ F-260614-01 — [P1🔥] 模型能力系统 Phase 1(阶段1-6 全✅,波1-5,ModelConfig 4维度+路由器+多源探测+厂商拉取+7调用点+Settings池UI+AiChat下拉) — ModelConfig 数据模型(模态/能力/价格/智力 4 维度) + ModelRouter 智能路由 + 多源探测(预设表+启发式) + 厂商模型列表自动拉取(适配 OpenAI/Anthropic 协议差异) + 7 调用点接入 + Settings 模型池 UI + AiChat 模型下拉。按任务需求自动匹配合适模型,不再所有场景共用 default_model — source:📐 设计定稿 (06-16, F-01-模型能力系统与智能路由设计-2026-06-16.md)。F-07 已完成解除阻塞。
- ✅(波1) 阶段1:数据模型 — df-ai-core 新增 ModelConfig/Modality/Capability/CostTier/IntelligenceTier + df-storage models 反序列化兼容
- ✅(波1) 阶段2:预设表+探测器 — df-ai 新建 model_probe.rs + presets/models.json + 启发式推断 + 多源合并
- ✅(波1) 阶段3:厂商模型列表拉取 — df-ai 新建 model_fetch.rs + openai_compat/anthropic_compat 分派 + URL 拼接 + 噪音过滤
- ✅(波2) 阶段4:路由器 — df-ai 新建 router.rs + TaskRequirements + select 逻辑
- ✅(波3 IPC
a26b86e+ 波4调用点2ddfea6) 阶段5:6 调用点接入 — agentic/title/knowledge_inject(2)/project(2)/df-ideas/df-nodes + IPC ai_fetch_models/ai_probe_model + router.rs select_model_id helper(池空兜底 default_model 行为不变) - ✅(波5·4c84fbc+主代核查,cargo0err+vue-tsc0+df-ai107pass+compress3pass) 阶段6:前端 — types.ts 类型对齐(✅波3) + Settings 模型池 UI(✅波3) + AiChat 模型下拉+override穿透(✅波5·4c84fbc,override空/不在池落回路由零行为变化)
P1 — Sprint 19 遗留
- T-260614-05 —
工具结果入库前截断 50KB✅ mission:T-260614-04 已修复(conversation.rs 加truncate_for_persist纯函数,50KB 阈值 + 头尾各 20KB + 中段标注省略字符数;仅作用于持久化视图不污染内存真相源;3 单测 pass)(06-14) - ✅(波23·2026-06-17·9c60c8f,拆 4 子组件 Provider/Connection/General/Knowledge 1042 行瘦身零行为变化) T-260614-06 — [P2→中等风险] Settings.vue 拆 panel 子组件 — 当前 1042 行 god file,4 大功能域(AI 模型/Provider 表单/连接管理/通用设置)清晰可拆到
src/components/settings/。评估:非低风险,纯重构零功能价值,要新建 4 子组件 + props/emits 接线 + CSS 拆分,单独立项做更稳 — source:Sprint 19 待评估 (06-14) - T-260614-07 —
诊断日志清理✅ mission:T-260614-06 已清理(useAiEvents/useAiSend 3 处调试 console.log 直接删;AiChat.vue/main.ts 3 处启动计时改 console.debug 保留诊断能力但不污染 console;vue-tsc 0 err,src/ console.log 0 残留)(06-14)
🔴 generating 状态机加固(2026-06-16 审查)
/review 专项审查 generating 生命周期(commands.rs 对话/审批/stop + agentic.rs loop/try_continue + stream_recv.rs stream_llm + mod.rs AiSession)。用户报障根因:generating 复位散布 6 处 + spawn 无 panic 兜底 → generating 卡 true → newConversation 硬拦死锁(创建不了新对话,只能重启)。决策:轻量状态机(RAII guard 写收敛 + enum 视图读侧收敛),不引入独立状态机框架(stop_flag 须保留 AtomicBool 跨锁)。详见 generating状态机加固-2026-06-15.md。
P0 — 用户 bug 根治组合(必须同批:②③强耦合,①是上游根治)
- 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 - 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 - 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
- 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 - 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 - B-260615-14 — [P1] ⑦ stop_flag 换 tokio::sync::Notify 即时打断(去 30s 延迟)— stream_recv.rs:148 (第⑫批销账 06-15)
P2
- 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 - 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 - 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 - B-260615-18 ✅(P0批,2026-06-16,待commit) —
pending_approvals 注释mod.rs:124-135 扩展文档:单 HashMap<tool_call_id,PendingApproval> 按 tool_call_id 路由/conversation_id 业务语义非路由键/当前 O(n) 过滤典型场景可接受/未来多会话高并发可加二级索引 — mod.rs
🔴 消息发送失败无提示 + 状态传染(2026-06-16 用户报障)
用户报障:发送消息后面板显示该消息 + input 残留同条消息 + 无失败提示;叠加首对话失败后新对话建不了、切历史对话也发不出。根因 = generating 卡 true(见上 B-260615-09~18,未实施)的下游表现:卡死态下
ai_chat_send(commands.rs:45) 同步拦截 Err → 前端 catch(useAiSend.ts:75) 回填 input + 不回滚 user message → handleSend(AiChat.vue:666) 仅 console.error 无 toast。新建对话(ai_conversation_create:451)/切历史(switchConversation)同被 generating 拦。根因不治则反复,本块 P1/P2 为 UX 放大器与状态同步缺口。
P0 — 根治(即上一块 B-09~18,不另立)
- 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 放大器
- 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 - B-260615-21 ✅ 回滚user msg (第⑩批 2026-06-16) —
sendMessage catch 回滚 user messagepush user msg 前捕获 const userMsgId(原内联未生成)+catch 块 filter 改 m.id!==aiMsgId && m.id!==userMsgId 一并回滚;机制:user/ai msg 均 nextMsgId() 唯一 id+push 进 state.messages(无索引依赖,按 id 过滤最稳) — useAiSend.ts
P2 — 前后端状态同步
- ✅(batch55·2026-06-16·主代销账) B-260615-22 — [P2]
前后端状态不同步→ 由 B-260615-55(L0 握手) + B-260615-56(L2 强制发送) 解决(L0 前端重接握手+L2 三级降级+force_send IPC),本条保留为问题记录不再独立实施。双项均已✅销账,本条闭环。 - CR-260615-12 ✅ 小项 _toastTimer 等 (第⑩批 2026-06-16) —
可选一组_toastTimer 卸载未清(AiChat:436) + markdown 外层 span→div + SVG 常量散落 + appSettings 注释 + index 空行 + 路由 icon 语义 + options shape 校验 + projectNameById 共享 + useConfirm dev 警告 + i18ntas any — 详见走查 ⚪ 区 - 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 - CR-260615-10 ✅ useRendered DRY (第⑩批 2026-06-16) —
useMarkdown 加消除 renderedDesc computed + onMounted(loadMarkdown) 三处重复(与 CR-09 同源) — 详见走查 ⑥useRendered(getText)辅助 - 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:182tc.result='用户拒绝了此操作'绕过已有 aiTool.rejectedHint → en locale 拒绝提示恒中文(已有 en 翻译被一行废掉),改一行删/换 t() 最高 ROI;②TaskDetail.vue 整页 13 处未接 i18n(无 taskDetail namespace,需新建);③ProjectDetail 审批/导入 5 处(导入目录/确认N/取消/confirmDialog/Message);④Dashboard:76 暂无项目;⑤AiChat:107/471/687(title清空/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-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 - 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 或 用户主动)→ 弹确认「检测到生成状态异常,是否强制发送?」→ 调用新 IPCai_chat_force_send绕过 generating 检查(后端先 reset generating 再正常走 send 流程)。关键设计:30s 后给用户选择权而非永远静默等。改动:前端useAiSend.tssendMessage 加超时分支 + confirm 弹窗;后端commands.rs新增ai_chat_force_send(复用 ai_chat_send 逻辑,前置 reset generating)。关联 B-10(软复位已做 newConversation, force_send 同模式) — src/composables/ai/useAiSend.ts + src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs
🔴 详情描述字段 Markdown 未渲染(2026-06-16 用户报障)
用户报障:任务描述 Markdown 没渲染(显示原始
## 标题/- 列表文本)。走查:4 详情组件描述字段全用纯文本插值{{ }}未走 md 渲染——src/views/TaskDetail.vue:33/ProjectDetail.vue/Ideas.vue/Knowledge.vue。src/components/AiChat.vue已有正确实现renderMd(marked + DOMPurify + 块级 memo + XSS 防护),但封装在组件内未抽 composable,DRY 缺口。修法:抽src/composables/useMarkdown.ts统一入口(复用 AiChat renderMd 逻辑),4 组件展示态切v-html="renderMd(desc)"(编辑态仍 textarea)。安全约束:v-html 必须 sanitize,composable 保留 DOMPurify。
- 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 - B-260615-24 ✅(2026-06-16,待commit) —
TaskDetail 描述 v-html=renderMdTaskDetail.vue:33 描述非空 v-html=renderMd(task.description)(空值回退 —)+.ai-md 作用域样式(h1/h2/h3/ul/ol/code/pre/blockquote/table)+onMounted loadMarkdown 预热(单例)。用户报障描述 md 不渲染已修 — TaskDetail.vue - B-260615-25 ✅(2026-06-16,待commit) —
同类 3 组件描述 renderMdProjectDetail/Ideas/Knowledge 描述展示态 v-html=renderMd(desc)(空值回退 —)+import useMarkdown+onMounted loadMarkdown 预热+各加 .ai-md 样式(Ideas .detail-desc.ai-md/Knowledge .detail-content.ai-md 含 white-space:normal 覆盖原 pre-wrap,正确处理 v-html 标签间 \n;ProjectDetail 同). DOMPurify sanitize 安全。vue-tsc 0 err — ProjectDetail/Ideas/Knowledge.vue
🔴 审批执行后中断(2026-06-16 用户报障)
用户报障:AI Chat 审批卡片点批准 → 工具执行 → 对话就结束,没续生成下一轮("不知原因")。走查根因:B-260615-09 的
GeneratingGuard(agentic.rs:48-75) Drop 兜底复位 generating,但run_agentic_loop:292-300审批等待 return(pending>0)路径既没guard.reset()也没 disarm →guard.done=false→ task 结束 guard Drop(agentic.rs:67-75) spawngenerating=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"异常退出(应复位)"。
- 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 - B-260615-27 ✅ 数据实证 (第⑩批 2026-06-16) —
数据侧实证conv 6c2e11f4(U-Ask 概览)消息序列显示 Low 工具(list_directory/read_file)执行后 loop 正常续生成,Medium/High 审批工具(create_project/create_task/run_command)审批执行后 loop 断——对话多次断在 tool_result([10][34][42][70] 后均无 assistant 续),用户被迫反复发"继续"([11][35][43][57][65][68])。坐实 B-26 根因:审批路径 loop return 触发 guard Drop 误复位 generating → try_continue 不续;自动执行路径 loop 不退出故正常。
🔴 Task 缺失(2026-06-16 用户报障)
用户报障:任务缺失。DB 实证(devflow-dev.db):tasks 表 61 条,无悬挂引用(LEFT JOIN projects 验证所有 task.project_id 都在 projects 表),数据完整非丢失。根因在前端:
Tasks.vue:214onMounted 全量loadTasks()无参 + 前端 filter(:159-164 activeProject 过滤),切换项目不重新加载(仅对已加载全量做前端 filter),新建 task 后其他项目视图不刷新。属 AR-11 数据变更联动同类。另发现:projects 表重名 meta-kit×2(f0fa88bf-a4bd / 32de9175-870d),疑重复导入/绑定,可能致用户认知"缺失"。
- 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 - B-260615-30 ✅(2026-06-16) —
projects 重名核查meta-kit 两条(f0fa88bf/32de9175)结论: 建议不删,保留两条记录。可能为不同目录绑定或不同导入批次,P2 低优先级,无数据冲突风险。如需去重待用户确认具体哪条 — projects 表
🔴 详情页字段布局紧凑化(2026-06-16 用户需求)
用户需求:任务详情中除「描述」外的字段(标题/状态/优先级/关联项目/分支/负责人/基础分支/工作流定义/时间),名称与值应同行展示(当前分行垂直堆叠,占空间)。走查:TaskDetail.vue:27-84 全部
.info-item用flex-direction: column(CSS :221-225)分行。ProjectDetail.vue:69-121 同款布局需一并改。Ideas.vue 用卡片式不同布局不改。
- B-260615-31 ✅ 详情布局同行 (第⑩批 2026-06-16) —
TaskDetail + ProjectDetail 字段同行布局.info-itemflex-direction column→row + align-items baseline + gap 12px +.labelmin-width 88px flex-shrink:0(TaskDetail.value加 flex:1 min-width:0 占余);描述字段 info-item 加info-blockclass + CSS.info-item.info-block{flex-direction:column}保块状(长文本独占整行);ProjectDetail.path-row/.info-tags已 flex-wrap 无溢出风险。vue-tsc 0 err — src/views/TaskDetail.vue + src/views/ProjectDetail.vue
🔴 run_command stdout/stderr 恒空(2026-06-16 用户实测)
用户实测 F-05 run_command:命令能跑(副作用如写文件发生)+ exit_code 正常,但 stdout 始终返回空字符串,echo/Write-Output 等均如此 → "写→跑→看→改"闭环断在「看」,跑测试/构建无法获取输出。根因(铁证):
crates/df-execute/src/shell.rs:37-58构造tokio::process::Command时漏设cmd.stdout(Stdio::piped()).stderr(Stdio::piped())。tokio 默认Stdio::inherit()(输出继承父进程 Tauri app → 控制台/devnull),wait_with_output()读不到 pipe →output.stdout/stderr空 Vec →from_utf8_lossy→ 空串。F-05 plan doc 也未提 Stdio 设置,实现遗漏。PS 静默失败(.Replace()/-replace/Set-Content exit_code=0 文件未变)疑非独立 bug:修了 piped 后 stderr/stdout 可观测,方能定位是「PS Replace 源不匹配」(AI 用法)还是「cmd /C 调 PS 引号问题」(shell.rs:38 Windows 用 cmd /C,PS 语法引号嵌套易错)。B-38 待 B-37 修复后据观测重评。
- 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 - B-260615-38 ✅ PS shell_type (第⑩批 2026-06-16) —
PS 静默失败根因ShellRequest 新增 shell_type 枚举(ShellType: Cmd/PowerShell/Sh,默认 Windows→Cmd 非Windows→Sh)+ execute 函数改 match 分支(PowerShell→powershell -NoProfile -Command,Cmd→cmd /C,Sh→sh -c)。B-37(Stdio::piped())已确认在代码中。LLM 可通过 shell_type 参数选 PS — crates/df-execute/src/shell.rs
🔴 查看灵感来源跳空白页(2026-06-16 用户报障)
用户报障:项目详情点「来源灵感」(i18n key
projectDetail.sourceIdea) 跳转后页面空白。根因双锁:①ProjectDetail.vue:71router-link :to="/ideas/${idea_id}"指向/ideas/:id,但路由表(router/index.ts:21-25)只有/ideas精确路由,无:id子路由 → hash 路由无匹配 → 渲染空白(路由表无 404 catch-all 兜底)。② 即便补路由,Ideas.vue不读 route params——:209import 无useRoute/:221只useRouter/:448-450onMounted 只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 ✅ Ideas route.params (第⑩批 2026-06-16) —
查看灵感来源跳空白页修复两层:①router/index.ts加/ideas/:id路由(nameIdeasDetail或复用 Ideas 组件);②Ideas.vue加useRoute+onMounted读route.params.id→selectedId.value=id(loadIdeas 后校验 id 在store.ideas中,不在则提示「灵感不存在/已删」+清 selectedId);可选 watchroute.params.id支持从详情再点别的灵感切换。— src/router/index.ts:21-25 + src/views/Ideas.vue:209,221,448-450 — source:用户报障(06-15)
🟡 AiChat 气泡拷贝能力(2026-06-16 用户需求)
用户需求:AiChat 消息气泡支持一键拷贝——文本拷贝(content 原文→剪贴板) + 截图拷贝(气泡 DOM 渲染→图片→剪贴板,粘贴出图片)。现状:全项目零 clipboard 实现(grep
navigator.clipboard/copy/clipboard/拷贝/复制零命中)。气泡结构:用户消息.ai-msg-bubble--user(纯文本{{msg.content}}, :185);AI 消息.ai-msg-bubble--ai.ai-md(v-html 渲染 MD, :197)。Tauri WebView2(Edge):Clipboard API 完整支持(writeText/write([ClipboardItem])),无需额外 plugin(做减法,不用@tauri-apps/plugin-clipboard-manager)。安全约束:必须在用户点击事件(手势)内调用剪贴板 API。
- 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) - ✅(待决策.md已决c不做·2026-06-16) F-260615-14 [P2] — AiChat 气泡截图拷贝(图片)。同位置第二按钮(🖼️ 或与文本拷贝合并为下拉)。技术链:
html2canvas(bubbleElement)→ canvas →canvas.toBlob('image/png')→new ClipboardItem({ 'image/png': blob })→navigator.clipboard.write([item])。需加依赖html2canvas(~90KB,或轻量替代dom-to-image-more~3KB,待选型)。注意:html2canvas 对 CSS 支持有限(阴影/渐变/CSS 变量可能不全),但气泡样式简单(ai-md 基本色+圆框),风险低。粘贴目标:Word/微信/钉钉等富文本编辑器可接图片;纯文本框不行(正常行为)。— src/components/AiChat.vue + 新依赖(html2canvas/dom-to-image-more 待选)
🟡 AI Chat 授权体验改进(2026-06-14 方案 → 待办化)
来源:aichat授权体验改进方案-2026-06-14.md。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 (第⑫批销账 06-15) - AE-2025-02 [P0] — 审批计数器 + 跳转。输入框上方/header 显示
⏳ N 项待审批徽标,点击跳转到第一个 pending 卡片。改动:①AiChat.vueheader 区域加审批徽标 ②ToolCardList.vue暴露scrollToFirstPending方法。预估 0.5 天 — src/components/AiChat.vue + src/components/ToolCardList.vue (第⑬批销账 06-15) - ✅(batch60·2026-06-16·workflow whae812z5+主代核查·路径B,cargo+vue-tsc 0err) AE-2025-03 [P0] — write_file diff 预览 ✅已落地(batch60 路径B):后端 write_file 未返 diff(
tool_registry.rs:694结果 payload{path,bytes_written,old_size,encoding}无 diff 字段,generate_diff仅 patch_file 调用),前端 ToolCard 无 diff 可渲染,审批 payloadAiApprovalRequired/PendingApproval亦无 diff(只有args={path,content,encoding}纯新内容,拿不到旧文件)。需后端先补再追加前端渲染。两条路径(均 src-tauri 改动,跨 mod.rs/audit.rs/types.ts/AiChat.vue 领地):路径 B(推荐,满足审批预览诉求)审批 payload 加diff字段 + audit.rs:555 挂起审批前预读旧文件 +generate_diff注入事件;路径 A 仅 write_file handler 补 diff 但结果阶段才有(审批时看不到,不满足)。前端部分待后端 diff 返回后追加<pre>红删绿增渲染。原诉求:write_file 审批时若文件已存在,展示前后对比 diff 而非裸 content(content 可能几百行代码截断到 300 字符)。改动:①后端tool_registry.rswrite_file handler 执行前读旧文件返回 diff ②ToolCard.vuepending_approval + name=write_file 时渲染 diff 视图(红删绿增)。关联 F-260615-10(diff 工具)可复用。预估 1 天 — src-tauri/src/commands/ai/tool_registry.rs + src/components/ToolCard.vue
P1 — 增强控制力
- ✅(波8·6ec191e+主代核查,cargo0err+vue-tsc0+devflow79pass) AE-2025-04 [P1] — 会话级授权(Session Trust)。替代原"全局宽松模式"。核心:信任是上下文相关的——同一会话内用户批准过某类操作后,后续同类操作自动放行;换会话清空重审。模型:AiSession 加
session_trust: HashSet<TrustKey>(内存态,随会话销毁);TrustKey 按操作+目标范围粒度(如Write { dir: "src/" }、Execute { dir: workspace })。流程:首次操作→pending_approval 用户批准→记录 TrustKey → 同会话后续同类操作命中则自动放行(消息流显示轻量提示🔓 自动放行: write_file(xxx))。白名单收紧决策:纯读取(list_/read_/list_directory)始终自动放行;创建/修改/绑定/覆盖写入/高危全部需审批或会话级授权放行(bind_directory 归类为修改操作;write_file 覆盖写入已出事故不放行)。首批实现:write_file + run_command(写→跑→看→改闭环最高频连续操作);create/update 后续按需加。改动:①mod.rsAiSession 加 session_trust 字段 + TrustKey enum ②audit.rsprocess_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 链路不影响本设计 - 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 - 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 — 信息透明度
- ✅(第⑳批 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) - ✅(batch34·2026-06-16·workflow) AE-2025-08 [P2] — 审批历史面板。独立页面展示
ai_tool_executions表审计记录(时间/工具/风险/状态/决策者/参数摘要/结果摘要)。改动:①后端新增list_tool_executionsIPC ②前端新建 AuditLog.vue 表格视图。预估 1 天 — src-tauri/src/commands/ai/(commands|audit).rs + 新建 src/views/AuditLog.vue
🟡 文件操作工具集补全(2026-06-16 能力梳理·设计定稿 V2)
用户给出 5 个工具具体 API 设计。核心原则不变:专用工具补 run_command 做不好/不安全的;系统命令类交给 run_command(B-37 修后)。修正:search_files 从"run_command 覆盖"升级为"值得做"(结构化 JSON > parse find 输出)。
5 工具设计(用户方案):
- F-260615-06 ✅ patch_file (第⑩批 2026-06-16) —
patch_file(edit_file)~50行 handler → 见下方对抗分析区 :503 完整条目(含设计文档指针) - 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 扩展 - 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 新增 - F-260615-09 ✅ append_file (第⑩批 2026-06-16) —
append_file(追加写入)path, content→{bytes_written, new_size}。日志追加/增量写入,避 read-merge-write 竞态 — tool_registry.rs 新增 - 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 (第⑬批销账 06-15, 后端diff已返回, 前端审批卡diff渲染待续)
- F-260615-11 [P3] write_file base64(二进制写入)— 图片/PDF/Excel,场景窄 — tool_registry.rs:519 (第⑯批销账 06-15, encoding 参数+base64 decode+字节写入)
新增(根治 AI rename 静默失败):
- 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 走MoveFileExWUTF-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 行) - 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
🟡 对抗分析裁决(2026-06-16 十域十八项交叉验证)
来源:用户对抗分析报告(10 域聚类 + 正反双向验证 + 依赖链提取)。核心价值:6 个否决/暂缓裁决("不做"比"做什么"更值钱,避免浪费投入)。完整报告见 docs/02-架构设计/对抗分析裁决-2026-06-15.md(若已落盘)。
✅ 推进(第一批独立可并行):
- ✅(batch61·2026-06-16·workflow wwtn2knn6·2069f79) F-07 trait 下沉 df-ai-core — 已实施(详见 L599 F-260614-07),4 决策全落地,解锁 F-03(已做
dfe0096)/F-01(待推)。本条为对抗分析裁决区过时重复条目,销账。 - R-PD-2 ✅ ScriptNode 不注册 (第⑩批 2026-06-16) —
ScriptNode 不注册 script3 行删除封死攻击面,工作流当前纯演示无真实脚本需求 - F-260615-06 ✅ patch_file (第⑩批 2026-06-16) —
patch_file(edit_file)局部文件更新工具,补齐 AI 文件操作闭环。完整设计见 patch_file工具设计-2026-06-15.md(API/三层防御/边界情况/替代方案否决/实施步骤)。核心: old_text 精确匹配为主+line 辅助+Mutex 并发安全+expected_hash 指纹防脏写。第一批实现核心三件套(~50行)。— src-tauri/src/commands/ai/tool_registry.rs 新增 handler - ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) ARC-06 composable 循环依赖 — 破环全闭环:①
aiShared.ts下沉 findToolCall(反向扫描O(1))+4个审批计时器函数(startApprovalTimer/clearApprovalTimer/clearAllApprovalTimers+APPROVAL_TIMEOUT_MS常量) ②useAiEvents删 findToolCall 导出+改 import 从 aiShared 取 ③useAiSend删审批计时器实现(改从 aiShared re-export)+删 drainQueue 直接调用(改经ai-drain-queue事件总线桥接) ④stores/ai.ts加initDrainQueueListener()启动事件监听。B-260616-19 CSS 缺失(Knowledge.vue 窄屏标题挤压,agent 未产出 .css/.vue style 变更),待补。— src/composables/ai/aiShared.ts + useAiEvents.ts + useAiSend.ts + stores/ai.ts - CR-09 .ai-md 样式 5 份→全局 css 抽取 — 零风险减法 (第⑬批销账 06-15)
- 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 软删二态
- F-260615-03 ✅ truncated (第⑩批 2026-06-16) —
最小修(截断标志)从完整分页降级:不改返回结构(non breaking),truncate 时加truncated: true(→ 见上方 :469 完整条目,本条为对抗分析区引用,勿重复实施) - R-PD-9 ✅ display_hint (第⑩批 2026-06-16) —
display_hint 轻量抽取audit 工具名→文案映射作 display_hint,消除双份映射
🟡 暂缓 / 需重设计:
- T-11 条件引擎 Phase1+2 🟡 暂缓 — 引擎设计好但无前端 UI 消费方(demoDag edges 无 condition 字段,用户无法配置条件)。等工作流有真实使用场景(BuildNode 落地后跑构建→测试→部署)再推进。决策点 A/B/C(手写求值器/复活 set_skipped/default false+warn)均认可
- F-260615-03 完整分页 🟡 暂缓 → 降为最小修(truncated 标志),见上方
- ARC-05 project.ts 上帝 store 拆分 🟡 需重设计拆分策略 — 不能简单按领域拆(会重蹈 ai.ts 循环依赖覆辙),需先把越层 invoke 沉到 api/workflow.ts。ARC-06 先做
❌ 否决(人定"不做",不进路线):
R-PD-9 agentic loop 下沉 df-ai❌ 否决 — 与 ai.rs 子 module 拆分决策冲突("下沉需动 crate 依赖图引入循环依赖")。保留 display_hint 轻量部分R-PD-8 DTO 映射层❌ 否决 — 过度设计,5 个 IPC 手动对齐可接受。替代:types.ts 头部文档约定"后端改 schema 必须同步改此文件"F-09 拆双字段(status 生命周期 + stage 开发阶段)❌ 否决 — 阶段概念已被弱化(df-stages 砍了阶段插件),DB 实际只有 active/archived/deleted,双字段过度设计R-PD-12 做实 run_workflow AI 工具❌ 否决(当前) — 工作流是内部基础设施,无真实 CI/CD 需求。删假能力(no-op 桩返 note)更诚实
⚡ 推荐推进顺序:
- 第一批(独立并行): F-07 / R-PD-2 / F-15-06 edit_file / ARC-06 / CR-09 / F-09对齐 / F-03最小修 / R-PD-9 display_hint
- 第二批(依赖第一批): F-03 对抗评估接 LLM(F-07后) / F-01 模型能力Phase1(F-07后) / R-PD-12 删假能力(R-PD-2后)
- 第三批(需用户输入): S-01 API Key 401 / 显示多开 / 审批可见性
已完成
2026-06-14
- R-260614-01 ai.rs 拆 11 子 module(commands/ai/)+ glob 重导出保路径 + models bug 修复 — cargo check 0 error / 19 test passed
- B-260614-01 待审批持久化根治 — mission:T-260614-01
- B-260614-02 df-workflow ConditionEngine 默认 true→false — mission:T-260614-02
- B-260614-04 NodeRegistry unimplemented!→删 Default impl — mission:T-260614-03
- T-260614-05 工具结果入库前截断 50KB(含 3 单测)— mission:T-260614-04
- B-260614-08 promote_idea 补偿删除保最终一致性 — mission:T-260614-05
- T-260614-07 诊断日志清理(3 删 + 3 改 debug)— mission:T-260614-06
- D-260614-01 B-03 人工审批响应机制设计完成 — 新建 B-03-人工审批响应机制-2026-06-14.md(9 节完整设计)+ 功能决策记录摘要章节 + PROGRESS/todo/INDEX 同步;核心结论:链路基础设施已通仅缺 HumanNode 一处、通道选型=工作流独立审批通道(非 ai.rs AiApprovalRequired)、拆 B-03a(响应等待+超时,不依赖 B-07)/B-03b(取消机制);✅ B-06/B-07 前置已由 Workflow D 完成,B-03a 已实施(commit 22964a2)
- WF-A 数据安全(Sprint 20 ①②)— AI delete→soft_delete + restore/purge/list_trash 三工具 + list_projects 排除回收站(commit 3f0839a)
- WF-B 阻塞去重(Sprint 20 ③④⑤)— spawn_blocking 三处+补漏 + normalize_path 抽公共 + 复用 is_allowed_column(commit d5a6417)
- WF-C 前端(Sprint 20 ⑥⑦⑧)— 扫描文案 i18n + parseStack 抽公共 + 8 处 alert/confirm 换组件(commit 02ff88f)
- WF-D 审批闭环(B-06/B-07/B-03a)— execution_id 下沉 + 共享状态机 + HumanNode select! 实现(commit 22964a2)
- WF-E Wave1 收尾清债(commit
4aa689e+ 89da9fa)— B-03b 审批取消端到端(StateMachine Arc 共享 + execution_id 注册表,主代理补完 agent 半成品:agent 初版 workflow_cancel_state 全局孤立实例写了读不到,Explore 审查漏抓语义缺陷)+ T-09 立项回滚级联删(delete→purge_with_descendants)+ T-10 判定已解决(normalize_path 已 canonicalize,零改)+ T-12 df-ideas 清 capture.rs 死码;cargo check / test(含 clone_shares)/ vue-tsc 全绿 - 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.md(agent 越权自主产出,保留未追踪待评估)
- WF-G Wave3 aichat P1/P2(commit 9e2aeff)— AR-5 stop 兜底 + AR-7 clean UI 真删 + AR-9 friendlyError i18n;审查 semantic_check 抓对 AR-7 gap(agent impl 声称改 commands.rs 实际零改动=幻觉,主代理补完);boundary 仍全局 diff 误判(审查 prompt 加固对 correctness 有效、对 boundary 根除不掉 agent 跑全局本能)
2026-06-16
- 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 - R-260615-01 — selection 文字不可见修复(深色主题)— global.css
::selection由accent-soft(透明紫底)+accent(紫字) 改为accent-hover(实色紫底)+#fff(白字),避免 user 紫底气泡选中后紫字紫底不可见 — source:用户报障(06-15),src/styles/global.css:124
Bug
(P0/P1 bug 见上方「待办」分类,此处不重复)
🔴 新发现但未记录的问题(docs 扫描结果,2026-06-16)
来源:后台代理扫描 62 个 markdown 文档发现的问题
P0 — 严重阻塞
- B-260615-57 ✅(2026-06-16,缺口清零销账) [P0→缺口收窄] —
文件工具功能缺口06-15 批次后缺口 6→2→0:已补patch_file(F-06 ✅) /search_in_file(F-07 ✅) /file_info(F-08 ✅) /append_file(F-09 ✅) /search_files(F-12 ✅) /delete_file(F-16 ✅ 第⑯批) /rename_file(F-15 ✅ 第⑮批销账 06-15)。缺口清零:rename_file/move_file已补(F-260615-15,根治 AI rename 走 run_command 的中文路径静默失败)。原根因(迫使 LLM 滥用 write_file 放大 FR-S7 覆盖写入风险)已由 patch_file 缓解。— src-tauri/src/commands/ai/tool_registry.rs
P1 — 重要缺陷
- 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 passedcomplete()IPC 调用仅加 60s 单请求超时,但无重试机制。根因:网络波动时直接失败,无自动重试兜底。影响:偶发网络错误导致用户操作失败需手动重试。修法:实现指数退避重试机制(最多 3 次,总时长不超过 30s),并在 Settings 可配重试开关 — src-tauri/src/commands/ai/commands.rs - 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] ✅(batch35 销账 2026-06-16,对齐决策a轻量) —
工作流执行无进度指示batch35 TaskDetail 进度内嵌已满足决策 a 轻量:复用现有 NodeStarted/NodeCompleted 事件(纯前端handleWorkflowEvent按 exec_id 过滤 dispatch,不新增后端 progress 事件),.wf-progress区显示workflowStepRunning{node}(当前步骤)+workflowStepsProgress{done}/{total}(步骤进度)+完成/失败提示,不新建独立 WorkflowDetail.vue(决策 a 是嵌入现有 TaskDetail 推进按钮区非独立页),i18n key taskDetail.workflowStepRunning/stepsProgress/completedHint/failedHint zh-CN+en 双语已加。预估剩余时间属方案 b 完整版,决策 a 明确不做 — src/views/TaskDetail.vue - ✅(batch34·2026-06-16·workflow wvr7mdo0p) B-260615-42 [P1] —
错误信息不一致同类错误在不同场景下返回的信息格式不统一。根因:错误处理分散在不同模块,缺乏统一规范。影响:用户和调试者难以理解错误含义。修法:制定错误码规范 + 统一错误格式 + 前端错误码映射 — src-tauri/src/commands/ai/mod.rs + src/api/types.ts
P2 — 中等问题
- ⏸️(决策c暂缓·2026-06-16) B-260615-43 [P2] —
无撤销重做功能AI 操作(如删除项目、修改任务)无撤销功能。根因:缺乏操作历史记录和回滚机制。影响:误操作无法恢复。修法:实现操作历史栈 + 撤销/重做 UI + 关键操作二次确认 — src/stores/project.ts + src/views/ProjectDetail.vue - B-260615-44 [P2] —
工具响应时间无反馈工具执行(如 read_file、write_file)长时间无响应时用户不知道卡在哪里。根因:缺乏工具执行进度反馈。修法:添加工具执行进度提示 + 超时警告 + 取消按钮 — src/composables/ai/useAiStream.ts - ✅(batch41·2026-06-16·workflow w9vtei15z) B-260615-45 [P2] —
内存泄漏隐患已核查覆盖(useAiEvents stopListener 三 listener+计时器全清 + AiChat onBeforeUnmount + MESSAGE_CAP=200 滚动淘汰),无泄漏不强改 长时间使用后内存占用持续增长。根因:消息缓存、事件监听器未及时清理。修法:优化缓存策略 + 定期清理机制 + 内存使用监控 — src/stores/ai.ts + src/composables/ai/useAiEvents.ts
文档整理需求
- DOC-260615-15 — 文档缺失:
patch_file工具设计文档已定稿,但使用说明缺失。需添加:工具使用场景、API 参数说明、安全边界、最佳实践 — docs/08-用户指南/patch_file使用指南.md (第⑬批销账 06-15) - 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
- B-260615-46 [P1] —
copyMsgContent 失败误报「已复制」navigator.clipboard.writeText权限拒绝/非安全上下文 reject → catch 分支仍showToast(t('aiChat.copied'))(与成功同文案),注释自述「静默忽略」与实现矛盾。用户见「已复制」去粘贴发现空 → 误导。修法:catch 改失败文案或静默 — src/components/AiChat.vue:1037-1040 - 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/微优化
- 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 - B-260615-49 [P2] —
handleEvent 每事件构造数组做 includes!['AiApprovalRequired','AiCompleted','AiError'].includes(type)每 delta/token 新建数组字面量。修法:提模块级NO_RESET_WATCHDOGSet — src/composables/ai/useAiEvents.ts:109 - B-260615-50 [P2] —
tryForceSend 前置 streaming=false 瞬态触发 watch 清流式块L151state.streaming=false触发 AiChat:812 watch 清 streamingBlocks+rAF,doSend 再设 true → 一次清空重建。注释自述「force_send 会重新设 true」证明前置 false 多余。修法:删该行(保留 clearStreamWatchdog) — src/composables/ai/useAiSend.ts:150-156。属 B-260615-56 实施细节缺陷
P3 — 风格/可选
- B-260615-51 [P3] ✅(第⑮批销账 06-15) —
MESSAGE_PUSH_BURST 单用常量stores/ai.ts:38 仅 watch(:93) 一处用,按风格可内联为 2 + 行内注释。两可(常量名助读),属风格偏好 — src/stores/ai.ts:38 - B-260615-52 [P3] —
useAiSend.ts:126 进 busy 分支后判断多余,直接赋值幂等 — src/composables/ai/useAiSend.ts:126-128if(!state.streaming) state.streaming=true冗余判断 - ✅(batch39·2026-06-16·workflow ww4wb66br) B-260615-53 [P3] —
watch(messages,{deep}) + JSON.stringify 全量快照已优化(snap 剔除 len/id/role 只留 n+tc,流式 delta 不再触发 body 重算,deep watch 保留 toolCalls 嵌套检测)。实际 watch 在 AiChat.vue:1128-1146(非 835) AiChat.vue:835 长+多 toolCalls 时每变更 deep 遍历+序列化。snap 短路兜底 + MESSAGE_CAP=200 封顶,典型场景可控,待超长会话反馈再优化 — src/components/AiChat.vue:835-863 - 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 输出。
🔧 2026-06-17 全量代码缺陷扫描(Rust后端+DAG引擎·仅走查·未实施)
周期性全量缺陷扫描(session-role-diagnose-only)。Rust 后端 11 项已完成;Vue 前端 + DAG 引擎因 API 速率限制失败待重跑;aichat 交互层扫描进行中(提高优先级,结果追加到下方「aichat 交互体验」专区)。
论证原则:每条经 file:line 源码佐证 + 触发条件分析 + 修复风险评估。只记录确定性 bug 或高概率问题。
🔴 P0 — 确定性 Bug(3 项·建议立即修复)
BUG-260617-01→ 已提升至 P0 阻断性 bug 区(🚨 立即修),见上方 L47 用户报错证据(2026-06-17): ① Anthropic:[1214][messages 参数非法。请检查文档。]→ 无 HTTP 数字前缀 → 走文本分支 →\|\| true→ retryable=true → 空耗 4 次重试 ② DeepSeek:400 Bad Request: {"error":{"message":"The reasoning_content in the thinking mode must be passed back to the API"}}→ 同理可能命中 根因: 审查 agent 误认为"400/403 走数字码 :424 is_status_retryable 返 false"。但实际 provider 错误消息格式多样——Anthropic 中文错误无 "HTTP 400" 前缀,直接落入\|\| true兜底分支。 修法: 移除\|\| true,unknown 默认false;同步修改单测 classify_unknown_retryable 断言为assert!(!classify_status_or_class("unknown"))。— src-tauri/src/commands/ai/stream_recv.rs:433 + :474-478(单测)BUG-260617-12→ 已提升至 P0 阻断性 bug 区(🚨 联动 BUG-01),见上方 L48 根因: 全代码库零处理 reasoning_content/thinking 字段:df-ai-core/src/provider.rs:22-39CompletionRequeststruct 无 reasoning_content 字段df-ai-core/src/provider.rs:92-99ChatMessagestruct 无 reasoning_content 字段crates/df-ai/下 anthropic_compat.rs / openai_compat.rs 零命中 reasoning|thinking 影响: 使用 DeepSeek thinking 模型时每次请求必 400,且被 BUG-01 (\|\| true) 放大至 4 次无效重试(浪费 token + 时间)。 修法方向: ① CompletionRequest 加reasoning_content: Option<String>+ ChatMessage 同加 ② agentic loop 重试时从上一轮 response 提取 reasoning_content 透传 ③ openai_compat 构建请求时映射到 API 的 reasoning_content 字段。— crates/df-ai-core/src/provider.rs:22-39,92-99 + crates/df-ai/src/openai_compat.rs
- BUG-260617-02 [P0] —
file_info全量读大文件到内存再截断前 8KB —tool_registry.rs:1125-1126tokio::fs::read(path)将整个文件读入内存(注释说">2MB跳过避免全量读"但实际先全量读再取前 8192 字节做二进制检测)。>2MB 文件触发 OOM 风险。修法:用File::open+read_exact()仅读前 N 字节,或加.take(8192)截断流式读取。— src-tauri/src/commands/ai/tool_registry.rs:1125-1126 - BUG-260617-03 [P0] — 路径遍历防护可被编码绕过 —
tool_registry.rs:78-95只做字符串..检测 + 小写化,URL 编码%2e%2e/ Unicode 同形字符可绕过。修法:先percent_decode再做词法..分段归一化检查;对不存在路径(new file) 也应规范化校验。— src-tauri/src/commands/ai/tool_registry.rs:78-95
🟡 P1 — 高概率问题(5 项·建议本轮修复)
- BUG-260617-04 [P1] —
ai_chat_force_send→ai_chat_send双锁竞态窗口 —commands.rs:763force_send 先 lock 复位 generating→释放锁→调 ai_chat_send 再 lock。两锁之间 stop IPC 可插入打断"原子复位+发送"语义;若 ai_chat_send 因 generating=true 被 reject → 前端状态不一致。修法:force_send 内联 spawn run_agentic_loop(同 regenerate/edit 模式),不走 ai_chat_send 门控。— src-tauri/src/commands/ai/commands.rs:744-763 - BUG-260617-05 [P1] —
try_continue_agent_loop4 次独立 lock 非原子化 —agentic.rs:943-1036至少 4 次state.ai_session.lock().await,每次 release 后其他 IPC 可修改 session → 续跑判断基于过时快照(如步骤 1 判 should_continue=true → 步骤 2 间 user 点 stop → 步骤 3 仍续跑)。修法:单次 lock 内完成所有字段读写,或引入结构化快照一次取出。— src-tauri/src/commands/ai/agentic.rs:943-1036 - BUG-260617-06 [P1] —
accumulate_tokens整数溢出无 saturating 保护 —conversation.rs:49-51old.unwrap_or(0) + add as i64长期对话累积接近 i64::MAX 后翻负。修法:改用.saturating_add()或类型改为u64。— src-tauri/src/commands/ai/conversation.rs:49-51 - BUG-260617-07 [P1] —
generate_diffLCS O(n*m) 内存爆炸 —tool_registry.rs:32-75标准 DP diff,5000 行输入 ≈ 200MB DP 表。虽有 changes>300 截断但截断前已分配计算完毕。修法:超长输入(均>1000行)跳过 LCS 改用 Myers diff 或直接返回截断 diff。— src-tauri/src/commands/ai/tool_registry.rs:32-75 - BUG-260617-08 [P1] — Lagged 循环每次重建 WorkflowRepo 连接池 —
workflow.rs:204forward 循环内每次WorkflowRepo::new(&forward_db),高频 Lagged 场景可能连接数暴增。修法:Repo 提到循环外创建一次或传 Arc。— src-tauri/src/commands/ai/workflow.rs:204
⚪ P2 — 关注项(3 项·低优先级)
- BUG-260617-09 [P2] — 递归深度 max_depth 由 LLM 参数控制无上限 —
tool_registry.rs:1391+/1511+LLM 可传入极大值,虽有 entries 上限隐式约束但防御不完整。修法:clamp 到合理范围(如 1-10)。— src-tauri/src/commands/ai/tool_registry.rs:1391-1545 - BUG-260617-10 [P2] — skills.rs OnceLock 初始化用同步 std::fs 阻塞 runtime —
skills.rs:69,168首次 skills 查询阻塞 tokio runtime 数十ms。影响极小(仅首次),但与规范偏差。修法:spawn_blocking包裹或接受当前行为。— src-tauri/src/commands/ai/skills.rs:69,168 - BUG-260617-11 [P2] —
read_file工具无 offset 时全量返回大文件 —tool_registry.rs:787-794无 offset 时content.clone()全量返回(1865 行/101KB 的 tool_registry.rs 自身即触发此问题)。修法:无 offset 默认返回 500 行 + has_more 提示翻页。关联 memory:read-file-pagination-needed(已记录方案)。— src-tauri/src/commands/ai/tool_registry.rs:787-794
📊 aichat 交互缺陷扫描统计(2026-06-17)
| 严重度 | 数量 | 编号范围 |
|---|---|---|
| 🔴 P0 | 1 | UX-01 (消息重叠) |
| 🟡 P1 | 9 | UX-02~11,13 |
| ⚪ P2 | 6 | UX-14~19 |
| LOW | 8 | UX-20~27 |
| INFO | 1 | UX-28 |
| 总计 | 25 | (含 4 项历史记录提升优先级 + 21 项新扫/确认) |
Top 5 推荐 immediate fix(ROI 排序):
- BUG-01 (
|| true→ false) — Rust 后端,一行改动消除无效重试 - UX-09 (startListener 异常吞噬) — 3 行 try/catch,防止事件黑洞
- UX-10 (AiError 不清 pendingApprovals) — 一行赋值消除残留 UI
- UX-11 (modelOverride 泄漏) — 注释已有要求,只缺实现
- UX-03 (copyMsgContent 误报) — catch 分支改文案
长期 / 待需求驱动(不进看板主线)
- 裁剪/压缩消息按需召回(Query Function + 分层存储)
- 停止生成 idle 即时优化(
tokio::sync::Notify替代 120s 轮询) - 模型能力系统 Phase 3(Agent 内智能路由 + 成本预算 + 模型级联)
node_executions全表 list 命令(当前只写不读)do_promotecrate 层 TODO(promotion.rs,现走前端闭环)
二次归档(2026-06-18 销账后, 整段全 [x]/纯历史迁此)
🔧 2026-06-17 知识库列表首次空(确定性 bug·根因确诊)
用户报:打开系统进知识库列表数据空,加载完成后仍不显示;需点"待处理"再回"知识库"才显示。session-role-diagnose-only,排查链路根因确诊。
根因(确定性):stores/knowledge.ts:31 _itemsReqSeq 序号被 loadList(:62)/loadCandidates(:80)/search(:92) 三者共用(都 ++_itemsReqSeq),设计为"丢弃旧响应防竞态",但漏了并发覆盖场景:
onMounted(Knowledge.vue:521-523)并发调用 loadList() + loadCandidates():
- loadList 发起(seq=1, await list)
- loadCandidates 发起(
++_itemsReqSeq→2,覆盖) - loadList 响应回来 → knowledge.ts:67
if (seq !== _itemsReqSeq)→ 1≠2 → return 丢弃 →state.items永空 - loadCandidates 响应 → seq=2 匹配 → 写
state.candidates
→ 首次进入 listItems computed(Knowledge.vue:297-298 读 store.items)空。点"待处理"(inbox)→ switchTab loadCandidates;再回"知识库"(library)→ switchTab 单独 loadList(无并发)→ seq 匹配→写 items→显示。完美解释"切走再回才显示"。
注释 knowledge.ts:29-30「items 序号覆盖 loadList/loadCandidates/search;candidates 与 items 共用同一队列」=有意共用序号,但没料到 loadList 被并发 loadCandidates 覆盖丢弃。
修法(确定性·唯一正确解·低风险):分序号——loadList/search(都写 state.items)共用 _itemsSeq;loadCandidates(写 state.candidates)用独立 _candidatesSeq。各自防竞态,互不丢弃。改 knowledge.ts:31 拆两序号 + loadList/search/loadCandidates 各用对应序号。
- ✅(2026-06-18·workflow fix-batch-260618 agent A·主代独立核验 vue-tsc EXIT 0) 🚨 B-260617-18 [P1] — 知识库列表首次进入必空(已修)。
_itemsReqSeq序号 loadList/loadCandidates/search 共用(knowledge.ts:31/:62/:80/:92),onMounted 并发 loadList+loadCandidates 时 loadList 的 seq 被 loadCandidates 的++覆盖 → loadList 响应 :67seq !== _itemsReqSeq丢弃 → state.items 永空 → 列表空。切 tab 再回(switchTab 单独 loadList 无并发)才显示。确定性 bug,修法:拆_itemsSeq(loadList/search) +_candidatesSeq(loadCandidates) 分队列防互相丢弃。— src/stores/knowledge.ts(:31 序号共用·:62/:80/:92·:67 丢弃逻辑) · src/views/Knowledge.vue(:521-523 onMounted 并发)
🔧 2026-06-18 Tasks 状态筛选窄屏换行布局(UI 调整·未实施)
用户报:任务队列(Tasks 页)状态筛选窄屏换行时,要图标独占上行、文字独占下行,不要图标+文字同行换行错乱。session-role-diagnose-only,定位 + 修法。
当前(Tasks.vue):
- filter-btn 模板 :34
{{ s.icon }} {{ s.label }}—— 图标文字纯文本拼接(emoji+空格+文字),无独立元素。 - filter-btn CSS :297-306 —— 无 display flex(button 默认),无响应式布局。
- filter-group :287-291
display:flex无 flex-wrap(nowrap),8 个状态按钮窄屏排不下溢出。
问题:窄屏按钮内容 图标 文字 在空格处断行 → 图标行1左、文字行2左,错乱(用户所见)。
修法(4 步):
- 模板拆 span:
<span class="filter-btn-icon">{{s.icon}}</span><span class="filter-btn-label">{{s.label}}</span>(图标文字独立元素,便于 flex 控制) - filter-btn CSS 加
display:inline-flex; align-items:center; gap:4px(宽屏 row 图标左文字右) @media (max-width: 断点)filter-btnflex-direction:column; gap:2px(窄屏图标上文字下)- filter-group 加
flex-wrap:wrap(按钮可换行,否则 nowrap 窄屏溢出)
断点按实际窄屏宽度定(filter-bar wrap 触发附近,DevTools 量)。
- ✅(2026-06-18·workflow fix-batch-260618 agent B·主代独立核验 vue-tsc EXIT 0) B-260617-19 [P3] — Tasks 状态筛选窄屏换行布局(已修)。filter-btn 图标文字纯文本拼接(:34)+ 无 flex(:297-306)+ filter-group nowrap(:287-291),窄屏换行错乱。修法:模板拆 span + filter-btn inline-flex + @media column + filter-group flex-wrap。— src/views/Tasks.vue(:34 模板·:287-291 filter-group·:297-306 filter-btn)
🔧 2026-06-18 AI Chat 对话列表"更早"折叠(功能增强·未实施)
用户需求:AI Chat 侧栏对话列表更早的对话默认折叠,点击展开。session-role-diagnose-only,定位 + 修法。
当前(AiChat.vue):
groupedActive(:1626-1645)已按 today/yesterday/earlier 时间分桶(timeBucket :1608 按日历日)。- 但分组标题(:83
.ai-conv-group-title)纯文本,无折叠交互,三组全展开。 - earlier 组平铺所有老对话。
修法(5 步):
- 折叠状态:
const collapsedGroups = ref<Set<string>>(new Set(['earlier']))(默认 earlier 收起)。 - 标题加交互(:83):
<div class="ai-conv-group-title" @click="toggleGroup(group.key)"><span>{{ collapsedGroups.has(group.key) ? '▸' : '▾' }}</span>{{ group.label }}<span class="ai-conv-group-count">{{ group.items.length }}</span></div>。 - items 折叠(:84 v-for conv):加
v-show="!collapsedGroups.has(group.key)"。 toggleGroup(key):切换 Set(仿 :2554 toggleSegment 模式)。- CSS:标题
cursor:pointer+ hover + 折叠图标/计数样式。
可选增强:localStorage 持久化折叠态跨刷新;yesterday 也默认折叠(视偏好)。
- B-260618-02 [P2] — AI Chat 对话列表 earlier 组默认折叠(✅ 已实现·超出原诉求:三组 today/yesterday/earlier 皆可折叠 + earlier 默认折叠 + 数量 + 空占位,见 UX-260618-00 细分③)。groupedActive 已按 today/yesterday/earlier 分桶(:1626),但标题(:83)无折叠交互三组全展开。修法:collapsedGroups ref 默认{'earlier'} + 标题点击 toggle(▾/▸+count)+ items v-show 折叠 + toggleGroup + CSS。— src/components/AiChat.vue(:83 分组标题·:1626-1645 groupedActive·:1608 timeBucket)
🔧 2026-06-18 走查·ToolCard run_command 增强(含 P3 文案·未实施)
核验工作区 ToolCard.vue(run_command 命令行常显+可折叠输出)+ ToolCardList.vue(全局收起下沉+首卡折叠 bug 修复)。session-role-diagnose-only。
ToolCard.vue run_command 增强 ✓:命令行常显($ command)+ 可折叠输出(stdout+stderr 合并 commandOutput),失败默认展开看 stderr / 成功折叠(watch status immediate)。commandOutput 空隐藏 toggle,shortCmd 首行截断。逻辑正确,质量好。
ToolCardList.vue ✓ 亮点:全局收起按钮从 topbar 下沉到首个可折叠分组标题行尾巴(firstCollapsibleGi)省空间;userCollapsedCards(UX-260617-23)修复首卡 ci===0 兜底展开导致无法折叠 bug——isCardExpanded 最优先查折叠记忆,toggleCardExpand 改用 isCardExpanded 真实态判断。逻辑自洽。
- B-260618-03 [P3] — ToolCard run_command 输出折叠文案语义不准(✅ 已修:ToolCard 单卡 toggle 三处[读文件预览 :78 + 命令输出 :124/126]用
aiTool.expand单数,ToolCardList 全局 :30 保留expandAll/collapseAll复数;zh-CN 补单数expand:'展开'key,en 既有;collapse 本就单数无需新增)。toggle 按钮 + title 复用aiTool.expandAll/collapse(i18n 值"全部展开/全部收起",本用于 ToolCardList 全局收起),但此处是单个命令输出的展开/收起,应为单数"展开/收起"。修法:新增aiTool.expand/collapse单数 i18n key(en+zh-CN),ToolCard.vue toggle 文案改用单数 key。— src/components/ToolCard.vue(run_command toggle 文案 :title/@click 处)