259 KiB
DevFlow 工作看板
来源:
docs/02-架构设计/功能决策记录-2026-06-14.md「需求与待办」+PROGRESS.md各 Sprint 遗留,2026-06-14 汇总去重 + 代码核对修正。 互操作:执行走 mission-control,回写 mission_id;审查走 cr;发布走 publish-*。 核对说明:2026-06-14 经代码勘察后修正——detached 卡死已部分修复降 P2、Sprint 19 遗留 3 项补入、依赖关系标注。
交接状态(2026-06-14)
代码健康度:cargo test --workspace 全过、npx vue-tsc --noEmit 0 error(主代理独立验证,非 mission 自报)。
工作区状态(重要):git diff 104 文件(8066+/6941-)是三层混合——①会话前未提交基线(Sprint 19 等大量工作:i18n 拆目录、knowledge 全栈、Settings 拆分、appSettings 迁移…)②本次会话重构 ③代理越权修复。接手前务必 git diff 通览区分,勿整体当作单一改动提交。
本次会话完成:
- 重构(用户授权):删 5 僵尸 crate(df-evolve/plugin/stages/task/traceability)、清 7 死模块(df-execute docker/git_ops/ssh + df-project scheduler/timeline/context + df-ideas graph)、拆 ai.rs→
commands/ai/11 文件、拆 ai.ts→6 composable、models 字段 bug 修复、coordinator B 路线标注 - 代理越权追加修复 6 处(已标✅,主代理验证编译+测试通过;逐行正确性建议接手方
git diff复核):B-01 审批持久化 / B-02 ConditionEngine 默认 false / B-04 删 NodeRegistry Default impl / T-05 工具结果截断 50KB / B-08 promote 补偿删除 / T-07 诊断日志清理
待设计交其他会话(核心):df-workflow 审批闭环三连 B-06/B-07/B-03。✅ B-03 设计已完成(接手会话,2026-06-14):见 B-03-人工审批响应机制-2026-06-14.md,通道选型定为 工作流独立审批通道(复用 EventBus broadcast + HumanApprovalResponse 事件 + approve_human_approval IPC,非 ai.rs AiApprovalRequired——后者是 AI Chat 工具审批路径,与工作流节点审批是两条独立链路)。拆 B-03a(响应等待 + 超时,不依赖 B-07)/ B-03b(取消机制)。B-06 / B-07 仍待实施(B-06 = execution_id 下沉并发隔离;B-07 = 共享 StateMachine 取消前置),是 B-03a 并发安全 / B-03b 的前置。
失控代理教训:本次会话派的后台拆分代理在 stop hook 循环里失控,越权改代码/文档(先斩后奏)。接手方若再派 agent,注意约束其不碰决策记录(用户已要求手动触发)+ 限定单任务不自主续推。
待办
📋 编排推进总览(2026-06-16 汇总)
未完成待办按可执行性分 8 组(详细条目见下方各分类,勿重复记录):
| 组 | 说明 | 代表项 |
|---|---|---|
| A todo 卫生 | 矛盾清理/被取代退役标注 | ✅ 本批:AR-1 退役 / AR-8 重评 |
| B 零风险减法 | 死链清/空壳合并 | ✅ ARC-02 死链已删 / ARC-03 前提失效转重评 |
| C 需用户输入 | 阻塞,无法代办 | S-260615-01 curl 测 / S-260614-01 多开澄清 / S-260614-02 实测重评 |
| D 功能增强 | 设计清晰可推进(P1) | F-15-01 审批选项 / F-15-02 task 详情 / F-15-03 分页⚠️breaking / F-15-04 卡片折叠 |
| E 架构重投入 | 需设计,非小改 | F-14-01 模型能力 / F-14-07 trait 下沉 / ARC-05·06 store 拆·循环依赖 |
| F 全局 review P2 需设计 | 安全/竞态收口 | R-PD-4/5/6/8/9/12/13 + R-PD-10 .map_err 85 处 |
| G 测试/dev 验证 | 收尾土壤 | ARC-08 dev 验证 / B-03b-R8 human 端到端 / T-14-01·02 Sprint 实测 |
| H 长期功能池 | 不进主线 | F-14-02~10 / T-14-06 Settings 拆 / T-14-11 条件引擎 / B-14-05 / B-03b-R9 |
推进原则:能并行不串行(独立子任务 8-12 并发);每批重汇总+全优先级重排+销账核对 ✅;C 组阻塞项不代办等用户。本会话首批:A 卫生 + B 清死链。
③④C类已迁(2026-06-16):需人工决策项(③产品取舍 7 / ④设计方向 7 / 重投入排期 9 / C用户实测 5)见 待决策.md,从本文件抽离职责分离。batch54 可自主批进展(2026-06-16·workflow wsfz7zl8i + 主代核查):复核-新⑪ 看门狗幽灵 ✅ 销账(清主窗口 state 视觉残留;核验更正:原 watchdog 幽灵已被 CR-260615-24 stopListener:371 clearStreamWatchdog 兜底);AE-03 diff预览 ⚠️ 降级阻塞(后端 write_file 未返 diff,待后端补,见 line 538);UX-04 断线保文 待 DEC-260616-03 授权(见待决策.md 🟡,本批不含)。
P0 — 阻断性 bug
- 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 aichat 消息全量重叠(5角度深入分析·未实施)
用户报 aichat 对话「大量重叠」「不单间距问题」。session-role-diagnose-only,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 正常但时序竞态" |
- B-260617-09 [P1] — aichat 消息全量重叠。用户报「大量重叠」「不单间距」。5 角度深入论证排除 CSS/markdown/数据/WebView2 后,最高概率根因更新为:虚拟滚动 IO 与 RO 时序竞态——IO 先于 RO 首次回调触发卸载分支时
itemMeta.height仍为 0 →:73height>0守卫失败 → 不设 minHeight → slot 塌 0 高 → 下条消息上浮重叠。次 probable:v-html DOM 破坏(marked+purify 产出异常 HTML 破坏 bubble 闭合标签)。修法方向:①IO 卸载分支加 fallback minHeight(如取 el.offsetHeight 或固定最小值 40px)②临时禁用虚拟滚动验证根因 ③DevTools 断点验证 IO/RO 时序。— src/composables/ai/useAiVirtualScroll.ts(:68-76 IO卸载分支 height守卫) + src/components/AiChat.vue(:400-401 shouldRenderMsg条件渲染) + src/composables/useMarkdown.ts(:188 renderMd sanitize)
🔧 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 新需求(已分析·待实施)
功能需求,已走查定位链路 + 方案记录,待用户确认后实施。
-
✅(batch38·2026-06-16·workflow wx5vd8ppk) F-260616-01 [P2] — aichat Agentic 最大循环轮次支持配置(Settings)。已落地(1:1 复刻 llm_concurrency,8 处改动全闭环)。现状:
MAX_AGENT_ITERATIONS=10硬编码常量(agentic.rs:33),复杂 agentic 任务未收敛即被截断,用户无法调。黄金参考:llm_concurrency——项目唯一成熟的「前端数字配置→AppState 字段→热改 command→透传 loop 形参」范式,本需求 1:1 复刻。改动 8 处:①state.rsAppState 加agent_max_iterations: Arc<AtomicUsize>(默认 10) ②agentic.rs常量改名DEFAULT_MAX_AGENT_ITERATIONS+run_agentic_loop增形参max_iterations+ 循环(L156)/超限消息(L321-332)用形参 +try_continue_agent_loop(L478) 补传 ③commands.rsai_chat_send(L114) 补传 + 新 commandai_set_agent_max_iterations(clamp 1-50 store) ④lib.rsinvoke_handler(L125 区)注册 ⑤api/ai.ts新setAgentMaxIterations(仿 L73setConcurrencyConfig) ⑥Settings.vuereactive(L615 区)+UI 数字 input(min1 max50,紧挨并发控制 L268)+watch 持久化+syncAgentMaxIterations+onMounted 推后端 ⑦i18n 补 zh/en 键。生效时机:loop 入口load取快照传值,当前 loop 锁定边界,热改下次发消息生效(防边界飘移;与llm_concurrency传 Arc 实时反映的区别)。keydf-ai-agent-max-iterations,范围 1-50 双 clamp。agentic.rs:27-32注释已预留此扩展点。低风险,复刻现成范式无新机制。— 详案见本地 plan 文件quizzical-prancing-hennessy.md。 -
✅(batch37·2026-06-16·workflow wg8x18y9y) F-260616-02 [P2] — 知识库「待审」(pending_review) 不应进第一个 tab(知识库/library)。决策 a 落地:inbox 收 candidate+pending_review,library 纯 published。现状:
Knowledge.vue顶层两 tab——library(知识库,L19) 用store.items、inbox(审核收件箱,L22) 用store.candidates。根因在数据源:①后端knowledge_list(knowledge.rs:71-81)status=None默认返回「非 archived 全部」= candidate + pending_review + published;②前端 storeloadList(knowledge.ts:60) 无 status 参数调它 →state.items含 pending_review;③knowledge_list_candidates(knowledge.rs:211-218) 只列candidate→ inbox 不含 pending_review。结论:pending_review 条目当前只在 library tab 与 published 混杂显示,inbox 反而不收它。改动方向(待定,含决策点需用户定):①最小改——后端knowledge_list默认排除范围从archived扩到archived + pending_review(或前端loadList后 filter 掉 pending_review,store 层兜底);②决策点:pending_review 从 library 排除后该去哪显示?(a) 扩 inbox 收件箱含 candidate+pending_review(语义=「待处理」收件箱)(b) 单纯从 library 消失、pending_review 暂无专门视图 (c) 加第三个 tab「待审」。倾向 (a):inbox 收 candidate+pending_review,library 纯 published,职责清晰零新 tab。— Knowledge.vue(L296-299 列表数据源) + stores/knowledge.ts(loadList/loadCandidates) + knowledge.rs(knowledge_list 默认过滤 + list_candidates 范围)。待用户定②后实施。 -
✅(batch44 后端 + batch45 前端·2026-06-16) F-260616-03 [P2] — aichat 达最大轮次时询问「是否继续」+ 确认后续 loop。全闭环:后端(batch44 AiMaxRoundsReached 暂停态+continue/stop IPC)+ 前端(batch45 操作卡 pendingMaxRounds+续/停按钮)。现状:
agentic.rs:321-332达max_iterations未收敛(!converged)→ emitAiError「达到最大轮次(N 轮),Agent 可能未完成」+ 直接走完成流程(save/标题/提炼+guard.reset+AiCompleted),用户无法续,复杂任务被硬截断。需求:达上限改 emit 询问 → 前端「继续/停止」操作卡 → 用户点继续后续 loop、点停止走完成。机制高度复用工具审批暂停-恢复:审批 =AiApprovalRequired(audit.rs:353 emit)→前端pending_approvals(useAiEvents.ts:219 push)→ai_approve(commands.rs:122)→try_continue_agent_loop(agentic.rs:388 续 loop)。本需求同构。改动骨架:①后端agentic.rs:321-332达上限分支改:不 emit AiError+完成,改 emit 新事件AiMaxRoundsReached+ 保持generating=true(暂停态,仿审批等待) ②mod.rs:71事件枚举加AiMaxRoundsReached { conversation_id }③新 commandai_continue_loop(用户点继续)→调try_continue_agent_loop续 loop(续跑轮数:重置计数再跑max_iterations轮——末轮 tool_result 已在 messages,续跑首轮自动回传 LLM,语义自洽) ④新 commandai_stop_loop(点停止)→走原完成流程(save+Completed) ⑤前端useAiEvents.ts加case 'AiMaxRoundsReached'(push 待确认态,仿 pendingApprovals) ⑥AiChat.vue加续跑操作卡 UI(继续/停止按钮)。设计决策点(待定):(a) 续跑轮数=重置计数再跑 max_iterations 轮(倾向) vs 单步 1 轮 (b) 防无限续:无硬上限(用户主动授权=同意烧 token,倾向) vs 加累计上限 (c) 事件/卡片:新增AiMaxRoundsReached+独立操作卡(倾向,语义清晰) vs 复用AiApprovalRequired(args/name 不搭)。关联 F-260616-01(max 可配)——两者协同=用户完全掌控 loop 边界,建议同批实施。— agentic.rs(达上限分支 L321-332 + try_continue 复用) + mod.rs(事件枚举) + commands.rs(新 command) + useAiEvents.ts + AiChat.vue。待用户定决策点后实施。 -
✅(batch42·2026-06-16·workflow w95hzp9tw) F-260616-04 [P1] — run_command 超时→LLM 重试→反复审批循环。已根治(tool_registry.rs 超时标注「勿盲目重试」+ DEFAULT_RUN_COMMAND_TIMEOUT_SECS 常量,handler map_err 改写)。根因链(探索确认):超时(默认 60s,
tool_registry.rs:491 unwrap_or(60))→tool_result 回传 LLM(commands.rs:168Err 分支)→LLM 重试同命令→新 tool_call_id(stream_recv.rs:176每轮新 UUID)→重新 insert pending(audit.rs:343)→重新审批,循环。症状「再过一会又提示 Run Command」即此。修复方向:①超时 tool_result 明确标注「命令执行超时({n}s),进程已终止,勿盲目重试同命令;确需更长时限重发时提高 timeout_secs」②run_command 默认超时可配(LLM 已可传timeout_secs参数,但默认 60 + 不主动调)③配合 F-260616-05 去重根治。— tool_registry.rs(超时标注) + commands.rs(回传内容)。 -
✅(batch53·2026-06-16·workflow wbh8mu1iq·run_command 循环核心解决) F-260616-05 [P1](共性根治)— agentic loop 工具调用去重缺失。batch53 落地 High risk 审批前去重(audit.rs find_cached_high_risk_result 反向扫 messages + args JSON 规范化匹配 + 排除 pending 占位 + decided_by=auto_dedup,解决 run_command 反复审批循环,F-04 超时标注 + batch53 去重双保险)。完整 agentic loop 通用去重(search_files 重复卡片 + 滑动窗口指纹 + 决策点)③类待用户定(窗口/粒度/措辞)
-
✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-06 [P2] — 「未找到挂起的审批」体验。全闭环落地:①
commands.rs:217ai_approve 幂等——内存 pending_approvals remove None 时查ai_tool_executions审计表,已 executed/rejected/failed 返回「已处理({status})」非报错 ②useAiSend.ts:305审批按钮防抖——_pendingApprovalIdsSet 守卫,同 id 短期多次点击只发一次 IPC,finally 块清理。— src-tauri/src/commands/ai/commands.rs + src/composables/ai/useAiSend.ts -
✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-07 [P2] — 流式对话失败自动重试。全闭环落地:①
agentic.rsstream_llm 调用处包重试循环(max_retries 形参,退避 1s→2s→4s,持 permit 防挤占) ②每重试 emitAiStreamRetry事件(attempt/max_attempts) ③耗尽走原有 guard.reset+return ④state.rs 加agent_max_retries:Arc<AtomicUsize>(默认3) ⑤新 IPCai_set_agent_max_retries(clamp 0-10) ⑥前端 api/ai.ts + types.ts(AiStreamRetry variant) + Settings.vue(input 0-10+debounce sync+i18n zh/en)。设计决策:一律尝试重试(简化策略,由退避控制成本),MidStream 重试语义等价于"看不到完整回复再试一次"。— src-tauri/src/commands/ai/agentic.rs + state.rs + commands.rs + lib.rs + src/api/ai.ts + types.ts + src/views/Settings.vue + src/i18n/{zh-CN,en}/settings.ts -
✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-08 [P2] — AI 数据获取工具 offset 分页 + 探总量。全闭环落地:①6 工具(list_projects/tasks/ideas/trash/read_file搜索模式/search_files)全部补 offset+limit 参数,返回 items/total/has_more 替代 truncated ②新增
get_project_count+get_task_count探总量工具 ③knowledge_search 解钳min(3)→min(20)。— src-tauri/src/commands/ai/tool_registry.rs + src-tauri/src/commands/knowledge.rs -
F-260616-09 [P1/架构·A路线✅(batch60·2026-06-16·workflow whae812z5+主代核查),B多会话并发待立项] — AiSession 单例 → 多会话并发架构(多窗口来回切 + 并行执行)。用户需求(3 条消息强化):「应用级支持同时具备多会话」+「同时具备多会话的能力」+「多个会话窗口来回切,并行执行」。现状(Explore 走查确认):
AiSession应用级单例(state.rs:164ai_session: Arc<Mutex<AiSession>>,init:205),messages/generating/stop_flag/pending_approvals/agent_language 全一份。设计「单例 + 软隔离」——generating 互斥(同一时刻只一个对话能跑) + 切换旧 loop 退出(agentic.rs:177-190B-260615-11) + 切换时 generating→readonly(commands.rs:873-881)。llm_concurrencyper_conv 因此退化为单对话内并发(state.rs:93-96注释已标注预留改 HashMap)。memory 已标记「AiSession 单例未动」架构债(aichat-arch-extensibility)。残留 bug(表象=新建会话上下文残留):前端newConversation(useAiConversations.ts:31-41) 漏清 queue/generatingConvId/agentRound/searchQuery;后端ai_conversation_create(commands.rs:784-821) 漏清 agent_language/stop_flag(设 true 停旧 loop 没复位 false)。用户「并行执行」明确=B 方向(A 只解切换残留不满足并行)。分档:A 短期隔离修复(补漏清字段,维持单例软隔离,解残留,低风险立即做,B 前置) + B 多会话并发架构(单例 →HashMap<conv_id, SessionState>,每会话独立 messages/generating/stop_flag/pending_approvals + llm_concurrency per_conv 改真 per-conv HashMap + loop 内校验改 conv_id 索引各自 session + 切换不退出旧 loop 各自跑完真并发 + 事件路由核对 conversation_id 全覆盖)。决策点(待定):(a) A 先做解燃眉 vs 直接 B——倾向 A 先(残留真 bug 立即解,B 架构另立项) (b) B 形态——AiSession 整体 HashMap vs 拆字段(messages/pending 已可按 conv reload,只拆 generating/stop_flag per-conv)——倾向后者侵入小 (c) B 并发上限——复用 llm_concurrency.global 限并发会话数防 token 暴增——倾向是 (d) 多窗口 UI 形态——(d1) 单面板侧栏会话列表切换+后台并行(改动小) vs (d2) 每会话独立 Tauri 窗口(useAiWindow.tsdetached 已有基础可扩,改动大但「窗口来回切」语义最贴)——待用户定,倾向先 d1 后 d2 (e) 旧 loop 切换处理——不退出各自跑完(真并发,倾向) vs 仍退出。— state.rs(AiSession 去单例化) + commands.rs(create/switch 重构 + 漏清补) + agentic.rs(:177-190 校验改 conv_id 索引) + context.rs(ContextManager per-conv) + useAiConversations.ts(漏清补) + stores/ai.ts(per-conv state) + llm_concurrency per_conv + useAiWindow.ts(若 d2 多窗口)。B 是大改架构升级,需单独设计文档 + 立项。待用户定 A 先做 / B 立项 / 多窗口 d1·d2 方向。 -
✅(batch58·2026-06-16·workflow w2dw65maz+主代核查,vue-tsc 0err) F-260616-10 [P1·bug] — provider 切换/默认值重启·刷新后失效(前端 activeProvider 不同步 DB 默认)。用户实测:Settings 设 A 为默认 → 重启 → 默认变 B(列表第一个)。根因全在前端,后端链路正确(独立核验不信 PROGRESS.md:「已修复」声明不可信):
ai_set_provider(commands.rs:747-774) 互斥落库✅ +update_full(crud.rs:1127) SQL UPDATE 含is_default✅ +get_active_provider(prompt.rs:9-32) 走DB.is_default✅。根因链:①重启后前端store.activeProvider = null(stores/ai.ts:53,无 localStorage 持久化 + 无启动恢复) ②loadProviders(useAiPanel.ts:91-93) 只拉列表不同步 DB 默认 → activeProvider 仍 null ③UI 错觉:activeProviderName(AiChat.vue:1169-1172) null 时 fallbackproviders[0](列表第一个) ≠ 用户设的 A → 「默认变另一个」 ④真覆盖:用户点 provider bar 切换cycleProvider(AiChat.vue:1180-1185),findIndex(activeProvider=null)返回 -1 →next = ps[(−1+1)%len] = ps[0]= 列表第一个 B →setProvider(B)→ 后端互斥落库把DB.is_default从 A 真改成 B。即使用户不点切换,UI 也显示 providers[0] 而非 A。修复方向:①loadProviders后同步activeProvider= DB.is_default 的 provider——后端加ai_get_active_providerIPC 返回当前默认 id,或listProviders返回值带active_provider_id②cycleProviderfindIndex=-1 陷阱修正(null 时应从 DB 默认起算,非 ps[0]) ③前端 activeProvider 持久化(localStoragedf-ai-active-provider,对齐df-ai-active-conv模式)。— useAiPanel.ts(loadProviders 同步默认) + AiChat.vue(:1169 activeProviderName fallback + :1180 cycleProvider findIndex 陷阱) + commands.rs(新 ai_get_active_provider IPC) + api/ai.ts + stores/ai.ts(activeProvider 持久化)。确定性 bug,低风险,可立即修。 -
✅(batch60合批·2026-06-16·主代核查,cargo 0err) F-260616-11 [P1·决策a累计计数] — 审批后 iteration 重置致 max_iterations/token 超配。决策(2026-06-16):a 累计计数(try_continue 不重置 iteration,跨审批续跑延续累计)。已实施:
iteration_used:usize累计字段(mod.rs:215/230AiSession+new init 0)+start_iteration:usize参数(agentic.rs:114run_agentic_loop signature +:176loop 边界start_iteration..max_iterations+:210一致性块iteration_used=iteration+1+:578try_continue 加参 +:675spawn 透传)+三处 reset(commands.rs:55/152/412ai_chat_send/ai_regenerate/ai_edit_last 新生命周期)+ai_approve 两处累计读 iteration_used(:260-262拒绝续跑 +:313-315通过续跑)+ai_continue_loop:601reset+传0(F-03 决策a 达max重计区分)。两路径严格:审批累计(ai_approve) vs 达max重计(ai_continue_loop,F-03 a 用户授权重来) vs 新消息 reset。边界case:start≥max loop 空区间→converged=false→AiMaxRoundsReached(防无限审批烧 token,正 F-11 语义)。原:try_continue_agent_loop重 spawnrun_agentic_loopiteration 从 0 重计致多次审批反复重跑 max_iterations 轮 token 失控。— agentic.rs + commands.rs + mod.rs。 -
F-260616-12 [P2/依赖F-09] — retry 持 permit 不释放(多会话隐患)。F-260616-07 落地的流式重试循环(
agentic.rs:238-239)重试期间持有 global+per_conv permit 不释放(注释「防新请求挤占」)。当前 AiSession 单例 + 主 loop 串行无影响,但 F-260616-09 多会话并发后,重试期间阻塞其他对话 LLM 调用。方向:多会话落地时核对——主 loop 串行下重试持 permit 防自己挤占无意义,倾向重试不持 permit 或仅持 per_conv。— agentic.rs:238 + F-260616-09 多会话架构。依赖 F-260616-09 立项后一并处理。
💡 2026-06-16 新需求(UX 交互优化·分析完成·待实施)
用户实时反馈的交互优化需求,已走查定位链路+方案记录。
-
UX-260616-01 [P2] — 工具调用失败时「重试」提示语义模糊。用户场景:Run Command 执行失败(exit_code 255,
headnot recognized on Windows),UI 显示「⚠ 调用失败,正在重试(1/4)…」+「重试」按钮。根因分析:「正在重试(n/m)」是AiStreamRetry事件(F-260616-07 流式 LLM 重试),通过useAiEvents.ts:177-186更新错误气泡内容;工具执行失败(run_command 等 Low 风险工具)走 AR-6 路径(audit.rs:640-652)→ emitAiToolCallCompleted(result=错误信息)→ 不触发 AiError 不触发 AiStreamRetry,错误包在 tool_result 回传 LLM 自行决策。问题:①用户看到的是「LLM 流重试」提示,非「工具执行失败」提示,语义错位 ②确定性失败(命令语法错/exit_code 非0)重试同命令必再败,「重试」按钮误导 ③工具实际结果(stdout/stderr/exit_code)已显示在 ToolCard 内,错误气泡的「重试」是消息级 regenerate(重新生成整条回复),非工具级重试。改动方向(待定):a)错误气泡区分两类——流式重试中显示「正在重试(n/m)…」(现有);工具执行失败不弹错误气泡(结果已在 ToolCard)或气泡文案改为「工具执行失败,查看上方结果」b)Fatal 类错误(4xx/鉴权/参数)气泡去掉「重试」按钮或改为「去设置」c)AiStreamRetry事件更新气泡时附带retryable标识,前端据此显隐重试按钮。—— useAiEvents.ts(:177-186 AiStreamRetry case) + AiChat.vue(错误气泡 UX-03 操作栏) + i18n ai.aiStreamRetry -
UX-260616-02 [P3] — 「全部收起」与搜索/技能区合并一行。当前 ToolCardList.vue 有两个独立行:①batch-approve 栏(line 4-11,pendingCount>0 时显示)②global-toggle 栏(line 14-17,collapsibleGroupCount>0 时显示「▾ 全部收起」)。用户要求将「全部收起」与附近的操作元素(search files 搜索文件/技能触发等)放到同一行,减少垂直空间占用。需确认:「search files」具体指哪个 UI 元素(i18n
ai.searchFiles渲染位置需定位,可能在输入框上方 skill 栏或工具卡区域)。改动方向:global-toggle 从独占行改为 inline 元素,与相邻操作栏 flex 同行。—— ToolCardList.vue(:13-17 template + :278-295 CSS .ai-tool-global-toggle) -
✅(workflow wfryptv2t·代理A核验已落地·2026-06-17·主代独立grep核验非误判) UX-260616-03 [P2] — 对话内容输出时自动收起旧工具卡片分组。已落地:①触发时机扩展——AiChat.vue:2093 agentRound watch(AiAgentRound 新轮次→collapseAllToolLists(buildActiveToolIds))收起上一轮;既有 messages deep watch(:2085)覆盖新消息追加+toolCall状态翻转;触发链 useAiEvents.ts:169
if(event.round>0) state.agentRound=event.round+ stores/ai.ts:75 agentRound字段。②collapseInactive增强——ToolCardList.vue:71 userExpandedCards 记忆态 Set,:180 isCardExpanded 优先读它(用户主动展开不被自动收起),:220 collapseInactive 仅清 expandedCards/expandedTools 不清 userExpandedCards。③CSS过渡留TODO——:373 注释:flex gap对零高item贡献双倍间距+auto→0不可纯CSS,需Transition JS钩子,属单卡级折叠重构,本轮不做(不影响功能收起)。需求:当 AI 输出新内容(流式 delta / 新工具调用 / 新轮次)滚动到下方时,上方已完成的旧消息中的工具卡片分组自动收起,保持视野聚焦当前内容。现状:机制已存在——ToolCardList.collapseInactive(activeIds)(:191-199)供父组件调用,AiChat.vue:1657-1661已有 watch 调用(refs 数组逐实例 collapseInactive)。增强方向:a)触发时机扩展——当前可能仅在特定时机调用,可扩展到:AiTextDelta新消息开始时 +AiAgentRound新轮次时 + 用户滚动接近底部时(跟随阅读位置自动收起已读内容)b)平滑过渡——收起加 CSS transition(高度动画 200ms)避免内容突然消失跳变c)可选:记忆用户手动展开的分组不自动收起(expandedCards Set 区分用户主动展开 vs 默认态)。—— ToolCardList.vue(collapseInactive + CSS transition) + AiChat.vue(watch 触发时机扩展) -
✅(batch66·2026-06-16·22d866d,formatToolResult 12工具覆盖+i18n 18key) UX-260616-04 [P1] — 审批通过后工具卡片仅显示裸 JSON。用户实测:审批前
pending_approval态渲染友好(参数键值对+风险提示+审批按钮),审批通过后completed态 body 仅显示一段 JSON 数据。根因:ToolCard.vue模板渲染链对read_file/list_directory/write_file有专门 UI 分支,其余工具全部走通用兜底(L110v-else-if tc.result && tc.status === 'completed')→formatToolResult(tc)→ 兜底return formatJson(r)裸 JSON 美化输出。代码核对:①formatToolResult(L270-289)只特化write_file,其余全formatJson②toolResultSummary(L476-498,header 摘要)覆盖list_tasks/list_projects/list_ideas/create_project/create_task/create_idea/update_project/delete_project/restore_project/purge_project/run_workflow共 11 工具,但 body 的formatToolResult不共享此逻辑 ③两函数覆盖范围严重不一致(header 有摘要但 body 展开仍裸 JSON)。后端返回值核对(tool_registry.rs):update_task→{id,field,updated}/delete_task→{deleted,id}/delete_file→{path,deleted,...}/run_command→{command,exit_code,stdout,stderr,...}(可能很大) /patch_file→{path,changed,size_diff,...}/append_file→{path,bytes_written,new_size}/rename_file→{from,to,renamed,...}/search_files→{path,pattern,results,total,...}/file_info→{path,exists,size,lines,...}/bind_directory→{id,path,stack,bound}/advance_task→{...}——全部走裸 JSON。修复方向:统一formatToolResult的 switch 覆盖全部工具产出人类可读摘要(复用toolResultSummary已有 i18n key + 补缺失 key 如commandOk/commandFailed/patchedFile/appendedTo/renamed/foundN等),同时补toolResultSummary缺失 case(update_task/delete_task/delete_file/run_command/patch_file/append_file/rename_file/search_files/file_info/bind_directory/advance_task)。——src/components/ToolCard.vue(formatToolResultL270 +toolResultSummaryL476) +src/i18n/{zh-CN,en}/aiTool.ts(补 key) -
✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a逐条续发) UX-260616-05 [P1] — 打断按钮只停当前回复 + 队列保留续发(不清队列)。用户需求:发送队列有消息时,打断按钮应只打断当前回复,并把队列中的多条消息一并发送给后续对话。现状:
stopChat(useAiSend.ts:365-370) L368state.queue = []清空队列 → 用户生成中排队的消息随打断被静默丢弃。技术底座已就绪(已核后端):stopChat→aiApi.stopChat→后端stop_flag.load(agentic.rs:178)→收尾save_conversation(:185 已生成文本入库)+emitAiCompleted(agentic.rs:190,注释明确「保证前端收事件时后端已可接下一条,发送队列续发不被拒」)→前端ai-drain-queue→drainQueue(useAiSend.ts:228) shift 队首续发。最小修复(确定):删 stopChat L368state.queue=[],打断后 AiCompleted 自然触发 drainQueue 续发队首;当前未完成回复已入库(:185)保留为截断回复,无需额外处理。决策点(待定)——「队列多条一并发送」语义:(a) 逐条续发(删清队列即可,零改 drainQueue,AiCompleted 链式触发每条,保各条上下文独立)vs (b) 合并成一条消息(队列多条 text 拼接送,需改 drainQueue 为 bulkDrain 或合并 text 调一次 sendMessage)。倾向 (a)(逐条保上下文独立+零改链路;"一并"理解为「都送出不丢弃」非字面合并),待用户确认。— src/composables/ai/useAiSend.ts(stopChat:365 + drainQueue:228) + 后端 agentic.rs(:178/:190 stop 收尾 emit AiCompleted 已就绪)。 -
✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策只编text) UX-260616-06 [P2] — 队列消息支持编辑。用户需求:队列中的消息支持编辑。现状:队列 UI(AiChat.vue:511-516) 每项只有 × 删除(cancelQueued:353),无编辑入口;队列项结构
{ text, skill?, enqueuedAt }(stores/ai.ts:70)。改动(纯前端低风险):队列项加「编辑」按钮 → 点击切 inline input(单行 textarea)→ 回车/失焦写回state.queue[idx].text、ESC 取消;新增editQueued(index, newText)(useAiSend.ts) splice 写回 + store 导出 + composable return。决策点(待定):skill 是否可编辑(倾向只编 text——skill 来自发送时技能联想,编辑态不暴露 skill 改,简化交互)。i18n 补 edit/save/cancel key。— src/components/AiChat.vue(:511-516 队列项 UI + .ai-queue-item CSS:2856) + src/composables/ai/useAiSend.ts(editQueued 新增) + stores/ai.ts + i18n。 -
✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a插队,边界:stop_flag复位降级可接受) UX-260616-07 [P2/依赖 UX-05] — 队列消息支持立即发送。用户需求:队列中的消息支持立即发送。现状:队列项只能 × 删除或等 drainQueue 串行轮到,无插队。决策点(待定)——「立即发送」语义:(a) 插队=打断当前+立即发这条(splice 出该项→stopChat→sendMessage 该条;复用 UX-05 stop→AiCompleted→drain 链路,当前未完成回复走 stop 截断保留)vs (b) 移到队首等当前完成优先(splice 出→unshift 队首,drainQueue 下一轮先发它,不打断当前)。倾向 (a)("立即"字面=马上发,且复用 UX-05 链路;但须注意 splice 后调 stopChat 已不清队列(UX-05),避免已发送项重复入队)。待 UX-05 打断语义定后同批设计(共享 stop+drain 链路)。— src/components/AiChat.vue(:511-516 队列项加「立即发送」按钮) + src/composables/ai/useAiSend.ts(sendQueuedNow 新增)。⚠ 2026-06-17 审查质疑:sendQueuedNow 存在时序隐患(
await stopChat()后ai_is_generating未及时复位,紧接的 sendMessage 可能命中 L1 入队而非立即发),原结论「stop_flag 复位降级可接受」存疑,详见 B-260617-01(待核验后端复位时机)。 -
F-260616-15 [P1] — AI Chat 上下文管理增强:会话分段 + 手动压缩 + 智能裁剪。三个子需求,详见 F-15-上下文管理增强设计-2026-06-16.md。阶段1 基础部分 ✅(波3·8a142c2 is_active 白名单+push token 修正);阶段1 剩余 ✅(波5·63bff8b compress_prompt四段式+compress_via_llm+context 9辅助方法,幂等/单向不可逆/纯函数/零行为变化,df-ai 107pass);阶段2 手动功能 ✅(波6·4194842+主代核查,cargo0err+vue-tsc0+devflow79pass,2 IPC ai_chat_clear_context/compress_context+3事件 AiContextCleared/AiCompressing/AiCompressed+前端2按钮+status分组渲染+useAiContext composable,clear标archived_segment不删DB/compress LLM失败不阻塞);阶段3 自动 ✅(波8·0202b51+主代核查,cargo0err+vue-tsc0+devflow79pass,loop顶部检测budget×0.6+压缩+降级原裁剪,set_compressing成对,延迟mutate失败零副作用)。F-15 全阶段完成 ✅。
核心问题:①
ai_chat_clear全删 DB 消息不可回溯 ②build_for_request超预算直接丢弃旧消息零保留(agentic.rs:226_trimmed被忽略) ③ 无压缩能力。统一方案:
ChatMessage.status扩展两个值archived_segment(分段标记)/compressed(压缩标记),is_active()改正面白名单matches!(status, None | Some("active"))(零行为变化,新状态自动不 active),sanitize_messagesstep 0 已是唯一发送过滤点改 is_active 即隔离旧段/压缩段。push()(context.rs:174) 不计!is_active()消息 token(当前全量计入致 restore 后 token 虚高误判超预算)。① 会话分段:IPC
ai_chat_clear_context:当前 active 消息标archived_segment+ 扣除 history_tokens + 落库。不插分隔线 system(前端按 status 渲染折叠分隔条)。三元组原子标记(复用build_eviction_units分组)。② 手动压缩:IPC
ai_chat_compress_context:取 active 消息 →compress_prompt四段式(意图/决策/文件/约束) →provider.complete()非流式调用(复用title.rs:111generate_title_via_llm模式 +build_provider_for) → 原始消息标compressed+ 插入摘要 system。压缩单向不可逆,DB 原始消息保留。③ 智能裁剪(自动压缩):agentic loop 循环体顶部
build_for_request前检测:history_tokens > budget×0.6且有可压缩消息 →compress_old_messages(保护区外消息 LLM 摘要 → 标 compressed → 插摘要 system)→ emitAiCompressing/AiCompressed。LLM 失败降级原有裁剪(不阻塞 loop)。compress_old_messages是手动/自动共用的公共函数。幂等:已 compressed 不二次压缩,is_compressing()标志防重入。实施顺序:阶段1 基础(is_active 白名单+push token 修正+compress_prompt+compress_via_llm+辅助方法) → 阶段2 手动功能(2 IPC+前端按钮+渲染+i18n) → 阶段3 自动(loop 压缩+事件+前端 loading)。阶段1-2 完成用户已有完整能力。
涉及文件:
df-ai-core/provider.rs(is_active 白名单) +df-ai/context.rs(push token 修正+辅助方法) +commands/ai/prompt.rs(compress_prompt) +commands/ai/title.rs 或新文件(compress_via_llm) +commands/ai/commands.rs(2 IPC) +commands/ai/agentic.rs(loop 自动压缩) +commands/ai/mod.rs(2 事件) +lib.rs+api/ai.ts+useAiPanel.ts+useAiConversations.ts:79(过滤扩展) +useAiEvents.ts+AiChat.vue+i18n/{zh-CN,en}/aiChat.ts -
✅(batch60·2026-06-16·workflow whae812z5+主代核查,cargo 0err) F-260616-13 [P2] — build_for_request 持锁重活 + system_prompt token 每轮重估。性能分析批次发现:每轮
stream_llm前session_arc.lock()(agentic.rs:251)持锁期间做TokenEstimator::estimate_text(system_prompt)(L252)+build_for_request(L253 history clone + 裁剪)。system_prompt loop 外固定传入,每轮重估其 token 是浪费(可缓存);build_for_request 持锁做 history clone 是重活,消息多时(200 cap)锁持有期长。方向:①system_prompt token loop 外算一次缓存 ②build_for_request 先 clone messages 释放锁再裁剪。低收益优化。— agentic.rs:251-253。 -
✅(主代核验·2026-06-16) F-260616-14 [P2/核验] — max_tokens 8192 截断 + 前端批量审批核验。核验结论:①
max_tokens=8192(实际agentic.rs:270,行号漂移)≈6-8k 字日常够用,长回复截断由 batch59 MidStream 保文兜底不丢文,可配低优先非阻塞(登记可选优化)②前端批量审批 AE-2025-01 已完整闭环(ToolCardList.vue:4-11ai-batch-approve栏 + AiChat.vue:444@batch-approve→store.batchApprove+ useAiSend.ts:338 遍历 pendingApprovals 逐个 approveToolCall + i18n approveAll/rejectAll),满足「一轮多 Med/High 全 insert pending→一次批量批全部」③audit.rs:496process_tool_calls一轮多工具 Med/High 进审批门控 insert pending + Low 并行执行链路通。无阻塞 bug,销账。— agentic.rs:270 + ToolCardList.vue + audit.rs:496。 -
✅(波4·2026-06-17·wd5a1dgqz+主代核查,cargo 0err+tool_registry 76test) F-260617-01 [P2] — patch_file 工具增强:行号区间替换 + 锚点匹配两种备选模式。当前 patch_file 仅支持
old_text精确匹配,对超长行(todo.md 单行 1000-2000 字符)+ 含大量反引号/引号/中文标点的场景,匹配极易失败(JSON 转义错位一个字符就全盘失败)。新增两种可选模式作为备选:方案 A — 行号区间替换:新增参数
replace_lines: { start: usize, end: usize }(1-based,含首尾行),直接按行号区间删除旧内容 + 插入new_text。不需要old_text。配合expected_hash乐观锁防并发修改后行号漂移。适用场景:超长行文件、已通过read_file确认行号的精确替换。方案 B — 锚点匹配:新增参数
anchor: { start: string, end: string },start/end是首尾行的子串标记。工具内部搜索start所在行号 → 搜索其后第一个end所在行号 → 转为行号区间替换。不需要完整原文。适用场景:只知道替换块的首尾特征文本、不想粘贴整段old_text省 token。三种模式互斥:
old_text(现有精确匹配)/replace_lines(行号区间)/anchor(锚点)三选一,同时传多个报错。三种模式都需要path+new_text。expected_hash可选,三种模式通用。实现要点:①方案 A/B 共用底层"按行号区间替换"逻辑(读全文 → 按行切分 → splice 替换 → 写回)②方案 B 锚点搜索用
lines[].contains(start)子串匹配(大小写敏感,与 search_files 一致),找不到返 Err ③方案 A 行号越界返 Err ④三种模式都走现有的人工审批(Medium 风险)⑤expected_hash在写回前校验文件未变。涉及文件:
src-tauri/src/tools/或 patch_file handler 所在位置(tool_registry.rs / AI 工具层)+ DevFlow 自身的 AI 工具定义(如果 patch_file 是 DevFlow 内置工具)。需确认 patch_file 当前实现位置。
🔧 任务模块全栈核对(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
🔴 aichat API Key 401 排查(2026-06-16)
用户报对话失败「调用失败: API Key 无效或无权限」+ 重设 key 无效。排查结论:代码链路全对(保存 keyring✓ / 读取 resolve_provider_secret✓ / 鉴权头 openai=Bearer·anthropic=x-api-key✓ / URL 智能拼接✓),401 来自服务端,非 devflow bug。根因四选一(key 无效 / provider_type 不匹配 / base_url 错 / model 名错)。详见 aichat-apikey-401排查-2026-06-15.md。
- S-260615-01 — [待用户确认根因] 用户跑直连测试(curl)区分 key/provider_type/base_url/model 哪项错,见详情文档「验证步骤」
- B-260615-01 ✅ wave(2026-06-16,待commit) —
Err 分支加诊断提取纯函数fmt_diag+extract_error_diag(name/status_or_class/timeout·connect 分类,14 单测);约束:LlmProvider trait 无 base_url/endpoint,仅 name() 近似 provider_type(provider_type + 实际请求 url + HTTP 状态码),当前只透传服务端文本看不出端点,401 难定位 — stream_recv.rs:107-113
🔴 流式响应中断误报排查(2026-06-16)
现象:AI 工具(write_file 等)执行成功(文件真写入 14.8KB),前端却弹「⚠ 响应中断(长时间无数据流)」误报,用户误以为失败重发。根因(架构层,非偶发):工具执行后 agent loop 进入下一轮 LLM 请求,等首 chunk 的静默期无心跳——后端
stream_recv.rs: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
🟦 架构审查待修项(2026-06-16)
纯架构层评估(边界/依赖/抽象/扩展性/状态管理),详见 架构审查-2026-06-15.md。共 🔴6 🟡6 ⚪4 + 亮点 6。与 06-14 三份报告去重(不重复 bug/性能 FR-、aichat AR-)。
立即(零风险减法)
- ARC-260615-01 ✅ wave(2026-06-16,待commit) —
删grep 验零消费者 + 删文件 + index.ts 清导出 + 清 appSettings.ts:8 过时注释stores/settings.tsmock 死代码 - ARC-260615-02 ✅(2026-06-16,待commit) —
删 nav 项(App.vue secondaryNav)+ 删/decisions路由死链nav.decisionsi18n key(zh/en)。决策治理 = F-260614-08 长期项未实现,入口提前占位成死链;dashboard.recentDecisions(Dashboard.vue)不同命名空间保留
短期(低成本)
- ✅(决策a·2026-06-16·转架构维护记录无代码) ARC-260615-03 — 重评降级(2026-06-16):df-execute 非空壳——shell.rs
execute()已完整实现(cmd/sh 跨平台 + kill_on_drop + timeout + env)+ 被 tool_registry.rsrun_command复用(F-260615-05)。原 todo「76 行/1 函数 + TODO」基于 lib.rs 空判,过时。合并进 df-nodes 破坏职责分离(节点定义 vs 执行运行时)+ 动依赖树,转架构维护决策,非清债 - ARC-260615-04 ✅ wave(2026-06-16,待commit) —
barrel 补导出(3 处 view 直连可选迁移,非强制)stores/index.ts补export useAiStore
中期(技术债)
- ✅(batch29·2026-06-16) ARC-260615-05 —
stores/project.ts 上帝 store 拆分(四领域+越层 invoke)核验越层 invoke 0 命中(已下沉 api/workflow.ts:approveHumanApproval:39/cancelHumanApproval:52 封装,stores/project/workflow.ts:78/101 调)+四领域拆分(god-store 拆分批已处理 stores/project/ 子 store barrel project.ts:122-123 委托)。vue-tsc exit 0 - ✅(batch29·2026-06-16) ARC-260615-06 —
events↔stream 循环依赖提 aiShared.ts 下沉 nextMsgId/_msgCounter 纯函数,useAiStream 不再 import useAiEvents(仅 5 处注释),useAiEvents→useAiStream→aiShared 单向 DAG。vue-tsc exit 0 - ✅(batch30·2026-06-16 评估维持现状) ARC-260615-06b [P3] — useAiEvents↔useAiSend 另一环(useAiEvents:19 import useAiSend/useAiSend:23 import useAiEvents),靠事件总线 ai-approval-clear-timers 部分缓解,非本批范围。遗留自 ARC-06 核验
- ARC-260615-07 — 其余见文档:df-core 改名 df-types(类型库非核心)/ src-tauri IPC 编排层抽取(df-app,5711 行成事实业务层) / AI agent loop 从 IPC 下沉 df-ai / 3 view 绕 store 调 api / 类型契约 ts-rs 代码生成 / AiSession 多会话(B 路线前置) / AppState 分组 / IPC 命名统一
渲染优化(自研块级 memo 已实施,2026-06-16)
- ARC-260615-08 ✅(2026-06-16,待commit) — [渲染优化·自研块级 memo 已实施] 流式 Markdown 渲染——保留 marked+DOMPurify+.ai-md 原样式不动,借鉴方案D流式核心(块级 memo:splitBlocks 代码围栏整体一块/非代码双换行切 → 前块缓存命中 O(末块) + 末块不缓存处理未闭合 token + rAF 节流合并多 delta 一帧)。退役 AR-1 纯文本短路 — 关联 AR-1 — 详见 aichat流式Markdown渲染调研-2026-06-15.md 【决策转向:先试方案D(markstream-vue@1.0.1 接入+vue-tsc 通过),但样式100%还原 .ai-md 成本高且脆(代码块 .code-block-container chrome / 暗色 --ms-* 变量 / prose .markstream-vue 作用域 4 处对接,随库升级漂移),用户优先原样式,转自研块级 memo——零样式对接(marked 输出标准 HTML + .ai-md 全覆盖) + D 级流式性能(O末块)。已实施:splitBlocks/parseBlock(memo)/parseBlockNoCache(末块)/renderStreamingMd/scheduleStreamParse(rAF)/renderContent + watch currentText→scheduleStreamParse/streaming 翻转清 rAF + onBeforeUnmount 清 rAF + 回退 markstream 依赖恢复 marked/dompurify + vue-tsc exit 0;留后续:dev 运行时流式验证(掉帧/长回答边界/末块未闭合表现)】
🔵 全局代码 review 待修项(2026-06-16)
7 维度并行深入扫(DRY/架构/潜在bug/简洁性/安全/AI可靠/工作流引擎),详见 全局代码review-2026-06-15.md。 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 — 重要缺陷
- B-260614-08 —
promote_idea 两步写非事务✅ mission:T-260614-05 已修复(idea.rs 第二步 update_full 失败时补偿删除已建 project;Repository 不支持跨 repo 共享事务对象,选补偿删除非真事务,改动最小;附 logging)(06-14) - T-260614-01 — [P1] Sprint 9/10/14/15/16/18 多项未 tauri dev 实测 — 评分 IPC 缩放 / update_full / promote_idea / Store getter / token 落库 / 知识库 Tier 1 全栈 / LLM 并发 Semaphore / 知识生命线(#54 跟踪)— source:Sprint 9-18 (06-14)
- T-260614-02 — [P1] 切对话不中断路由:部分场景运行时实测(A 路线场景 2/3) — source:Sprint 8 (06-14)
P1 — 设计完成待实施
- ✅ F-260614-01 — [P1🔥] 模型能力系统 Phase 1(阶段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。7 大类 20 个改进项,按原文优先级分 4 批。去重:§1.3 复制 ⊂ F-260615-13/14(已写)、§3.4 新建中断 ⊂ B-260615-10(已做软复位,本文档加 confirm 增强)、§6.2 分离窗口同步 ⊂ CR-24/25(已写)。状态: 待讨论→看板跟踪。
第一批 — 最高频痛点(每次对话都遇到)
- UX-2025-01 ✅ 选文字(文本偏移量方案) (第⑩批销账 06-15) — 流式渲染中选中文字保持(§2.1)。现象:AI 流式输出时选文字被 delta 打断丢失选区。方案 A(推荐):
selectionchange+ 选区在消息容器内 → 暂停 rAF 流式 parse → 选区结束恢复;方案 B:已完成块独立 DOM 节点不参与 v-html 替换。关联 ARC-08 块级 memo(splitBlocks 已按块切分,末块单独更新天然支持方案 B)— src/components/AiChat.vue + src/composables/ai/useAiEvents.ts - ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-02 [P1] — 消息操作栏(复制 / 重新生成)(§1.2+§1.3)。AI 气泡 hover 显示操作栏:「📋 复制」+「🔄 重新生成」。复制 = F-260615-13 合并实现;重新生成:后端删最后一条 AI 消息 + 用倒数第二条 user 消息重跑 agentic loop(新增
ai_regenerateIPC)。代码块额外 hover 浮出「📋 复制代码」按钮(§2.2 子项)。— src/components/AiChat.vue + src-tauri/src/commands/ai/commands.rs - ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-03 [P1] — 错误气泡操作入口(error_type 枚举本身 batch34 B-42 已加,本批前端消费)(§4.1)。错误气泡底部增加按钮区:「🔁 重试」(取上一条 user 消息重发)+「⚙ 去设置」(仅 auth/provider 错误时显示,跳 Settings→AI Tab)。后端
AiError加error_type: Option<ErrorType>枚举(auth/network/timeout/provider_config/unknown),前端据此显隐按钮。— src-tauri/src/commands/ai/mod.rs(AiChatEvent) + src/api/types.ts + src/components/AiChat.vue - ✅(batch59·2026-06-16·workflow wdlt0xfl6) UX-2025-04 [P1] — 断线保文(§4.2)。保文核心落地:stream_recv.rs
StreamResultenum(Complete/Partial/InitFailed 三分支),MidStream 失败(流中途 chunk error/idle timeout/有 partial_text 未 finished)返Partial{partial_text}不重试保文;agentic.rs 收 Partial → 入库 assistant 消息 + emit AiCompleted(incomplete=true,非 AiError) + 追加系统提示「⚠ 响应因网络中断不完整」;useAiEvents.ts:177 补AiStreamRetrycase(消除死事件穿透) + AiCompleted case 处理 incomplete(系统提示气泡镜像后端);types.ts:204incomplete?:boolean。对齐决策 a1(Init 可重试/MidStream 保文不重试) + UX-04 决策 a。「继续生成」按钮暂未做(保文核心硬需求已满足,按钮可复用 UX-2025-03 重试能力或按需补)。— src-tauri/src/commands/ai/stream_recv.rs + agentic.rs + src/composables/ai/useAiEvents.ts
第二批 — 显著提升日常效率
- ✅(第⑳批 2026-06-16) UX-2025-05 [P2] — 代码块语法高亮 + 复制按钮(§2.2)。高亮部分完成:highlight.js(core+16 语言按需注册,~50-60KB gzip)+ marked.use code renderer→highlightCode(已注册语言按 lang 高亮,否则 highlightAuto)+ github-dark 主题(贴合 #0c0e1a 深底)+ DOMPurify 默认保留 class。复制按钮留 TODO(需 AiChat.vue hover 浮层+事件委托,待 UX-02/F-260615-13 合并)。— src/composables/useMarkdown.ts + package.json(highlight.js ^11.11.1)
- ✅(batch41 store + batch43 UI·2026-06-16·workflow wcy478m3y) UX-2025-06 [P2] — 对话搜索(§3.1)。全闭环:store(batch41 searchQuery+filteredConversations+i18n)+ UI(batch43 AiChat 侧栏 header 搜索框+Ctrl+K 聚焦+平铺渲染+空结果态)。注:无独立 Sidebar.vue,侧栏内联 AiChat.vue
- UX-2025-07 ✅(第⑲批 2026-06-16) —
键盘快捷键onGlobalKeydown(AiChat.vue)+onMounted/onBeforeUnmount window listener(对齐 _unlistenToolSlow 生命周期);实现 Ctrl+L(confirmClearChat,streaming 守卫)/Ctrl+B(toggleSidebar)/Esc(confirmState.visible→answerConfirm(false)>props.detached→closeDetachedWindow 优先级);Ctrl+N/K/R 留 TODO(WebView2 浏览器级 preventDefault 不可靠待实测);Esc 冒泡与 textarea handleKeydown 不冲突(skill 浮层 Esc 由 textarea handler 先消费)。vue-tsc 0 err。原设计:Ctrl+N 新建/Ctrl+K 搜索/Ctrl+L 清空/Ctrl+Shift+C 复制/Ctrl+R 重新生成/Esc 关闭/Ctrl+B 切侧栏 — src/components/AiChat.vue - ✅(batch47·2026-06-16·workflow wjkywbhff) UX-2025-08 [P2] — 新建对话 confirm 弹窗(§3.4 增强)。已落地(confirmNewConversation handler + 两处新建按钮改造 + 复用 useConfirm/ConfirmDialog + i18n)。注:生成态判定用 isViewingGenerating(非 state.generating 实际是 streaming),commands.rs 零改(复用 store.newConversation 软复位 B-260615-10)
第三批 — 锦上添花
- ✅(batch51·2026-06-16·workflow wdfffj1ti) UX-2025-09 [P2] — 编辑已发送消息并重新生成(§1.1)。全栈落地:架构发现 messages 是 ai_conversations JSON 列非表行,status 加 ChatMessage struct(向前兼容老 JSON)非 V17 迁移。df-ai provider.rs status+is_active / context.rs sanitize 过滤 truncated+replace+truncate / commands.rs ai_chat_edit IPC(复用 ai_regenerate spawn)/ api+useAiSend editMessage / AiChat 末条 user 编辑按钮 / i18n 4 key。只末条 active user 可编辑+generating 拦截+truncated 软删保留 DB 不进上下文/视图
- ✅(batch52·2026-06-16·workflow w91svrokz) UX-2025-10 [P2] — @ 实体引用(§1.4)。全栈落地:AiChat @ popover(复用技能架构 .ai-mention-* + detectMentionTrigger 排除邮箱 + selectMention [类型:名] 插入 + 键盘导航互斥技能 + 项目/任务各 20 分组)+ prompt.rs 任务段注入(state.tasks.list_active,签名零变化降级避碰 ai_chat_send 领地)+ i18n 5 key。降级:实体 id 未透传(凭名称+状态对齐),最近文件联想无 store 略
- UX-2025-11 [P2] — 输入框高度提升(§1.5)。textarea 最大高度 120px→200px(~10 行),超过后再内部滚动。或可拖拽 resize handle。— src/components/AiChat.vue (第⑬批销账 06-15)
- UX-2025-12 [P2] — 历史消息分页懒加载(§2.3)。▶ 已打开推进(2026-06-17 用户决定不再等痛点,与 UX-19 一并纳入)。方案:switchConversation 首次加载最近 50 条,滚顶加载更多(需后端 messages 查询加 offset/limit 支持)。关联 B-260629(messages 无上限)。决策点(待定)——与 UX-19(虚拟滚动)二选一解决长对话渲染卡顿:分页(后端配合,简单稳定,但切换/搜索跨页体验割裂)vs 虚拟滚动(纯前端,体验流畅,但流式末条挂载复杂)。倾向 UX-19 虚拟滚动优先(纯前端零后端改动+体验优),本项分页作为虚拟滚动不足时的补充方案。— src/stores/ai.ts + src/composables/ai/useAiConversations.ts + 后端 conversation.rs
- UX-2025-13 [P2] — 消息时间戳(§2.4)。气泡下方极小字号(dim 色)展示相对时间(复用 time.ts 已有 common.justNow 等 key);hover tooltip 展示完整时间。— src/components/AiChat.vue (第⑬批销账 06-15)
- ✅(第㉒批 2026-06-16) UX-2025-14 [P2] — Provider 切换反馈(§5.1)。cycleProvider(L739-748)主体切换逻辑保留,末尾加 showProviderSwitched:复用 AiChat 自管 showToast(info 型,model 名插值 aiChat.providerSwitched)+ 临时 bar(providerBarVisible + Transition ai-provider-switch 0.15s 淡入/淡出 + 2000ms 自动淡出,展示 aiChat.providerSwitchHint model 名小字+accent 脉动圆点)。toast 调研:AiChat 已有自管 reactive showToast(分离窗口无根 toast 故自管),复用无降级。_providerBarTimer onBeforeUnmount 清(L1067)。stores/ai.ts 未改(cycleProvider 在 AiChat.vue 内)。— src/components/AiChat.vue + i18n(aiChat.providerSwitched/providerSwitchHint zh/en)
- ✅(第㉓批 2026-06-16) UX-2025-15 [P2] — 技能参数用法展示(§5.2)。浮层 .ai-skill-item 改 column:.ai-skill-item-row(name/desc/src 单行)+ code.ai-skill-item-hint(v-if s.argument_hint,等宽 dim 11px,L390)。chip(L354-361):.ai-skill-chip-main(name+desc+code.ai-skill-chip-hint v-if pendingSkill.argument_hint L358)+ x 关闭(@click clearSkill,补 aria-label=aiChat.clearSkill 无障碍)。复用 var(--df-font-mono)+ui-monospace fallback。JS 零改动(仅 template 结构+CSS,/ 联想/selectSkill/clearSkill/Escape/Enter 逻辑全保留)。i18n 无新增(argument_hint 是 skill 数据自带,复用 aiChat.clearSkill)。— src/components/AiChat.vue
- ✅(第㉑批 2026-06-16) UX-2025-16 [P2] — 侧栏宽度可拖拽(§6.1)。右边缘 4px 拖拽条(col-resize,hover accent 高亮),mousedown→document mousemove(startWidth+delta)→mouseup;clamp 120-280(setSidebarWidth 内兜底 NaN/负数/缺省)。持久化:move 高频只改 state 不落盘,up 一次性 persistUiState 写 df-ai-ui(复用现有机制,App.vue key 已注册)。宽度绑定 CSS 变量
--sidebar-width(非内联 width,保 sidebar-slide 折叠过渡不被压过)。卸载兜底 onBeforeUnmount 摘 listener 防泄漏。body.ai-sidebar-resizing 锁 cursor+user-select 防抖动/误选。— src/components/AiChat.vue + src/composables/ai/useAiPanel.ts + src/stores/ai.ts(sidebarWidth:200) + i18n(aiChat.sidebarResizeHint zh/en) - ✅(batch49·2026-06-16·workflow wkan76fon) UX-2025-17 [P3] — 对话置顶(§3.2)。全栈落地(df-storage pinned 字段+V16迁移+set_pinned + IPC ai_conversation_set_pinned + stores/AiChat 排序 pinned DESC,updated_at DESC + 3 处图钉按钮)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue,conversation.rs:141 补 pinned:false 必填(跨 batch44 领地 proactive-bug-fix),filteredConversations 搜索态未动(降级)
- ✅(batch46 后端 + batch48 前端·2026-06-16) UX-2025-18 [P3] — 对话导出(§3.3)。全闭环:后端(batch46 ai_conversation_export IPC commands.rs:843-911)+ 前端(batch48 exportConversation 封装 + AiChat 3 处导出按钮 markdown/json/txt 菜单 + Blob 下载 + sanitizeFilename + 外部点击关闭)。注:无独立 Sidebar.vue 侧栏在 AiChat.vue
- UX-2025-19 [P2] — 消息列表虚拟滚动(§7.2)。▶ 已打开推进(2026-06-17 用户决定不再等痛点,与 UX-12 一并纳入,作为首选方案)。方案:集成 vue-virtual-scroller 或自研 IntersectionObserver 懒渲染;流式最后一条始终保持挂载(参照现有 onContentChange/isNearBottom 滚动逻辑)。与 UX-12 分页二选一(见 UX-12 决策点),倾向本项优先(纯前端零后端改动+体验优,P3→P2 升级因纳入推进)。— src/components/AiChat.vue + 新依赖(vue-virtual-scroller)
- ✅(第㉒批 2026-06-16) UX-2025-20 [P3] — 空状态引导 + 标题过渡(§7.3+§7.4)。空态:messages 空+非流式时 .ai-empty-prompts 2 列网格 4 示例卡片(点击 sendExamplePrompt 填 inputText+复用 handleSend 发送)。无 provider 显「去设置」按钮(goToSettings:嵌入 router.push('/settings'),分离窗口降级 showToast 提示去主窗口)。标题淡入:titleFlash ref + watch(activeConv.title)空→非空(loadConversations 回填触发)置 flash 驱动 0.3s keyframes(opacity+transform)+ AI 生成标识图标(success 对勾,仅 flash 期显,绑定 conv.id===activeConversationId)。_titleFlashTimer 320ms 清 + onBeforeUnmount 兜底(L1065)。— src/components/AiChat.vue + i18n(aiChat.examplePrompt1~4/goToSettings/goToSettingsHint zh/en)
🟡 AI Chat 授权体验改进(2026-06-14 方案 → 待办化)
来源:aichat授权体验改进方案-2026-06-14.md。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
待澄清 / A-B 待定
- S-260614-01 — [用户已澄清 2026-06-17] 「显示多开」= 多个会话来回切都可对话(非 AI Chat 窗口多开)。关联 T-260614-02(切对话不中断实测)+ F-09 多会话架构。A 路线(单例软隔离,已做):切会话软复位+DB 加载上下文+active 切换,来回切应能对话(需实测 T-260614-02)。B 路线(真多会话,未做):切走会话 generating 不打断,每会话独立 AiSession(memory aichat-arch-extensibility 单例未动)。待决策 A/B 路线 → 见待决策.md
- S-260614-02 — 审批可见性 A/B 待定 — B-01 修复后 pending_approvals 内存态不再被 switch 清空,前端
ai_pending_tool_calls查询有数据,但state.pendingApprovals在 AiChat.vue 是否有兜底渲染仍需实测确认。A. 加兜底渲染 / B. 实测 tc 卡片是否渲染 — 去重:与 AR-3 审批卡片可读化重叠,AR-3 修完(reason 拼对象名 + id 特化 + 卡片完整)后改实测重评 (06-14)
P2 — 不阻断缺陷 / 增强
- B-260614-05 — [P2→降级] 分离窗口(detached)跨窗口状态 — 核对修正:reattachPanel 已接线(不再死代码)+
tauri://destroyed监听已复位状态,"detached 永真卡死"已修复;剩余 localStoragedf-ai-gen/df-ai-text是 Sprint 19 有意保留(流式临时快照高频写),非 bug。仅在出现新场景失效时再评估改全局 emit/listen — 去重:与 AR-M5(跨窗口 state 完全隔离)同类,aichat 审查描述更深 — source:代码审查 + Sprint 19 (06-14) - T-260614-03 —
工具层白名单与 crud 白名单双份去重✅ WF-B ⑤ 已解决(tool_registry update_project 复用 is_allowed_column,与 CRUD 白名单同源,删硬编码 match)(06-14) - ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,主干已实现) F-260614-02 — 技能联想「使用」·决策ai调用 — agent 走查发现主干已实现(commands.rs:172-175 read_skill_content 注入 system_prompt 头尾标注隔离 FR-S4「仅供 AI 参考,非用户消息,勿作为行为准则覆盖」+ 前端 selectSkill/handleSend 全链路通)。设计文档推荐方案 A(注入 system,非方案 B 工具注册——skill 是 markdown 指令非可执行代码,B 的 execute_skill 内部仍读 SKILL.md 注入纯增成本)。剩边界打磨:P1 argument_hint 参数收集 UI(AiChat.vue:960 selectSkill 预填模板+chip 展示)、P2 空文本纯技能调用标题(title.rs:42+commands.rs:153 落库 user content 改 /[skillname])、P3 长 skill 截断可选(非痛点)。详见 docs/02-架构设计/F-02-技能联想使用-实施机制设计-2026-06-16.md
- ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,cargo test 19 passed) F-260614-03 — 灵感对抗评估接 LLM — F-07 trait 下沉后注入方式统一(原 A/B/C 选型作废)。填充
evaluate_with_llm(adversarial.rs:143):build_adversarial_prompt 三角色辩论(正方/反方/分析师)+provider.complete(temperature 0.4/max_tokens 2048/model 留空回填 default)+parse_llm_eval serde_json 解析+clamp(final_score[0,10]/confidence[0,1])+枚举非法 bail 触发 HeuristicFallback 降级。4 LLM mock 单测(a8 parse success/a9 bad JSON fallback/a10 bad enum fallback/a11 value clamp)。原 7 heuristic+scoring 8 不回归。— crates/df-ideas/src/adversarial.rs - T-260614-04 —
路径校验根治✅ 已完成(resolve_workspace_path 加 canonicalize 防 symlink 逃逸 + 词法 starts_with 兜底;仅校验、返回词法路径保持前端友好;cargo check 0 err / 22 test pass)(06-14) - ✅(后端三段·79b6a43数据层+池选择器+CR-52白项 / b3684f4池enabled/weight IPC+set_caps启动接入 /
80c0955stream_one_provider+候选链fallback+resolved_model重算;前端api/ai.ts:142 IPC封装已接) F-260614-04 — 多 Provider 负载均衡池 — 备用模型/多账号聚合,全局容量=min(各 provider 上限之和, global_cap) (06-14)。注:UI 配置面板(provider 池 enabled/weight 可视化设置)是否完成待核验——后端 IPC+前端 API 层已就绪 - ✅(波20-22·2026-06-17·e3cd448/e1d396d/4317d02,F-05 全链闭环·务实偏离 content:String+parts 非 Vec) F-260614-05 — 模型能力系统 Phase 2 — 多模态消息支持:ChatMessage.content: String → Vec(Text/Image);前端粘贴/拖拽图片;vision 模型自动路由 (06-14)
- ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+vue-tsc 0err) F-260614-06 — 导入历史项目(scan 第二步) — 6 决策全落地:①description 走 LLM(scan.rs extract_description 复用 complete)②采样扩
images:Vec<ImageRef{alt,src}>(scan.rs:330/343)+readme 剥 frontmatter/TOC/纯徽章行截 8KB(SAMPLE_README_MAX 2000→8000:346)+徽章域黑名单 5 域(shields.io/badge.fury/badgen 等:397-401)+is_badge_image+is_pure_badge_line③image 多模态留接口待 F-260614-05④monorepo 一层(is_monorepo:112+discover_projects:156 展开 packages//apps/ detect_stack 空过滤)⑤批量(scan_directory_for_projects:343 纯规则发现标已绑定 + import_projects_batch:415 并发 LLM llm_concurrency permit 限流非原子逐项独立)⑥抽 create_with_binding(:59 create/import 共用 缓解 :211 TODO)。前端 Projects.vue 导入 modal+api/project.ts 两 API+i18n 双语+scan.rs 单测全。— crates/df-project/scan.rs + src-tauri/commands/project.rs + lib.rs + api/project.ts + Projects.vue + i18n。详见功能决策记录(06-14) - T-260614-09 —
idea.rs 物理删不级联✅ WF-E 完成(idea.rs:149 delete→purge_with_descendants,1 行,签名兼容)(06-14, commit89da9fa) - T-260614-10 —
findBinding canonicalize✅ WF-E 判定已解决(normalize_path 已含 canonicalize 优先 + 词法回退,find_binding_conflict 两端对称已用,无需重复加;零改动)(06-14, commit89da9fa) - ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+7单测) F-260614-07 — [架构前置] df-ai-core trait 下沉拆 crate — 4 决策全落地:①df-ai-core 新 crate(仅 trait+数据结构 LlmProvider+ChatMessage 等,ContextManager/TokenEstimator 留 df-ai)②构造注入 Engine::new(Arc)+Engine::heuristic()(idea.rs Some/None 分支,语义等价决策② Option 参数)③LLM 失败降级 EvaluatedBy 三态(Llm/Heuristic/HeuristicFallback:28)+warn④provider 应用层装配 idea.rs build_default_provider(DB is_default→build_provider→Option→Arc::from:192)。df-ai provider.rs:15
pub use df_ai_core::provider::*re-export(外部 use 路径不变,df-nodes 零改动)+lib.rs:17pub use df_ai_core。解锁 F-03 注入(其他 crate 依赖 df-ai-core trait 非 df-ai impl)。— crates/df-ai-core(新)+crates/df-ai+crates/df-ideas+src-tauri/commands/idea.rs。详见设计文档 F-07-df-ai-core-trait下沉设计 - ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-08 — 决策治理产品化 — 活契约/规格契约自检机制产品化为可操作功能(当前散落文档机制) — source:功能决策记录缺口
- ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-09 — 项目 status 字段治理 — status 状态机规范化(planning/active/archived 等枚举约束) — source:功能决策记录缺口
- ⏸️(待决策.md已决c暂缓·2026-06-16) F-260614-10 — 知识库 MCP Server + Tier 2/3 — 对外 MCP 暴露 + 分层存储(当前仅 Tier 1 全栈) — source:PROGRESS Sprint15
- ⏸️(待决策.md已决暂缓·2026-06-16) T-260614-11 — 全局#6 条件表达式引擎升级 — df-workflow conditions 仅支持 true/false 字面量,升级为真表达式求值 — source:PROGRESS 全局问题
- T-260614-12 —
df-ideas 死代码✅ WF-E 部分完成(capture.rs 删 CaptureInput/IdeaCapture 死码,保留 Idea/IdeaScores 共享实体;promotion/scoring/adversarial 内"两套 Recommendation/PromotionPolicy 死枚举"嫌疑 agent 未确认存在/保留为对外契约,本次未动,待复查)(06-14, commit89da9fa) - F-260615-01 ✅(2026-06-16,待commit a3cccb070fe9c8821) — [P1 功能增强] HumanNode 审批节点支持自定义选项 + 单选/多选类型。现状:config
options: Vec<String>已支持任意数量(2/3/…数量扩展已通),但decision是单String仅单选语义,options空=自由文本。增强目标:不止「同意/拒绝」二选一,可配置 N 个候选项 + 单选(single)/多选(multiple)两种类型。改动面:①df-core/events.rsWorkflowEvent::HumanApprovalRequest加select_type、HumanApprovalResponsedecision 单值→多值(decisions: Vec<String>或保留 decision 兼容 + 加 decisions)②human_node.rsconfig 解析select_type+ 校验(多选时每项 ∈ options,可加 min/max 选中数约束)③IPCapprove_human_approval签名 ④前端stores/project.tsapprove +api/types.ts事件类型 + 审批弹窗 UI(单选 radio / 多选 checkbox)⑤单测改断言 + 新增多选/超限测。注意:向后兼容现 single 调用方,select_type缺省 = single — source:用户需求(06-15),crates/df-nodes/src/human_node.rs + src-tauri/src/commands/workflow.rs + src/stores/project.ts — ✅已实施(a3cccb070fe9c8821,2026-06-16,待commit):6 文件契约向后兼容(events.rs SelectType 枚举 Single/Multiple 缺省 Single+HumanApprovalResponse decision+decisions 双字段/human_node.rs 校验 single len==1·multiple len≥1·∈options/workflow.rs IPC 加 decisions+select_type Option 缺省兼容/types.ts/project.ts approve/ProjectDetail.vue checkbox 多选 UI)。cargo check 0 err/df-nodes 21 test(含 4 新增多选)/vue-tsc 0 err。主代理核查契约向后兼容 + 6 文件边界 - F-260615-02 ✅(2026-06-16,待commit) — [P1 功能] task 详情查看。✅已实施:get_task_by_id IPC(复用 TaskRepo::get_by_id ok_or_else 转 Result)+lib.rs 注册+taskApi.get+/tasks/:id 路由+TaskDetail.vue(11 字段:标题/描述/状态/优先级/关联项目 router-link 解析名/分支标签/负责人/基础分支/工作流定义/创建更新时间;复用 constants/project 标签+formatDate+watch route.params.id 重载)+Tasks.vue 列表项 @click router.push;TaskRecord TS 类型已存在无需新增;主代理核查 cargo check 0 err/vue-tsc 0 err/git diff 6 文件边界干净。现状:
src/views/Tasks.vue仅列表,无独立 TaskDetail 视图/路由(grep 仅ProjectDetail.vue嵌套任务命中,无独立详情页)。需求:点击 task 查看详情。数据模型已就绪(df-storage/src/models.rs:53TaskRecord12 字段:id / project_id / title / description / status / priority / branch_name / assignee / workflow_def_id / base_branch / created_at / updated_at)。改动面:①新建src/views/TaskDetail.vue视图 + 路由(router/index.ts/tasks/:id)②task 详情 IPC(get_task_by_id,核对commands/task.rs现有 IPC 是否已有,无则补)③Tasks.vue列表项点击 → 跳详情 ④详情页字段展示(title/description 渲染、status/priority 标签、关联项目名解析 project_id→name、branch/assignee 信息、时间戳)⑤可选:详情页内编辑(update_taskIPC 已存在,FR-D6)。— source:用户需求(06-15),src/views/Tasks.vue + src/router/index.ts + src-tauri/src/commands/task.rs - F-260615-03 ✅ truncated 标志 (第⑩批 2026-06-16) —
list 工具截断标志(list_trash补漏) 原完整分页方案,对抗分析降级。现状:4 工具truncate(50)硬截断,AI 不知被截断会回复「不存在」。对抗分析裁决:数据量未到 50 条线(projects=5, tasks=61),完整分页 breaking change 风险 > 收益 → 降级为最小修:不改返回结构(non breaking),只在 truncate 时加truncated: true标志 + 工具描述注明"最多返回 N 条"。未来数据量到时再升完整分页(F-03-full)。改动面:tool_registry.rs 4 handler 截断处加 truncated bool 返回 — src-tauri/src/commands/ai/tool_registry.rs:127-165,377-379 — source:用户需求(06-15)+对抗分析裁决(06-15) - F-260615-04 ✅ 卡片分组增强 (第⑩批 2026-06-16) —
read_dir / read_file 工具卡片连续时折叠/收起提高信息密度。现状:AI 探查目录常连续调多个 read_dir + read_file(先列目录再读多个文件),每个结果独立卡片平铺,长列表/大文件内容占满屏幕,信息密度低。需求:相邻同类读取卡片支持折叠——默认收起只显摘要(如「read_dir: 12 项」「read_file: src/main.rs (234 行)」),点击展开看详情;或连续 N 个同类卡片归组折叠。改动面:①ToolCard.vue加折叠态(collapsedref + 摘要/详情双视图 + chevron 图标 + 高度过渡)②连续同类检测/归组(ToolCardList.vue按tool.name分组,已有列表容器适合放分组逻辑)③摘要提取(read_dir 数项数 / read_file 文件名 + 行数,解析 result)④折叠态持久化(可选,localStorage 按 conv)。关联信息密度构想(memory: devflow-info-density-concept,卡片折叠是其中一环)。— source:用户需求(06-15),src/components/ToolCard.vue + src/components/ToolCardList.vue — 勘察完成(2026-06-16,wxflofhf2):feasible/plan 11 步跨 5 文件(ToolCardList/ToolCard/useAiSend/stores/ai/global.css)。risk 标低但实为 UX 新行为+改核心 ai 状态文件(useAiSend.ts/stores/ai.ts)+plan 细节有误(useAiSend composable 无 emit 方法)。拆小或留待:先做 ToolCardList 分组+单卡折叠摘要(限定不碰 useAiSend/stores),完整折叠交互归信息密度构想单独立项 - F-260615-07 ✅ search_in_file (第⑩批销账 06-15) —
read_file 加 search 参数已落地(见 :493 search_in_file 同条)。[P2 功能] read_file 加 search 参数(单文件内关键词定位)。现状:read_file 仅 offset/limit 分页(tool_registry.rs:458-501),无原生搜索——AI 找关键词需多次分页扫描定位,token 浪费 + 多次 IPC。用户实测确认 offset 0基 + limit=1 单行精确读取可用,但定位靠 AI 多轮分页。方案:① schema 加search(string,可选)参数 ② handler 内若有 search:读全文(1MB 限内)→ 按行过滤含 search 的 → 返回matches: [{line, content}](line 为 1基行号,须在描述注明与 offset 0基的差异)③ 匹配超量:默认返前 50 匹配 + total + has_more。改动面:tool_registry.rs read_file handler(:458-501)schema 加参数 + 返回结构扩展(有 search 返 matches,无 search 走原 content 路径,两路径并存)。权衡:vs 独立 grep 工具——read_file search 满足单文件定位主场景(最小改动,复用现有工具);跨文件搜索待 B-260615-37(run_command stdout)修后用 grep/findstr 验证够不够用,不够再立 grep 工具(对标 Claude Code Grep)。大小写:默认区分(确定性优先),case_sensitive 参数先不加(做减法)。— source:用户建议(06-15),src-tauri/src/commands/ai/tool_registry.rs:458-501
🟡 文件操作工具集补全(2026-06-16 能力梳理·设计定稿 V2)
用户给出 5 个工具具体 API 设计。核心原则不变:专用工具补 run_command 做不好/不安全的;系统命令类交给 run_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] —
classify_status_or_class尾部|| true导致所有 unknown 错误标记 retryable —stream_recv.rs:433函数末尾硬编码|| true,400/403 等 Fatal 错误被误标为可重试 → agentic loop 无效重试循环浪费 token 和时间。修法:移除|| true,unknown 默认false(保守不重试),与retry::is_status_retryable语义一致。一行改动,零风险,最高 ROI。— src-tauri/src/commands/ai/stream_recv.rs:433 - 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 交互体验缺陷(高优先级专区)
用户指令:aichat 界面交互体验问题提高优先级。本区集中记录 AiChat.vue + ToolCard.vue + composables/ai/* 交互层缺陷。 扫描 agent 运行中,结果持续追加。已存在问题来自用户实测 + 历史走查。
🔴 已确认交互 Bug
- UX-260617-01 [P0]🎯 — 消息/工具卡大量重叠堆叠(用户实测确认)。5 角度深入分析(todo.md L116-180+):CSS 声明链路正常、scoped 无冲突、DOM 结构正确 → 排除常规原因。核心嫌疑:虚拟滚动 IO 半激活态不一致——部分 item 被 IO 管理(可卸载)、部分不被管理(永远渲染) + height=0 时 minHeight 未设置 → slot 塌 0 → 重叠。需复现定位确切触发路径后修复。— src/components/AiChat.vue + src/composables/ai/useAiVirtualScroll.ts
- UX-260617-02 [P1]🎯 — CR-59 审查发现:
onPoolWeightChange缺乐观更新 revert 逻辑 — Settings.vue:643-656 weight 输入 debounce 300ms 调 IPC,成功后未像 onPoolToggle 一样 revert 到服务端值。IPC 失败或延迟时 UI 显示旧值而非回滚。修法:补 revert 分支(对齐 onPoolToggle 模式)。— src/views/Settings.vue:643-656 - UX-260617-03 [P1]🎯 — copyMsgContent 失败误报「已复制」(已记录未修) —
AiChat.vue:1037-1040clipboard.writeText 权限拒绝 → catch 仍 showToast('copied')。用户见"已复制"去粘贴发现空。修法:catch 改失败文案或静默。(已在 AI 链路审查 B-260615-46 记录,提升优先级至此区)。— src/components/AiChat.vue:1037-1040 - UX-260617-04 [P1]🎯 — offsetToDOMPosition 恒返回 root 致选区恢复错位(已记录未修) —
AiChat.vue:908-921createTreeWalker 后 currentNode 初始=root 非 Text → ?? 恒取左 → 循环无 nextNode 推进 → 选区恒错位。UX-2025-01 功能实际失效。(已在 B-260615-47 记录,提升优先级至此区)。— src/components/AiChat.vue:908-921
🟡 待扫描确认的交互问题(aichat agent 结果回填后补充/降级)
以下条目等 aichat 交互层缺陷扫描 agent 完成后,根据实际发现:
- 确认存在 → 补充 file:line + 触发条件 + 修复建议
- 不存在或极低概率 → 标记 ✅ 排除
- 新发现 → 追加新条目
UX-260617-05 [P2]🎯 — (待扫描确认→降级) 流式渲染增量追加闪烁✅ 排除:streamingBlocks rAF 节流 + scheduleStreamParse 双层机制完备,无 dup 风险(scan 未发现闪烁 bug,rAF 块级 memo + snap 短路兜底有效)UX-260617-06 [P2]🎯 — (待扫描确认→升级) wasNearBottom→ 合并入 UX-260617-21(stopChat 竞态窗口,含 scroll 一致性)UX-260617-07 [P2]🎯 — (待扫描确认→降级) 审批顺序✅ 低风险:审批按 tool_call_id 串行处理,无并行冲突(F-05 超时静默是更大问题)- UX-260617-08 [P1]🎯 — switchConversation/loadConversations 异常静默吞没 —
useAiConversations.ts:36-44catch 为空 +:117-119switchConversation catch 仅messages=[]无反馈。网络抖动/IPC 断开 → 对话列表突然变空/切换后空白用户不知原因。修法:catch 加 toast('loadConvFail','warning');loadConversations 失败保留旧列表+stale 标记。— src/composables/ai/useAiConversations.ts:36-44,117-119 - UX-260617-09 [P1]🎯 — startListener 吞掉注册异常导致全量事件静默丢失 —
useAiEvents.ts:354-373IIFE 内aiApi.onEvent(handleEvent)异常被完全吞没(无 console.error/toast/log)。所有聊天事件(文本/完成/审批)全丢 → UI 表现"发了消息永远不回"。修法:IIFE 内加 try/catch + console.error + throw 让外层 finally 清理。— src/composables/ai/useAiEvents.ts:354-373 - UX-260617-10 [P1]🎯 — AiError 不清理 pendingApprovals,错误后残留审批卡 —
useAiEvents.ts:326-349AiError case 做 clearStreamWatchdog/清queue/推错误气泡但从未清 pendingApprovals。出错时有 tool_call 处于 pending_approval → 残留可点击审批按钮。修法:加state.pendingApprovals=[]+clearAllApprovalTimers()。— src/composables/ai/useAiEvents.ts:326-349 - UX-260617-11 [P1]🎯 — modelOverride 跨对话泄漏 —
useAiSend.ts:67模块级 ref,注释明确要求"前端应同步清 null"但无任何代码执行此清除。switchConversation/newConversation 不触碰。对话A选模型覆盖→切对话B→override 静默生效。修法:switchConversation 末尾加modelOverride.value=null。— src/composables/ai/useAiSend.ts:64-67 UX-260617-12 [P3]🎯 — (待扫描确认→升级) generating crash 兜底→ 合并入 UX-260617-01(消息重叠根因含状态不一致)
🟡 aichat 扫描新增发现(MEDIUM · 8 项)
- UX-260617-13 [P1]🎯 — 刷新后主面板无流式恢复,用户看到"假死" — 分离窗口(resumeInDetached)有完整恢复链(ai_is_generating→恢复streaming→推送占位气泡→localStorage currentText),主面板完全没有等价逻辑。刷新后 streaming=false + 无占位气泡 + 后端事件到达不进流式分支 → 文本仅 AiCompleted 时一次性显示退化为非流式体验。修法:主面板 onMounted/client-ready 后调 ai_is_generating IPC,true 则对齐 resumeInDetached 恢复链。工作量较大但用户感知最强。— src/composables/ai/useAiEvents.ts:155-179 + src/composables/ai/useAiWindow.ts:64-89
- UX-260617-14 [P2]🎯 — 审批超时 130s 静默复位无反馈 —
ToolCard.vue:507,541-54超时仅approving.value=false,无 toast/error/日志。用户无法区分"审批完成状态没更新"和"超时复位"。修法:超时时 warning toast + APPROVE_LOADING_TIMEOUT_MS 改为共享常量导入。— src/components/ToolCard.vue:507,541-544 - UX-260617-15 [P2]🎯 — regenerate/editMessage 丢弃图片 parts —
useAiSend.ts:148-190regenerate/edit 均无 parts 参数。含图片消息重生成/编辑后图片消失。修法:扩展 regenerate/editMessage IPC 签名透传 parts;或至少 UI 层对含 parts 消息禁用按钮/confirmDialog 提示。— src/composables/ai/useAiSend.ts:148-190 - UX-260617-16 [P2]🎯 — 无路由离开/关闭保护,未发送输入静默丢失 — AiChat.vue 缺 beforeRouteLeave/onBeforeUnload 守卫。输入框有内容/已粘贴图片时切页面 → 内容瞬间消失无提示。修法:beforeRouteLeave guard + detached window beforeunload 检查 inputText/pendingImages/pendingSkill 非空时 confirmDialog。— src/components/AiChat.vue (缺失守卫)
- UX-260617-17 [P2]🎯 — ToolCardList 分组名硬编码中文绕过 i18n —
ToolCardList.vue:259-274nameMap 全硬编码中文('读取文件'等)。英文 locale 下分组标题仍显示中文。修法:value 替换 i18n key(ai.toolGroup.readFile)。— src/components/ToolCardList.vue:259-274 - UX-260617-18 [P2]🎯 — isToolFailure() 正则过于宽泛误判合法输出为失败 —
ToolCard.vue:220-224/执行失败|failed|error[:\s]/i匹配任何含这些子串的文本。search_files 结果列 error.log / run_command stderr 含 warning 均被标红。修法:收窄为/^(执行失败|Error:|Failed:)/m或按工具类型分别配置检测策略。— src/components/ToolCard.vue:220-224 - UX-260617-19 [P2]🎯 — stopChat 本地先行 reset 与后端竞态窗口 —
useAiSend.ts:479-488先设 streaming=false 再发 IPC stop。IPC 失败时后端继续生成但前端已停止;若后续事件也丢失则永久卡死。注释承认风险但无缓解。修法:stop 后启动 5s 守护超时,未收到 Completed/Error 则推提示或重试 stop。— src/composables/ai/useAiSend.ts:479-488
⚪ aichat 扫描新增发现(LOW · 8 项)
- UX-260617-20 [P3]🎯 — base64 图片 parts 无大小预算,200 条消息上限不感知单条体积 — MESSAGE_CAP=200 仅按条数 splice,单条 base64 可达数 MB。极端 200条×5MB=~1GB 全挂 reactive state。修法:增加基于序列化大小的预算(total parts size<50MB),超出裁剪最旧消息 parts。— src/api/types.ts:228-243 + src/stores/ai.ts:78-87
- UX-260617-21 [P3]🎯 — Markdown 表格溢出气泡容器 —
.ai-msg-bubble缺表格专属溢出处理。AI 返回≥4列表格撑破宽度溢出到侧边栏。修法:.ai-md table { display:block; overflow-x:auto; max-width:100% }。— src/components/AiChat.vue CSS (~L3259) - UX-260617-22 [P3]🎯 — 空内容 AI 回复渲染不可见气泡 —
AiChat.vue:457v-if 条件使空 content 已完成消息整个 bubble 不渲染 → avatar+一片空白。修法:空 content 显示最小化占位(empty)或隐藏整条含 avatar。— src/components/AiChat.vue:457 - UX-260617-23 [P3]🎯 — 搜索结果选中不清除搜索框,搜索视图不退出 —
AiChat.vue:46点击结果 switchConversation 但不清 searchQuery。搜索文字保留+仍显示扁平列表。修法:点击 handler 加searchQuery=''自动退出搜索模式。— src/components/AiChat.vue:46 - UX-260617-24 [P3]🎯 — 删除当前活跃对话无特殊警告 —
AiChat.vue:1140-1145confirmDelete 通用文案不区分活跃对话。对比 confirmNewConversation 有上下文感知警告。修法:id===activeConversationId 时改醒目提示说明消息区将清空。— src/components/AiChat.vue:1140-1145 - UX-260617-25 [P3]🎯 — parseBlockNoCache 非 null 断言无运行时防护 —
AiChat.vue:848purify!.sanitize(marked!.parse(...))。marked/DOMPurify 动态 import 失败时 TypeError → rAF 循环中断流式卡死。修法:防御性 null check + 降级 escapeFallback。— src/components/AiChat.vue:848 - UX-260617-26 [P3]🎯 — startListener/stopListener 竞态:_startPromise 未在 stop 中清零 —
useAiEvents.ts:354+378stop 不清 _startPromise。极速 mount/unmount/mount(HMR) → 返回过期 promise。修法:stop 中加_startPromise=null。— src/composables/ai/useAiEvents.ts:378-388 - UX-260617-27 [P3]🎯 — ConfirmDialog 危险按钮默认标签"删除"语义不安全 —
ConfirmDialog.vue:9-10默认$t('common.delete')。非删除场景(高危工具审批)忘传 dangerLabel → 按钮显示"删除"。修法:默认值改为$t('common.confirm')。— src/components/ConfirmDialog.vue:9-10
ℹ️ 架构观察(INFO · 2 项·不进修复队列)
- UX-260617-28 [INFO] — 双监听器同通道 fragility — useAiEvents + useAiContext 各自 listen('ai-chat-event'),人工协调防双重处理(AiCompressing flag)非架构保证。未来新增事件处理可能触发双重 bug。长期考虑单一分发器模式。— src/composables/ai/useAiEvents.ts:269 + src/composables/ai/useAiContext.ts:85-105
📊 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,现走前端闭环)