文档: 审查销账与CR-260622-01结案(TD-260621-05双轨销账+BUG残留销账+CR残留登记+REFACTOR描述更新)
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
- **影响**:1.32M 接近模型上下文上限,长会话必爆;成本几乎全烧在重复搬运文件内容。
|
||||
- **待核验源码点**:compress 模块(`src-tauri/src/commands/ai/` 下 compress 相关)——压缩后重组 messages 时是否真替换为摘要,还是仅打标记原样回灌。
|
||||
- **关联**:印证 [[aichat-techdebt-audit-2026-06-21]] 3-compress 单元的 2 个 smell。
|
||||
- **[2026-06-24 走查结论:已隐式修复,非当前缺陷]** 源码逐环核验:`build_for_request`(context.rs:147)两分支(未超预算 :161 / 超预算裁剪 :200)都走 `sanitize_messages`,step0(:231-234) `filter is_active()` 把 `status=compressed` 滤出发送 prompt;`is_active` 白名单(provider.rs:97-99 `None|Some("active")`)使 compressed→false;`compress_old_messages`(:820-831)标 compressed+扣 token,`push`(:76)仅 active 计 history_tokens → **compressed 真不进 LLM prompt**。is_active 白名单提交 `8a142c2`(2026-06-17)在 P0-1 实测会话(84dc36c8 ~06-22)之前。报告所见「66 compressed 但 prompt 1.32M」真因 = F-15 白名单前的老数据残留 / 或落库全量消息数 vs 送 LLM prompt token 测量混淆。**compress_with_focus 不必要**。
|
||||
|
||||
### [P0-2] 工具返回值协议不一致:`patch_file` / `read_file` 部分路径裸吐文件全文
|
||||
|
||||
|
||||
10
docs/todo.md
10
docs/todo.md
@@ -40,10 +40,12 @@
|
||||
- [x] 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-conv**。`useAiEvents.ts:99/113` 模块级单实例;DirAuthDialog:60/MaxRoundsCard:54 守卫仅判 isViewingGenerating 未比对 conversationId(pendingMaxRounds 纯 boolean 无 convId 字段)。真并发下挂起弹窗错显于非挂起会话。修:pendingMaxRounds 改 ref<string|null>(convId)+ DirAuth 守卫加 convId 比对。归 F-09 B 路线。
|
||||
- [x] TD-260621-03 [P1·patch_file 落地债 F-260615] ✅(读改写整体移入 _patch_guard 锁内串行化防 lost update + 删 entry().or_insert() 内存泄漏 + drop 写序列后释放·2026-06-21) — **patch_file 读改写在 FILE_LOCKS 外,lost update**。`tool_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。
|
||||
- [x] 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 透传。
|
||||
- [x] ✅(2026-06-24·ba811ea chat.rs executed→completed 6处:460幂等读/584 ai_approve写/634判定/668 IPC返回/885 authorize_dir日志/889 authorize_dir写 + df-storage V27 迁移 UPDATE 存量 executed→completed + v27 两测试 unify/idempotent·cargo check EXIT 0 + df-storage 78 passed) **TD-260621-05** [P1·归 SMELL-P1-6] — **审批状态字符串双轨**(已统一)。核验全绿:前端无硬编码 'executed'(grep 全 src 零命中,AuditLog:84 是 executed_at 字段名/settings:120 是文案)+ 无 WHERE status='completed'/'executed' 统计查询(仅 pending 查询)+ find_cached SW-16 已透传真实值兼容双值 + ai_authorize_dir 无第二处判定。治 AuditLog executed 记录显示错位(蓝pending标签+raw"executed")+ DTO audit/mod.rs:53 契约对齐。**注:仅统一状态值(String 仍 String),SMELL-P1-6 String→newtype/enum 强类型化整体仍待决策(独立项,本修复不涉)**。
|
||||
|
||||
**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)。
|
||||
|
||||
**[2026-06-24 核验销账]**:audit_tool_call 吞错 ✅已修(commit a7dbd50,finalize:35 `let _ =`→match Err tracing::error,对齐 audit_finalize)+ switch+delete retain 占位终态化 ✅已修(a7dbd50,conversation/chat finalize_pending_placeholders pub(crate))+ patch_file FILE_LOCKS 永不清理 ✅已修(TD-260621-03 06-21,_patch_guard 串行化 + 删 entry().or_insert())+ PerConvState.created_at ✅已是正确态(mod.rs:772 #[allow(dead_code)]+预留注释「待 upsert 接入会话创建时间」)+ PendingApproval.diff ✅已是正确态(阶段3a 下沉 enum ApprovalKind::Risk{diff} 活调用链 audit/remote_bridge/useAiEvents,误删断前端 diff 预览);剩 3 项登记不动 —— try_continue pending_conv_id 🟡语义复杂(R-PD-6 优先审批 conv_id 设计+try_continue 回退,非确定性低风险)/ MidStream chunk.error 🟡UX 结构变更(Partial 保文加 err 字段+前端渲染,非 bug)/ auto-compress save 🟡需理解 loop save 时机判真缺口。
|
||||
|
||||
**销账核对(已登记 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 完成 + 用户实测后可清。
|
||||
@@ -440,7 +442,7 @@
|
||||
|
||||
**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`
|
||||
- [ ] **REFACTOR-260619-04 [P1·部分已做]** — **ToolCard.vue 拆分**。**2026-06-24 核验**:ToolCard 已从 1527→**428 行**,已抽 `ToolResultBody.vue` + `useToolCardHeader.ts` + `useToolCardRender.ts`(主体瘦身完成);但原计划的 `useToolFormat.ts`/`useToolApproval.ts`/`ToolCardHeader.vue`/`ToolApproval.vue` 未独立(当前 useToolCard/useToolCardHeader/useToolCardRender 三模块)。剩余:审批状态机独立(useToolApproval)+ 头部组件(ToolCardHeader)。风险:折叠态 shouldKeepOpen 共享 / 审批状态机断链(B-260616-08 回归)/ ToolCardList 批量审批联动。— `src/components/ToolCard.vue` + `ToolCardList.vue`
|
||||
- [x] ✅(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`
|
||||
- [x] ✅(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`
|
||||
- [x] ✅(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`
|
||||
@@ -960,8 +962,10 @@
|
||||
> **诊断推翻的误判**(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 实例,大列表性能)
|
||||
> - [x] ✅(2026-06-24·ebba973) BUG-260624-01 残留:① MessageList:503 scrollToFirstPending return 过早修复(遍历全实例调,各内部 querySelector 无 pending 则 no-op,治 pending 落后续实例滚不到)+ ② ToolCardList listener 加 props.toolCalls.some(pending) 过滤短路(降大列表 N 实例×M emit 的 DOM 查询)。**scrollToFirstPending 时间窗去抖评估后不做**:同 id 连发被上游守卫拦截(AiDirAuthRequired/AiApprovalRequired useAiEvents:359/472 some 去重 + AiToolCallStarted:427 findToolCall 守卫),不同 id 连发是多 pending 合理场景每次滚到首个合理,vue-tsc EXIT 0。
|
||||
> - [ ] 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)
|
||||
> - [ ] CR-260622-01-P2-1 [P2低优·前端排序] — **ChatMessage.timestamp 打戳不可靠致时序错乱**。`provider.rs:44 ChatMessage::system()` 用插入时刻(now_millis_i64)打戳,压缩摘要/主题标记 system 消息(mod.rs:767/923/971 insert_at(0))timestamp=插入时刻晚于 user 消息创建时刻 → 前端按 timestamp 排序错乱。DB created_at 全消息统一 conv_created_at 不乱,乱的是 ChatMessage.timestamp 字段。修:前端消息列表排序弃 ChatMessage.timestamp 改 seq/created_at;或 insert_at 时 system 摘要 timestamp 显式置被压缩区间首条消息 timestamp。
|
||||
> - [ ] CR-260622-01-P2-2 [P2低优·非代码文件缓存] — **HTML/markdown/JSON 等非代码文件无 session 级缓存**。read_symbol(7c2e3b2/7f6aa1e)治 .rs/.ts/.vue 代码文件(降 24.4x),但非代码文件 grammar 不支持走 read_file 全文回灌,LLM patch 后重读确认模式无机制拦截(实测 mysql_guide.html read 4×)。修:工具侧加 session 级文件指纹缓存(path→hash+content,未变更返"缓存命中"提示);或 prompt 侧约束 patch 后不重读(归 B 路线)。
|
||||
|
||||
|
||||
13
docs/待审查.md
13
docs/待审查.md
@@ -23,7 +23,7 @@
|
||||
|
||||
## 当前队列
|
||||
|
||||
### CR-260622-01 AI Chat 真实会话实测分析(84dc36c8·prompt 1.32M/completion 7.5K·3P0+3P1+2P2)— 🟡 待审
|
||||
### CR-260622-01 AI Chat 真实会话实测分析(84dc36c8·prompt 1.32M/completion 7.5K·3P0+3P1+2P2)— ✅ 已审(ISSUES·低优残留2项·P0清零)
|
||||
|
||||
- **范围**:实测数据反推(**非静态代码审查**)。从生产 DB 会话 `84dc36c8`(D:\Downloads\kms,整理三知识库 HTML)全量 86 条消息反推上下文/工具/agent 行为缺陷。报告 `docs/05-代码审查/aichat-会话实测分析-2026-06-22.md`,分析脚本 `.temp/analyze_session.py`,DB 只读 `%APPDATA%/top.1216.devflow/devflow-dev.db`。
|
||||
- **维度**:(a) 独立核验 db 实测数据(token 比/重复 tool 结果/system 污染/时序);(b) 核验源码根因(报告 finding 均为"假设"待证)。
|
||||
@@ -42,6 +42,17 @@
|
||||
|
||||
---
|
||||
|
||||
**复审结论(2026-06-24·3 agent 独立 grep/read 核验当前源码形态,防上下文污染)**: ✅ **ISSUES(低优残留,P0 清零可归档)**
|
||||
|
||||
- **P0-1/P0-2/P0-3 已销账**(报告 06-23/06-24 修正段:压缩 is_active 白名单隐式修复 / truncate 破坏 JSON 修 / 重复 tool_result 实测 tool_call_id 全不同证伪)
|
||||
- **P1-2 末尾中断 🟡缓解**:`guard.rs` GeneratingGuard RAII(正常 reset + Drop 兜底)+ ConvState 5态 enum 状态机已系统性覆盖"generating 散布复位 + 无 panic 兜底"原根因;报告 06-23 修正推翻原假设(84dc36c8 seq85 后疑 max_iterations 收敛/自然停,非 generating 卡死)。归 BUG-260623-03 审批 pending 无超时(待决策,推荐前端倒计时)。
|
||||
- **P2-1 system 时序 🟡部分缓解**:DB created_at 全消息统一 conv_created_at 不乱;但 `ChatMessage.timestamp`(provider.rs:44 system() 用插入时刻)system 摘要打戳不可靠 → 前端排序应弃 timestamp 改 seq/created_at。role 污染数据级偶发(压缩摘要 LLM 输出含 assistant 语义,非结构 bug)。
|
||||
- **P2-2 非代码文件缓存 🔴仍存**:read_symbol(7c2e3b2/7f6aa1e)治 .rs/.ts/.vue 代码文件(降 24.4x);HTML/markdown/JSON 等非代码文件无 session 级缓存,LLM 仍 read_file 全文回灌。
|
||||
|
||||
**待修项回流 todo**:🟡 2 项低优(CR-260622-01-P2-1 前端排序弃 timestamp / CR-260622-01-P2-2 非代码文件 session 缓存,归 B 路线 prompt 策略或工具侧缓存)。
|
||||
|
||||
---
|
||||
|
||||
### CR-260620-04 Agent 最大轮次支持「不限」(working tree 未提交:0=不限 约定·config.rs clamp 0-50 + mod.rs effective_max 0→usize::MAX + GeneralPanel checkbox + App.vue clamp + i18n labelUnlimited) — ✅ 已审(PASS·⚪2 WATCH·巡检 2026-06-20 独立 grep/read 核验控制流)
|
||||
|
||||
- **范围**:新功能。约定 0=不限:后端 config.rs:69 clamp(1,50)→(0,50);mod.rs:542 loop 上界算 effective_max(0→usize::MAX,for 到不了上界,靠 stop_flag/收敛/审批退出,达上限暂停 AiMaxRoundsReached 不触发);前端 GeneralPanel 加「不限」checkbox(绑 agentMaxIterations===0)+ input :disabled + clamp 支持 0 + computed unlimited;App.vue:107 clamp 跟进(iter===0?0:...);i18n zh/en desc 加 0=不限 + labelUnlimited。
|
||||
|
||||
Reference in New Issue
Block a user