优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)

This commit is contained in:
2026-06-18 22:57:19 +08:00
parent 0ca5d9805f
commit a2871a66e0
87 changed files with 5720 additions and 3012 deletions

View File

@@ -0,0 +1,254 @@
# Todo 归档 2026-06-18
> 从 docs/todo.md 迁移的已完成项
> 归档日期: 2026-06-18
> 来源: 三文档巡检归档 (todo.md/待决策.md/待审查.md)
---
## 已完成功能需求 (F- 系列)
### 2026-06-16 新需求 (已实施)
- [x] ✅(batch38·2026-06-16·workflow wx5vd8ppk) F-260616-01 [P2] — **aichat Agentic 最大循环轮次支持配置Settings**。已落地(1:1 复刻 llm_concurrency,8 处改动全闭环)。
- [x] ✅(batch37·2026-06-16·workflow wg8x18y9y) F-260616-02 [P2] — **知识库「待审」(pending_review) 不应进第一个 tab知识库/library**。决策 a 落地:inbox 收 candidate+pending_review,library 纯 published。
- [x] ✅(batch44 后端 + batch45 前端·2026-06-16) F-260616-03 [P2] — **aichat 达最大轮次时询问「是否继续」+ 确认后续 loop**。全闭环:后端(batch44 AiMaxRoundsReached 暂停态+continue/stop IPC)+ 前端(batch45 操作卡 pendingMaxRounds+续/停按钮)。
- [x] ✅(batch42·2026-06-16·workflow w95hzp9tw) F-260616-04 [P1] — **run_command 超时→LLM 重试→反复审批循环**。已根治(tool_registry.rs 超时标注「勿盲目重试」+ DEFAULT_RUN_COMMAND_TIMEOUT_SECS 常量,handler map_err 改写)。
- [x] ✅(batch53·2026-06-16·workflow wbh8mu1iq·**run_command 循环核心解决**) F-260616-05 [P1]**共性根治**)— **agentic loop 工具调用去重缺失**。**batch53 落地 High risk 审批前去重**(audit.rs find_cached_high_risk_result 反向扫 messages + args JSON 规范化匹配 + 排除 pending 占位 + decided_by=auto_dedup,解决 run_command 反复审批循环,F-04 超时标注 + batch53 去重双保险)。
- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-06 [P2] — **「未找到挂起的审批」体验**。**全闭环落地**:①`commands.rs:217` ai_approve 幂等——内存 pending_approvals remove None 时查 `ai_tool_executions` 审计表,已 executed/rejected/failed 返回「已处理({status})」非报错 ②`useAiSend.ts:305` 审批按钮防抖——`_pendingApprovalIds` Set 守卫,同 id 短期多次点击只发一次 IPC,finally 块清理。
- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-07 [P2] — **流式对话失败自动重试**。**全闭环落地**:①`agentic.rs` stream_llm 调用处包重试循环(max_retries 形参,退避 1s→2s→4s,持 permit 防挤占) ②每重试 emit `AiStreamRetry` 事件(attempt/max_attempts) ③耗尽走原有 guard.reset+return ④state.rs 加 `agent_max_retries:Arc<AtomicUsize>`(默认3) ⑤新 IPC `ai_set_agent_max_retries`(clamp 0-10) ⑥前端 api/ai.ts + types.ts(AiStreamRetry variant) + Settings.vue(input 0-10+debounce sync+i18n zh/en)。
- [x] ✅(batch56·2026-06-16·workflow wsxxurgbr+主代核查) F-260616-08 [P2] — **AI 数据获取工具 offset 分页 + 探总量**。**全闭环落地**:①6 工具(list_projects/tasks/ideas/trash/read_file搜索模式/search_files)全部补 offset+limit 参数,返回 items/total/has_more 替代 truncated ②新增 `get_project_count`+`get_task_count` 探总量工具 ③knowledge_search 解钳 `min(3)``min(20)`
- [x] ✅(batch58·2026-06-16·workflow w2dw65maz+主代核查,vue-tsc 0err) F-260616-10 [P1·bug] — **provider 切换/默认值重启·刷新后失效(前端 activeProvider 不同步 DB 默认)**。用户实测Settings 设 A 为默认 → 重启 → 默认变 B(列表第一个)。**根因全在前端,后端链路正确**(独立核验不信 PROGRESS.md)`ai_set_provider`(`commands.rs:747-774`) 互斥落库✅ + `update_full`(`crud.rs:1127`) SQL UPDATE 含 `is_default`✅ + `get_active_provider`(`prompt.rs:9-32`) 走 `DB.is_default`✅。**根因链**:①重启后前端 `store.activeProvider = null`(`stores/ai.ts:53`,无 localStorage 持久化 + 无启动恢复) ②`loadProviders`(`useAiPanel.ts:91-93`) 只拉列表**不同步 DB 默认** → activeProvider 仍 null ③**UI 错觉**`activeProviderName`(`AiChat.vue:1169-1172`) null 时 fallback `providers[0]`(列表第一个) ≠ 用户设的 A → 「默认变另一个」 ④**真覆盖**:用户点 provider bar 切换 `cycleProvider`(`AiChat.vue:1180-1185`)`findIndex(activeProvider=null)` 返回 **-1** → `next = ps[(1+1)%len] = ps[0]` = 列表第一个 B → `setProvider(B)` → 后端互斥落库把 `DB.is_default` 从 A **真改成 B**
- [x] ✅(batch60合批·2026-06-16·主代核查,cargo 0err) F-260616-11 [P1·决策a累计计数] — **审批后 iteration 重置致 max_iterations/token 超配**。决策(2026-06-16):**a 累计计数**(try_continue 不重置 iteration,跨审批续跑延续累计)。**已实施**:`iteration_used:usize` 累计字段(`mod.rs:215/230` AiSession+new init 0)+`start_iteration:usize` 参数(`agentic.rs:114` run_agentic_loop signature +`:176` loop 边界 `start_iteration..max_iterations` +`:210` 一致性块 `iteration_used=iteration+1` +`:578` try_continue 加参 +`:675` spawn 透传)+三处 reset(`commands.rs:55/152/412` ai_chat_send/ai_regenerate/ai_edit_last 新生命周期)+ai_approve 两处累计读 iteration_used(`:260-262` 拒绝续跑 +`:313-315` 通过续跑)+`ai_continue_loop:601` reset+传0(F-03 决策a 达max重计区分)。两路径严格:审批累计(ai_approve) vs 达max重计(ai_continue_loop,F-03 a 用户授权重来) vs 新消息 reset。
- [x] ✅(batch60·2026-06-16·workflow whae812z5+主代核查,cargo 0err) F-260616-13 [P2] — **build_for_request 持锁重活 + system_prompt token 每轮重估**。性能分析批次发现:每轮 `stream_llm``session_arc.lock()``agentic.rs:251`)持锁期间做 `TokenEstimator::estimate_text(system_prompt)`L252+ `build_for_request`L253 history clone + 裁剪。system_prompt loop 外固定传入,**每轮重估其 token 是浪费**可缓存build_for_request 持锁做 history clone 是重活消息多时200 cap锁持有期长。**方向**①system_prompt token loop 外算一次缓存 ②build_for_request 先 clone messages 释放锁再裁剪。低收益优化。
- [x] ✅(主代核验·2026-06-16) F-260616-14 [P2/核验] — **max_tokens 8192 截断 + 前端批量审批核验**。核验结论:①`max_tokens=8192`(实际 `agentic.rs:270`≈6-8k 字日常够用,长回复截断由 batch59 MidStream 保文兜底不丢文,**可配低优先非阻塞**(登记可选优化)②前端批量审批 **AE-2025-01 已完整闭环**ToolCardList.vue:4-11 `ai-batch-approve` 栏 + AiChat.vue:444 `@batch-approve``store.batchApprove` + useAiSend.ts:338 遍历 pendingApprovals 逐个 approveToolCall + i18n approveAll/rejectAll满足「一轮多 Med/High 全 insert pending→一次批量批全部」③audit.rs:496 `process_tool_calls` 一轮多工具 Med/High 进审批门控 insert pending + Low 并行执行链路通。无阻塞 bug销账。
- [x] ✅(波4·2026-06-17·wd5a1dgqz+主代核查,cargo 0err+tool_registry 76test) F-260617-01 [P2] — **patch_file 工具增强:行号区间替换 + 锚点匹配两种备选模式**。当前 patch_file 仅支持 `old_text` 精确匹配对超长行todo.md 单行 1000-2000 字符)+ 含大量反引号/引号/中文标点的场景匹配极易失败JSON 转义错位一个字符就全盘失败)。新增两种可选模式作为备选。
- [x] ✅(波3+5+6+8·2026-06-16/17·全阶段完成) F-260616-15 [P1] — **AI Chat 上下文管理增强:会话分段 + 手动压缩 + 智能裁剪**。三个子需求,详见 [F-15-上下文管理增强设计-2026-06-16.md](./02-架构设计/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 全阶段完成 ✅**。
### 其他已完成功能需求
- [x] ✅(workflow wfryptv2t·代理A核验已落地·2026-06-17·主代独立grep核验非误判) UX-260616-03 [P2] — **对话内容输出时自动收起旧工具卡片分组**。**已落地**:①触发时机扩展——AiChat.vue:2093 agentRound watch(AiAgentRound 新轮次→collapseAllToolLists(buildActiveToolIds))收起上一轮;既有 messages deep watch(:2085)覆盖新消息追加+toolCall状态翻转;触发链 useAiEvents.ts:169 `if(event.round>0) state.agentRound=event.round` + stores/ai.ts:75 agentRound字段。②collapseInactive增强——ToolCardList.vue:71 userExpandedCards 记忆态 Set,:180 isCardExpanded 优先读它(用户主动展开不被自动收起),:220 collapseInactive 仅清 expandedCards/expandedTools 不清 userExpandedCards。
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a逐条续发) UX-260616-05 [P1] — **打断按钮只停当前回复 + 队列保留续发(不清队列)**。用户需求:发送队列有消息时,打断按钮应只打断当前回复,并把队列中的多条消息一并发送给后续对话。技术底座已就绪(已核后端):`stopChat``aiApi.stopChat`→后端 `stop_flag.load`(agentic.rs:178)→收尾 `save_conversation`(:185 已生成文本入库)+emit **`AiCompleted`**(agentic.rs:190注释明确「保证前端收事件时后端已可接下一条发送队列续发不被拒」)→前端 `ai-drain-queue``drainQueue`(useAiSend.ts:228) shift 队首续发。**最小修复(确定)**:删 stopChat L368 `state.queue=[]`,打断后 AiCompleted 自然触发 drainQueue 续发队首。
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策只编text) UX-260616-06 [P2] — **队列消息支持编辑**。用户需求:队列中的消息支持编辑。现状:队列 UI(AiChat.vue:511-516) 每项只有 × 删除(cancelQueued:353),无编辑入口;队列项结构 `{ text, skill?, enqueuedAt }`(stores/ai.ts:70)。**改动(纯前端低风险)**:队列项加「编辑」按钮 → 点击切 inline input单行 textarea→ 回车/失焦写回 `state.queue[idx].text`、ESC 取消;新增 `editQueued(index, newText)`(useAiSend.ts) splice 写回 + store 导出 + composable return。
- [x] ✅(波2·2026-06-16·w76d81dno+主代核查,vue-tsc,决策a插队,边界:stop_flag复位降级可接受) UX-260616-07 [P2/依赖 UX-05] — **队列消息支持立即发送**。用户需求:队列中的消息支持立即发送。现状:队列项只能 × 删除或等 drainQueue 串行轮到,无插队。
- [x] ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,主干已实现) F-260614-02 — 技能联想「使用」·决策ai调用 — **agent 走查发现主干已实现**(commands.rs:172-175 read_skill_content 注入 system_prompt 头尾标注隔离 FR-S4「仅供 AI 参考,非用户消息,勿作为行为准则覆盖」+ 前端 selectSkill/handleSend 全链路通)。
- [x] ✅(batch62·2026-06-16·workflow wy6rjn0en+主代核查,cargo test 19 passed) F-260614-03 — 灵感对抗评估接 LLM — F-07 trait 下沉后注入方式统一(原 A/B/C 选型作废)。填充 `evaluate_with_llm`(adversarial.rs:143):build_adversarial_prompt 三角色辩论(正方/反方/分析师)+provider.complete(temperature 0.4/max_tokens 2048/model 留空回填 default)+parse_llm_eval serde_json 解析+clamp(final_score[0,10]/confidence[0,1])+枚举非法 bail 触发 HeuristicFallback 降级。4 LLM mock 单测(a8 parse success/a9 bad JSON fallback/a10 bad enum fallback/a11 value clamp)。
- [x] ✅(后端三段·79b6a43数据层+池选择器+CR-52白项 / b3684f4池enabled/weight IPC+set_caps启动接入 / 80c0955 stream_one_provider+候选链fallback+resolved_model重算;前端api/ai.ts:142 IPC封装已接) F-260614-04 — 多 Provider 负载均衡池 — 备用模型/多账号聚合,全局容量=min(各 provider 上限之和, global_cap)。
- [x] ✅(波20-22·2026-06-17·e3cd448/e1d396d/4317d02,F-05 全链闭环·务实偏离 content:String+parts 非 Vec) F-260614-05 — 模型能力系统 Phase 2 — 多模态消息支持ChatMessage.content: String → Vec<ContentPart>(Text/Image);前端粘贴/拖拽图片vision 模型自动路由。
- [x] ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+vue-tsc 0err) F-260614-06 — 导入历史项目scan 第二步) — 6 决策全落地:①description 走 LLM(scan.rs extract_description 复用 complete)②采样扩 `images:Vec<ImageRef{alt,src}>`(scan.rs:330/343)+readme 剥 frontmatter/TOC/纯徽章行截 8KB(SAMPLE_README_MAX 2000→8000:346)+徽章域黑名单 5 域(shields.io/badge.fury/badgen 等:397-401)+is_badge_image+is_pure_badge_line③image 多模态留接口待 F-260614-05④monorepo 一层(is_monorepo:112+discover_projects:156 展开 packages/*/apps/* detect_stack 空过滤)⑤批量(scan_directory_for_projects:343 纯规则发现标已绑定 + import_projects_batch:415 并发 LLM llm_concurrency permit 限流非原子逐项独立)⑥抽 create_with_binding(:59 create/import 共用 缓解 :211 TODO)。
- [x] ✅(batch61·2026-06-16·workflow wwtn2knn6+主代核查,cargo 0err+7单测) F-260614-07 — **[架构前置]** df-ai-core trait 下沉拆 crate — 4 决策全落地:①df-ai-core 新 crate(仅 trait+数据结构 LlmProvider+ChatMessage 等,ContextManager/TokenEstimator 留 df-ai)②构造注入 Engine::new(Arc<dyn LlmProvider>)+Engine::heuristic()(idea.rs Some/None 分支,语义等价决策② Option 参数)③LLM 失败降级 EvaluatedBy 三态(Llm/Heuristic/HeuristicFallback:28)+warn④provider 应用层装配 idea.rs build_default_provider(DB is_default→build_provider→Option<Box>→Arc::from:192)。
- [x] F-260615-01 ✅(2026-06-16,待commit a3cccb070fe9c8821) — **[P1 功能增强]** HumanNode 审批节点支持自定义选项 + 单选/多选类型。现状config `options: Vec<String>` 已支持任意数量2/3/…数量扩展已通),但 `decision` 是单 `String` 仅单选语义,`options` 空=自由文本。
- [x] F-260615-02 ✅(2026-06-16,待commit) — **[P1 功能]** task 详情查看。✅已实施:get_task_by_id IPC(复用 TaskRepo::get_by_id ok_or_else 转 Result)+lib.rs 注册+taskApi.get+/tasks/:id 路由+TaskDetail.vue(11 字段:标题/描述/状态/优先级/关联项目 router-link 解析名/分支标签/负责人/基础分支/工作流定义/创建更新时间;复用 constants/project 标签+formatDate+watch route.params.id 重载)+Tasks.vue 列表项 @click router.push。
- [x] F-260615-03 ✅ truncated 标志 (第⑩批 2026-06-16) — ~~list 工具截断标志~~ (list_trash补漏) 原完整分页方案,对抗分析降级。
- [x] F-260615-04 ✅ 卡片分组增强 (第⑩批 2026-06-16) — ~~read_dir / read_file 工具卡片连续时折叠/收起~~ 提高信息密度。
- [x] F-260615-07 ✅ search_in_file (第⑩批销账 06-15) — ~~read_file 加 search 参数~~ 已落地。
- [x] F-260615-13 ✅ 复制按钮 (第⑩批 2026-06-16销账) — ~~消息气泡复制按钮~~ 已落地AiChat.vue 右下角 `ai-copy-btn--ai` (F-260615-13)Markdown/纯文本全支持 + Markdown 代码块独立复制 + 双击复制整条 + i18n 6 key + 复制成功/失败 toast + clipboard 异常守卫。
- [x] F-260615-14 ✅ 消息操作栏 (第⑩批 2026-06-16销账) — ~~消息操作栏(复制 / 重新生成)~~ 已落地(操作栏 `ai-msg-actions` 已移除复制按钮(仅留「重新生成」并 gate 到末条 AI 消息),复制统一走气泡内右下角图标 `ai-copy-btn--ai`F-260615-13
### UX 交互改进 (已完成)
- [x] UX-2025-01 ✅ 选文字(文本偏移量方案) (第⑩批销账 06-15) — **流式渲染中选中文字保持**§2.1)。
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-02 [P1] — **消息操作栏(复制 / 重新生成)**§1.2+§1.3)。
- [x] ✅(batch36·2026-06-16·workflow w098aapgd) UX-2025-03 [P1] — **错误气泡操作入口**(error_type 枚举本身 batch34 B-42 已加,本批前端消费)§4.1)。
- [x] ✅(batch59·2026-06-16·workflow wdlt0xfl6) UX-2025-04 [P1] — **断线保文**§4.2。保文核心落地stream_recv.rs `StreamResult` enum(Complete/Partial/InitFailed 三分支)MidStream 失败(流中途 chunk error/idle timeout/有 partial_text 未 finished)返 `Partial{partial_text}` **不重试保文**
- [x] ✅(第⑳批 2026-06-16) UX-2025-05 [P2] — **代码块语法高亮 + 复制按钮**§2.2)。**高亮部分完成**:highlight.js(core+16 语言按需注册,~50-60KB gzip)+ marked.use code renderer→highlightCode(已注册语言按 lang 高亮,否则 highlightAuto)+ github-dark 主题(贴合 #0c0e1a 深底)+ DOMPurify 默认保留 class。
- [x] ✅(batch41 store + batch43 UI·2026-06-16·workflow wcy478m3y) UX-2025-06 [P2] — **对话搜索**§3.1)。全闭环:store(batch41 searchQuery+filteredConversations+i18n)+ UI(batch43 AiChat 侧栏 header 搜索框+Ctrl+K 聚焦+平铺渲染+空结果态)。
- [x] UX-2025-07 ✅(第⑲批 2026-06-16) — ~~键盘快捷键~~ onGlobalKeydown(AiChat.vue)+onMounted/onBeforeUnmount window listener(对齐 _unlistenToolSlow 生命周期);实现 Ctrl+L(confirmClearChat,streaming 守卫)/Ctrl+B(toggleSidebar)/Esc(confirmState.visible→answerConfirm(false)>props.detached→closeDetachedWindow 优先级)。
- [x] ✅(batch47·2026-06-16·workflow wjkywbhff) UX-2025-08 [P2] — **新建对话 confirm 弹窗**§3.4 增强)。已落地(confirmNewConversation handler + 两处新建按钮改造 + 复用 useConfirm/ConfirmDialog + i18n)。
- [x] ✅(batch51·2026-06-16·workflow wdfffj1ti) UX-2025-09 [P2] — **编辑已发送消息并重新生成**§1.1)。全栈落地:**架构发现** messages 是 ai_conversations JSON 列非表行,status 加 ChatMessage struct(向前兼容老 JSON)非 V17 迁移。
- [x] ✅(batch52·2026-06-16·workflow w91svrokz) UX-2025-10 [P2] — **@ 实体引用**§1.4)。全栈落地:AiChat @ popover(复用技能架构 .ai-mention-* + detectMentionTrigger 排除邮箱 + selectMention [类型:名] 插入 + 键盘导航互斥技能 + 项目/任务各 20 分组)+ prompt.rs 任务段注入(state.tasks.list_active,**签名零变化**降级避碰 ai_chat_send 领地)+ i18n 5 key。
- [x] UX-2025-11 [P2] — **输入框高度提升**§1.5。textarea 最大高度 120px→200px~10 行),超过后再内部滚动。或可拖拽 resize handle。
- [x] UX-2025-13 [P2] — **消息时间戳**§2.4)。气泡下方极小字号(dim 色)展示相对时间(复用 time.ts 已有 common.justNow 等 keyhover tooltip 展示完整时间。
- [x] ✅(第㉒批 2026-06-16) UX-2025-14 [P2] — **Provider 切换反馈**§5.1。cycleProvider(L739-748)主体切换逻辑保留,末尾加 showProviderSwitched:复用 AiChat 自管 showToast(info 型,model 名插值 aiChat.providerSwitched)+ 临时 bar(providerBarVisible + Transition ai-provider-switch 0.15s 淡入/淡出 + 2000ms 自动淡出,展示 aiChat.providerSwitchHint model 名小字+accent 脉动圆点)。
- [x] ✅(第㉓批 2026-06-16) UX-2025-15 [P2] — **技能参数用法展示**§5.2)。浮层 .ai-skill-item 改 column:.ai-skill-item-row(name/desc/src 单行)+ code.ai-skill-item-hint(v-if s.argument_hint,等宽 dim 11px,L390)。
- [x] ✅(第㉒批 2026-06-16) UX-2025-16 [P2] — **侧栏宽度可拖拽**§6.1)。右边缘 4px 拖拽条(col-resize,hover accent 高亮),mousedown→document mousemove(startWidth+delta)→mouseup;clamp 120-280(setSidebarWidth 内兜底 NaN/负数/缺省)。
- [x] ✅(batch49·2026-06-16·workflow wkan76fon) UX-2025-17 [P3] — **对话置顶**§3.2)。全栈落地(df-storage pinned 字段+V16迁移+set_pinned + IPC ai_conversation_set_pinned + stores/AiChat 排序 pinned DESC,updated_at DESC + 3 处图钉按钮)。
- [x] ✅(batch46 后端 + batch48 前端·2026-06-16) UX-2025-18 [P3] — **对话导出**§3.3)。全闭环:后端(batch46 ai_conversation_export IPC commands.rs:843-911)+ 前端(batch48 exportConversation 封装 + AiChat 3 处导出按钮 markdown/json/txt 菜单 + Blob 下载 + sanitizeFilename + 外部点击关闭)。
- [x] ✅(第㉒批 2026-06-16) UX-2025-20 [P3] — **空状态引导 + 标题过渡**§7.3+§7.4)。空态:messages 空+非流式时 .ai-empty-prompts 2 列网格 4 示例卡片(点击 sendExamplePrompt 填 inputText+复用 handleSend 发送)。无 provider 显「去设置」按钮(goToSettings:嵌入 router.push('/settings'),分离窗口降级 showToast 提示去主窗口)。标题淡入:titleFlash ref + watch(activeConv.title)空→非空(loadConversations 回填触发)置 flash 驱动 0.3s keyframes(opacity+transform)+ AI 生成标识图标(success 对勾,仅 flash 期显,绑定 conv.id===activeConversationId)。
### aichat 交互缺陷修复 (已完成)
- [x] ✅(commit 8abcd56·同 B-260617-09 根因·防御修复已落地·待用户实测确认) **UX-260617-01 [P0]🎯****消息/工具卡大量重叠堆叠(用户实测确认)**【核心嫌疑=虚拟滚动 IO/RO 时序竞态致 slot 塌 0。**防御修复已落地**(useAiVirtualScroll.ts IO 卸载分支 minHeight 三层 fallback + registerSentinel 首次挂载 40px 兜底)。vue-tsc EXIT 0。**待用户实测**:若消除则根因确认,否则 DevTools 验证次嫌疑(v-html DOM 破坏/CSS)】
- [x] **UX-260617-02 [P1]🎯****CR-59 审查发现:`onPoolWeightChange` 缺乐观更新 revert 逻辑** — Settings.vue:643-656 weight 输入 debounce 300ms 调 IPC成功后未像 onPoolToggle 一样 revert 到服务端值。IPC 失败或延迟时 UI 显示旧值而非回滚。
- [x] **UX-260617-03 [P1]🎯****copyMsgContent 失败误报「已复制」(已记录未修)**`AiChat.vue:1037-1040` clipboard.writeText 权限拒绝 → catch 仍 showToast('copied')。用户见"已复制"去粘贴发现空。
- [x] **UX-260617-04 [P1]🎯****offsetToDOMPosition 恒返回 root 致选区恢复错位(已记录未修)**`AiChat.vue:908-921` createTreeWalker 后 currentNode 初始=root 非 Text → ?? 恒取左 → 循环无 nextNode 推进 → 选区恒错位。UX-2025-01 功能实际失效。
- [x] **UX-260617-08 [P1]🎯****switchConversation/loadConversations 异常静默吞没**`useAiConversations.ts:36-44` catch 为空 + `:117-119` switchConversation catch 仅 `messages=[]` 无反馈。网络抖动/IPC 断开 → 对话列表突然变空/切换后空白用户不知原因。
- [x] **UX-260617-09 [P1]🎯****startListener 吞掉注册异常导致全量事件静默丢失**`useAiEvents.ts:354-373` IIFE 内 `aiApi.onEvent(handleEvent)` 异常被完全吞没(无 console.error/toast/log。所有聊天事件(文本/完成/审批)全丢 → UI 表现"发了消息永远不回"。
- [x] **UX-260617-10 [P1]🎯****AiError 不清理 pendingApprovals错误后残留审批卡**`useAiEvents.ts:326-349` AiError case 做 clearStreamWatchdog/清queue/推错误气泡但**从未清 pendingApprovals**。出错时有 tool_call 处于 pending_approval → 残留可点击审批按钮。
- [x] **UX-260617-11 [P1]🎯****modelOverride 跨对话泄漏**`useAiSend.ts:67` 模块级 ref注释明确要求"前端应同步清 null"但**无任何代码执行此清除**。switchConversation/newConversation 不触碰。对话A选模型覆盖→切对话B→override 静默生效。
- [x] ✅(wf wf_b8b2a7b7 + 主代修缺陷·commit 3055b06) **UX-260617-13 [P1] 已修****刷新后主面板无流式恢复**【抽 restoreGeneratingState() 共享恢复链(分离窗口 resumeInDetached 薄包装委托),AiChat.vue onMounted 主面板分支(detached=false)调 store.restoreGeneratingState({fromMainPanel:true})。**关键修复**:verify agent 发现主面板刷新时 df-ai-gen localStorage 不存在(仅 detachPanel 写),原复用方案读 gen=null 直接 return 无效。加 fromMainPanel 选项绕过 localStorage:用 state.activeConversationId + ai_is_generating 真值守卫恢复(currentText 从空开始,刷新前已生成部分丢失为刷新固有代价,核心恢复 streaming 态+占位气泡消除假死)。幂等守卫(state.streaming 已 true 跳过)+ R4 后端真值守卫保留。vue-tsc EXIT 0】
- [x] **UX-260617-14 [P2]🎯****审批超时 130s 静默复位无反馈**`ToolCard.vue:507,541-54` 超时仅 `approving.value=false`,无 toast/error/日志。用户无法区分"审批完成状态没更新"和"超时复位"。
- [x] **UX-260617-15 [P2]🎯****regenerate/editMessage 丢弃图片 parts**`useAiSend.ts:148-190` regenerate/edit 均无 parts 参数。含图片消息重生成/编辑后图片消失。
- [x] **UX-260617-16 [P2]🎯****无路由离开/关闭保护,未发送输入静默丢失** — AiChat.vue 缺 beforeRouteLeave/onBeforeUnload 守卫。输入框有内容/已粘贴图片时切页面 → 内容瞬间消失无提示。
- [x] **UX-260617-17 [P2]🎯****ToolCardList 分组名硬编码中文绕过 i18n**`ToolCardList.vue:259-274` nameMap 全硬编码中文(`'读取文件'`等)。英文 locale 下分组标题仍显示中文。
- [x] **UX-260617-18 [P2]🎯****isToolFailure() 正则过于宽泛误判合法输出为失败**`ToolCard.vue:220-224` `/执行失败|failed|error[:\s]/i` 匹配任何含这些子串的文本。search_files 结果列 error.log / run_command stderr 含 warning 均被标红。
- [x] **UX-260617-19 [P2]🎯****stopChat 本地先行 reset 与后端竞态窗口**`useAiSend.ts:479-488` 先设 streaming=false 再发 IPC stop。IPC 失败时后端继续生成但前端已停止;若后续事件也丢失则永久卡死。注释承认风险但无缓解。
### 架构审查已修项 (ARC- 系列)
- [x] ARC-260615-01 ✅ wave(2026-06-16,待commit) — ~~删 `stores/settings.ts` mock 死代码~~ grep 验零消费者 + 删文件 + index.ts 清导出 + 清 appSettings.ts:8 过时注释
- [x] ARC-260615-02 ✅(2026-06-16,待commit) — ~~`/decisions` 路由死链~~ 删 nav 项App.vue secondaryNav+ 删 `nav.decisions` i18n keyzh/en。决策治理 = F-260614-08 长期项未实现,入口提前占位成死链;`dashboard.recentDecisions`Dashboard.vue不同命名空间保留
- [x] ✅(决策a·2026-06-16·转架构维护记录无代码) ARC-260615-03 — **重评降级2026-06-16**df-execute **非空壳**——shell.rs `execute()` 已完整实现cmd/sh 跨平台 + kill_on_drop + timeout + env+ 被 tool_registry.rs `run_command` 复用F-260615-05。原 todo「76 行/1 函数 + TODO」基于 lib.rs 空判,过时。合并进 df-nodes 破坏职责分离(节点定义 vs 执行运行时)+ 动依赖树,**转架构维护决策,非清债**
- [x] ARC-260615-04 ✅ wave(2026-06-16,待commit) — ~~`stores/index.ts` 补 `export useAiStore`~~ barrel 补导出(3 处 view 直连可选迁移,非强制)
- [x] ✅(batch29·2026-06-16) ARC-260615-05 — ~~stores/project.ts 上帝 store 拆分(四领域+越层 invoke~~ 核验越层 invoke 0 命中(已下沉 api/workflow.ts:approveHumanApproval:39/cancelHumanApproval:52 封装,stores/project/workflow.ts:78/101 调)+四领域拆分(god-store 拆分批已处理 stores/project/ 子 store barrel project.ts:122-123 委托)。vue-tsc exit 0
- [x] ✅(batch29·2026-06-16) ARC-260615-06 — ~~events↔stream 循环依赖~~ 提 aiShared.ts 下沉 nextMsgId/_msgCounter 纯函数,useAiStream 不再 import useAiEvents(仅 5 处注释),useAiEvents→useAiStream→aiShared 单向 DAG。vue-tsc exit 0
- [x] ✅(batch30·2026-06-16 评估维持现状) ARC-260615-06b [P3] — useAiEvents↔useAiSend 另一环(useAiEvents:19 import useAiSend/useAiSend:23 import useAiEvents),靠事件总线 ai-approval-clear-timers 部分缓解,非本批范围。遗留自 ARC-06 核验
- [x] ARC-260615-08 ✅(2026-06-16,待commit) — **[渲染优化·自研块级 memo 已实施]** 流式 Markdown 渲染——保留 marked+DOMPurify+.ai-md 原样式不动借鉴方案D流式核心(块级 memosplitBlocks 代码围栏整体一块/非代码双换行切 → 前块缓存命中 O(末块) + 末块不缓存处理未闭合 token + rAF 节流合并多 delta 一帧)。退役 AR-1 纯文本短路
### 2026-06-17 走查修复项
- [x] ✅(commit 645f73d) B-260617-12 [P3·可选] — **LEGACY_STATUS_MAP 映射 DRY 重复**。抽 `mapLegacyStatus(status)` 函数收敛两处 `LEGACY_STATUS_MAP[status] ?? status` 一行重复。vue-tsc EXIT 0。—— src/constants/project.ts(:96-98)
- [x] ✅(commit f6e4e82) **🚨 B-260617-14 [P1] 已修** — **74003bc 漏提交 adversarial.rs + project.rs**【补提两文件 reasoning_content: None,HEAD 干净 checkout 编译通过】
- [x] ✅(不做·优化方向错误) B-260617-15 [P2·可选] — **reasoning_content 落库存储膨胀**【核验 DeepSeek API 规范:有 tool_calls 的 assistant 消息 reasoning_content **必须**在后续请求回传(否则 400 "must be passed back")。verify agent "只走顶层"分析错误——实际需 messages 数组回传。落库剥离会导致重新加载对话后历史消息无 reasoning_content → DeepSeek 400(重新引入 BUG-12)。reasoning_content 必须保留在内存+落库+请求全链路,不可优化】
### 2026-06-18 用户反馈交互调整
- [x] **UX-260618-00本批 4 项 AiChat/ToolCard 交互调整)**:
- **AI 气泡复制去重**(UX-260618-01):删 `ai-msg-actions` 内复制按钮(栏仅留「重新生成」+gate 末条 AI `isLastAi`),复制统一走气泡内右下角图标 `ai-copy-btn--ai`
- **run_command 显命令 + 可折叠输出**(UX-260618-02):`toolDisplayName` 加 run_command 分支显 `$ <命令首行截断>`(折叠头部可见)+ body 新增命令行(`$`+完整命令)常显 & 输出可折叠(失败默认展开看 stderr/成功折叠,本地 ref `cmdOutputExpanded`+watch status)。
- **历史消息时间戳全显「刚刚」**(BUG-260618-01):根因 `ChatMessage`(df-ai-core/provider.rs)无 timestamp 字段,后端从未持久化每条消息时间,前端加载历史 `timestamp: Date.now()` 全塞当下。
- **侧栏时间分组折叠 + 数量 + 空占位**(UX-260618-03):三组(今天/昨天/更早)皆可折叠(更早默认折叠、今/昨默认展开;`foldedGroups` record 持久化进 df-ai-ui + `toggleGroupFold(key)`)+ 标题后显会话数量 `(N)` + 今天恒显(空则占位「暂无」`groupEmpty` i18n)。
- [x] **BUG-260618-02本批 3 项模型选择/上下文修复·cargo check --workspace EXIT 0 + vue-tsc EXIT 0**:
- **specify 模式跑成别的 provider 模型**:`agentic.rs:350` `ProviderPool::select` 原传 `None`(无视用户指定),改传 `model_override.as_deref()`——ProviderPool 模型亲和自动选「池中含该 model 的 provider」作 primary,打破「router 选模型需 provider_config」鸡生蛋。
- **测试连接/拉取模型后权重失效**:`commands.rs ai_fetch_models``updated.model_configs = configs.clone()` 直接覆盖,丢弃用户在 Settings 调过的 model weight/enabled/label。改合并:新探测值为主(更新能力维度),按 model_id 匹配保留旧 weight/enabled/label,新模型用探测默认。
- **压缩上下文前端折叠藏消息**:用户诉求=压缩只后端 LLM 省 token,界面不藏消息。后端压缩逻辑正确(compress_via_llm 摘要 + is_active 白名单不发 LLM + DB 保留原文),问题在前端。
### 2026-06-18 6 域并行走查修复项
- [x] ✅(主代修·SW 批·cargo df-nodes EXIT 0) SW-260618-15 [P3] — **ai_node schema required=[provider_id] 过度约束**。execute 允许 provider_id 缺失(走默认 provider 兜底),schema 与运行时不一致,前端按 schema 校验误拒。把 provider_id 从 required 移除。
- [x] ✅(主代修·SW 批·vue-tsc EXIT 0) SW-260618-18 [P3] — **wasNearBottom 模块级切会话不重置**。会话 A 上滑切 B 后首滚到底误触发一次 collapseAllToolLists(收起本身无害但语义偏差)。建议 watch(activeConversationId) 内重置 true。
- [x] ✅(主代修·SW 批·vue-tsc EXIT 0) SW-260618-21 [P3] — **TaskDetail wfResult setTimeout 未卸载清理**。wfResult 终态提示 3s timer,组件卸载后仍触发写已销毁 ref。补 wfResultTimer + onBeforeUnmount。
- [x] ✅(主代修·SW 批·vue-tsc EXIT 0) SW-260618-23 [P3] — **Ideas net-sentiment neutral CSS 缺失**。sentimentClass 返回 'neutral' 但 CSS 只定义 positive/negative,中性无样式。纯样式缺口。
- [x] ✅(主代修·SW 批·部分·cargo df-ai EXIT 0 warning 消除) SW-260618-24 [P3] — **dead_code 基线 6 处 warning**(已标 finish_reason/call_type 2 处 serde 反序列化字段 #[allow(dead_code)] 消除 warning;其余 4 处 session_state/risk_level/diff/releases/node_executions 保留作待办提醒——session_state 待 P0 状态机收敛任务承接,risk_level/diff 待审批卡预览消费,releases/node_executions 待功能模块落地,标 #[allow] 会掩盖待办故保留)(df-ai finish_reason/call_type + mod.rs SessionState/session_state/risk_level/diff + state.rs releases/node_executions)。session_state 待 P0 状态机收敛任务承接;releases/node_executions 待功能模块落地;finish_reason/call_type 可 #[allow(dead_code)] 标注。非本次引入,勿回退。
### 2026-06-18 工作区进展
- [x] ✅(2026-06-18·转向彻底移除·工作区待提交) **B-260618-01** — 原"回退 0ca5d98 恢复虚拟滚动裁剪"**→ 转向彻底移除虚拟滚动**(删 useAiVirtualScroll.ts + AiChat.vue 移除全链路。理由IO/RO 时序竞态致重叠shouldRender 恒 true 时 IO 仍设/清 sentinel minHeight 竞态源未除)+ 消息量级不需要。B-260617-09 真闭环。
- [x] ✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) **B-260618-03****ToolCardList 首卡折叠 bug**`isCardExpanded`ToolCardList.vue`ci===0` 兜底 `return true` 覆盖用户折叠——多卡分组首卡默认展开,用户点折叠后 ci===0 仍 true 重新展开。**修复**:加 `userCollapsedCards` 记忆态,`isCardExpanded` 最优先判断用户折叠。
- [x] ✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) **B-260618-04****ToolCardList 全部展开/收起下沉 + 宽度对齐U-260618**。①global-toggle 从 topbar 下沉到首个可折叠分组标题行尾巴firstCollapsibleGi computed与 run_command 分组标题同行chevron 移除 margin-left:auto②分组标题行加 max-width:var(--df-msg-max-width)(90%) 对齐气泡/卡片宽度(原无 max-width 拉满 100%③global-toggle 移除独立按钮 padding/背景高度=文字行高不撑高标题行。
- [x] ✅(2026-06-18·工作区待提交·vue-tsc EXIT 0) **B-260618-05****AI 独立窗口吸附挤出屏幕 + 宽度偏窄**。①吸附挤出:`syncToMain` 把 AI 窗口放主窗口右(pos.x+size.width+GAP),主窗口最大化时 AI x=屏幕宽+4 挤出屏幕。**修复**:syncToMain 增强——主窗口 isMaximized 时 unmaximize + 主+AI 宽度超屏幕时缩小主窗口(newMainWidth=screenWidth-GAP-AI_WIDTH-pos.x,最小 400px 保护)+ _isSyncing flag 防 syncToMain 改主窗口 size 触发 onResized 递归;屏幕宽度用顶层函数 `currentMonitor()`(@tauri-apps/api/window,非 Window 实例方法)。②独立窗口窄:detachPanel width 520→600 对齐吸附态 AI_DOCK_WIDTH。
### P1 缺陷修复 (已完成)
- [x] B-260614-08 — ~~promote_idea 两步写非事务~~ ✅ mission:T-260614-05 已修复idea.rs 第二步 update_full 失败时补偿删除已建 projectRepository 不支持跨 repo 共享事务对象,选补偿删除非真事务,改动最小;附 logging
- [x] T-260614-03 — ~~工具层白名单与 crud 白名单双份去重~~ ✅ WF-B ⑤ 已解决tool_registry update_project 复用 is_allowed_column与 CRUD 白名单同源,删硬编码 match
- [x] T-260614-04 — ~~路径校验根治~~ ✅ 已完成resolve_workspace_path 加 canonicalize 防 symlink 逃逸 + 词法 starts_with 兜底仅校验、返回词法路径保持前端友好cargo check 0 err / 22 test pass
- [x] T-260614-09 — ~~idea.rs 物理删不级联~~ ✅ WF-E 完成idea.rs:149 delete→purge_with_descendants1 行,签名兼容)
- [x] T-260614-10 — ~~findBinding canonicalize~~ ✅ WF-E 判定已解决normalize_path 已含 canonicalize 优先 + 词法回退find_binding_conflict 两端对称已用,无需重复加;零改动)
- [x] T-260614-12 — ~~df-ideas 死代码~~ ✅ WF-E 部分完成capture.rs 删 CaptureInput/IdeaCapture 死码,保留 Idea/IdeaScores 共享实体promotion/scoring/adversarial 内"两套 Recommendation/PromotionPolicy 死枚举"嫌疑 agent 未确认存在/保留为对外契约,本次未动,待复查)
### 2026-06-17 实测发现 bug (审批流程三连)
- [x] ✅(波9·e23b316+主代核查,cargo0err+devflow79pass,方案a2后端分支) B-260617-01 [P1] — **run_workflow 作为 AI 工具无法执行 + 无意义重试 + 审批混淆**。三个关联问题:
- **问题 1**`run_workflow` handlertool_registry.rs:530-546仅持有 `db: Arc<Database>`,无法访问 `AppHandle/State`registry/event_bus/workflows Repo返回 `Err("run_workflow 工具需经 Tauri IPC 执行...")`。审批通过后 `ai_approve`commands.rs:329`ai_tools.execute()` → handler 必然 Err → `audit_status="failed"` → 错误信息作为 tool_result 回传 LLM。工作流实际未执行任务状态未推进。
- **问题 2**handler Err → tool_result=错误提示 → LLM 认为失败 → 重试(1/4) → 同结果循环。工具定义未标 `no_retry`,浪费 token + 用户反复审批。
- **问题 3**:前端渲染的是 AI 工具审批tool_call pending_approval非工作流 HumanNode 审批。用户批准后工作流引擎从未启动HumanNode 审批节点从未到达。
- **修复方向****方案 A推荐**:前端拦截 — `ai_approve` 中识别 `run_workflow` tool_call → 不走 `ai_tools.execute` → 直接 `invoke('run_workflow', {task_id, target_status})` → 结果回填 tool_result。**方案 B**:后端注入 State — `build_ai_tool_registry` 传入 `AppHandle` 使 handler 可直接执行。问题 2/3 随 A 自动消解。
### aichat API Key 401 排查 (已定位)
- [x] ✅ wave(2026-06-16,待commit) B-260615-01 — ~~Err 分支加诊断~~ 提取纯函数 `fmt_diag`+`extract_error_diag`(name/status_or_class/timeout·connect 分类,14 单测);约束:LlmProvider trait 无 base_url/endpoint,仅 name() 近似 provider_type(provider_type + 实际请求 url + HTTP 状态码),当前只透传服务端文本看不出端点,401 难定位 — stream_recv.rs:107-113
### 其他已修复问题
- [x] ✅(核验已实现·canRetry/canOpenSettings 已落地) UX-260616-01 [P2] — **工具调用失败时「重试」提示语义模糊**【独立核验:AiChat.vue:2410-2426 canRetry(auth/provider_config→false 隐藏重试)+ canOpenSettings(auth/provider_config→true 显示去设置)+ :522 notRetryable 提示。Fatal 类错误(4xx/鉴权)已去掉重试按钮改去设置。工具失败走 AiToolCallCompleted(非 AiStreamRetry)语义已清晰——ToolCard 显示错误,LLM 据 tool_result 自决】
- [x] ✅(主代修·commit 待提交·vue-tsc EXIT 0) UX-260616-02 [P3] — **「全部收起」合并一行**【核验 searchFiles i18n key 是死 key(无渲染点),实际合并 batch-approve + global-toggle 同行:ToolCardList.vue template 包 .ai-tool-topbar flex row + global-toggle margin-left:auto 右对齐】
- [x] ✅(第㉒批 2026-06-16) UX-2025-19 [P2] — ~~消息列表虚拟滚动~~ **已被 B-260618-01 彻底移除虚拟滚动取代**。原 e38474b 自研 IntersectionObserver+sentinel+末条保活, 因 IO/RO 时序竞态致消息重叠反复(shouldRender 恒 true 仍重叠), 最终删除 useAiVirtualScroll.ts + AiChat.vue 移除全链路。
### 2026-06-16 走查修复项
- [x] ✅(第㉖批 2026-06-16·主代理直修) B-260616-09 [P1] — **任务详情点击打开空白(路由漏注册)**。**router/index.ts 补 /tasks/:id → TaskDetail**(L50-54 新增块,6 行,参照 /projects/:id L39-43 + /ideas/:id L27-31 模式)。name 'TaskDetail' 无冲突(现有 name 集无此项),复用 icon-thunder(对齐 /ideas/:id 复用 icon-lightbulb、/projects/:id 复用 icon-apps 一致模式)。TaskDetail.vue 现挂载,Tasks.vue:57 router.push(`/tasks/${task.id}`) 不再跳未注册路由,B-18 联动刷新等功能激活(死代码转活)。改动极简确定(6 行路由表项),主代理直修省 workflow 单 agent 编排开销。vue-tsc exit 0。
- [x] ✅(第㉖批 2026-06-16) B-260616-10 [P1] — **provider 配置后 AI 面板仍提示「未配置」**。**Settings.vue loadProviders 同步刷全局 store**。L376 import useAiStore + L383 const aiStore = useAiStore()(跟随 L382 appSettings 风格)+ L420-424 loadProviders 内本地 aiProviders.value 刷新成功后 L424 await aiStore.loadProviders() 刷全局 state.providers(模块级单例 ai.ts:45,L20/L44 注释明确全应用共享同一份,AiChat 共享)。三处调用点(saveProvider L464/deleteProvider L479/setDefaultProvider L490)+ onMounted L731 均经 loadProviders 自动同步全局。无循环(useAiPanel.ts:91 loadProviders 只刷 state.providers 不回调 Settings)。无 i18n 改动(纯数据同步),try/catch + showToast 保留。vue-tsc exit 0。
- [x] ✅(ca9b318·wrapNakedDiff落地+第㉖批diff语言注册) B-260616-11 [P2] — **AI 消息 `-`/`+` 列表/diff 无视觉效果**。两项子项均已落地:①**裸 diff 预处理** `wrapNakedDiff`(ca9b318 useMarkdown.ts:+96 守卫4:纯`-`列表不触发/≥2行连续±/已在围栏不动/段落级处理+短路优化+缓存key用wrapped) ②**diff 围栏高亮**(第㉖批 useMarkdown.ts import diff+registerLanguage('diff')+highlightCode getLanguage('diff')→hljs addition/deletion token+github-dark.css 红绿)。
- [x] ✅(第㉗批 2026-06-16·workflow) B-260616-19 [P2] — **知识库详情窄屏标题挤压成竖线(一行一字)**。三因子叠加Knowledge.vue`.kn-layout` grid `grid-template-columns: 360px 1fr`L567——左列表死钉 360px、右详情 `1fr` 拿剩余,**无响应式断点**;窗口窄(如总宽 500px减 padding 40+gap详情列塌缩到 ~80px 甚至更小。②`.detail-title`L625`font-size:18px` + `word-break:break-word` —— 窄列里中文每字宽度 > 列宽,逐字换行 → 竖线化。③`.detail-actions`L626`flex-shrink:0` —— 编辑/发布/拒绝 3 按钮不缩,优先占位,把 `.detail-title-row`(`flex:1`) 挤到零头。`.kn-detail-panel`(L570-576) 无 `min-width` 兜底。改动待定①grid 列宽改 `minmax(240px,360px) 1fr` + `@media (max-width:760px)` 切单列(`grid-template-columns:1fr`,列表详情上下堆叠);②详情面板加 `min-width:0` 配合标题行 `min-width` 防塌缩③可选标题字号响应式降级18px→15px。— src/views/Knowledge.vue。**低风险纯 CSS**,无逻辑/i18n/数据变动。
---
> 归档自 todo.md 2026-06-18 三文档巡检