Files
DevFlow/docs/todo.md

150 KiB
Raw Blame History

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 僵尸 cratedf-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-14B-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-21 AI Chat 技术债审查(workflow wf_92b61c35-4d2 · 10 模块串行 grep 核验)

详单见 aichat-技术债审查-2026-06-21.md。0 P0 / 7 P1 / 25 P2 / 40 P3 / 已登记 8 / 新债 64。P1#5/#6 已即时修并自验过

P1 已即时修(2):

  • (主代修·chat.rs:458-461 ai_approve exec_result 包 tokio::time::timeout(60s) 对齐 ai_authorize_dir:658·cargo check EXIT 0) TD-260621-05a[P1🔴] ai_approve 缺 60s 执行超时(用户"静默吞消息"bug F-260620 同型复发)。ai_authorize_dir 有超时 ai_approve 漏。卡死→generating 永真→前端 130s 看门狗吞消息。
  • (主代修·useAiConversations.ts:99 switchConversation activeConversationId 设定后加 state.streaming = state.generatingConvs.has(id)·vue-tsc EXIT 0) TD-260621-06a[P1🔴] switchConversation 不复位 streaming,真并发切非生成会话残留 stop 按钮→点 stop 发错会话。

P1 归路线(5 项·涉大改/行为变更/需设计·未即时修):

  • TD-260621-01 [P1·归 F-09 B 路线 per-conv state] (批2-A 落地·useAiStream _watchdogTimers Map<convId,timer> + _legacyWatchdog fallback 双路·onStreamTimeout 携 convId 仅清该 conv·2026-06-21) — watchdog 单计时器全清误杀并发会话useAiStream.ts:30 _streamWatchdog 模块级单计时器无 per-conv 隔离;onStreamTimeout 全清 generatingConvs+queue+currentText。F-09 真并发下 A 超时连带杀 B + 静默丢用户排队输入(注释称"防丢失"但 clear 即丢失)。修:watchdog 改 per-conv Map<convId,timer>,onStreamTimeout 携 convId 仅清该 conv;queue clear 改仅清当前 conv 关联。归 F-260616-09 B 路线统一改造。
  • TD-260621-02 [P1·归 F-09 B 路线 per-conv state] (批2-A 落地·pendingMaxRounds ref<string|null> 挂起 convId + 守卫比对 activeConversationId·DirAuthDialog visibleDirAuths filter conversationId·2026-06-21;🟡D pendingDirAuths 全清→per-conv filter 由 wqnd4axf8 补) — pendingDirAuth/pendingMaxRounds 全局单 ref 非 per-convuseAiEvents.ts:99/113 模块级单实例;DirAuthDialog:60/MaxRoundsCard:54 守卫仅判 isViewingGenerating 未比对 conversationId(pendingMaxRounds 纯 boolean 无 convId 字段)。真并发下挂起弹窗错显于非挂起会话。修:pendingMaxRounds 改 ref<string|null>(convId)+ DirAuth 守卫加 convId 比对。归 F-09 B 路线。
  • TD-260621-03 [P1·patch_file 落地债 F-260615] (读改写整体移入 _patch_guard 锁内串行化防 lost update + 删 entry().or_insert() 内存泄漏 + drop 写序列后释放·2026-06-21) — patch_file 读改写在 FILE_LOCKS 外,lost updatetool_registry.rs:1247 指纹校验(1176)+old_text CAS(1209)+new_content 计算(1197-1240)全在无锁纯读段;FILE_LOCKS(1242-1269)仅序列化 backup→tmp→rename 不防读写交错。两并发 patch 同文件:B 用基于旧内容算的 new_content 覆盖 A。write_file 不取 FILE_LOCKS 同根因。修:读+校验+new_content 整体移入锁内,或 per-path Mutex。
  • TD-260621-04 [P1·patch_file 落地债 F-260615] (read_file 三返回点返 file_hash 闭环 + 抽 compute_file_hash helper read_file/patch_file 共用消格式漂移·2026-06-21) — expected_hash 契约破裂,指纹防并发形同虚设tool_registry.rs:1112 schema 描述"read_file 返回的 file_hash"但 read_file handler(892-976)从不返回该字段。expected_hash 实由 patch_file 内部 format!("{}_{}",modified_secs,len)(1180)即造即比,LLM 拿不到前置值只能瞎传/省略→常 mismatch bail 或被跳过。L3 防行号漂移设计在实际 LLM 调用链落空。修:read_file 返回 file_hash 同格式闭环回传。
  • TD-260621-05 [P1·归 SMELL-P1-6] — 审批状态字符串双轨。chat.rs:466/668 写 "executed" vs audit/mod.rs:490/545 + cache.rs:113 写 "completed",DTO 文档(models.rs:221)只列 completed。审计表混存两种"成功"态。归 SMELL-P1-6(String→enum)统一,行为变更需评估 AuditLog 显示 + find_cached 透传。

P2 精选(25 项·详单见报告):try_continue pending_conv_id 跨会话泄漏(agentic:1314)/ patch_file FILE_LOCKS 永不清理内存泄漏(tool_registry:1248)/ auto-compress 成功未即时 save(agentic:778)/ MidStream 保文 chunk.error 未回填 err(stream_recv:275)/ audit_tool_call let _ = 吞 insert 错(finalize:35)/ switch+delete retain 未配套 finalize 占位终态化(conversation:317)/ PerConvState.created_at 全死字段(mod:520)/ PendingApproval.diff 写而不读(mod:570)。

销账核对(已登记 8 复核):SMELL-P1-6(line221·#5 覆盖 executed/completed 双轨)/ MED-1(tool_registry:546 bind_directory)/ REFACTOR-260619-08(tool_registry:881 拆)/ REFACTOR-260619-09(context.rs:1 God 1552行)/ ARC-260619-05(agentic:318 超长)/ UX-260617-28(双监听器 INFO)。均仍在未完成,审查复核确认,保留

残留诊断代码(待清):chat.rs authz_debug/authz-debug.log(L2 调试遗留,P2 报 chat.rs:548 硬编码绝对路径),L2 完成 + 用户实测后可清。


🔍 2026-06-21 查询能力缺口 + run_workflow 缺陷(本轮排查登记·未实施)

用户排查任务/项目/灵感查询能力现状,登记缺口为待办/缺陷,本轮不实施代码。详单见 查询能力补全方案-2026-06-21.md

  • BUG-260621-01 [P1🔴·确定修法] — run_workflow 空 dag 反序列化失败(missing field nodes),阻塞任务工作流推进 2026-06-22 修复:DagDef.nodes/edges 各加 #[serde(default)],{} 反序列化成空 DagDef → workflow.rs:91 is_empty() 进 template_for 选模板。2 单测防回归(空 {} + 非空零回归),df-workflow cargo test 51 passed。— crates/df-workflow/src/dag_def.rs + src/views/TaskDetail.vue(:333)

  • F-260621-02 [P2] — 任务/项目/灵感列表查询维度补全(下沉后端+关键词搜索+排序分页) 2026-06-22 实施:三实体新增 list_by_query 动态 WHERE(累积式 where_clauses+params_vec 收口)+ order_by 白名单防注入 + limit/offset 钳制;命令层 list_{tasks,projects,ideas}Option<XxxQuery> 双参向后兼容(旧无参/单参路径等价全量);前端 Tasks/Ideas status/keyword 筛选下沉后端 query(Tasks.vue buildTaskQuery / Ideas.vue watch 防抖)。索引缺口已补(2026-06-22 V26 migrate_v26 idx_tasks_priority/assignee + SCHEMA 同步,commit 82059ba)+ P3 分页 UI 已落(Paginator.vue 客户端切片 + Tasks/Ideas/Projects 三视图接,commit 83cee61)。详见查询能力补全方案-2026-06-21.md。— commands/{task,project,idea}.rs + crates/df-storage/src/crud/{task,project,idea}_repo.rs + src/api/{task,project,idea}.ts + src/views/{Tasks,Projects,Ideas}.vue


📋 编排推进总览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-20 BUG-260620-05 F-260619-03 工程内路径误弹窗(reload_allowed_dirs 丢 workspace_root·b22e9ae 回归)

现象:AI Chat file_info 等文件工具访问工程内路径(如 docs/02-架构设计/单对话并行多轮-设计-2026-06-20.md)触发授权弹窗,用户称"本来就有访问权限,为什么还来申请"。

根因:state.rs:586-619 reload_allowed_dirs 合并 kv_dirs(KV allowed_dirs)+ project_dirs(projects.bind_directory)all_dirs,仅当 all_dirs.is_empty() 时(:618)才插入 workspace_root。KV 配过 allowed_dirs 或有绑定项目 → all_dirs 非空 → workspace_root 不入 persistent → 工程内路径(在 workspace_root 下)is_authorized(:356 starts_with)失败 → check_path_authorizationNeedsAuthorization → 弹窗。

注释承诺失配::346-348 / :583 / :617 注释均称"KV 未配时保持 workspace_root 免授权(向后兼容)",但 :618 条件用 all_dirs(含 project_dirs),project_dirs 非空也丢 root。b22e9ae "reload 尊重用户 persistent" 引入的回归。

用户政策指令(2026-06-20):"默认直接访问,明确知道无权限才申请"。指向工程内(workspace_root 下)默认免授权,仅 workspace_root 外非白名单路径才弹窗。

修法(层1·1 行确定性 bug):state.rs:618 去掉 if all_dirs.is_empty() 条件包裹,无条件 set.insert(workspace_root_path())(工程内始终免授权,对齐用户政策 + 注释承诺)。不破坏现有能力:workspace_root 经 set/get_allowed_dirs(:664/680 filter root)本就不可见不可删,"用户删 root"语义未落地,保 root 无冲突。

层2 待决策(政策范围):用户"默认直接访问"是否要求更宽语义——workspace_root 外的非黑名单路径也默认放行(翻转 is_authorized 为黑名单制,实质去白名单)?涉 P0 去固定根决策方向,见待决策.md。dev 自用场景层1 即满足。

关联:F-260619-03(todo :528 b22e9ae 回归源)/ 待决策 workspace_root 分发适配(方案b 用户项目绑定)/ memory devflow-project-path-binding 状态:🔴 待用户授权(层1 1 行修法可直改;层2 待政策澄清)


2026-06-19 BUG-260619-06 L0 clear 致冷启动审批丢失(批2+跨批遗留·非batch8回归·方案A已修复)

来源:CR-260619-06 巡检独立核验(不信主代自审 PASS)。F-09 batch8(commit 6ad4ec2)在 L0 握手新增 session.pending_approvals.clear(),与冷启动 restore 重建链路时序冲突,致重启后待审批工具完全丢失

现象:重启 devflow 后,DB 里 status=pending 的待审批工具(tool_call)在前端不显示 toolCard,用户无法审批;即使触发审批报「未找到挂起的审批」。restore 功能被 batch8 clear 抵消。

根因(跨函数时序冲突 + 设计契约违反):

  1. restore 填充(冷启动同步):AppState::init(state.rs:500 setup block_on)→ restore_pending_approvals(audit/restore.rs:26)从 DB list_pending(:27)重建 pending 到 session.pending_approvals 内存。
  2. clear 必触发(冷启动必达):AiChat.vue onMounted → store.startListener()useAiEvents.ts emit('ai-client-ready') → L0 回调(lib.rs:38)→ lib.rs:62 原为 session.pending_approvals.clear() 无条件清空全部(含 restore 重建)。
  3. 前端不显示:ai_pending_tool_calls(commands/chat.rs:692 数据源 = session.pending_approvals.values() 内存,非 DB)→ clear 后返空。
  4. 审批报错:ai_approve(commands/chat.rs:344)→ DB find_by_tool_call_id(:358 match)查到 status=pending 但不在「已处理」白名单(:360 executed/rejected/failed)→ :370 return Err「未找到挂起的审批」

batch8 回归证据:git show 6ad4ec2 -- lib.rs 确认 session.pending_approvals.clear()(:62)是 batch8 新增;批2 版本 L0 只 reset generating,不 clear pending。

设计契约直接违反:commands/conversation.rs:312-315 switchConversation 用 retain(... != Some(&conversation_id))(:315)精确保留 restore 重建的 pending,注释明说「防 init 重建的内存 HashMap 被清空,重启恢复链路:restore → switchConversation → ai_pending_tool_calls → ai_approve」。L0 :62 clear() 无条件清空全部 → 断该链路。

修复方案:

  • 方案 A(推荐):lib.rs:62 clear()retain(|_, a| !a.recovered)。仅清非 recovered(本次会话/HMR 死 pending),保留 restore 重建(PendingApproval.recovered=true audit.rs:331)。对齐 switchConversation 保护意图,两全。
  • 方案 B:删 lib.rs:62(批2 前无此 clear;pending 清理由 switchConversation retain / delete_conversation retain :1723 / ai_chat_clear 各路径精确管理)。

核验清单(实施时):① cargo check --workspace;② 手测:DB 注入 status=pending 记录 → 重启 → 前端显待审批 → 审批成功落库;③ 回归 HMR 场景(后端不重启,死 pending 仍被清)。

关联:F-09 batch8 / CR-260619-06 / 详单见 待审查.md CR-260619-06


🔴 2026-06-18 Agentic 最大轮次设置不生效(设 30 仍按 10 截断·session-role-diagnose-only·未实施

用户报Settings 里 Agentic 最大循环轮次设 30但实际跑到 10 就提醒「达到最大」。走查全链路定位根因 + 记 todo不改代码。

现象:设置项显示 30前端持久值实际 loop 跑 10 轮即触发 AiMaxRoundsReached(达 max 提醒)。

根因(后端内存态 + 恢复同步绑错生命周期) 后端 agent_max_iterationsArc<AtomicUsize> 纯内存(state.rs:242重启回默认 10DEFAULT_MAX_AGENT_ITERATIONS),自身不落 DB。持久化只在前端 appSettings(SQLite),后端靠前端启动时调 ai_set_agent_max_iterations IPC 同步恢复。而该同步 syncAgentMaxIterations() 只绑在 GeneralPanel.onMountedGeneralPanel.vue:267)—— GeneralPanel 是 Settings 页子组件,只在用户打开 Settings 时才挂载。两失效场景(任一即中):

  1. 重启后直接用 AI Chat没先开 Settings 页) → GeneralPanel 未挂载 → onMounted 不跑 → 后端停留默认 10 → loop 10 轮提醒;前端 Settings 显示 30持久值与后端实际 10 不一致。
  2. 即便开了 SettingsappSettings 缓存异步未就绪时settings.agentMaxIterations 初值读到默认 10:153)→ onMounted 把 10 sync 给后端onMounted 只跑一次cache 就绪后不重跑)。

agent_max_retries 同病(同款 onMounted sync :269,后端 agent_max_retries 同款内存态 state.rs:285-287)。

排除项

  • 后端 clamp 1-50commands.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(应用启动必挂载)+ 确保 appSettings loadAll 就绪后再调(读持久真值)。利用前端已有 SQLite 持久化,几行改动,立即生效。
  • C 彻底(后端持久)AppState::init 从 DB settings 表读 agent_max_iterations/retriesai_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。

  • (主代核验·tauri.conf.json:28 ["nsis"] 已入库 commit a2871a6 非临时工作区·当前 Windows 开发保留合理·WATCH:跨平台意图待用户,若需 mac/Linux 改 "all" 或按平台条件配置) B-260617-11 [P2] — tauri.conf.json 打包目标收窄bundle.targets: ["nsis"](:28)锁 Windows NSIS,已入库。当前 Windows 开发保留;跨平台待用户定。—— src-tauri/tauri.conf.json(:28)

🔧 2026-06-17 走查·DeepSeek reasoning_content 实施审查P1 提交不完整)

核验 HEAD 74003bcBUG-260617-12 修复提交)+ 工作区残留。session-role-diagnose-only逐行核验非信声明。闭环逻辑正确(见 :49 注记),但发现提交不完整 P1

  • (2026-06-20 核验·MidStream 保文已回填 reasoning_content 到 ChatMessage,mod.rs:1058/1116/1121·P3 观察项已处理) 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 验证)

  • 嫌疑 aensure_conversation_title 在 spawn 后台 taskagentic.rs:924-931generate_title_via_llmtitle.rs:90/120 网络耗时)未完成时用户已刷新/重启 → 标题未落库 → 重启读 NULL → "新对话"。
  • 嫌疑 btitle.rs:95 let _ = conv_repo.update_field(...) 落库失败被吞(无日志),标题没入库但 :96 emit 照发(前端短暂更新,重启丢)。
  • 嫌疑 ctitle.rs:141-142 双层信号量 acquire_global+acquire_per_conv 阻塞permit 未释放)→ ensure 永不完成。

矛盾点:用户"很多内容多次对话"应多次触发 ensureagentic.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 + 06-19核验补漏已落地:commands.rs:1404/useAiConversations.ts:80 → 阶段1跳过),B 设计完成 2026-06-19(F-09设计文档)·阶段2 批1-8 待拍板 b-1(messages per-conv)启动(见待决策.md)·messages per-conv 已落地(2026-06-20 核验):mod.rs:326 pub per_conv: HashMap<String, PerConvState> + :384 conv(&mut self, conv_id) 访问器 + :394 conv_read + tests_f09_per_conv 测试模块 + 决策e 真并发已上线(见文件末 F-09 决策e 段,CR-260620-02 审 PASS)****] — AiSession 单例 → 多会话并发架构(多窗口来回切 + 并行执行)。用户需求(3 条消息强化):「应用级支持同时具备多会话」+「同时具备多会话的能力」+「多个会话窗口来回切,并行执行」。现状Explore 走查确认):AiSession 应用级单例state.rs:164 ai_session: Arc<Mutex<AiSession>>init:205messages/generating/stop_flag/pending_approvals/agent_language 全一份。设计「单例 + 软隔离」——generating 互斥(同一时刻只一个对话能跑) + 切换旧 loop 退出(agentic.rs:177-190 B-260615-11) + 切换时 generating→readonly(commands.rs:873-881)。llm_concurrency per_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.ts detached 已有基础可扩,改动大但「窗口来回切」语义最贴)——待用户定,倾向先 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"))(零行为变化,新状态自动不 activesanitize_messages step 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:111 generate_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→ emit AiCompressing/AiCompressed。LLM 失败降级原有裁剪(不阻塞 loopcompress_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-app5711 行成事实业务层) / 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:289 ai_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 序列化等低危)。
  • (workflow w2xkw4ybh 抽 register_data_tools(18 持 db 工具:list_projects/list_tasks/list_ideas/update_project/create_project/bind_directory/create_task/update_task/advance_task/run_workflow/delete_task/create_idea/delete_project/restore_project/purge_project/list_trash/get_project_count/get_task_count)+ 主代抽 register_file_tools(10 文件工具:run_command/read_file/list_directory/write_file/patch_file/file_info/append_file/delete_file/rename_file/search_files)·build_ai_tool_registry 1091→7 行·加基线测试 test_build_ai_tool_registry_baseline_tool_count(Database::open_in_memory 断言 len()==28 + tool_names() 集合锁定)·主代核查 cargo check --workspace EXIT 0 + cargo test df-ai 119 passed + devflow 基线 1 passed + vue-tsc EXIT 0) SMELL-P0-2[P0🔴] tool_registry.rs:363 build_ai_tool_registry 1091 行单函数 — 拆分为按功能分组注册函数(register_data_tools 18 db 工具 / register_file_tools 10 文件工具)build_ai_tool_registry 收敛到 7 行编排 + 基线测试锁定工具数防回归。注(2026-06-20 核验):基线已升至 29(tool_registry.rs:1841 test_build_ai_tool_registry_baseline_tool_count 断言 len()==29,18 data + 10 file + 1 http;F-260619-03 Phase A 后 http_request 注册致 +1)。
  • (2026-06-20 核验·AiChat.vue 4075→750 行·src/components/ai/ 子组件已拆 ConversationSidebar/MessageList/ChatInput/TopBar/DirAuthDialog/MaxRoundsCard·状态/composable 已外移·与 REFACTOR-260619-07 同件) SMELL-P0-3[P0🔴] AiChat.vue 4026 行 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-execute crate 零测试(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下沉 标注实际实施状态
  • (agent crud-split 实施 + 主代兜底核验·crud.rs 2212行→crud/ 6文件(mod/settings/project_repo/task_repo/conversation_repo/idea_repo)·re-export pub use *_repo::* 零调用方改动·宏 pub(crate) use + 子模块 use super::impl_repo·基线测试 all_known_tables_have_column_whitelist(12表)+ all_repos_constructible_in_memory(13Repo)·主代 cargo check --workspace EXIT 0 + df-storage 35+11 passed + devflow 96 passed) 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,待核对。

🆕 sweep 派生登记2026-06-18·workflow w2xkw4ybh data 抽出 + wo7d6zdqm 多维度 sweep:

  • ARC-260618-01-d [P1/🟡已实现待启用] — conditions 条件引擎(核验2026-06-22:conditions.rs:31 ConditionEngine + executor.rs:106 已调 evaluate 求值 + executor.rs:95 cfg!(feature="conditions-eval") feature flag 控制,求值逻辑已实现待产品确认启用时机)。df-workflow/conditions.rs ConditionEngine 零消费(无调用方),Edge.condition 字段存储但工作流执行器未求值分支——边条件当前形同虚设。产品决策点:① 实现条件求值(executor 在节点完成后求值出边 condition 决定路由)还是 ② 标 #[allow(dead_code)] + 文档明示"条件为预留未启用"。涉及行为变更(executor 控制流改变),确认产品意图后再实施。对齐 H 组长期池 T-14-11 条件引擎(line 42 仅指针无详情)。— crates/df-workflow/src/conditions.rs(ConditionEngine) + crates/df-workflow/src/executor.rs(边求值点)
  • (2026-06-22 核验·adversarial.rs:147 self.recommendation_for(&analyst.final_assessment) 已守卫 final_assessment/recommendation 一致性,矛盾组合经 recommendation_for 收敛·漏勾销账) ARC-260618-01-e — adversarial evaluate_with_llm 一致性。已有守卫。df-ideas/adversarial.rs evaluate_with_llm 返回的 final_assessment/recommendation 两个字段语义一致性未校验(如 final_assessment=强烈反对 但 recommendation=promote 的矛盾组合无守卫)。LLM 输出存在字段间语义漂移风险。产品决策点:① 加一致性校验(矛盾时降级或重评)还是 ② 视为 LLM 自由表达不加约束。涉及行为变更(评估结果可能被改写),确认产品意图后再实施。— crates/df-ideas/src/adversarial.rs(evaluate_with_llm 返回结构)
  • (2026-06-20·formatRelativeZh→formatRelative 重命名 6 文件:time.ts 定义+注释去「中文」限定 + ConversationSidebar/MessageList/ActiveProjectsPanel/AuditLog/Tasks 5 调用点·AiChat 已拆 750 行无引用·grep 全 src 无残留 + vue-tsc --noEmit EXIT 0) SW-260618-21 [P2] — formatRelativeZh 重命名 formatRelativesrc/utils/time.ts formatRelativeZh 名带 Zh 后缀但实际非中文硬编码(已读 i18n.global.locale, sweep 批2 time.ts formatDate 已修 CR-260615-08 漏修),函数名误导。原缓做前提(AiChat 沉淀)已满足,本次落地。— src/utils/time.ts(formatRelative) + 5 调用点(ConversationSidebar/MessageList/ActiveProjectsPanel/AuditLog/Tasks)
  • (2026-06-20 核验·resolveLang 已抽 src/composables/ai/aiShared.ts:30-35 resolveAiLang 共享,消除重复) SW-260618-22 [P2/已知 B-260617-03] — useAiSend resolveLang DRYuseAiSendresolveLang 与其他 composable 语言解析逻辑重复,应抽 aiShared 共享。已知登记为 B-260617-03,此处补 sweep 维度记录便于检索。— src/composables/ai/useAiSend.ts(resolveLang) → 抽 src/composables/ai/aiShared.ts

🟡 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 — 不阻断缺陷 / 增强

  • (2026-06-20 核验·reattachPanel 接线 useAiWindow.ts:98 + tauri://destroyed 监听:86 已修·localStorage df-ai-gen/df-ai-text Sprint19 有意保留) B-260614-05 — [P2→降级] 分离窗口detached跨窗口状态 — 核对修正reattachPanel 已接线(不再死代码)+ tauri://destroyed 监听已复位状态,"detached 永真卡死"已修复;剩余 localStorage df-ai-gen/df-ai-text 是 Sprint 19 有意保留(流式临时快照高频写),非 bug。仅在出现新场景失效时再评估改全局 emit — 去重:与 AR-M5跨窗口 state 完全隔离同类aichat 审查描述更深 — source:代码审查 + Sprint 19 (06-14)

🔧 2026-06-18 6 域并行走查 sweep-fixworkflow 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_line to_lowercase→to_ascii_lowercase(非ASCII索引错位/panic)④ scan.rs is_monorepo workspaces 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.rs ai_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 structprovider /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.rsmax_cost=Low/ compress.rsmax_cost=Medium按错误档位过滤可能选不到合适模型。

用户决策2026-06-18API 无判别依据的 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/intelligencerouter.rs 去步骤4min_intelligence 过滤)/步骤5max_cost 过滤)/步骤7Reverse(cost_tier) 排序)→ 排序纯 weight。TaskRequirements 的 min_intelligence/max_cost 字段 + 8 调用点(title.rs max_cost=Low / compress.rs max_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 bugAiChat.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/tdtable-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:blockoverflow 在 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·sizehas_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 从对话提取的知识内容 :120v-html="renderedContent"
2 项目卡片 Projects.vue:148 {{ project.description }} aiScan 填的 LLM 描述(:256 无详情渲染
3 灵感列表pending 待评审) Ideas.vue:47 {{ idea.description.slice(0,60) }} 灵感描述 :63v-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


🔧 2026-06-18 用户实测·aichat bug 反馈(review 会话 2 agent 并行审查·独立 grep/read 核验·session-role-diagnose-only·待实施)

用户实测报 2 bug。review 会话派 2 agent 并行核验源码,根因 + 修法定位完成,待主代实施。

  • (workflow wwchro468 general-purpose agent 实施 6 文件 + 主代兜底核验·前端 ToolCard.vue advance_task 渲染取 r.title||r.id(:474/:891)+TASK_ID_TOOL_ARG 映射(:799)+displayArgValue task_id→title(:834)+i18n zh/en aiTool.ts;后端 audit.rs resolve_task_label(查 tasks 表 :151 三臂对齐 resolve_project_label)+build_approval_reason advance_task 分支(:173)+tool_registry.rs 删 tool_display_hint advance_task 项(:1575 让 :1549 模板生效)·主代 cargo check -p devflow EXIT 0 + vue-tsc EXIT 0 + grep 印证) UX-260618-14 [P1]advance_task 审批卡/结果渲染缺任务名 + 显 UUID/空白。①审批卡 ToolCard.vue:800 PROJECT_ID_TOOL_ARG 未登记 advance_task → id 裸显 UUID;②审批 reason audit.rs:163 tool_display_hint 短路致 tool_registry.rs:1546 模板死代码 + :172 任务 id 误查 projects 表;③结果 ToolCard.vue:477 模板取 r.id(UUID)非 r.title(TaskRecord 有 title models.rs:57),"空白"=UUID 不可读非字段缺失。修法 B(最快):ToolCard:477 取 r.title fallback r.id + i18n 改 任务「{title}」已推进至 {status};A1 登记 TASK_ID_TOOL_ARG(advance_task/run_workflow)id→title 回显;Ctool_registry.rs:1572 tool_display_hint 让 :1546 模板生效 + audit.rs 加 resolve_task_label。附带::1546/:1547 模板均死代码 + audit.rs:172 任务 id 误查隐患。— src/components/ToolCard.vue(:474-478,800-825) + src-tauri/src/commands/ai/{tool_registry.rs:1546/1572,audit.rs:163/172} + src/i18n/{zh-CN,en}/aiTool.ts

  • (agent ux15-batch1 方案A实施 3 文件 + 主代兜底核验·stream_recv 5处重试路径移除AiError emit + agentic Fatal/耗尽单emit AiError + useAiEvents AiStreamRetry首次创建+后续更新·主代 cargo check EXIT 0 + cargo test df-ai 119 passed + vue-tsc EXIT 0 + grep核验5处emit判定 + 修正注释残留 :251/:299) UX-260618-15 [P1] 第一批(方案A根治N+1) 流式失败重试(1/4 2/4 3/4)每轮独立气泡。N+1 降为「1 重试气泡 + 1 最终错误气泡」。第二批(方案C 聚合气泡显示错误原因)可选增强待定。后端每轮重试 emit AiError(stream_recv.rs:206/265/306/364/407 5 处 InitFailed 返回点·注(2026-06-20 核验):UX-15 已实施,5 处均改为「不 emit AiError,返回 InitFailed{retryable,error} 交调用方」,注释明示「UX-260618-15: 不再 emit AiError」,见 stream_recv.rs:120/203/262/303),前端 useAiEvents.ts:326 AiError case 每次 push 新气泡无去重;AiStreamRetry(:177)本意更新末条合并但被新 AiError 破坏 → N+1 独立气泡。agentic.rs:262 注释误以为 stream_llm 只 emit 一次(实多次)。用户诉求升级:不只是修 bug,要聚合显示设计**(重试 1→4 轮过程聚合单气泡 + 最终结果,勿一排错误吓人)。详见 review 会话重试聚合设计方案(3 角度论证:可行性/前后端一致性/架构整合)。修法 A(推荐):stream_recv.rs 5 处 InitFailed 移除 AiError emit(改纯返 retryable,emit 权交调用方)+ agentic.rs 重试 loop 只 emit AiStreamRetry、耗尽/Fatal 才 emit 最终 AiError。影响仅流前失败重试路径(Fatal/MidStream 保文/正常错误/压缩失败不受影响)。— src-tauri/src/commands/ai/{stream_recv.rs:200/258/298/358/398,agentic.rs:207-300} + src/composables/ai/useAiEvents.ts:177/326

  • (主代串行·doc-discovery 源码核验 time.ts:44(i18n as any).global.locale.value === 'en'·CR-260618-24 修复闭环 + CR-260618-25 A-time 复审 双印证·漏销账补登) UX-260618-16 [P1🔴]time.ts:44 formatDate 漏 .value 致 en locale i18n 失效(CR-260618-24 批2 审查发现·Agent C 铁证)。legacy:false composition 模式 i18n.global.locale 是 ref,(i18n as any).global.locale === 'en' 比对象===字符串恒 false → en locale 下 formatDate 永走 zh-CN 分支,i18n 化失效(对齐 CR-260615-08 未完成回归)。铁证:App.vue:227/GeneralPanel.vue:259 均用 i18n.global.locale.value,唯 time.ts:44 漏。修法:补 .value(i18n as any).global.locale.value === 'en'(locale 取值 'en' 非 'en-US',App.vue:227 印证)。— src/utils/time.ts:44

  • (2026-06-20 核验·handleApprovalMulti 已随审批逻辑迁移到 src/components/project/ApprovalDialog.vue:91-102·submitting try/finally 已正确对齐 handleApproval + 模板 :25 :disabled="submitting || multiDecisions.length === 0" 已绑·todo 行号 431-437/196-202 过时为迁移前 ProjectDetail.vue 坐标) UX-260618-17 [P1🟡]ProjectDetail.handleApprovalMulti 漏 submitting 复位(防双击破口)(CR-260618-25 前端审查发现·Agent B 对抗核验)。原状:ProjectDetail.vue handleApprovalMulti 无 submitting set true/finally,模板只绑 :disabled="multiDecisions.length === 0" → 多选审批 IPC 进行中按钮不禁用,可重复触发。已修:迁移 ApprovalDialog.vue 时补 submitting 自治 + try/finally + 模板绑 submitting。— src/components/project/ApprovalDialog.vue(:91-102 + :25)

🔧 2026-06-19 文件拆分升级3 代理并行分析·建任务·未实施)

大文件统计(总 51143 行)+ 3 代理并行分析拆分方案。与已有 SMELL-P0-2(tool_registry)/SMELL-P0-3(AiChat.vue)/SMELL-P1-9(crud.rs) 合并。通用执行原则:可见性升级(私有 fn/struct → pub(super))/ 测试跟随被测函数 / 不改逻辑不改 await 边界不改签名 / 三段式验证(cargo check+test+clippy 分 crate,对齐 workflow-cargo-timeout-wrap)/ 保守方案(热路径整块搬不拆函数体)。

P0(超红线/最脏,本周期优先)

  • (2026-06-20 核验已拆分·commands.rs 已删除→src-tauri/src/commands/ai/commands/ 目录 5 模块:chat.rs(56888 字节·发送审批控制 B 域)/config.rs(5496·杂项 E)/conversation.rs(38573·会话CRUD D)/mod.rs(2058·re-export only,pub use self::{chat,conversation,provider,config}::* 透传)/provider.rs(15262·提供商 C)·glob 链保留:commands/mod.rs:13 注释明 pub use self::{chat,conversation,provider,config}::* → ai/mod.rs pub use self::commands::* 透传 commands::ai::*) REFACTOR-260619-01 [P0]commands.rs(ai,1923 行)拆 5 模块。5 组职责正交:B 发送审批控制(1140,13 IPC)/C 提供商(305)/D 会话CRUD(360)/E 杂项(63)+A helper。先拆 C provider_cfg.rs(零风险试水:仅依赖 ai_providers 无 session 锁/per_conv/spawn)→ D conversation_crud.rs(conversation.rs 已存在,命名避冲突)→ E misc_cfg.rs→ B chat_control.rs(最复杂,等 F-09 B 批4 更稳)。关键:pub use self::commands::*; glob 保留(mod.rs:58)否则 invoke_handler 注册断。— src-tauri/src/commands/ai/commands.rs + mod.rs:58
  • (2026-06-22 核验·types+SSE 已抽 crates/df-ai/src/anthropic_helpers.rs(5 struct+2 const+apply_anthropic_event),anthropic_compat.rs 残留 881 行 Provider+impl(Rust 硬约束 impl 不跨文件不可再拆),lib.rs:4-5 双模块注册·对齐 _compat/_helpers 同构约定(openai/model_fetch/model_probe/context 同)·漏勾销账) REFACTOR-260619-02 — anthropic_compat.rs 拆模块。已完成(types+SSE 抽出)。
  • (2026-06-20 核验已拆分·audit.rs 已删除→src-tauri/src/commands/ai/audit/ 目录:mod.rs(31813 字节·process_tool_calls 热路径 + 编排)/reason.rs(7796·build_approval_reason)/finalize.rs(3874·audit_tool_call+audit_finalize,mod.rs:115 pub(crate) use finalize::{audit_finalize, audit_tool_call})/cache.rs(7685·find_cached_high_risk_result)/diff.rs(1686·build_write_file_diff)/restore.rs(10033·restore_pending_approvals)/idea_source.rs(13607·任务→灵感溯源)/data_change.rs(2432)/utils.rs(1486·risk_str/常量)) REFACTOR-260619-03 [P0]audit.rs(959 行)拆 5 模块audit/{mod,list,reason,finalize,dedup,process}.rsprocess_tool_calls(250+行热路径)整块搬 process.rs 不拆函数体(锁内 await 边界不动,CR-260618-11#5 性能注记)。11+ 私有 fn 升 pub(super):audit_tool_call/audit_finalize/find_cached_high_risk_result/build_write_file_diff/build_approval_reason 等。PENDING_APPROVAL_PLACEHOLDER/risk_str 提 mod.rs 共享。— src-tauri/src/commands/ai/audit.rs

P1

  • REFACTOR-260619-04 [P1]ToolCard.vue(1527 行)拆 5 子组件+composable+util。先 useToolFormat.ts(纯函数 ~280,零风险)+ ToolResultBody.vue(420 最大块);再 ToolCardHeader/ToolApproval/useToolApproval。风险:折叠态 shouldKeepOpen 三处共享 / 审批状态机断链(B-260616-08 回归)/ ToolCardList 批量审批联动。— src/components/ToolCard.vue + ToolCardList.vue
  • (2026-06-20 核验已拆分·agentic.rs 已删除→src-tauri/src/commands/ai/agentic/ 目录:mod.rs(83390 字节·run_agentic_loop 主 loop)/guard.rs(2894·GeneratingGuard+ContinueSnapshot 抽出对齐 A+E+F agentic_runtime)·stream_recv.rs(独立文件,StreamOutcome+stream_one_provider 对齐 B+C agentic_stream)) REFACTOR-260619-05 [P1]agentic.rs(1231 行)抽 agentic_runtime.rs + agentic_stream.rs。主 loop 是单函数 720 行不可按函数拆(工具执行/审批是 loop 内 if 分支;process_tool_calls 在 audit.rs)。先抽 A+E+F agentic_runtime.rs(GeneratingGuard+try_continue_agent_loop+ContinueSnapshot,~195,最高收益最低风险)+ B+C agentic_stream.rs(StreamOutcome+stream_one_provider,~165)。D run_agentic_loop 等 F-09 B 批4 落地再评估(避免 per_conv 双线作战)。— src-tauri/src/commands/ai/agentic.rs
  • (2026-06-20 核验·已拆:ai_node.rs 423 + ai_self_review_node.rs 447 + ai_helpers.rs 298,测试分块清晰) REFACTOR-260619-06 [P1]ai_node.rs(1107 行)拆 3 模块ai/{mod,params,ai_node,self_review}.rs。params.rs(provider 解析 helper ~270)+ ai_node.rs(~130)+ self_review.rs(~300)。测试分块清晰(752/939/1069 三段),低风险。fixture provider_stub/config_with 留 params.rs pub(super)。— crates/df-nodes/src/ai_node.rs
  • (2026-06-20 核验已拆分·src/components/ai/ 子目录已存在子组件:ConversationSidebar.vue + ChatInput.vue + MessageList.vue + TopBar.vue + DirAuthDialog.vue + MaxRoundsCard.vue·AiChat.vue 从 4075 行缩到 750 行·状态/composable 已外移,模板拆分完成) REFACTOR-260619-07 [P1](已有 SMELL-P0-3)AiChat.vue(4075)拆 ConversationSidebar/ChatHeader/MessageList/ChatInput。方案已定,状态/composable 已外移,拆 template+局部 script。前置:先提交工作区未提交改动(本会话 shouldRenderMsg/scroll/1214 预检等)。— src/components/AiChat.vue
  • (2026-06-22 核验·tool_registry.rs:442-514 已拆 register_*_tools 系列 data/file/http/project/task/workflow/idea/trash,SMELL-P0-2 批次落地·漏勾销账) REFACTOR-260619-08 — tool_registry.rs 按功能分组注册函数拆。已完成。register_crud_tools/register_file_tools(已抽)/register_workflow_tools 等,每个 <200 行。— src-tauri/src/commands/ai/tool_registry.rs

P2 暂缓(窗口未到/收益低)

  • REFACTOR-260619-09 [P2 暂缓]context.rs(1332)等 F-15 压缩链路稳定再拆。生产 745+测试 587。impl 跨文件方案(同 crate 多 impl 块,零字段可见性改动)。先 sanitize.rs(最大连续块 ~184)。当前 context.rs 被 F-15/压缩频繁改动,拆分窗口未到。— crates/df-ai/src/context.rs
  • (2026-06-22·scan.rs(实际741行)拆 scan/{mod,stack,discover,readme,sample}.rs,共享 SAMPLE_IGNORED_DIRS/truncate_chars 提 mod.rs pub(super),has_file_with_ext/read_readme_raw 跨模块降 pub(super),测试 29→29 不降,cargo check --workspace EXIT 0 外部消费方零改) REFACTOR-260619-10 — scan.rs 拆 4 模块。已完成。
  • (2026-06-20 核验·crud.rs 已删→crates/df-storage/src/crud/ 5 文件 mod/conversation_repo/idea_repo/message_repo/project_repo·与 SMELL-P1-9 同件) (已有 SMELL-P1-9)crud.rs(2212)按表拆 project_repo/task_repo/conversation_repo/idea_repo。— crates/df-storage/src/crud.rs

执行顺序建议:01-C provider_cfg(零风险试水) → 02 anthropropic → 03 audit → 04 ToolCard useToolFormat → 05 agentic_runtime → 06 ai_node → 07 AiChat.vue → 08 tool_registry。每步 cargo check+test+clippy 分 crate + 手测。


🔧 2026-06-19 命令行黑窗修复 + GLM 1214 数据调查DB 直查定位)

用户报两问题:①执行命令行弹黑窗闪烁 ②GLM 1214 messages 非法(多轮)。Build 版无 tracing subscriber 看不到日志,改 DB 直查(C:\Users\23780\AppData\Roaming\top.1216.devflow\devflow.db)定位。

  • (2026-06-19·df-execute shell.rs 全仓唯一子进程源(grep 确认)·tokio Command 加 creation_flags(0x0800_0000) CREATE_NO_WINDOW·cargo check df-execute EXIT 0 无 warning) B-260619-01 [P1]执行命令行弹黑色窗口闪烁。Windows tokio::process::Command 创建 cmd/powershell 子进程默认带控制台窗口(黑窗闪现)。修:#[cfg(windows)] cmd.creation_flags(0x0800_0000)(CREATE_NO_WINDOW)。tokio Command Windows 自带 creation_flags 方法(无需 std CommandExt trait)。覆盖 run_command 工具 + 工作流 shell 节点(全经 df-execute)。— crates/df-execute/src/shell.rs(:86-93)

  • (2026-06-22·conversation.rs:65 TRUNCATE_THRESHOLD 50KB→8KB + HEAD/TAIL 各3KB + 截断提示含原字节数让 LLM 感知非全文·cargo check devflow EXIT 0 + conversation 测试 25 passed·仅作用持久化视图不污染内存真相源) B-260619-02 — GLM 1214 tool_result 过大截断。已修(大小根因,配 CR-260620-04 配对错根因互补)。DB 直查报错对话 f64dee94:全量 546 条(多数 compressed),active(发往GLM)仅 7 条结构合法(system/assistant/user/assistant/user 交替,tool_use↔tool_result 配对 orphan=0,无连续 role,57K 字节 ~19K tokens 未超 GLM 128K)。真凶:单条 tool_result 巨大(read_file 整文件 #2=21843B/#3=16511B/#6=11482B),T-05 截断阈值 50KB 未触发(21KB<50KB),但 GLM anthropic 端点单条 tool_result content 限制更严(~10-20KB 即拒 1214)。次要:#4 assistant content 空(len=0 只有 tool_use);"回复一半消失"=1214 在 GLM 流极早(message_start 前)error→stream_recv InitFailed→emit AiError→前端清 currentText,保文逻辑未覆盖。修法方向:① tool_result 截断阈值收紧 50KB→8-10KB(read_file/list_directory 大文件截断+提示)② MidStream 早 1214 保文(apply_anthropic_event error 时若已 message_start 不清流式)③ 空 assistant convert 兜底。已加诊断(待重编译验证):anthropic_compat precheck(首条/连续/input/空content/orphan 5 类 Init bail)+ MidStream error 附 messages 摘要(SSE error chunk 塞摘要到前端 raw)+ HTTP/1.1 治 GLM HTTP/2 RST + 错误源链进 anyhow 文案。— crates/df-ai/src/anthropic_compat.rs(precheck/summarize/MidStream 摘要/version HTTP_1_1) + tool_result 截断(T-05 tool_registry.rs/audit.rs 阈值 50K→8-10K) + MidStream 保文(stream_recv.rs)

  • (2026-06-20·cargo df-ai+devflow EXIT 0 零 warning + vue-tsc EXIT 0·主 worktree 改,workflow worktree baseRef 故障绕过) CR-260620-04 [P0]1214/400 messages 非法·配对错根因(compress 绕过 sanitize)+ 停用模型路由穿透。用户贴 deepseek 400「tool_calls must be followed by tool messages」/「tool must be response to tool_calls」确认 tool_use/tool_result 配对错(与 B-260619-02 tool_result 大小根因互补,1214 多根因)。根因:sanitize_messages(context.rs:212 畸形配对自愈,三档+孤儿)只在 build_for_request 调(主对话),compress_via_llm(compress.rs:46)构造 prompt 不经它(:70 extend active_msgs 直进)→ 压缩带毒 → 400「自动上下文压缩失败」。修法:① context.rs:212 fn sanitize_messagespub fn(跨 crate);② compress.rs 空检查后 let active_msgs = df_ai::context::ContextManager::sanitize_messages(active_msgs) 治毒 + :90 错误带 active_count 诊断;③ 停用模型 agentic/mod.rs:188,450 model_override .any(|m|m.model_id==id)==id && m.enabled(会话内 stale override 不穿透 disabled)。关联:B-260619-02(tool_result 大小根因 GLM 1214,截断 50K→8-10K 仍待做 :412)/ F-09 newConversation(CR-260620-02 )/ 用户实测 deepseek 400。未做:tool_result 截断(B-260619-02 大小根因,GLM 若还犯需做)+ stream_recv SSE 错误 summarize(诊断仅 compress 层,stream_recv 待补)。— crates/df-ai/src/context.rs + src-tauri/src/commands/ai/compress.rs + src-tauri/src/commands/ai/agentic/mod.rs


🔧 2026-06-19 ToolCard 同批/跨轮 UI 合并子代理方案·其他会话实施·P1

用户需求:AI 多轮 read_file(轮1×2 + 轮2×2)前端一排展开卡片("上面两个下面两个"),希望 UI 合并。后端 convert 已合并连续 tool_result(B-260619-03),前端展示层对齐。子代理分析:同批同类合并已存在(ToolCardList groupedToolCalls L92-110,calls>=2 显标题行+默认收起),痛点是跨轮(轮1/轮2 各独立 ToolCardList 实例→各成分组)。用户强调"谨慎不出错"。

  • B-260619-04 [P1]ToolCard UI 合并:跨轮同类自动收起 + 标题信息增强。推荐方案 A(不合并卡片本体/不跨 message 边界/不动数据结构,只做 ① 标题增强 ② 新轮收起旧轮):
    • 现状(已查清):ToolCardList.vue groupedToolCalls 按 tc.name 全局聚合,calls>=2 显「[图标] 读取文件 [N] ▸」标题行 + 默认收起(initDefaultCollapse L139-147)。同批 read_file×2 应已是折叠组;用户「下面又两个」=下一轮新 message 的独立 ToolCardList 实例→第二个分组(跨轮未合并)。
    • 方案 A 改动:
      1. 标题信息增强(低风险):收起态「读取文件 [2]」→「读取 2 个文件 · shortPath1 · shortPath2」(从 group.calls[].args.path 取前 2-3 个 shortPath 截断)。新增 groupSummary(group) computed + i18n 键 aiTool.readNFiles(zh/en,漏 en 键 vue-i18n prod 报错)。
      2. 新轮到达自动收起旧轮已完成组(中风险):AiChat.vue watch 末尾 message toolCalls 到达→对前序 message 的 ToolCardList 调 expose 新方法 collapseCompletedGroups()(只收起 calls 全 completed 且无 pending_approval 的组,尊重 userExpandedCards 记忆)。
    • 前置必做(步骤1,0 改动验证):跑 read_file×2 确认同批是否真成分组标题(排除 initDefaultCollapse watch 一次性锁 bug——immediate 在 toolCalls 异步到达 len===0 跳过,L152-160 initialized 锁可能误锁)。若同批未分组→先修此 P0 前置。
    • 风险(全列,谨慎):R2/R3 收起的分组 display:none 会盖过审批卡 shouldKeepOpen v-show(现有风险),自动收起必须跳过含 pending_approval 的组;R5 流式 calls 1→2 跃变时不应收起用户正在看的卡(组内已有 userExpandedCards 命中→该组不默认收起);R7 scrollToFirstPending 兜底(offsetParent===null→全展开重查)需保证自动收起走 collapsedGroups 机制而非硬 display:none。
    • 谨慎实施步骤:① 0 改动验证同批分组 → ② 标题增强(低风险) → ③ 新轮收起旧轮(中风险,跳 pending) → ④ 流式跃变保护 → ⑤ 回归(审批徽标/批量审批/切对话/High 二次确认)。
    • 不推荐方案 B(跨轮全局合并,破坏 message 边界,审批/折叠重构高风险)/ C(纯视觉框,不解决占用)。 — src/components/ToolCardList.vue(groupedToolCalls L92-110/initDefaultCollapse L139-160/isCardExpanded L222-234/collapseInactive L265-296) + src/components/AiChat.vue(watch messages) + src/i18n/{zh-CN,en}/aiTool.ts(readNFiles)

💡 2026-06-19 新需求(任务关联灵感·已分析·待实施)

用户需求:推进任务时能即时、方便地关联到灵感及灵感的对抗式评估等相关信息。当前关联链路是 Idea → promote → Project → Tasks任务和灵感只能通过项目间接关联无法直接追溯。

  • (2026-06-20 核验已落地·migrations.rs:47 (20, migrate_v20) + :361 migrate_v20 列存在探测 ALTER + :562/:579 V9_SQL tasks 建表带 idea_id 列(新库直有,老库 V20 补)·models.rs:39 + :82 TaskRecord pub idea_id: Option<String> + #[serde(default)] 兼容旧 JSON·crud/task_repo.rs:34 task_from_row row.get("idea_id") + :52 INSERT + :63 UPDATE + :83/:184/:205 SELECT 列表(list_active/find_by_id/list_deleted)全含 idea_id·测试 migrations.rs:1052 v20_legacy_db_adds_idea_id_column + :1078 列存在跳 ALTER 验证·对齐 F-260619-05 已标 同实现) F-260619-01 [P2]任务关联灵感TaskRecord 新增 idea_id 字段。推进任务时即时查看关联灵感的描述、多维评分(scores)、对抗式评估(ai_analysis)等信息,辅助决策。

    数据模型变更TaskRecord 新增 pub idea_id: Option<String>(关联灵感 ID可空=未关联),#[serde(default)] 兼容旧 JSON。

    涉及改动7 处)

    1. 迁移 V20migrations.rstasks 表 ALTER TABLE ADD COLUMN idea_id TEXTnullable老数据 NULLV1 建表 SQL 同步补 idea_id 列新库直接有steps 数组追加 (20, migrate_v20);用 column_exists 探测(同 v17/v18/v19 模式),对新库/老库均安全。

    2. Modelmodels.rsTaskRecord 加 pub idea_id: Option<String> + #[serde(default)]

    3. CRUD 层crud/task_repo.rstask_from_rowidea_id: row.get("idea_id")?impl_repo! 的 insert/update SQL 加 idea_id 列 + params 占位;list_active / list_deleted / advance_status_atomic 的显式 SELECT 列表补 idea_id。

    4. 白名单crud/settings.rstasks 白名单加 "idea_id"(允许 update_field 改关联)。

    5. 命令层commands/task.rsCreateTaskInputpub idea_id: Option<String>create_task 构造 TaskRecord 时写入 idea_idupdate_task 对 idea_id 做跨表存在性校验(对标 project_id 校验模式:查 ideas 表确认存在,空值=解除关联允许通过)。

    6. AI 工具层commands/ai/tool_registry.rscreate_task 工具 schema 加 idea_id 可选参数;update_task 工具 schema 加 idea_id描述说明可关联灵感

    7. 前端:任务创建表单加灵感选择器(下拉选 ideas 列表,可空);任务详情页加关联灵感卡片(只读,展示灵感标题 + 描述摘要 + 多维评分 scores + 对抗式评估 ai_analysis + promoted_to 状态);update_task 支持 idea_id 字段更新(空值解除关联)。前端获取方式:任务详情展开时若 idea_id 非空,调 list_ideas 或新增 get_idea_by_id IPC 拉取关联灵感记录渲染卡片。

    验收标准:① 创建任务时可选关联灵感;② 任务详情页展示关联灵感的描述 + 评分 + 对抗式评估;③ 可更新/解除关联update_task idea_id = "" 清空);④ 老任务idea_id NULL无回归cargo check --workspace EXIT 0 + vue-tsc EXIT 0

    crates/df-storage/src/{models.rs,migrations.rs,crud/task_repo.rs,crud/settings.rs} + src-tauri/src/commands/{task.rs,ai/tool_registry.rs} + 前端任务组件


💡 2026-06-19 新需求MCP Server·灵感来源·待实施

灵感来源:9ffa5f15 DevFlow MCP Server对抗式评估 7.2/10推荐 with resources。 评估关键结论技术切入点和研发性价比极佳ToolDefinition ↔ MCP schema 几乎一致,转换成本极低),但反方强度 0.78——暴露 delete_project 等破坏性操作缺乏鉴权/确认/回滚,外部 AI 幻觉可能导致不可逆误删。必须在协议接入的同时做工具集安全降级

  • (2026-06-20 核验已落地·crates/df-mcp 存在:lib.rs+protocol.rs+server.rs+tools.rs,pub use server::run_server 导出 ·src-tauri/src/main.rs:16 args.iter().nth(1) == Some("mcp-server") 子命令分支 + :28 run_mcp_server + :99 df_mcp::run_server(&db_path, read_only) 启动 ·src-tauri/Cargo.toml:36 df-mcp = { path = "../crates/df-mcp" } 依赖已接 ·lib.rs 注释含安全降级(High 默认拒绝/Medium 日志/--read-only/不暴露文件工具)对齐设计) F-260619-02 [P2]DevFlow MCP Server对外暴露任务/项目/灵感管理能力。以 MCPModel Context Protocolserver 形式对外暴露 DevFlow 数据层工具,让外部 AI 客户端Claude Code / Cursor / Cline直接读写 DevFlow 的项目、任务、灵感数据。

    形态:独立 stdio MCP server 子命令(方案 Adevflow mcp-server 启动,应用未运行时也能被 Claude Code 拉起。

    协议实现

    • initialize → 返回 server info + capabilities(tools)
    • tools/list → 从 AiToolRegistry::tool_definitions() 转换为 MCP Tool 格式
    • tools/call → 路由到 handler返回 content: [{ type: "text", text: JSON }]
    • 推荐用 rmcp官方 Rust SDK或最小化手写 JSON-RPC 2.0 over stdio
    • MCP Tool schema 与现有 ToolDefinition 结构几乎一致name/description/parameters JSON Schema写一个 tool_definition_to_mcp() 转换函数即可

    工具集(首版只暴露数据层,不暴露文件系统工具)

    • 项目list_projects / get_project / create_project / update_project / delete_project / bind_directory
    • 任务list_tasks / create_task / update_task / advance_task / delete_task
    • 灵感list_ideas / create_idea / update_idea / delete_idea / evaluate_idea
    • 工作流run_workflow首版只读/需确认)
    • 其他list_trash / restore_project

    ⚠️ 安全降级(对抗评估反方核心风险,必须同步实施)

    • High 风险操作delete/purge/run_workflow默认拒绝 + 返回「请在 DevFlow 应用内执行」——防外部 AI 幻觉导致不可逆误删(反方强度 0.78 核心论点)
    • Medium 风险操作create/update默认允许 + 日志审计(写 ai_tool_executions 表留痕)
    • 可选 --read-only 标志仅暴露 list/get 工具
    • 不暴露文件系统工具read_file/write_file/delete_file 等),避免绕过应用内路径校验
    • 仅 stdio / 127.0.0.1,不暴露网络
    • 不引入 MCP 客户端功能DevFlow 只做 server

    数据库访问

    • stdio server 启动时打开同一 SQLiteapp_data_dir/devflow.db--db 参数指定)
    • SQLite WAL 模式确认:需确认 df-storage 已启用 WAL反方论点并发锁竞争未处理。若未启用 WAL首版要求关闭 DevFlow 再用 MCP server或 MCP server 只读连接

    配置与发现

    • devflow mcp-server --print-config 输出 Claude Code / Cursor 的 MCP 配置 JSON 片段
    • Claude Code 接入:claude mcp add devflow -- devflow.exe mcp-server

    涉及文件

    • 新建 crates/df-mcp/Cargo.toml / src/{server.rs, tools.rs, protocol.rs}
    • 修改 src-tauri/src/main.rsCLI 参数解析,mcp-server 子命令分支)
    • 修改 src-tauri/Cargo.toml(加 df-mcp 依赖 + clap
    • 可选:ai_tools.rs 把 handler 闭包抽为可独立复用函数

    验收标准

    1. Claude Code 配置后能 list_projects / list_tasks / create_task / advance_task 成功
    2. 应用运行时 MCP server 也能读写WAL 并发安全)
    3. High 风险操作delete/purge/run_workflow被拒绝并返回友好提示
    4. cargo check --workspace EXIT 0 + cargo test -p df-mcp(基础协议测试)
    5. devflow mcp-server --print-config 输出正确配置片段

    — 新建 crates/df-mcp/ + src-tauri/src/main.rs + src-tauri/Cargo.toml + 可选 crates/df-ai/src/ai_tools.rs


💡 2026-06-19 新需求AI 工具文件访问动态权限模型·已分析·待实施)

用户需求:将现有 workspace_root 单一根目录模型,升级为动态白名单池 + 运行时权限申请机制(类似 macOS / VS Code 的权限申请模式)。解决「锁太死」(只能绑定单一 workspace_root和「放太宽」的矛盾。

与 MCP ServerF-260619-02无关:本需求是 DevFlow 应用内部 AI 工具read_file/write_file 等)的文件系统访问权限升级,不涉及对外 MCP 协议暴露。

  • F-260619-03 [P1] AI 工具文件访问动态权限模型(Phase A 持久白名单 + Phase B 会话临时/弹窗 + Phase C 黑名单 + workspace_root 去固定根完整,CR-09 + b22e9ae reload 尊重用户 persistent)

    核心机制:当 AI 调用 read_file / write_file 等文件系统工具时,路径校验从「单一 workspace_root 前缀匹配」升级为「动态白名单池校验 + 未命中则挂起 Agentic Loop 向前端弹窗申请授权」。

    数据结构变更

    1. 持久化白名单Settings KV 存储):app_settings 表 key=allowed_dirsvalue=JSON 数组 ["E:/wk-lab/u-abc", "E:/wk-lab/u-img"]。前端 Settings 页提供列表增删改查 UI。
    2. 会话级临时白名单(内存):AiSession 新增 session_allowed_dirs: HashSet<PathBuf>,仅限当前会话有效。
    3. 全局状态AppState 引入 AllowedDirs { persistent: HashSet<PathBuf>, session: HashSet<PathBuf> },替代写死的 workspace_root()

    权限拦截与申请流程

    1. 规范化路径canonicalize(解析软链接、...)。
    2. 检查白名单:判断 canonicalize 后的真实路径是否在持久化或会话白名单中(real_path.starts_with(allowed_dir))。
    3. 命中则放行:执行原逻辑。
    4. 未命中则拦截:挂起当前 Agentic Loop → Tauri 事件向前端弹窗 AiDirAuthRequired { path, tool_name } → 用户选择「仅本次」(加入 session_allowed_dirs/「未来都允许」(写入 DB Settings + 更新 persistent/「拒绝」→ 恢复执行。

    授权粒度:弹窗中建议授权目标文件所在的项目根目录(而非具体文件),减少弹窗频率。用户可在弹窗中手动收窄或放宽范围。

    安全防护(三层)

    • 第一层 canonicalize:解析软链接后校验真实路径,防软链接逃逸(授权目录本身也需 canonicalize
    • 第二层白名单real_path.starts_with(allowed_dir) 校验。
    • 第三层黑名单兜底:保留现有 validate_path,禁止系统敏感目录(/etc/varC:\Windows\System32 等)。

    写操作额外约束写操作write_file/delete_file/patch_file即使目录已授权仍走现有 RiskLevel 审批流Medium/High 需用户确认);delete_file 始终 High 风险审批,不受白名单影响。

    分阶段实施

    阶段 内容 复杂度 优先级
    Phase A Settings 持久化白名单 + resolve_workspace_path 改造为多目录校验canonicalize + starts_with P1
    Phase B 会话级临时授权 + Agentic Loop 挂起/恢复 + 前端弹窗 UI + Tauri 事件 P2
    Phase C 软链接深度防护 + 系统目录黑名单完善 + 写操作额外约束 P2

    Phase A 成本最低但收益最大——立即解决「只能绑定单一 workspace_root」的限制且为后续动态授权打好数据基础。

    与现有架构的契合点

    • SettingsRepo KV 存储:已有,allowed_dirs 直接复用,零迁移成本。
    • RiskLevel 审批流:已有完整 tool approval 机制,动态授权可视为「路径级别的 approval」。
    • Agentic Loop已有挂起/恢复能力(审批等待),扩展路径授权挂起是同构的。
    • Tauri 事件系统:已有 ai-chat-event,新增 AiDirAuthRequired 事件类型即可。

    关键改动点

    • tool_registry.rshandler 闭包需引入 AllowedDirsArc 引用(当前闭包是无状态 Box::new(|args| ...)),调整注册逻辑。
    • resolve_workspace_path(或等效路径校验函数):从单一 workspace_root 前缀匹配 → 多目录白名单 canonicalize 校验。
    • state.rsAppState 新增 allowed_dirs 字段。
    • commands/ai/mod.rsAiSession 新增 session_allowed_dirs 字段。
    • agentic.rs:捕获 PATH_AUTH_REQUIRED 信号 → 挂起 → emit 事件 → 等待恢复。
    • 前端 Settings 页:新增「授权目录」管理 UI。
    • 前端 AiChat新增路径授权弹窗组件。

    验收标准

    1. Settings 页可管理持久化授权目录列表(增删改查)
    2. AI 访问授权目录内文件正常执行,无额外弹窗
    3. AI 访问授权目录外文件时弹窗申请,用户可选择「仅本次」/「未来都允许」/「拒绝」
    4. 软链接逃逸被 canonicalize 校验拦截
    5. 系统敏感目录始终被拒绝(黑名单兜底)
    6. cargo check --workspace EXIT 0 + vue-tsc EXIT 0

    src-tauri/src/state.rs + src-tauri/src/commands/ai/{mod.rs,tool_registry.rs,agentic.rs} + src-tauri/src/commands/settings.rs + 前端 Settings 页 + AiChat 弹窗组件


F-260619-05 任务可关联灵感tasks ↔ ideas 关联)

背景:当前 tasks 表无 idea 关联字段,任务与灵感(来源/启发无结构化关联。projects 表已有 idea_id REFERENCES ideas(id)(项目关联灵感),任务侧缺失。

现状(核对 df-storage/src/migrations.rs

  • tasks 表(:378-391id/project_id/title/description/status/priority/branch_name/assignee — 无 idea 关联
  • projects 表(:373idea_id REFERENCES ideas(id) — 项目关联灵感(可复用模式
  • ideas 表(:354-365source TEXT(自由文本来源)
  • releases 表(:399task_ids JSON发布关联多任务1对多 JSON 数组先例)

设计点(待决策)

  1. 关联粒度:一对一(source_idea_id任务来源单个灵感vs 一对多(related_idea_ids JSON 数组,借鉴 releases.task_ids
  2. 存储tasks 加 idea_id 列(迁移,复用 projects.idea_id 外键模式vs 关联表 task_idea_links(多对多,灵活但复杂)
  3. 方向单向任务→灵感vs 双向(灵感侧反向显示关联任务列表)
  4. AI 工具update_task 支持 idea_id白名单加列vs 新工具 link_task_idea
  5. 前端:任务卡片/详情显示关联灵感 + 选择关联mention 复用 ChatInput @灵感)

推荐tasks 加 idea_id TEXT REFERENCES ideas(id)(复用 projects 模式1对1 起步,后续按需扩展 1对多灵感侧反向查询 list_tasks_by_idea

依赖:改 df-storage/migrations.rs + models.rsTaskRecord 加 idea_id— 与消息级溯源 P0 改同文件冲突,待 P0 完成后实施

改动点

  • crates/df-storage/src/migrations.rstasks 加 idea_id 列(新迁移 V
  • crates/df-storage/src/models.rsTaskRecord 加 idea_id 字段 + from_row + insert/update SQL
  • crates/df-storage/src/crud/task_repo.rs:白名单 + idea_id 读写
  • src-tauri/src/commands/ai/tool_registry.rsupdate_task 支持 idea_id或新工具
  • 前端:任务卡片/详情关联灵感展示(友好 title 非裸 id+ 选择

验收

  1. 任务可关联灵感(创建/更新时指定 idea_id
  2. 任务卡片显示关联灵感title 友好展示)
  3. 灵感详情可查关联任务列表(若双向)
  4. cargo check --workspace EXIT 0 + vue-tsc EXIT 0

状态 已实施(tasks.idea_id 1对1 单向,commit 4a87c55,粒度/方向已定)


💡 灵感模块升级(2026-06-21,批1-3c·已实施)

灵感模块可信度+体验+评估历史+统计+晋升携带+关联关系全面升级。对齐 memory devflow-idea-module(df-ideas 评估闭环已通,对抗未接 LLM/晋升走前端)后续延伸。

批1 可信度+体验:

  • P0 evaluated_by 落库(ai_analysis 关联谁评的)+评估深度标签动态(LLM/启发式/降级,直显评估来源可信度)+评估失败重试(瞬时失败可恢复)+列表排序(评分/时间,支持按评分挑高价值)+详情描述可编辑(描述可订正)+已晋升项目跳转(promoted_to → 项目直跳)

批2 评估历史全栈:

  • idea_evaluations 版本快照表(每次评估留版本可追溯)+IdeaEvalRepo(版本数据访问层)+evaluate_idea 每次 insert 历史(评估产生历史记录)+list_idea_evaluations IPC(暴露历史查询)+IdeaDetail 版本时间线(前端时间线组件渲染历次评估)

批3 IdeasPanel 统计看板:总数/待评估/已立项/平均分(模块概览一眼可见)

批3a 晋升携带:ProjectDetail 来源灵感卡片(反查 idea_id 显示评估结论,项目可追溯灵感来源)

批3b 关联关系 schema:ideas.related_ids JSON 列 + IdeaRecord 字段(为关联 UI 打底)

批3c 关联关系前端:IdeaDetail 关联灵感展示区 + 多选管理选择器 + Ideas.vue update-related handler

P1 信号词否定前缀:scoring.rs count_any 不复用/无增长等否定不计正分(修评分虚高,如"无增长"误算"增长"正分)

验证:cargo check src-tauri + df-storage 62/11 tests + df-ideas 22 tests + vue-tsc 灵感模块零新错

连带修:augmentation/resolvers.rsResolverRegistry import(预存编译阻断,顺手补)

状态: 批1-3c 全完成(主代独立验证 cargo + tests + vue-tsc 通过)

⚠️ 灵感升级-遗留缺陷(2026-06-21·待另一会话修)

  • (2026-06-22 核验·settings.rs:123 ideas 白名单已含 related_ids,14 列非 13,批3c 落地时已补,关联 update_field 放行非静默失败·过时 todo 销账) related_ids 白名单 — 原"P0 功能缺失"为过时信息,实际已落地。
  • [P2 并发·🟡部分完成] 评估历史 version 并发重复:idea.rs:288-293 evaluate_idea 已加重试兜底 + V25 (idea_id,version) 唯一约束(migrations.rs)兜底。约束+重试就位,运行时确认约束生效后销账。单用户桌面低概率,严格讲需加唯一约束或事务。批2 引入。
  • [P2 运行·未验] 批1-3c 前端运行时未实测:编译/vue-tsc 过,但运行(评估历史加载/关联管理交互/统计 computed/ProjectDetail 来源卡片反查 store.ideas)未实际跑过。需 npm run tauri dev 实测。

💡 灵感模块-来源采集/追溯(2026-06-21·待办·新功能范畴)

继批1-3c 升级后的下一步:灵感来源录入增强 + 自动采集机制。属新功能范畴,需独立设计采集规则/触发/去重,下一阶段推进。

  • F-260621-01 [P2/新功能]source 字段录入增强 + 自动采集机制
    1. source 录入增强:灵感捕捉模态框加 source / tags / priority 输入(现仅描述),让录入即结构化(来源/标签/优先级)
    2. 自动采集机制:从对话 / 任务 / 代码中自动提炼灵感(降低录入门槛,捕捉随手遗失的灵感)
    • 需设计:采集规则(什么信号算灵感 — 关键词/意图/重复模式)/ 触发时机(对话结束/任务完成/手动触发)/ 去重(相似灵感合并避免洪水)/ 用户确认流(自动采集不污染灵感池,需用户确认入库)
    • 依赖:批1-3c 升级完成(已有 source/tags 字段基础) + 对话历史可读(F-15 archived_segment 状态) + 任务可读
    • 关联:memory devflow-idea-module / [aichat-skill-slash-autocomplete]

⚠️ 预存债-非灵感模块:ToolResult 类型缺字段(用户并行开发中间态·阻断 build)

用户并行开发工具结果展示时,ToolResultBody.vue + composables/ai/useToolCard.ts 引用了 ToolResult 类型不存在的字段(vue-tsc 7+ 错)。非本次灵感模块改动引入,是用户工作区并行开发中间态。不阻断 vite dev(dev 宽松),阻断 npm run build(prod 严格)。

  • (2026-06-22 核验·useToolCard.ts:88-100 ToolResult 已含 output_mode/matches/files/counts/total_files 五字段(F-260621 grep 跨文件搜索)+注释说明三模式语义·ToolResultBody.vue:71-95 三模式渲染消费·useToolCard.ts:424-446 formatGrep 消费·vue-tsc --noEmit EXIT 0 无 TS 错·漏勾销账) TD-260621-07 [P2/预存债·待用户对齐]ToolResult 类型缺 output_mode/files/counts/matches/total_files 字段

    • 现状:ToolResult(types.ts)未定义 output_mode / files / counts / matches / total_files 五字段,但 ToolResultBody.vue + useToolCard.ts 模板/computed 已引用 → vue-tsc 报 7+ Property does not exist
    • 定位:用户并行开发工具结果展示增强(可能是为了批1 评估深度标签/批2 历史时间线之外的工具结果多模式渲染 — output_mode 暗示 tab/列表/树等多种展示模式),属进行中的中间态
    • 修法:① ToolResult 类型补齐 5 字段(对齐后端返回结构) + ② ToolResultBody.vue / useToolCard.ts 消费方与类型对齐(可能需后端 tool_result_summary IPC 返回结构同步扩字段)
    • 不阻断 dev:vite dev 宽松回退,vue-tsc prod build 严格;用户 dev 调试不受影响,仅 build/release 受阻
    • 待用户:确认字段语义(output_mode 取值集合 / files 结构 / counts 计什么)后对齐,非本会话灵感模块范畴
  • (workflow wf 批1·streamingGuard.ts 新增 setStreaming/forceResetStreaming guard + 14 处 state.streaming 散布赋值收敛(useAiSend 7/useAiEvents 2/useAiStream 1/useAiConversations 2/useAiWindow 2/useAiPanel 1)+ feature flag df-ai-generating-statemachine(appSettings,关时回退散布语义)+ onStreamTimeout 改走 forceResetStreaming 兜底 + scripts/verify-streaming-guard.mjs 9 单测全过·vue-tsc EXIT 0·cargo check EXIT 0) TD-260621-GUARD[P0·用户实测卡死根因] generating 状态机前端落地(对齐 memory devflow-generating-statemachine + 专项设计文档)。后端 RAII guard(B-09)+ session_state() enum 视图(B-12)+ per_conv 化(F-09 批4)历史批次已落地,本批补前端 streaming 写收敛:state.streaming 散布在 6 文件 14 处直接赋值,任一 return 漏写/前端 JS 异常跳过复位 → streaming 永久 true 卡死输入框。收敛到 setStreaming 单一写入口(合法性观测日志 + generatingConvs 联动 + flag 灰度),onStreamTimeout 走 forceResetStreaming 兜底复位 Idle+清 currentText/queue。注:TD-260621-06a(switchConversation 重算 streaming)本批已纳入 guard(convId=id 幂等联动)。

    • 关联:批1-3c 灵感升级无涉(独立预存债)

F-260620-01 跨端 AI Chat:微信小程序 ↔ Rust 云后端 ↔ DevFlow 桌面端

背景:微信小程序远程用 DevFlow AI Chat,桌面端与小程序双向实时同步(微信电脑+手机同时在线模式)。 设计:F-260620-01-跨端AIChat-微信小程序-2026-06-20.md(三层架构 df-tunnel/df-relay/df-miniapp + Rust 云后端选型) 关系:灵感 4495fbcd(选型论证,pending_review)+ 任务 6c816709(todo)

实施分阶段:

  • P1 df-relay 云后端(axum WS Server + 广播中继 + 鉴权)— 无依赖
  • P2 df-tunnel 桌面端(WS client + 事件桥接 ai-chat-event 透传 + 指令路由 + 配对绑定)— 依赖 P1
  • P3 df-miniapp 小程序(前端 UI 复用 + WSS 连云)— 依赖 P1/P2
  • P4 双向同步完善(冲突处理 + 安全 + 离线降级 + 部署)— 依赖 P1/P2/P3

状态: Phase2 三层全落地(2026-06-22:df-relay 2b8b30e Hello握手+鉴权+ConnRegistry配对路由+BroadcastMessage透传 / df-tunnel 25d6565 WS客户端出站穿NAT+心跳25s+指数退避+TunnelCommand 5变体 / df-miniapp 280baea uni-app WS连relay透传+18变体AiChatEvent镜像+useAiChat分派;双 crate 独立消息骨架不依赖 src-tauri/df-types 避跨 crate 强耦合)。📐 Phase3 联调设计已出 F-260622-01(协议统一方案A纯透传 + AiSession桥接风险 + 4阶段路线),待实施。

Phase3 联调待办(D1-D6 决策点推荐:A纯透传 / 全19变体透传 / EventBus汇聚 / switch不处理 / 强类型保留 / 桥接层R1兜底):

  • (2026-06-22)F-260622-01-阶段1(D1=A):tunnel CommandHandlerserde_json::Value + parse_payload_from_broadcast 返回 Value(入站纯透传,cargo check + 5 测试过,D5 保留 TunnelCommand 弱校验)。出站 send_raw_event 移阶段2(无阶段1 验证场景,阶段2 EventBus 接入同步加)
  • (2026-06-22)F-260622-01-阶段2:前置决策 ② EventBus Value(0fb5af5)+ emit 双写全接入 chat19/audit12/guard3/stream_recv2/agentic18(612de4c)+ send_raw_event(030ddb6)+ tunnel subscriber(ed8e2fc)。后端上行闭环(EventBus→tunnel→relay→miniapp)。剩余:relay_url/device_id Settings UI(方案A)+ token 生产级(Phase4)
  • (2026-06-22)F-260622-01-阶段3:remote_bridge 桥接(0fb5af5)+ miniapp 4命令(8ccdce2)+ on_command 注册(ed8e2fc)。下行闭环(miniapp Command→tunnel on_command→remote_bridge→Tauri command)
  • F-260622-01-阶段4:真机联调 + 多会话并发验证矩阵(6 场景含 F-09 跨端并发)
  • 核验项:ai_chat_send 内部 generating guard(chat.rs:354-358 有 is_gen → Err<String>);R1 仍加作双保险(桥接层前置 + 内部 guard 双层)
  • 核验项:ai_chat_stop AwaitingApproval(chat.rs:1469-1484 finalize_pending_placeholders + retain 清 pending + generating=false + emit AiCompleted,R3 已缓解)

AI Chat 跑题/抓不住重点改进(跑题修复)

背景:AI Chat 对话跑题/抓不住重点。根因(深度分析):系统提示无聚焦 + 意图未接入 loop + 压缩丢重点 + 工具挤占 + 无主题检测。 设计:改进方案 Plan agent 规划中(优雅/可靠/易迭代三原则)

修复分阶段(每阶段独立可回退,均 完成):

  • P0 系统提示聚焦(prompt.rs ## 聚焦准则/## Focus 独立段,102d398)
  • P0 意图接入 agentic loop(intent filter_tool_defs 收敛工具 29→5-10 + 三重 fallback,102d398)
  • P1 压缩增强(compress prompt 主题锚点 + 失败兜底 extract_keyword_summary,013ce21)
  • P1 工具结果压缩(should_summarize/extract_key_info view-only 不改持久化,013ce21)
  • P2 主题检测(TrackedMessage.topic + 双高置信保守 + tokenize 2-gram 修复中文锚点,a2db5c7)
  • 苛刻测 38(边界/对抗:全漂移/全停用词/全错误行/2KB 边界/连续主题切换)+ db 基线试验记录文档

状态: P0-P2 全完成(合并 de04970 + a2db5c7 + 38 苛刻测 + 试验记录文档)。待重启 dev 实跑验证 LLM 实际响应效果(机制测过 ≠ LLM 行为改善)。


单对话内并行多轮推理(主循环 ReAct 串行 → 规划式并行)

背景:单对话内并行现状评估——工具级并行(Low risk join_all,audit/mod.rs:448/499) + 主/辅助 LLM 2 路并发(per_conv permits=2,state.rs:121),但主循环单链 ReAct 不支持并行多轮推理(run_agentic_loop agentic.rs:331-1206 一轮一轮串行,无法拆子流并行)。这是单对话内并行的真正天花板,permits=2 是配套限流(非瓶颈)。

根因:ReAct 每轮依赖前轮结果,不产生并行多轮需求。要并行多轮需规划式分解,coordinator.rs/conditions 空壳未落地(SMELL-P2-4 B 路线占位 / memory aichat-arch-extensibility)。

方向(架构·规划放哪层——分轻/重):

  • 轻规划融入意图层:扩展 intent.rs tool_subset_forplan_hint(工具编排/可并行组/顺序)+ suggested_model_tier 模态。保持纯函数零延迟(intent 已接入 loop,跑题 P0 filter_tool_defs 收敛工具 29→5-10 + 三重 fallback,102d398)。轻规划是自然延伸(意图→编排提示)。
  • 重规划独立 planning 层:coordinator.rs 空壳 → 演进 planning,消费 intent + plan_hint,做任务分解/子流并行调度/条件分支(conditions)。接入 loop,有状态(intent.rs 是无状态纯函数,重规划违背其定位,不塞进去)。
  • 三层链路:intent(识别,纯函数)→ plan_hint(意图层轻规划)→ planning(独立重规划/调度)→ execution(loop 按 plan 并行跑)

决策点(待定):

  • (a) 轻规划先扩(intent → plan_hint,低风险,跑题 P0 基础延伸)vs 直接重规划 planning(大改,coordinator 落地)
  • (b) planning 形态:coordinator.rs 空壳演进 vs 新建 plan.rs
  • (c) 并行多轮语义:用户是否真需单对话内并行探索多方案(当前 ReAct 单链足够大多数场景,可能非刚需)

关联:intent.rs(CR-08)/ 跑题 P0 intent 接入(102d398)/ coordinator.rs 空壳(SMELL-P2-4)/ conditions(T-260614-11)/ F-09 B 路线 / memory aichat-arch-extensibility / aichat-roadmap-ab-split(B 路线)

状态:🔧 Phase1 已落(2026-06-22 PLANNING_ENABLED=true 主 loop 规划开关启用 + filter_tool_defs_planned 接入点就绪 commit cf7fe86,plan_hint 函数 PLAN_HINT_ENABLED 就绪双层开关可回退)。Phase2 评估(2026-06-22 agent 核验):plan_hint 完整非空壳(4 启发式规则 plan_hint.rs:264-351,Phase1 已生效生产路径非占位),Phase2=接 planner.rs LLM Plan DAG 重写主 loop 调度(L 工作量+🔴高回归风险:主 loop 绑 F-09并发/DIRAUTH/generating状态机/断路器/压缩 6 子系统),暂缓先验 Phase1 收益(埋点 agentic/mod.rs:648-656 就绪跑数据)。📋 待决策(Plan 严格调度 vs hint 模式双语义未定 plan_hint.rs:20-25)。属 aichat B 路线(决策能力)范畴,P0-P2 跑题已大幅改善 ReAct 单链覆盖大多数场景。


🟡 F-09 决策e 前端 newConversation 未跟进(中断弹窗遗留·用户实测)

背景:用户实测——生成中点新建对话,弹「当前正在生成,确定中断并新建?」。F-09 决策e 真并发已落地(切会话/新建不该打断旧 loop,各 conv 并行),该弹窗与并行矛盾。

根因:前端 newConversationA 路线单 loop 软隔离遗留(B-260615-10 + F-260616-09 A 路线),没随决策e B 路线升级:

  • AiChat.vue:246-251 confirmNewConversation:生成中弹「中断并新建」确认(B-260615-10 软复位,单 loop「中断当前+新建」逻辑)
  • useAiConversations.ts:78 state.generatingConvs.clear():A 路线 F-260616-09 清字段,清空所有后台生成跟踪(后台 loop 后端还在跑,前端丢失跟踪 → 侧栏不显旧 conv 生成态 / 事件路由错乱)
  • 对比 switchConversation:89-91:决策e 已落地(「允许生成中切换:后台继续生成,事件按 conversation_id 路由」)。newConversation 漏升级

决策e 应有行为:新建对话 = createConversation + 切 active,旧 conv 后台 loop 继续(后端 loop 按 conv 存在性退出,新建不删旧 conv 不打断)。不弹中断 + 不 clear generatingConvs(后台生成态保留,侧栏显双会话生成)。

修法:

  • confirmNewConversation 去弹窗,直接 store.newConversation(决策e 新建=切到新 conv,旧后台继续,非中断)
  • newConversationgeneratingConvs.clear()(后台生成继续不清);messages/queue/pendingApprovals/currentText 是新 conv 本地视图复位,保留
  • 核对:新建后旧 conv 后台事件路由正常(generatingConvs 含旧 conv,isGenerating(旧) 仍 true,侧栏双生成) + AiCompleted/AiError 按 conversation_id 正确收尾旧 conv

优先级:🟡 P1(决策e 体验破坏:用户期望并行被中断提示打断,且 clear 致后台生成前端丢失跟踪) 关联:F-09 决策e(CR-07 已审 PASS 后端真并发)/ B-260615-10 软复位(待废弃)/ F-260616-09 A→B 路线 / memory devflow-aichat-review-pending 状态: 已实施(CR-260620-02 审查 PASS,confirmNewConversation 去弹窗 + newConversation 去 clear,2026-06-20)


🟡 MED-1 bind/create/update_project(Medium)AI 工具路径审批执行后 reload 未兑现(CR-260620-01 审查 MED)

背景:CR-260620-01 审查发现 — bind_directory/create_project/update_project 是 RiskLevel::Medium(tool_registry.rs:483/506/536),走 pending 审批,执行在 chat.rs ai_approve(非 process_tool_calls 内联)。bind_dir_to_project / repo.update_field 直写 DB 绕过 project.rs IPC,批3 三处 reload(create_with_binding/update_project/relocate)覆盖不到 AI 工具绑定路径。tool_registry.rs:545-549 注释承诺"reload 兜底"但代码空头。

后果:AI 用 bind_directory/create_project/update_project 工具绑定目录后,新目录未进白名单 → 后续文件工具仍误弹窗,需手动 Reload/重启。(用户手动绑定走 IPC project.rs 已修批3;AI 工具绑定路径漏)

修法(单点):chat.rs ai_approve 执行 Medium 工具成功后,若 tool_name ∈ {bind_directory, create_project, update_project},调 state.reload_allowed_dirs()。覆盖所有 Medium path 绑定工具审批执行路径。

优先级:🟡 MED(非安全,用户可手动 Reload;AI 主动 bind 场景,用户主诉 IPC 路径已解决) 关联:CR-260620-01 审查 MED-1 / 批3 project.rs reload(IPC 路径已修)/ agent3 问题5 状态: 已实施(chat.rs ai_approve 两执行点 path 工具后 reload_allowed_dirs,cargo check EXIT 0,2026-06-20)


LOW F-09 stale 注释 + 死 i18n key 清理(CR-260620-02 审查 LOW)

背景:CR-260620-02 审查 — F-09 去中断弹窗后,3 处子组件 stale doc 注释仍写"带二次确认"(TopBar.vue:163 / ConversationSidebar.vue:226/242)+ i18n key confirmNewConvWhileGenerating(zh-CN settings:120 / en:119)成死 key(0 消费方)。

修法:删死 i18n key + 改 stale 注释为"决策e 直接新建"。 优先级: LOW(质量清理,可选) 关联:CR-260620-02 审查 LOW-1 / F-09 决策e 状态: 已实施(删 aiChat.ts confirmNewConvWhileGenerating key zh/en + 改 TopBar/Sidebar 3 处 stale 注释,vue-tsc EXIT 0,2026-06-20)


🔍 2026-06-22 miniapp 功能走查(workflow wf_ddd49ac8 · 5 域并行核验 + 主代终态抽查)

用户 /review miniapp + workflow 汇总安排。5 agent 独立 grep/read 核验源码(不信注释/文档) + 主代终态核验(ws.ts 真路径 src/api/ws.tsrelay.rs:255/355、remote_bridge 命令数 10 非 11)。18 findings → 去重 P1×5 / P2×3 / P3×1,2 项 refuted。关联 F-260620-01 阶段4(真机联调前置)。守 session-role-diagnose-only:本会话仅走查 + 登记,未实施代码。

P1(阻塞真机/可靠性·5 项):

  • P1-D-260622 [P1🔴·真机分发最高阻断] — token/deviceId 硬编码 + 无配对绑定 + 无 storage 持久化config.ts:35-39 硬编码本机 device_id+token(注释自认「联调期硬编码,后续做配对绑定」),:52-54 setConfig 仅内存赋值无 uni.setStorageSync。真机分发不能用本机硬编码 device_id。修:settings 页输入 + setConfig 持久化 storage + getConfig 读回退 default。— apps/df-miniapp/src/config.ts
  • P1-A-260622 [P1🔴·可靠性] — 心跳 pong 闭环缺失ws.ts:226{control_kind:'ping'},relay.rs:355 miniapp 入站全标 MessageKind::Command 透传 device,但 ping 无 cmd 字段 → remote_bridge.rs:143 match 无 ping 分支 → device 收不到有效命令无 pong 回;useAiChat.ts:440 onControl 仅 console.log 无 pong 处理;ws.ts:245-257 重连仅靠 onClose/onError 无心跳超时。移动网络 TCP 半连接高发,device 静默挂死无感知。修:relay 解析 ping 构造 Control 类型 + device 响应 pong + miniapp 维护 lastPongTime 看门狗(3×interval 未收 pong 主动重连)。— ws.ts + relay.rs:handle_inbound_text + useAiChat.ts
  • P1-B-260622 [P1🔴·首屏连不上·2026-06-23核验已修] — 握手判定脆弱ws.ts:185 text.includes('"error"') 字符匹配判握手失败(真机业务消息含 error 字符误判);同时 ws.ts:181 注释「relay 无显式 ack」错——relay.rs:255 实发 {control_kind:"hello_ack"} 但代码漏检。修:handleHandshakeResponse 改结构化解析——判 msg.kind==='control' && msg.control_kind==='hello_ack' 为成功,error 判 msg.error 字段存在性。— apps/df-miniapp/src/api/ws.ts
  • P1-C-260622 [P1🔴·体验阻断·2026-06-23 核验已修] — 冷启动不恢复会话历史App.vue:8-10 onLaunch 仅 connect,chat/index.vue:62-64 onShow 仅 scrollToBottom,useAiChat.ts:61 activeConversationId 初始 null → 真机首次打开空白。修:连接成功后 refreshConversations;chat onShow 中 activeConversationId=null 且有会话时自动选最近会话 + switchConversation(load_messages)。— App.vue + chat/index.vue + useAiChat.ts
  • P1-E-260622 [P1🟡·多轮场景·2026-06-23] — continueLoop/stopLoop 无 UI 入口useAiChat.ts:699-700 已导出,useAiChat.ts:241-250 AiMaxRoundsReached 仅 clearWatchdog + 系统气泡「请去桌面端处理」,chat/index.vue 全文无调用。多轮场景用户真机无法继续/停止。修:max rounds 时 chat 渲染继续/停止按钮调 continueLoop/stopLoop。— apps/df-miniapp/src/pages/chat/index.vue

P2(UX/健壮性):

  • P2-B-260622 [P2·quickfix·2026-06-23核验已修] — deviceOnline 离线不复位useAiChat.ts:76-77 注释自认「MVP 不做超时复位」,全 src 无 deviceOnline.value=false,onStatus 断开/重连只 clearWatchdog。device 断线状态滞后。修:onStatus 回调 disconnected/reconnecting 加 deviceOnline.value=false(一行)。— useAiChat.ts
  • P2-C-260622 [P2·quickfix·2026-06-23] — 路由表注释陈旧(双侧)types/relay.ts:86 称「8 条」实际 match 10 条;remote_bridge.rs:13-25 注释列 9 条实际 10(F-#95 扩展 list_conversations/load_messages 未同步注释)。修:双侧注释补 list_conversations/load_messages + 数字改 10。— types/relay.ts:86-88 + remote_bridge.rs:13-25

P3/defer:

  • P2-A-260622 [P2·defer·2026-06-23] — messages 内存态无上限(OOM)useAiChat.ts:56 reactive 无 MAX_MESSAGE。联调短会话无风险,长会话再做(MAX_MESSAGES=100 + truncate 保留最新 50)。— 已修:watch trim MAX_STORE_MESSAGES=500(渲染另有 visibleMessages slice 200 兜底)。
  • P3-A-260622 [P3·defer·2026-06-23] — 输入框单行非 textareachat/index.vue:176-184 <input type="text">。MVP 单行可接受,多行场景再升级 textarea auto-height。— 已改 input→textarea auto-height(max-height 120px + flex-end 对齐;⚠️ 原生层级遮联想浮层风险待用户实测,fallback cover-view)。

已驳回(refuted·不记 todo):regenerate 双 user 气泡(UI 展示层非逻辑错,switchConversation 即清)/ 看门狗 130s(实现正确无需修)/ 审批卡+流式光标(完成项核对,非问题)。

真机分发前置序(P1 按依赖):P1-D(token/配对)→ P1-A(心跳闭环)→ P1-B(握手解析)→ P1-C(冷启动恢复)→ P1-E(max UI)。P2-B/C 可同批 quickfix(注释对齐 + 一行复位)。

用户补充核验(2026-06-22 用户详读源码+设计文档,主代逐项核源码判真伪):

⚠️ 用户分析 P0 三项(协议三重不匹配 / EventBus 未接 / 桥接层占位)经源码核验全部失实,系基于 Phase3 阶段1-3 落地前的设计文档「计划态」描述,未核当前代码。证据:

  • P0-1 协议:crates/df-tunnel/src/tunnel.rs:457/327/424 tunnel 纯透传 payload Value 上抛 on_command,from_value::<TunnelCommand> 强类型反序列化(方案A 阶段1 已落地 commit 0fb5af5)→ miniapp {cmd,args} 反序列化在桥接层 remote_bridge.rs:96 from_payload 成功,非阻断。tunnel.rs:457 注释自证「不反序列化为 TunnelCommand 强类型」。
  • P0-2 EventBus:src-tauri/src/lib.rs:128 state.ai_event_bus.subscribe()(tunnel subscriber 已接)+ remote_bridge.rs:303/332/358/388 + commands/ai/mod.rs:209/225 publish_event 跨端透传。已双写接入(todo line 760 commit 0fb5af5/612de4c emit 双写 chat19/audit12/guard3/stream_recv2/agentic18)。event_bus.rs:53/115#![allow(dead_code)]+「骨架阶段」注释是过时历史标注(publish_event 现有调用方 remote_bridge/mod),非未接入。
  • P0-3 桥接层:remote_bridge.rs 完整实装(handle_remote_command:121 + MiniCommand:83 + from_payload:96 + match 路由 10 条 + route_list/load_messages publish_event)+ 已 wire lib.rs:150-154 on_command = Arc::new(...handle_remote_command...) + :171 .connect(&relay_url,&device_id,&token,on_command.clone())。非占位/设计态。remote_bridge.rs:40-42#![allow(dead_code)]+「零调用方」注释写于联调前,现 lib.rs 已调(过时标注,待清)。

属实补充项(用户提出 + 主代核源码确认,追加 todo):

  • P1-F-260622 [P1🟡·2026-06-23] — 消息操作缺失(复制 / regenerate UI 入口)regenerate()(useAiChat.ts:520)已实现但 chat 页无入口;复制消息功能全缺(桌面端有 hover 操作栏)。修:消息气泡加长按/点击操作(复制全文 / regenerate)。— apps/df-miniapp/src/pages/chat/index.vue
  • P1-G-260622 [P1🟡·体验·2026-06-23] — Markdown 渲染简陋MdView.vue 用 rich-text 无代码高亮 / 图片预览 / 链接拦截(MdView.vue 注释自认弱点);流式无节流(注释承认「长回复卡顿后续 P1 加 setTimeout 节流」)。修:评估 mp-html CLI local import <node> 子组件不渲染问题的修复,或换方案 + 流式 setTimeout 节流。— apps/df-miniapp/src/components/MdView.vue
  • P2-D-260622 [P2·健壮·2026-06-23核验已修] — 断连期间操作静默丢失wsClient.send() 失败仅 console.warn + 返 false,但 stop()/approve()/authorizeDir() 忽略返回值 → 用户点「批准」UI 已乐观移除 pending 但命令未发出。修:这些方法检查 send 返回值,失败 toast 提示 + 不移除 pending。— apps/df-miniapp/src/composables/useAiChat.ts
  • P2-E-260622 [P2·健壮·2026-06-23] — 审批乐观移除无失败回退。approve() 即 splice pending,若 device 端 tool_call_id 已被桌面端处理致审批失败,miniapp 已移除 UI 误导用户以为成功。修:approve 失败(超时 / AiError 事件)回填 pending。— useAiChat.ts
  • P3-B-260622 [P3·真机前置] — manifest appid 占位PLACEHOLDER 无法真机调试,需微信公众平台注册的真 appid。— apps/df-miniapp/src/manifest.json
  • P3-C-260622 [P3·清理·2026-06-23 核验已清] — mp-html 死依赖残留。package.json 仍引 mp-html@^2.5.2 但 MdView 已改 rich-text 弃用。确认不回退 mp-html 则移除依赖(当前 src/components/mp-html/ 预留保留,依 dead-code-reserve-keep 待回退决策)。— apps/df-miniapp/package.json核验:package.json 已无 mp-html + grep 全 src 零引用,已清(陈旧 todo)
  • P3-D-260622 [P3·体验·2026-06-23] — token 用量 UI 缺失tokenUsage 状态有但页面未展示。— apps/df-miniapp/src/pages/chat/index.vue

2026-06-23 miniapp 全功能审查加固(workflow wll7qabgr + 主代串行)

6 区域并行审查(chat/会话页/useAiChat/连接层/组件/工程;2 区域限流主代补审)。自主实施(逐批 vue-tsc 0 + build DONE 核验):

  • F1 AiConvStateChanged idle/error 终态漏 clearWatchdog → 补 clearWatchdog(兑现注释承诺,防 timer 泄漏假超时)
  • F2 regenerate() 无 generating 守卫 → 加守卫 + toast(防 send 静默 return 无反馈)
  • F3 flushCurrentText 启发式回填错位 → 引入 currentAssistantMsgId 按 id 精确回填(治 retry/error/compressed 气泡穿插污染)
  • F9 stop() 无终态兜底 + chat 页无停止按钮 → stopping 标志乐观复位 + 忽略迟到 delta + 生成中显「停止」按钮(原 generating 时 send 禁用且无停止入口,用户无法中断生成)
  • F21 连接状态裸枚举字符串 → 中文文案 + 断开/重连中点状态栏手动重连
  • switch 加 default warn / ws.ts 二进制帧 warn / scheduleReconnect 加 MAX_RECONNECT_ATTEMPTS=20 + 手动重连复位计数
  • 删 pages/test 死页(uniapp alpha tap 排查探针,grep 零引用)

06-22 走查项核验销账(源码已修,标 [x]):P2-B deviceOnline 复位 / P2-D 断连操作 send 返回值检查 / P1-B 握手结构化解析

06-22 项部分缓解(alternative approach):P1-A 心跳 pong 闭环 → 当前用「任意入站消息」活性检测(HEARTBEAT_DEAD_FACTOR)替代 pong 往返,半开死连接已有兜底;pong 精确闭环见 MINIDEC-260623-06 ⏸️ / P1-C 冷启动恢复 → active conv id 已持久化 + 握手发 list_conversations;冷启动自动 load_messages 待核

剩余开放项 → 待决策(见 待决策.md df-miniapp 组):MINIDEC-260623-01 配置层(=旧 P1-D)/ 02 配对 / 06 心跳历史协议 / 07 regenerate+备份组件漂移。(03 重连续流 / 04 审批双源 / 05 会话页重命名 已实施,见下)。06-22 走查剩余 [ ]:P1-A(缓解)/P1-E(MaxRounds UI)/P1-F(复制+regenerate UI)/P1-G(Markdown 渲染)/P2-C(注释)/P2-E(审批回填)/P3-A/B/C/D。

2026-06-23 miniapp 用户需求批(req1/2/3 · 用户决策后实施)

用户 3 要求:(1) 会话重命名对齐桌面端 (2) 断网消息不丢失+从远端拉完整 (3) 审批断网重连恢复卡片状态。逐批 cargo check 0 + vue-tsc 0 + build DONE 核验。

  • req1 会话重命名:remote_bridge rename_conversation 路由(调 ai_conversation_rename + 推 AiConversationList 刷新)+ useAiChat renameConversation(乐观本地更新)+ conversations/index.vue 长按 uni.showModal 编辑。
  • req2 断网不丢消息:useAiChat syncOnConnect —— ws 'connected' 触发发 load_messages 拉当前会话完整历史(断网 missed 消息恢复 + 兼修冷启动空白 P1-C-260622)。
  • req3 审批重连恢复:remote_bridge sync_pending 路由(读 AiSession.pending_approvals 按 conv 过滤,重发 AiApprovalRequired/AiDirAuthRequired)+ useAiChat onStatus 'connected' 清 pendingApprovals + 发 sync_pending 重建 + handleEvent 同 id 去重防竞态。审批卡改从 pendingApprovals 独立面板渲染(与 messages 解耦,避重连 load_messages 替换 messages 的竞态;落地 MINIDEC-04 单一渲染源决策 a),内联工具卡保留状态徽标/参数/结果。
  • P1-C-260622 冷启动恢复 顺带修复(syncOnConnect 初始连接也拉历史 + active conv id 已持久化)。

2026-06-23 miniapp 🟡 收尾批(P1-E/F + P2-C/A + P3-D/C · 自主推进)

用户定「自用」后低风险收尾项一次性做完。逐项 vue-tsc 0 + build DONE 核验。

  • P1-E MaxRounds 继续/停止:useAiChat maxRoundsActive ref(AiMaxRoundsReached 置 true,continueLoop/stopLoop/send/switch/new 清)+ chat 「已达最大轮次」面板(继续一轮/停止按钮)。
  • P1-F 重发 + 复制:chat 输入栏「重发」按钮(调 regenerate,非生成中且有消息时显)+ 消息 @longpress 复制全文(uni.setClipboardData)。
  • P3-D token 用量:顶栏显 🪙 total(ai.tokenUsage)。
  • P2-C 注释陈旧:relay.ts 路由表 12→14(+ rename/sync_pending)+ remote_bridge.rs 表头补两行。
  • P2-A messages 内存上限:useAiChat watch trim MAX_STORE_MESSAGES=500(渲染另有 visibleMessages slice 200)。
  • P3-C mp-html 死依赖:核验 package.json 已无 + grep 全 src 零引用(陈旧 todo,无需改)。
  • MINIDEC-07 随 P1-F 选 a(regenerate 加 UI 不删)+ 备份组件保留预留,已落。

剩余 :P1-A 心跳 pong(已缓解=MINIDEC-06 暂缓)/ P3-B manifest appid(真机前置,需注册)。(P1-G/P2-E/P3-A 见下 polish 批已落)

2026-06-23 miniapp polish 批(workflow wll377z2c 设计 → 串行实施)

workflow 3 agent 并行设计(无限流)→ 汇总排序 → 主代串行实施,逐项 vue-tsc 0 + build DONE。

  • P2-E 审批失败回填(low):useAiChat lastApprovedTc ref —— approve(approved=true) 记录,device 返 AiError 回填 pendingApprovals + toast「审批操作未生效,请重试」让用户重试;AiApprovalResult/AiToolCallCompleted 确认 → 清;切会话/新建/断连 → 清。
  • P1-G Markdown 渲染(medium):新建 utils/mdRenderer.ts —— marked HTML 后处理注入 inline style(pre 深底等宽/code 橙色/a 蓝下划线/table border),规避 marked renderer v18 签名风险(rich-text 不认 class)。chat/index.vue renderMd + MdView.vue 接入。链接跳转 rich-text 不可行( 节点事件屏蔽)降级展示+长按复制,跳转留 mp-html 后续。
  • P3-A textarea 多行(high):input→textarea auto-height(max-height 120px + .bar flex-end 对齐)。⚠️ 原生层级遮联想浮层风险待用户实测(textarea 原生组件渲染在 WebView 之上,可能遮盖 .mention-pop);若遮挡严重,fallback 联想浮层改 cover-view(单独任务,本次未做)。

miniapp 自用阶段功能全部闭环。剩 P1-A(暂缓)/P3-B(appid 真机前置,需你注册)。

与主走查重复(用户项 = 已登记,跳过):用户 P1-5(配置硬编码)= P1-D / P1-6(MaxRounds UI)= P1-E / P2-11(deviceOnline 不复位)= P2-B / P2-9(R1 并发防护)= remote_bridge.rs:27-33 check_generating_reject 已实现(send_message 路由前置 generating 检查)。


2026-06-24 aichat 可靠性修复(消息重叠×5 + 授权弹窗卡死 · 诊断workflow→论证workflow)

用户「从历史消息找需求逐一比对」→ 捞出反复报告未根治的可靠性 bug。诊断 workflow(4 agent 只读核验) 定根因 → 论证 workflow(4 角度:正确性/回归/完整性/边界) 验正确性+完整性 → 补 2 处(完整性高优+回归中优)。vue-tsc 0。

BUG-260624-01 消息重叠/堆叠根治(用户 2d89921a×3/d056b5ad×2 共 5 次报告):

  • 根因:currentText 全局单例 + flushCurrentText 末尾盲写 + MessageList streamingBlocks(:69 实例级)跨轮/跨会话残留 + 渲染 isLastAi&&currentText 非 streaming 态也驱动。
  • 修复(4 处协同,治本非补丁):① useAiEvents flushCurrentText 回填后自清 currentText(消除调用方清空顺序依赖)② MessageList currentText watch 归零清 streamingBlocks+lastStreamText(新轮)③ 1011 v-if 加 streaming(堵非 streaming 残留)④ activeConversationId watch 切会话清 streamingBlocks+lastStreamText+cancelAnimationFrame(治 switchConversation/restoreGeneratingState 残留根因,论证完整性高优)。

BUG-260624-02 授权弹窗卡死根治(用户 8e4944b7/b1f644a7「没审批直接死了」):

  • 根因:统一审批开关 df-ai-unified-approval 默认开 → path 审批走 ToolCard 内联,但同名工具≥2 分组默认折叠(group-hidden display:none)→ 审批卡不可见 → 5min 超时(APPROVAL_TIMEOUT_MS)静默 deny → 误显「用户拒绝」卡死。ToolCard 根 :5 动态拼 ai-tool-card--'+tc.status 已挂 pending_approval class(scrollToFirstPending 可命中)。
  • 修复:useAiEvents AiDirAuthRequired/AiApprovalRequired case 内 emit('ai-pending-arrived');ToolCardList onMounted listen → scrollToFirstPending(自动展开折叠组+scroll,原仅手动徽标触发)+ 卸载竞态 disposed 标志(论证回归中优)。

诊断推翻的误判(anti-contamination 印证):压缩「未降prompt」= 指标口径(accumulate_tokens 跨轮累计非单轮;压缩真省,is_active 白名单剔除 compressed) / generating 状态机 = 已落地(RAII guard guard.rs + enum 视图 session_state + per_conv,原创建对话硬拦已删)。task #120 关闭。

剩余低优 :

  • BUG-260624-01 残留:scrollToFirstPending 去抖(同 toolCallId 不重复 scroll,防多 pending 连发滚动打断)+ 全实例广播过滤(props.toolCalls.some 跳过无 pending 实例,大列表性能)
  • BUG-260624-02 残留:审批超时倒计时 UI(5min 无倒计时,ToolCard pending 态显 mm:ss 到期前 60s 变红)+ 核 detached 窗口 emit 作用域(分离窗口是否独立 startListener,否则 ai-pending-arrived 收不到)
  • MessageList:503 badge scrollToFirstPending return 过早(既有 bug,遍历实例应找含卡的那个,不首个 return)
  • F-09 根因:streaming/currentText 全局单例改 per-conv(根治多会话串扰,见 docs/待决策.md)