62 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-18 归档: 已完成
[x]与历史分析段已迁至 07-项目管理/todo归档/2026-06-18.md。
交接状态(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-18 更新)
未完成待办按可执行性分 8 组(详细条目见下方各分类,勿重复记录):
| 组 | 说明 | 代表项 |
|---|---|---|
| A todo 卫生 | 矛盾清理/被取代退役标注 | ✅ 本批已完成 |
| B 零风险减法 | 死链清/空壳合并 | ✅ 本批已完成 |
| 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 🟡,本批不含)。
📦 已完成项归档: 07-项目管理/todo归档/2026-06.md — 2026-06-18 拆分, 已完成
[x]与历史分析段迁此。另有 2026-06-18.md — 本次归档。
🔴 2026-06-18 Agentic 最大轮次设置不生效(设 30 仍按 10 截断·session-role-diagnose-only·未实施)
用户报:Settings 里 Agentic 最大循环轮次设 30,但实际跑到 10 就提醒「达到最大」。走查全链路定位根因 + 记 todo,不改代码。
现象:设置项显示 30(前端持久值),实际 loop 跑 10 轮即触发 AiMaxRoundsReached(达 max 提醒)。
根因(后端内存态 + 恢复同步绑错生命周期):
后端 agent_max_iterations 是 Arc<AtomicUsize> 纯内存(state.rs:242),重启回默认 10(DEFAULT_MAX_AGENT_ITERATIONS),自身不落 DB。持久化只在前端 appSettings(SQLite),后端靠前端启动时调 ai_set_agent_max_iterations IPC 同步恢复。而该同步 syncAgentMaxIterations() 只绑在 GeneralPanel.onMounted(GeneralPanel.vue:267)—— GeneralPanel 是 Settings 页子组件,只在用户打开 Settings 时才挂载。两失效场景(任一即中):
- 重启后直接用 AI Chat(没先开 Settings 页) → GeneralPanel 未挂载 → onMounted 不跑 → 后端停留默认 10 → loop 10 轮提醒;前端 Settings 显示 30(持久值)与后端实际 10 不一致。
- 即便开了 Settings,
appSettings缓存异步未就绪时 →settings.agentMaxIterations初值读到默认 10(:153)→ onMounted 把 10 sync 给后端(onMounted 只跑一次,cache 就绪后不重跑)。
agent_max_retries 同病(同款 onMounted sync :269,后端 agent_max_retries 同款内存态 state.rs:285-287)。
排除项:
- ✅ 后端 clamp 1-50(
commands.rs:1680)——30 不会被压成 10。 - ✅
@change→IPC链路(GeneralPanel.vue:226-237)——用户改值当时会同步,当前会话生效;失效只在重启/未挂载场景。 - ✅ loop 入口 load 快照(
commands.rs:138/264/739/879)——热改后当前 loop 不受影响是设计(注释 :1670),但用户是发新消息仍 10,非当前 loop 热改问题。
证据链:state.rs:282-284(默认 DEFAULT) · agentic.rs:45(DEFAULT_MAX_AGENT_ITERATIONS=10) · commands.rs:1674-1683(set 只 store AtomicUsize 不写 DB) · GeneralPanel.vue:262-270(恢复 sync 在 onMounted,非根组件) · agentic.rs:891-909(!converged emit AiMaxRoundsReached)。
修法(二选一):
- B 推荐(前端小改):
syncAgentMaxIterations()/syncAgentMaxRetries()从 GeneralPanel.onMounted 上提到 App.vue 根 onMounted(应用启动必挂载)+ 确保appSettingsloadAll 就绪后再调(读持久真值)。利用前端已有 SQLite 持久化,几行改动,立即生效。 - C 彻底(后端持久):
AppState::init从 DB settings 表读agent_max_iterations/retries,ai_set_*IPC 同时写 DB。后端自持不依赖前端同步时机,改动较大但根治。
待用户确认:触发时是否「重启应用后直接发消息(没先开 Settings 页)」——若是,根因 1 坐实。
- ✅(workflow wuirgcxoy·App.vue:215-224 onMounted loadAll+migrate 后加 setAgentMaxIterations/setAgentMaxRetries 恢复同步(clamp 对齐 GeneralPanel:228/245)·GeneralPanel:266 onMounted sync 保留双保险(IPC 幂等)·修bug正向重启后正确恢复持久值·主代 vue-tsc EXIT 0) B-260618-23 [P1] — Agentic 最大轮次设置不生效(设 30 仍按 10 截断)。后端
agent_max_iterations(state.rs:242) 纯内存 AtomicUsize 重启回默认 10、不落 DB;恢复同步syncAgentMaxIterations只绑GeneralPanel.onMounted(:267),GeneralPanel 仅 Settings 页挂载 → 重启后直接用 AI Chat 不同步 → 后端用默认 10 与前端显示 30 不一致 → 达 10 提醒。agent_max_retries同病。修法 B(推荐):sync 上提到 App.vue 根 onMounted + 等 appSettings 就绪;修法 C:后端 AppState::init 从 DB 读持久化 + set IPC 写 DB。—src/components/settings/GeneralPanel.vue(:262-270) +src/App.vue(根 onMounted) 或src-tauri/src/state.rs(:282-287) +commands.rs(:1674-1683)
🔧 2026-06-17 走查·tauri 打包目标收窄 + 状态映射 DRY(仅分析·未实施)
本轮 git diff 核验工作区未提交改动 + 最新提交 1cd7652。session-role-diagnose-only。
- B-260617-11 [P2] — tauri.conf.json 打包目标收窄未提交,若误入库锁死非 Windows 构建。工作区改动
bundle.targets: "all" → ["nsis"](src-tauri/tauri.conf.json:28),收窄到仅 Windows NSIS 安装包。若意图为本地只打 Windows 包,合理;但若随其他改动一并提交,macOS(dmg/app)、Linux(deb/appimage)构建将不可用,影响其他开发者/CI。确认点:该改动是临时本地构建还是有意入库?临时则建议提交前 revert 此行;有意则建议改为按平台条件配置而非硬编码单一 target。—— src-tauri/tauri.conf.json(:28)
🔧 2026-06-17 走查·DeepSeek reasoning_content 实施审查(P1 提交不完整)
核验 HEAD 74003bc(BUG-260617-12 修复提交)+ 工作区残留。session-role-diagnose-only,逐行核验非信声明。闭环逻辑正确(见 :49 注记),但发现提交不完整 P1。
- B-260617-16 [P3·可选] — Partial(MidStream 保文)回填半截 reasoning_content 语义待评。agentic.rs MidStream 保文路径用
round_reasoning_content(本轮部分累积)写回 ChatMessage,回传下一轮 DeepSeek 会看到不完整推理。属异常路径(网络中断保文),保文后已加"响应不完整"系统提示,用户大概率重发。影响小,登记观察。— src-tauri/src/commands/ai/agentic.rs(:765)
🔧 2026-06-17 对话标题不更新(还叫"新对话"·待运行时验证)
用户报:连续对话很多内容 + 刷新重启后,标题仍"新对话"。session-role-diagnose-only,排查链路 + 记 todo。
排查结论(排除项):
- ✅ save_conversation 不覆盖 title(核验 conversation.rs:166-194:已落库分支只改 messages/updated_at/token/model,不碰 title;首次落库
title:None:194)。排除"save 重置 title"。 - ✅ title 初始 None(懒创建不落库 commands.rs:1286 + 首次落库 None)→ ensure_conversation_title :38-42
is_some()=false → 不跳过,应生成。 - ✅ extract_title 兜底(title.rs:157 取首句前 30 字)即使 LLM 失败也应落库非"新对话"。
最可能根因(待运行时 tracing 验证):
- 嫌疑 a(主):ensure_conversation_title 在 spawn 后台 task(agentic.rs:924-931),
generate_title_via_llm(title.rs:90/120 网络耗时)未完成时用户已刷新/重启 → 标题未落库 → 重启读 NULL → "新对话"。 - 嫌疑 b:title.rs:95
let _ = conv_repo.update_field(...)落库失败被吞(无日志),标题没入库但 :96 emit 照发(前端短暂更新,重启丢)。 - 嫌疑 c:title.rs:141-142 双层信号量 acquire_global+acquire_per_conv 阻塞(permit 未释放)→ ensure 永不完成。
矛盾点:用户"很多内容多次对话"应多次触发 ensure(agentic.rs:462/776/843/930 四点),总有完成的——除非每次刷新打断或信号量持续阻塞。确诊需运行时日志:grep tracing 看"标题生成跳过"(title.rs:72)/"更新对话失败"(conversation.rs:186) / ensure 是否触发。
修法方向(待确诊后):① ensure 改同步(loop 内 await,完成后再 emit Completed)② :95 update_field 失败加 error 日志 ③ 排查信号量阻塞。
- ✅(2026-06-18·根因重定:非刷新时序,是 LLM 标题生成卡住时无即时兜底·修:title.rs ensure_conversation_title 进入即 extract_title 兜底落库+emit(LLM 成功覆盖)+LLM 调用包 tokio::time::timeout 20s 防卡死堆积·cargo check src-tauri EXIT 0) B-260617-17 [P2] — 对话标题不更新(对话很久/刷新后仍"新对话")。save 不覆盖 title(已排除)。根因(重定):
ensure_conversation_title原仅在 LLM 返回 None 或 provider 构建失败时落 extract_title 兜底,但 LLM 卡住(generate_title_via_llm 的 llm_concurrency 信号量 acquire 阻塞/网络挂起/spawn 未跑完)时既不返回 None 也不落库 → 标题长期停留"新对话"。spawn_ensure_title在 agentic 4 处(471/785/852/939)触发均中招(原记"嫌疑 a 刷新时序"为表象之一)。修法:进入即 extract 兜底落库+emit(侧栏即时非新对话),LLM 成功后覆盖;LLM 调用包 20s 超时,超时/失败保留兜底。—src-tauri/src/commands/ai/title.rs(ensure_conversation_title + generate_title_via_llm 外层 timeout)。配套(历史会话点开重总结):ai_conversation_switch(commands.rs:1454) 加AppHandle参数,title 空(record.title.is_none())时drop(session)后get_active_provider+spawn_ensure_title触发重生成(无 provider 静默跳过,不阻塞切换;ensure 内 title Some 判断防重复)。前端无需改(AppHandle 自动注入)。cargo check src-tauri EXIT 0。
💡 2026-06-16 新需求(已分析·待实施)
功能需求,已走查定位链路 + 方案记录,待用户确认后实施。
-
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 是大改架构升级,需单独设计文档 + 立项。✅ 已决(2026-06-16·待决策归档):a — A 隔离修复 + B 立项(🔥 B 阶段高优先级尽快实施),UI d1+d2 都做(侧栏切换 + 独立 Tauri 窗口)。 -
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 交互优化·分析完成·待实施)
用户实时反馈的交互优化需求,已走查定位链路+方案记录。
-
✅(F-15 全阶段完成·波3 is_active白名单+push token / 波5 compress_prompt四段式+compress_via_llm+context辅助 / 波6 阶段2手动 2IPC+3事件+前端2按钮 / 波8 阶段3自动 loop顶部检测压缩+降级·主代分波核查 cargo0err+vue-tsc0+df-ai/devflow test passed) 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
🔴 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 哪项错,见详情文档「验证步骤」
🟦 架构审查待修项(2026-06-16)
纯架构层评估(边界/依赖/抽象/扩展性/状态管理),详见 架构审查-2026-06-15.md。共 🔴6 🟡6 ⚪4 + 亮点 6。与 06-14 三份报告去重(不重复 bug/性能 FR-、aichat AR-)。
中期(技术债)
- 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 命名统一
P1 — 重要缺陷
- 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)
- ✅(workflow w999qdu86·ai_approve None 分支 match 三路分流对齐 audit_finalize·cargo check EXIT 0 + vue-tsc EXIT 0 + 主代 grep 印证) BUG-260618-11 — [P0🔴]
commands.rs:289ai_approve 幂等路径unwrap_or_default吞 DB 错误 — audit.rs 已修(B-260617-17 match 三路分流),但 ai_approve 的 find_by_tool_call_id 仍 unwrap_or_default。DB 故障→Err 压 None→返回「未找到挂起审批」误导。修复: 对齐 audit_finalize 模式 match { Ok(Some)→rec, Ok(None)→return Err, Err(e)→error!+return Err } — source:CR-260618-11 batch69 全面走查 (06-18)。行为变更:DB 故障现返回「查询审批记录失败(DB 故障),请重试」明确错误(原误导为「未找到挂起」),两者同属 Result Err 分支调用方语义不变,仅文案更精确可重试。
🔴 架构坏味道全面扫描(2026-06-18·6路并行 Explore agent·57K行代码)
来源: God文件/长函数 + 循环依赖/耦合 + DRY违反 + 错误处理不一致 + 命名API/类型安全 + 测试覆盖/死代码。6 维度全覆盖。
🔴 P0 必须修复 (4项):
- ✅(workflow w5siwnipj 核验+主代实施·agentic:348/knowledge_inject:130,139,312/idea:292-296 加 tracing::warn 降级不改返回值·audit:344 误报(CR-11 已修三路 match)·commands:290 BUG-11 已修·cargo check EXIT 0 + vue-tsc EXIT 0) SMELL-P0-1 — [P0🔴]
unwrap_or_default吞错 5 高危闭环。agentic.rs:348(provider 池空走原空池兜底)/knowledge_inject.rs:130,139,312(检索/解析失败走原降级不注入/提炼跳过)/idea.rs:296(tags 坏降级空继续评估)本批 match+warn(空 Vec 零行为变更);audit.rs:344 误报(audit_finalize CR-11 已修三路 match,line 漂移+旧版本);commands.rs:290 BUG-11 已修。58 处全量排查降 P2 非紧急(高危已清,余为 Option 取默认/JSON 序列化等低危)。 - SMELL-P0-2 — [P0🔴]
tool_registry.rs:363 build_ai_tool_registry1091 行单函数 — 拆分为按功能分组注册函数(register_crud_tools / register_file_tools / register_workflow_tools 等),每个 <200 行可测试。 - SMELL-P0-3 — [P0🔴]
AiChat.vue4026 行 God 组件 — 拆分: ConversationSidebar(侧栏+搜索) / MessageList(消息列表+流式) / ChatInput(输入框+附件) / ApprovalPanel(审批卡片)。目标单组件 <500 行。 - ✅(workflow wowdnw4ba·tests/shell.rs 5 #[tokio::test] 覆盖成功/非零/超时/env/working_dir + shell.rs:55 过时TODO注释清理·execute逻辑零改动·主代 cargo test -p df-execute 5 passed + cargo check --workspace EXIT 0) SMELL-P0-4 — [P0🔴]
df-executecrate 零测试(shell.rs 120行已集成工作流) — 补 shell 命令执行+超时+输出截断基础测试。
🟡 P1 应该改进 (9项):
- ✅(workflow w8774xcev·7 agent 按 crate 全域扫·绝大多数 no-action:生产 unwrap 全在 #[cfg(test)] 或已是安全 expect/unwrap_or(crates 错误处理健康,之前多批重构清理)·仅 df-workflow dag.rs:106/135 两处运行时 unwrap 加 safe 注释(contains 守卫+in_degree 全覆盖不变量)·主代 cargo check --workspace EXIT 0 + cargo test df-workflow EXIT 0) SMELL-P1-1 — [P1🟡] 生产代码 206 处
unwrap()排查——逐处加注释说明为何安全(如"guaranteed by upstream invariant")或改expect("msg")/?/ok_or_else - ✅(workflow wuirgcxoy no-action·前提过时:err_str helper 已存在 src-tauri/src/commands/mod.rs:23·纯 .map_err(|e|e.to_string()) 样板 0 残留(仅 mod.rs:22 注释字面量)·101 处已用 err_str·剩 5 处 String 上下文闭包+28 处 anyhow 不同抽象层不应统一·主代 grep 印证) SMELL-P1-2 — [P1🟡] IPC 层
.map_err(err_str)?样板 100+ 处 —— 抽fn err_msg(s: &str) -> impl FnOnce(E) -> AppError或 Result 扩展方法 - ✅(workflow wuirgcxoy no-action·前提不成立:views 仅 AuditLog.vue:102 直接 invoke·invoke 已统一 src/api 层封装(9 文件)·try-catch-finally 副作用高度异构(scanError/showToast/task=null/递归回退等)无标准 loading/error/invoke 三件套可封装·主代 grep 印证) SMELL-P1-3 — [P1🟡] Vue views try-catch-finally invoke 模式 15+ 处重复 —— 抽
useTauriCommand<T>(cmd, args)composable 封装 loading/error 状态 - ✅(workflow w8774xcev 评估·df-nodes Cargo.toml 依赖 5 crate 属实·task_state_machine.rs 零 crate 依赖(仅 HashMap+OnceLock)+单一消费者 advance_task_atomic·拆出不解锁循环依赖·结论缓做:收益不抵成本,触发条件=第二 crate 需复用状态机判定时再拆) SMELL-P1-4 — [P1🟡] df-nodes 聚合点依赖 5 crate —— 评估拆分 df-task-state-machine(状态机从节点实现分离)
- ✅(workflow weckqp9mv no-action·现场核验两半:advance_task_atomic 已在领域层 df-nodes/task_advance_node.rs:43(状态机+CAS+bump_rounds),3 IPC 调用点 task.rs:171/workflow.rs:319/tool_registry.rs:565 纯 thin 转发零逻辑✅已达成;record_to_idea 下沉经依赖图核验为错误方向——df-ideas→df-storage 倒依赖违反六边形架构(IdeaRecord 是 storage DTO,IPC 层是 storage-DTO→domain-entity 映射的正确 adapter 归属)·主代 grep 印证) SMELL-P1-5 — [P1🟡] IPC 层职责泄漏 ——
advance_task_atomic(workflow.rs) /record_to_idea(idea.rs) 转换逻辑下沉 domain 层 - SMELL-P1-6 — [P1🟡] String 类型滥用替 newtype/enum —— execution_id/status/tool_type 等 5+ 处改强类型
- ✅(workflow weckqp9mv no-action·enum 负收益:archived/pinned 是 bool DB 列直映射,动作式 enum 在 crud 边界每调必 bool↔enum 转换只搬噪声;2 crud+2 IPC 调用者紧邻定义已自解释;真正歧义在前端 store.archiveConversation(id,true) 但正解是拆 archive/unarchive 两 TS 方法(纯前端重构)非推 enum 到 JS 边界;IPC 引 enum 触 Tauri 序列化层 prod runtime 才报错·主代 grep 印证) SMELL-P1-7 — [P1🟡] bool 参数陷阱 ——
set_archived(id, bool)/set_pinned(id, bool)改 enum - ✅(workflow wowdnw4ba·6篇文档加「实施状态(2026-06-18核对)」标注段·grep file:line佐证·不改设计正文·主代 grep 6篇标注段全在:B-03:11/AiNode:11/类型对齐:7/F-01:11/F-05:10/secret:10) SMELL-P1-8 — [P1🟡] 架构文档过时 6 篇更新 —— B-03/AiNode自审/前后端类型对齐/F-01/F-05/secret下沉 标注实际实施状态
- SMELL-P1-9 — [P1🟡] crud.rs 2212 行按表拆分 —— project_repo / task_repo / conversation_repo / idea_repo 独立文件
⚪ P2 可选优化 (5项):
- ✅(workflow wowdnw4ba no-action·SMELL描述过时:实测run方法151行+2参非描述的282行+8参·SW-01 TOCTOU保留(line136守卫/138-153对称emit)·描述系历史形态已被先前重构改善·销账) SMELL-P2-1 — run_workflow_inner 282行+8参数 → 配置对象封装 + 阶段子函数提取
- ✅(workflow wowdnw4ba·main.ts app.config.errorHandler + ErrorBoundary.vue onErrorCaptured 降级UI+重试 + i18n error.ts×2中英对称·未嵌入仅提供能力零行为变更·主代 vue-tsc EXIT 0) SMELL-P2-2 — 前端全局 ErrorBoundary(Vue errorHandler)
- ✅(workflow w8774xcev 评估·描述修正:NodeContext 无 metadata 字段(node.rs:13-26),含 metadata 的是 NodeOutput(node.rs:34)·NodeOutput.metadata 死字段(2构造点初始化空 HashMap+0消费者)·ShellRequest.env 同死字段(5构造点全 HashMap::new() 空)·改结构体波及小但纯类型整洁 ROI 低·建议直接删字段或保留,不实施) SMELL-P2-3 — HashMap<String,String> → 结构体(NodeContext.metadata / ShellRequest.env)
- ✅(workflow w5siwnipj 核验·line 3 模块注释已有「⚠ B 路线占位...有意保留空壳,勿删」三要素齐全·零外部调用·保留 B 路线预留点) SMELL-P2-4 — coordinator.rs 空壳标注 roadmap 已完成(line 3 注释),保留非删(B 路线 aichat 决策能力预留,对齐 memory aichat-arch-extensibility)
- ✅(workflow w5siwnipj 核验 7 处+主代补 #6/#7 注释·全部标注完成·cargo check df-ai EXIT 0) SMELL-P2-5 — #[allow(dead_code)] 7 处全部标注完成(实际 7 非声称 12,部分已清)。5 处原有注释(prompt.rs:147/compress.rs:45,98 F-15 阶段预留 + openai_compat.rs:81,98 SW-24 反序列化对齐)+ 本批补 anthropic_compat.rs:61 id/:65 stop_reason 注释(对齐 openai SW-24 风格)。全合理保留无真死代码。另:cargo warning 死字段 4 处(PendingApproval.risk_level/diff + AppState.releases/node_executions)属另一类未标 allow,待核对。
🟡 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-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
待澄清 / 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 单例未动)。✅ 已并入 F-260616-09(2026-06-16 已决 a):A 路线已实施待实测(T-260614-02),B 高优先立项,详见待决策归档/2026-06.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 — 去重:与 AR-M5(跨窗口 state 完全隔离)同类,aichat 审查描述更深 — source:代码审查 + Sprint 19 (06-14)
🔧 2026-06-18 6 域并行走查 sweep-fix(workflow wd2fnjh3s·6 agent·主代核查 cargo check --workspace EXIT 0 + vue-tsc EXIT 0)
开放式扫模块找新问题 + 修确定性低风险 bug + 记歧义为新 todo。6 agent 修 12 处 / 新 todo 27 项(去重后 24 项入此节,3 项重复跳过:conditions 未实现=T-260614-11 / AiSession 单例跨会话 clear=F-260616-09 B 路线 / dead_code 6 处基线 warning 合 1 条)。主代独立核验(不信 agent 自报 checkOk):cargo check --workspace EXIT 0(仅 6 pre-existing dead_code warning 基线)+ vue-tsc EXIT 0 + grep 抽查 scan.rs 索引对齐/audit.rs 吞错分流/commands.rs retain 源码形态全正确。审查防污染拦截 1 瞬态误报:fe-other agent vue-tsc 抓 ToolCard.vue:653 cmdOutput TS6133 never read,实为 fe-aichat 改 ToolCard 中间态(computed 已加模板未引用)被并发 vue-tsc 抓到,最终态模板 line 122/128 已引用,全量 vue-tsc 复核 EXIT 0 确认非 bug(memory review-batching-worktree-transient 教训)。
已修 12 处(主代核查全通过):
- df-ai:① context.rs
estimate_message累加 parts Text/Image.base64 token(F-05 回归,大图绕过预算致 provider 400/500,加单测 estimate_message_counts_parts_tokens)② anthropic_compat.rs Image 转换 clone→move(DRY/效率) - df-storage/project/ideas:③ scan.rs
is_pure_badge_lineto_lowercase→to_ascii_lowercase(非ASCII索引错位/panic)④ scan.rsis_monorepoworkspaces null 误判(.is_some_and(!is_null))⑤ adversarial.rs MockProvider 补 reasoning_content(测试 E0063 编译失败) - rust-ipc:⑥ audit.rs
audit_finalize拆 unwrap_or_default 吞错(Err/None 分流+error 日志,对齐 B-260617-17)⑦ commands.rsai_conversation_delete补非活跃对话 pending_approvals retain(对齐 switch,修死审批残留)⑧ audit.rs 抽 PENDING_APPROVAL_PLACEHOLDER 常量(防去重漂移误命中) - fe-aichat:⑨ ToolCard.vue isToolFailure/commandOutput 下沉 computed(复用 parsed,DRY/性能)⑩ AiChat.vue 清 4 处虚拟滚动残留死注释(B-260618-01 已移除)
- fe-other:⑪ Projects.vue onUnmounted 清 _toastTimer(timer-leak)⑫ Knowledge.vue refConvTitle parseContext 两次→一次(冗余 JSON.parse)
新 todo 24 项:
🔴 P1(2·均需确认设计意图/跨函数,非立即修)
- ✅(workflow wexu1isx1·cargo check EXIT 0 + cargo test df-workflow 23 passed executor 取消不回归) SW-260618-01 [P1] — executor Ok 路径取消节点事件/状态不一致(TOCTOU 残留)。executor 阶段三 Ok 分支:已取消节点(set_cancelled 后 execute 返 Ok)跳过 set_completed 正确,但仍 emit NodeCompleted(:139-144 不在 is_cancelled 判断内)+ 仍 outputs.insert(:145)。对比 Err 分支 :151-157 已对称处理(取消则 emit NodeCancelled 替代 NodeFailed)。结果:前端收到状态机=Cancelled 但事件=NodeCompleted 的矛盾信号。修法:把 emit Node Completed 与 outputs.insert 移入 if !is_cancelled 分支。但 outputs.insert 移入涉及下游 inputs 行为变更(当前测试锁定 run Ok=取消不中止工作流),需先确认设计意图:Ok 路径取消到底该不该中止后续层?属行为变更,先确认再改。— crates/df-workflow/src/executor.rs(:131-145)
- ✅(workflow wexu1isx1 + 主代修借用E0502 helper改接&mut session disjoint borrow·cargo check EXIT 0) SW-260618-02 [P1] — 审批占位 tool_result 在 stop/clear/create/delete 清 pending_approvals 时未替换终态文本。占位「需要用户审批,等待确认」残留在 messages,下次发送会把占位文本喂给 LLM。涉及 5 处 .clear()(commands.rs:415/762/863/1317/1334),需统一 replace_tool_result_content 终态化。属行为变更 + 跨多函数,需设计统一收口(可走 session_state 视图 mod.rs:393 已就位待替换)。— src-tauri/src/commands/ai/commands.rs(:861-869 ai_chat_stop 审批清理分支,同型 5 处)
🟡 P2(8)
- ✅(评估暂缓·url 模式当前不可达(前端 Phase2b 只产 base64)+ anthropic_compat 已 warn;加 commands 层校验需追 parts 全链路(IPC→ContextManager→provider)定校验点有歧义 + 误拒合法 url 风险,待 url→base64 预拉落地后 url 模式变可达再加) SW-260618-03 [P2] — Anthropic 图片 url 模式静默 400。url 模式只 warn + 发空 data(必触发 Anthropic 400)。注释声明 url 模式当前不可达(前端 Phase2b 只产 base64),但 url→base64 预拉未落地前前端误传 url 会静默 400。建议 commands 层加 parts 合法性校验:url 片无 base64 直接拒绝。— crates/df-ai/src/anthropic_compat.rs(:356-378)
- ✅(workflow wexu1isx1·KnowledgeEventsRepo::list_recent ORDER BY timestamp 兜底·cargo check EXIT 0 + df-storage test passed) SW-260618-04 [P2] — knowledge_events 通用 query 硬编码 ORDER BY created_at 崩溃。impl_repo! 宏 query() 硬编码 ORDER BY created_at DESC,但 knowledge_events 表无 created_at(只有 timestamp)。当前无调用方(均走 list_by_knowledge),误调 state.knowledge_events.query() 触发 SQLite "no such column"。修法:① 加专用 query 带 ORDER BY timestamp ② 重构宏支持 per-table 排序列(架构级)。属行为/架构改不直修。— crates/df-storage/src/crud.rs(:139-163 宏 query + :1592-1611 KnowledgeEventsRepo)
- ✅(评估暂缓·当前 PENDING_APPROVAL_PLACEHOLDER 集中常量+SW-24/CR-01 防漂移有效,内容匹配可靠;改进是未来防御(占位含动态片段)+ 跨 df-ai crate 加 status 字段设计成本高) SW-260618-05 [P2] — pending 占位靠内容字符串匹配(应改 status/kind 字段)。find_cached_high_risk_result(audit.rs:464)用 PENDING_APPROVAL_PLACEHOLDER 字符串内容排除 pending 占位,依赖内容相等,若未来占位文本含动态片段(如审批 id)失效。建议 ChatMessage 加 status 字段或 ToolResultKind 枚举(pending/resolved)区分。跨 df-ai crate 需设计。— src-tauri/src/commands/ai/audit.rs(:458-464)
- ✅(主代修·SW-06·vue-tsc EXIT 0) SW-260618-06 [P2] — ToolCard 双 watch(props.tc.status) 合并。合并 watch 放原 watch2 位置(approving :552/approvingTimer :553/cmdOutputExpanded :534 三 ref 都已定义避 TDZ——原 watch1 在 :535 immediate 访问 approving/approvingTimer 会 TDZ),加 immediate(原 watch1 语义),callback 内 cmdOutputExpanded(completed 初始化)+ approving 复位(pending_approval 判断)合并。immediate 边界:s=初始 status 时 cmdOutputExpanded 仅 completed 设(初始非 completed 无副作用)+ approving 初始 false/approvingTimer 初始 null(if null 跳过 clearTimeout)安全。删原 watch1+watch2,合并 watch 放原 watch2 位置。— src/components/ToolCard.vue
- ✅(主代修·SW-07·vue-tsc EXIT 0) SW-260618-07 [P2] — AiChat 双 watch(currentText) 合并。删原 :2040 watch(currentText→onContentChange),并入 :2047 watch(currentText→scheduleStreamParse) 单 callback:先 onContentChange(滚动跟随)后 scheduleStreamParse(rAF 分块),保持原注册顺序(:2040 先 :2047 后 → callback 内同序)。:2039 watch(messages.length→onContentChange)不同源保留。无 immediate(两原 watch 均无),无 TDZ(onContentChange :2032/scheduleStreamParse :912 均在前)。— src/components/AiChat.vue
- ✅(主代修·SW-08·vue-tsc EXIT 0) SW-260618-08 [P2] — AuditLog.vue 全硬编码中文未 i18n。新建 auditLog 命名空间(en/zh-CN:title/refresh/desc/loading/empty + col×7 + pager×4 + risk×3 + status×6 + decided×2)+ AuditLog.vue 引入 useI18n,template 15 处文本 t() 化 + riskLabel/statusLabel/decidedLabel 改 t(
auditLog.xxx.${val})(class 映射 statusClass/riskClass/decidedClass 保留不国际化)。模式对齐 Dashboard/Knowledge/Ideas。— src/i18n/{en,zh-CN}/auditLog.ts(新建) + src/views/AuditLog.vue - ✅(主代修·SW-09·cargo check df-nodes EXIT 0 无 warning + cargo test 82 passed 0 failed) SW-260618-09 [P2] — AiNode/AiSelfReviewNode provider 解析+构建 DRY 重复。抽 2 helper:resolve_and_parse(合并 resolve_provider+parse_params)+ provider_from_params(封装 build_provider 5 行)。AiNode :254-262(9 行)→ 2 行;SelfReview parse(:465-466)+ build(:493-498)→ resolve_and_parse + provider_from_params(中间 task 读保留,p 生命周期跨 task 读正确)。messages/temperature 差异不抽(AiNode 用 p.system_prompt/p.temperature;SelfReview 强约束 Some(0.0)+REVIEW_SYSTEM_PROMPT,各节点自有)。原 resolve_provider/parse_params fn 保留(test 直接调用),helper 上层封装。— crates/df-nodes/src/ai_node.rs(2 helper 新增 + 2 节点 execute 改)
- ✅(主代修·SW-10·cargo check df-ai EXIT 0 无 warning + cargo test df-ai 112 passed 0 failed) SW-260618-10 [P2] — reqwest Client 构建重复(OpenAI/Anthropic Provider::new)。抽
lib.rs::build_provider_client()pub(crate) fn(connect_timeout 30s + 回退 Client::new + tracing::warn 集中),两 Provider::new 调crate::build_provider_client()消除 7 行逐字重复。副作用 grep 核验:Client import 两文件仍用于 struct 字段(self.client:Client);warn 仍多处用(openai:503/anthropic:183/330/364/497/521/535);Duration import 仍用于 :484/:481 timeout。纯 DRY 抽函数零行为变化。— crates/df-ai/src/lib.rs(+14) + openai_compat.rs(-8+2) + anthropic_compat.rs(-6+2)
⚪ P3(14)
- ✅(评估暂缓·当前构造器保证非空不可达,外部 struct 字面量非预期路径,加 normalize 属未来防御 ROI 低) SW-260618-11 [P3] — OpenAI 图片构造防御缺失。Image{base64:Some, media_type:None} 或 url+base64 双空退化为空 image_url.url(400)。当前构造器保证非空故不可达,外部 struct 字面量构造缺防御。可加 ContentPart::normalize。— crates/df-ai/src/openai_compat.rs(:345-358)
- ✅(评估确认·设计合理:archived 状态变动需动 updated_at 排序,pinned 排序走 set_pinned 专用方法不碰 updated_at 保值语义;非 bug) SW-260618-12 [P3] — ai_conversations 白名单 pinned 与 archived 不对称。白名单含 archived 不含 pinned,启用 pinned 会动 updated_at 破坏置顶时间语义。建议先确认产品意图(或把 archived 也移除强制走专用方法)。— crates/df-storage/src/crud.rs(:372-375)
- ✅(评估暂缓·当前文档约定+测试守护够,内部 normalize 需改 crud 宏影响面大 ROI 低) SW-260618-13 [P3] — find_path_conflict 跨层规范化靠文档约定。期望调用方先 normalize_path,第三个调用方漏掉则静默误判。可选内部 normalize 或文档+测试守护。— crates/df-storage/src/crud.rs(:681-695)
- ✅(评估不做·注释已承认权衡:误读为 0=未达阈值无副作用,迁 app data dir ROI 低) SW-260618-14 [P3] — keyring failcount sidecar 用 current_dir 跨启动不稳。注释已承认权衡(误读为0即按未达阈值无副作用)。若要稳定需迁 app data dir。— crates/df-storage/src/secret.rs(:34-38)
- ✅(主代修·SW-16·cargo check --workspace EXIT 0 + cargo test -p devflow 92 passed 0 failed) SW-260618-16 [P3] — F-05 去重审计 status 固定 completed 不透传 rejected/failed。find_cached_high_risk_result 加 audit_repo 参数 + 改 async + 返回 Option<(String,String)>(content,status);命中旧 tool_result 后查 AiToolExecutionRepo::find_by_tool_call_id(old_id)(已存在 crud.rs:1256,复用零新增方法)拿缓存来源真实 status(completed/rejected/failed),透传 audit_tool_call 替代固定 "completed";审计记录缺失/查询失败 fallback completed(不阻塞去重降级原行为)。调用点 :625 拆 (cached,status)+.await / :639 &status。审计语义与结果内容一致,防"rejected/failed 结果记 completed"误导安全追溯。— src-tauri/src/commands/ai/audit.rs(find_cached 签名+返回+status 查询 / process_tool_calls :625/:639)
- ✅(评估不做·破坏 UX-260616-03 记忆态设计) SW-260618-17 [P3] — ToolCardList 4 Set 跨会话不重置累积。评估结论:不做。userExpandedCards/userCollapsedCards 是 UX-260616-03「用户主动展开/折叠记忆态」设计(切回 A 恢复展开记忆=feature 非 bug);expandedCards/expandedTools 已被 collapseInactive 按 activeIds 过滤清。切会话清 Set 会破坏记忆态。「内存慢增长」是记忆保留代价(tc.id 全局唯一 UUID 短字符串,描述自承认「无错配」),可忽略。— src/components/ToolCardList.vue
- ✅(主代修·SW-19·vue-tsc EXIT 0) SW-260618-19 [P3] — combineAndTruncateLines 与 cmdOutput 合并逻辑冗余。抽
combineOutputs(stdout, stderr)共享函数,combineAndTruncateLines(:472)与 cmdOutput computed(:652)都复用。逻辑收敛,行为不变。— src/components/ToolCard.vue(:463 函数 + :472/:652 调用) - ✅(主代修·SW-20·vue-tsc EXIT 0) SW-260618-20 [P3] — Ideas 本地 parseTags 与 store 版 DRY 重复。删本地 parseTags(IdeaRecord),import stores/knowledge.ts parseTags(tags:string|null),3 调用点改传 .tags(currentIdea.tags / i.tags)。复用 store 版,签名收敛。— src/views/Ideas.vue(:214 import + :148/:149/:278 调用)
- ✅(评估暂缓·chip/tag 数量小,O(n) filter + 模板重复求值影响可忽略,优化 ROI 低) SW-260618-22 [P3] — Knowledge getCategoryCount+parseTags 模板重复求值。getCategoryCount v-for 每项 O(n) filter,parseTags 同模板调 2 次。chip/tag 数量小影响可忽略,优化项。— src/views/Knowledge.vue(:314-317 + 132-133)
ℹ️ 架构观察(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
🔧 2026-06-18 模型能力维度(cost_tier/intelligence)数据无客观依据·定位完成待实施
session-role-diagnose-only·铁证定位(不臆想)。用户追问"高/中/低价、标准/增强怎么定的",定位根因后用户决策:API 无判别依据的维度不要瞎填/写死,去掉。
根因(铁证:model_fetch.rs:247-259 ModelsList struct):provider /v1/models API 仅返回 {data:[{id}]},不含 cost_tier/intelligence/modalities/capabilities/context_window 任何能力判别字段。这 5 维度 100% 由 model_probe::probe() 填:预设表(presets/models.json 写死 16 条)+ 启发式(模型名关键词猜)。零客观依据、必然过时、对新模型全错。
症状实证:GLM-5.2(旗舰)被启发式猜成 standard/medium(名不沾 4o/lite 词素走默认兜底);GLM-4.5 被模糊匹配命中 glm-4 → plus/medium(低估)。
路由副作用:router.rs 用 cost/intel 做硬过滤(步骤4 intelligence >= min_intelligence、步骤5 cost_tier <= max_cost)+ 排序(步骤7 Reverse(cost_tier))。title.rs(max_cost=Low)/ compress.rs(max_cost=Medium)按错误档位过滤,可能选不到合适模型。
用户决策(2026-06-18):API 无判别依据的 cost_tier/intelligence → 去掉,不写死不瞎填。(曾尝试补预设表硬编码 GLM 新模型,用户否决:模型数据不该写死进代码会过时。已回滚清理,git diff 证两文件零残留。)
- ✅(workflow wexu1isx1·router删min_intelligence/max_cost字段+步骤4/5 filter+步骤7 Reverse(cost_tier)+9调用点+test改写·cargo check EXIT 0 + cargo test df-ai 109 passed) B-260618-03 [P1] 后端路由解耦 cost_tier/intelligence —
router.rs去步骤4(min_intelligence 过滤)/步骤5(max_cost 过滤)/步骤7(Reverse(cost_tier)排序)→ 排序纯 weight。TaskRequirements 的 min_intelligence/max_cost 字段 + 8 调用点(title.rsmax_cost=Low /compress.rsmax_cost=Medium /agentic.rs·project.rs·ai_node.rs·knowledge_inject.rs·adversarial.rs·df-ideas 的 min_intelligence)同步清理。因 API 无判别依据这俩维度数据不可信,不应参与硬路由。属主链路由行为变更,需 cargo test router 全验证 + 核对 title/compress 选模型不退化。—crates/df-ai/src/router.rs+ 调用点 - ✅(workflow wexu1isx1·heuristic_infer cost/intel 中性默认 Medium/Standard + presets/models.json 17条删两字段·cargo check EXIT 0 + cargo test df-ai 109 passed) B-260618-04 [P2] model_probe 去瞎填 + 预设表机制存废 —
heuristic_infer的 cost/intel 猜测(model_probe.rs:158-163)+presets/models.json两字段,改中性默认(medium/standard)或标记"无依据"。预设表(16 条写死)随厂商发新模型必过时,存废待定。注:modalities/capabilities/context_window 同 API 不返回也瞎填,但功能性维度(vision/embedding/code_gen)有路由意义(选错致 vision 任务选纯文本模型),性质不同,保留但需 UI 暴露用户可改(另立)。—crates/df-ai/src/model_probe.rs+presets/models.json - ✅(主代修·UX-04·vue-tsc EXIT 0 + grep 核验无 tagCost/tagIntel 残留) UX-260618-04 [P2] 前端删 cost/intel 标签 — 自主裁决全删(用户已全局决策去掉 cost/intel·B-260618-03 + 独立于后端路由 + grep 确认仅 2 组件引用 + 回退易)。ProviderPanel.vue 删 tag-cost/tag-intel 两 span;AiChat.vue modelOptionLabel 删 intel/cost 拼接只返 label/model_id;i18n en/zh settings.ts 删 tagCost/tagIntel 命名空间 + 注释 4 维度→模态+能力。—
src/components/{settings/ProviderPanel,AiChat}.vue+src/i18n/{en,zh-CN}/settings.ts - ✅(workflow wexu1isx1 + 主代修前端 types.ts 删 'free'·CostTier 枚举删 Free + test 同步·cargo check EXIT 0 + cargo test df-ai-core 24 passed + vue-tsc EXIT 0) B-260618-05 [P3] CostTier::Free 死档 + 枚举清理 — 预设表 0 条 free + 启发式从不赋 Free(只写 low/medium/high)。Free 变体形同虚设(router 测试
same_weight_picks_cheaper_cost_tier用到但实际数据不产生)。B-260618-03 解耦后评估删 Free 变体 / 收敛 CostTier 枚举。—crates/df-ai-core/src/model.rs
🔧 2026-06-18 AI Chat markdown 表格布局破坏(确定性 CSS bug·未实施)
用户截图反馈"展示样式有问题",分析截图(AI 回复测试报告 markdown 表格 + 连续失败重试卡片)。session-role-diagnose-only,定位根因 + 修法。
核心问题:markdown 表格(测试报告 #/组件/触发条件/代码位置 四列)行列对齐丢失、单元格堆叠换行。
根因(确定性 CSS bug):AiChat.vue:3366-3371
.ai-msg-bubble.ai-md :deep(table) {
display: block; /* ← 破坏表格布局上下文 */
max-width: 100%; width: max-content; overflow-x: auto;
}
display:block 让 table 失去 display:table 的表格布局上下文 → 内部 thead/tbody/tr/td(table-row-group/row/cell)脱离 table 上下文 → 行列对齐丢失,单元格塌成块级堆叠。视觉分析的"表格列间距/行高/列标题与行叠放/代码位置文字溢出换行"全是此根因表象(非独立间距问题)。注释意图(UX-260617-21)是"宽表格横向滚动不撑破气泡",但 display:block 实现破坏了表格结构。
修法(确定性):移除 display:block,改父容器滚动保持 table 布局:
.ai-msg-bubble.ai-md { overflow-x: auto; } /* 气泡横向滚动 */
/* :deep(table) 不设 display,保持默认 table 布局 */
或 markdown 渲染给 table 包 <div style="overflow-x:auto">(更可控,不依赖气泡 overflow)。
附带观察:连续失败重试卡片(1/4 2/4 3/4)堆叠密集——但这是连续失败重试的功能现象(BUG-260617-01 ||true 已修 924158c;截图若仍复现需另查重试逻辑),非纯样式。
- ✅(workflow w5siwnipj 核验两处 display:block(AiChat.vue:3366 局部 + ai-md.css:59 全局根因)+主代实施·移除两处 display:block/overflow-x:auto·table 恢复默认 display:table 列对齐·气泡 .ai-msg-bubble--ai.ai-md 加 overflow-x:auto 承载宽表格滚动·vue-tsc EXIT 0) 🚨 B-260618-06 [P1] — markdown 表格 display:block 破坏布局。两处根因:全局 ai-md.css:59 + AiChat.vue:3366 局部覆盖。display:block 让 table 失去表格布局上下文 tr/td 塌堆叠丢列对齐(截图测试报告表格错乱根因)。修法:两处移除 display:block(overflow 在 display:table 下浏览器忽略一并移除),table 恢复默认 display:table + max-width:100%,宽表格横向滚动交气泡容器 .ai-msg-bubble--ai.ai-md overflow-x:auto 承载(CSS spec table-display 下 overflow 须由 block 父容器)。— src/styles/ai-md.css(:59) + src/components/AiChat.vue(:3366 UX-260617-21 方案修正)
🔧 2026-06-18 aichat 工具结果渲染核对(agent 全量核对·session-role-diagnose-only)
agent 核对 ToolCard.vue 各工具结果渲染 vs 后端 tool_registry.rs 返回结构,找"结果字段被丢弃/显示不全/裸 JSON"。search_files 已修(body 文件列表 + bar pattern,本轮)。余 11 条待修。
P0
- ✅(workflow w2drz3ppo 核验+主代实施 run_workflow 显 execution_id 删误导 workflowHint·vue-tsc EXIT 0) UX-260618-05 [P0] — run_workflow 结果丢 execution_id。header 硬显 workflowHint「请到工作流页面运行」(误导,工作流已触发)+ body 走裸 JSON。后端返
{task_id, target_status, execution_id, status, note},核心 execution_id 没渲染,用户无法对应执行实例。修:加专门 case 显 execution_id / task_id→target_status,删误导 workflowHint。—ToolCard.vue(:809 toolResultSummary + formatToolResult default)
P1
- ✅(workflow w2drz3ppo+主代 patch_file 模板分支渲染 diff 红绿行+resultDiffLines computed·vue-tsc EXIT 0) UX-260618-06 [P1] — patch_file 丢 diff。后端
generate_diff产完整 unified diff(tool_registry.rs:1148),formatToolResult 只显「已修改 path(±N 字节)」,diff 整个丢弃,用户无法验证 AI 改了什么。修:加专门 body 分支复用 write_file 审批卡 diffLines 红绿行渲染。—ToolCard.vue(:424) - ✅(workflow w2drz3ppo+主代 delete_file formatToolResult+toolResultSummary 加 backup_path·vue-tsc EXIT 0) UX-260618-07 [P1] — delete_file 软删丢 backup_path。只显「已软删除(已备份)」,backup_path(回收站文件名)不显,用户不知从哪恢复。修:补「已备份至 {backup_path}」。—
ToolCard.vue(:418) - ✅(workflow w2drz3ppo+主代 read_file 模板 has_more 显「shown/total 行,已截断」·vue-tsc EXIT 0) UX-260618-08 [P1] — read_file 丢截断提示。>500 行默认截断返前 500 行,bar 只显 lines·size,has_more/returned_lines 丢弃,用户不知是截断。修:has_more=true 时显「显示 returned_lines/lines 行,已截断」。—
ToolCard.vue(:72-82) - ✅(workflow w2drz3ppo+主代 advance_task 加 review_rounds 退回累加(>0 显)·vue-tsc EXIT 0) UX-260618-09 [P1] — advance_task body 裸 JSON。后端返完整 TaskRecord(~15 字段含 review_rounds),formatToolResult 只显「任务 id 推进至 status」,body 走裸 JSON。修:body 显 status + review_rounds(退回累加值)。ToolResult type 缺 review_rounds 字段需补。—
ToolCard.vue(:414) - ✅(workflow w2drz3ppo+主代 list_trash toolResultSummary case 返计数,被 UX-12 list_* 重写吸收·vue-tsc EXIT 0) UX-260618-10 [P1] — list_trash 缺 header 摘要。toolResultSummary 覆盖 list_tasks/projects/ideas 漏 list_trash,折叠态无计数。返回结构与 list_projects 同构
{items,total,has_more}。修:补 case 返计数。—ToolCard.vue(:769)
P2
- ✅(workflow w2drz3ppo+主代 list_directory 模板 truncated 显「已截断,仅前 1000 项」·vue-tsc EXIT 0) UX-260618-11 [P2] — list_directory 丢 truncated。达 1000 条上限截断返 truncated:true,前端无提示。修:truncated=true 显「已截断,仅前 1000 项」。—
ToolCard.vue(:85-104) - ✅(workflow w2drz3ppo+主代 list_* 重写修 Array.isArray 对象误判+结构化列表+file_info modified+write_file old_size/encoding+rename_file cross_volume/bytes_moved·vue-tsc EXIT 0) UX-260618-12 [P2] — list_ body 全裸 JSON + 各工具零散字段丢失*。① list_projects/tasks/ideas/trash body 走裸 JSON(items 完整 Record),应加专门 body 分支列表渲染 name+关键字段;② file_info 丢 modified;③ write_file 丢 old_size/encoding;④ rename_file 丢 cross_volume/bytes_moved。ToolResult type 缺 execution_id/note/modified/old_size/cross_volume/bytes_moved/truncated 等字段需补。—
ToolCard.vue(formatToolResult + 各 body 分支 + ToolResult type :181)
🔧 2026-06-18 列表摘要 Markdown 语法字符泄露(三类列表·session-role-diagnose-only·未实施)
用户报:AI Chat 对话列表、知识库列表、待审核项目列表的摘要信息都显示 Markdown 语法字符(
**/#/`/-/>)。走查定位链路 + 记 todo,不改代码。
核心问题:列表/卡片摘要字段用 {{ }} 纯文本插值,但数据源是 AI 生成内容(含 Markdown 语法),既未渲染富文本也未剥离语法字符 → 裸字符外露。详情页都正确 v-html 渲染,列表层缺处理。
| # | 位置 | 代码 | 数据源(AI 生成·含 MD) | 详情页对照 |
|---|---|---|---|---|
| 1 | 知识库列表 | Knowledge.vue:71 {{ item.content }} |
AI 从对话提取的知识内容 | :120 已 v-html="renderedContent" ✅ |
| 2 | 项目卡片 | Projects.vue:148 {{ project.description }} |
aiScan 填的 LLM 描述(:256) |
无详情渲染 |
| 3 | 灵感列表(pending 待评审) | Ideas.vue:47 {{ idea.description.slice(0,60) }} |
灵感描述 | :63 已 v-html="renderedDesc" ✅ |
AI Chat 对话列表(存疑待核):侧栏对话项只渲染 {{ conv.title }}(AiChat.vue:49/122/171),无摘要字段。若用户所说"摘要"即标题,则需核后端自动生成标题(title.rs generate_title_via_llm)是否产出 Markdown 语法——前端 {{ }} 会原样显示。倾向标题本应纯文本,若 LLM 偶发带 ** 等需后端 prompt 收口或前端 strip。
根因:列表摘要本应纯文本预览,三处均走 {{ }} 插值,AI 生成源文本含 Markdown 未剥离。
修法方向(待实施·二选一):
-
A 推荐:加
stripMd(text)util(剥离**/__/_/#//`/-/>/[]()/![]()等语法留纯文本),列表摘要统一过滤。列表空间小 +-webkit-line-clamp` 截断(Knowledge 卡片)+ 富文本样式不可控,剥离比渲染更合适。 -
B:列表也
v-html统一渲染——不推荐(卡片截断 + 富文本样式溢出/<p>撑高)。 -
✅(主代串行·规避 AiChat God 文件·新建 src/utils/markdown.ts stripMd 剥离 MD 语法(粗体/斜体/标题/代码/列表/引用/链接/图片)+ Knowledge.vue:71/Projects.vue:148/Ideas.vue:47 三列表摘要接入·修自引入注释含 */ 致 JSDoc 提前闭合 TS1011/1127·改安全措辞·vue-tsc EXIT 0) UX-260618-13 [P2] — 列表摘要 Markdown 语法字符泄露。三处列表摘要 stripMd 过滤。AiChat 标题 strip 子项:规避 God 高频文件·留待并发期后(待核 title.rs 是否产 MD)。—
src/views/{Knowledge,Projects,Ideas}.vue+src/utils/markdown.ts::stripMd