Files
DevFlow/temp/下一批次规划-2026-10-02.md
T
lxy 36bc485112 优化: 界面交互完善与设置体验
- 审批卡尊重滚动意图,等长切换回底,排队超时按钮修活
- 带图编辑提示不静默丢,删除目标回写,切换文案消歧
- 会话键盘可达+搜索态重命名,筛选计数口径对齐
- 子任务提示正名,看板加载态
- 主题跟随系统,表单脏态拦截,密钥显隐留空提示
2026-10-03 16:27:48 +08:00

120 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 下一批次规划走查(2026-10-02)
> 走查代理产出,主代理汇总核验。守 session-role-diagnose-only:仅走查+规划,未改代码。
> 批次边界:本批在修 = 上下文管理 B1-B5(walkthrough 后段全部 ✅ 已销账:B1/B4/B5=7c62d8d、B2=9b89610、B3=df3ed55+e7cd035、B4 存量晋升=migrations V43)+ 12 张截图 UX 样式清单 + U 系竞态守卫(U1-U4);工作区另有「中断根治」12+2 未提交文件(本走查对象之一)。以上全部排除,只找增量。
> 排除声明(会话重启后追加):工作区后追加的 migrations.rs V43 迁移、AuditLog/settings/GitChanges/ProjectCard/TaskBatchBar/TaskDetail/Tasks 改动 = 本批已完成的 UX 修复,不在走查范围。
> 方法:3 路并行走查代理(后端 ai 命令 / 前端 ai 组件 / df-ai+df-storage crates)+ 主代理独立走查工作区未提交改动(D 线)+ 「已修」声明抽查。一切结论以当前源码为准。
---
## 一、增量问题清单
### D 线(主代理走查:工作区未提交改动本身的正确性)
#### D1 [P1] G2 唯一率「写间隔豁免」是无操作——本批声明修复②实际未生效
- **位置**:`src-tauri/src/commands/ai/agentic/helpers.rs:290-311`(`is_repetitive_exploration` 新增 effective 段)
- **根因**:每个签名的**首现必然被 push**(`last_idx.get(sig)` 为 None → exempt=false),而后续 `sort()+dedup()` 后 effective 的去重集合 ≡ 全样本去重集合——豁免逻辑对分子零影响;分母 `total = samples.len()`(:290)又未过滤豁免样本 → `unique_ratio` 与旧算法**逐位等价**。
- **附加 off-by-one**:`samples[j + 1..=i]`(:298)含当前样本自身——写类签名的再现因**自己 is_write=true** 被自豁免(区间应为 `samples[j+1..i]`)。
- **测试无效**:新增 4 个测试在旧代码下同样通过(逐个复算:cleanup 场景 8/12=0.67、pure_wandering 3/9=0.33、adjacent 3/9=0.33、flips_verdict 走单点分支 streak=3)——测试无鉴别力,「11+1 测试过」不代表修复生效。
- **影响**:`[g,w,g,w,g,w,...]`(同 grep 签名+同写签名,如反复 patch 同参数失败重试)distinct=2/12=0.17 < 0.4 → 仍误熔断;「清理任务 write→grep 验证」唯一率误判(本批修复目标)实际依旧。
- **修复方案**:反转 push 条件——豁免的再现应计为「新的有效单元」(夹写重查=新鲜验证活动)push 入 effective,未豁免的再现不入(已被首现覆盖);分母同步改 `effective.len()`(或对偶实现:exempt 计数同时从分子分母扣)。区间改 `samples[j+1..i]`。
- **验证**:补 `[g,w,g,w,g,w,g,w,g,w,g,w]`(g/w 各同签名、写类)用例——修正后应 `false`(当前 `true` 误熔断);再跑全量 G2 测试确认死磕场景仍 `true`。
#### D2 [P2] ai_resume_interrupted 防双跑检查在锁外——双探针 TOCTOU 双 spawn
- **位置**:`src-tauri/src/commands/ai/commands/chat.rs:2443`(`is_active` 检查)→ :2477 附近 transition → :2486 `try_continue_agent_loop`
- **根因**:`is_active` 在 session 锁外读;主窗口+分离窗口(AiDetached 自有 listener+看门狗)同 conv 看门狗先后到期时,两探针都可先过 `is_active=false`;后进锁者 `conv_read` 尾部仍是 Tool(先者新 loop 尚未产出消息)→ 再次 transition(Generating→Generating 迁移失败仅 warn 不阻断)→ 再次 spawn → epoch takeover 抖动 + 短窗口双 loop 并行(可能重复执行工具轮)。
- **修复方案**:session 锁内复查 `conv_states.is_active(&conversation_id)`,命中直接返回 `"generating"`(同时把 transition 失败从「warn 不阻断」改为「返回 generating 不续跑」)。
- **验证**:代码审阅锁内复查;手测主/分离窗口对同会话同时断流双探测。
#### D3 [P2] 达上限自动完成复用「网络中断」文案——语义错位误导排障
- **位置**:`src-tauri/src/commands/ai/agentic/mod.rs:1139-1174`(teardown emit incomplete=true)→ `src/composables/ai/useAiLifecycleEvents.ts:106-112`
- **根因**:达 max_iterations 自动收尾走 `AiCompleted(incomplete=true)`,前端 incomplete 分支统一补 `ai.responseIncomplete`「⚠ 响应因**网络中断**不完整…可重新发送以获取完整回复」——用户实际是轮次耗尽自动收尾,看到的却是网络中断+重发指引。
- **修复方案**:incomplete 气泡按来源分文案(后端事件加 `incomplete_reason` 字段,或前端按 pinned_goals/max-rounds 特征分流到新 i18n key「已达最大轮次自动收尾」)。
- **验证**:跑满 max_iterations 的会话看收尾气泡文案。
#### D4 [P2] 自动续跑提示气泡不路由会话——落错视图(已知家族新实例)
- **位置**:`src/composables/ai/useAiStream.ts:164-167`(`onStreamTimeout` attempts===0 分支)
- **根因**:「正在自动恢复…」气泡无条件 `state.messages.push`(当前活跃视图),convId 为后台会话时气泡落到无关会话——与已登记 9-28 #6「错误气泡推错视图」同家族,系本批新代码引入。
- **修复方案**:push 前加 `convId === state.activeConversationId` 守卫(后台会话静默重挂即可,通知走 notify 通道)。
- **验证**:后台会话看门狗到期,观察活跃会话是否冒出无关气泡。
#### D5 [P2] 尾部是 user 消息的中断被误判「finished」——补假收敛信号
- **位置**:`src-tauri/src/commands/ai/commands/chat.rs:2454-2457`(尾部 role 匹配)
- **根因**:interrupted 判定只覆盖 `Tool` 尾与 `Assistant` 带工具调用尾;末条是 **user**(loop 在首 token 前死亡:启动段 panic/配额拦截/网络失败)时返回 `"finished"` → 前端补「响应在收尾阶段中断,已自动完成收敛。」假信号,实际什么都没生成。
- **修复方案**:尾部 `User` 也判 interrupted 走 `resumed`(续跑语义正确),或返回独立状态让前端用中性文案。
- **验证**:构造发送后首 token 前断流场景,看前端是否误报「已收敛」。
#### D6 [P3] 审批执行超时错误丢失工具名与平台提示
- **位置**:`src-tauri/src/commands/ai/audit/mod.rs:260`
- **根因**:ai_approve 旧路径超时错误带「工具名 + Windows PowerShell `;` vs `&&` 提示」,统一收敛进 execute_with_heartbeat 后错误串只剩「工具执行超时(Ns),已中止(防死锁)」——可诊断性回退。
- **修复方案**:错误串补 `name`(函数内已知)。
- **验证**:人为构造超时工具审批,看 tool_result 文本。
#### D7 [P3] 达上限 NeedAction 系统通知文案与新语义不符
- **位置**:`src-tauri/src/commands/ai/agentic/mod.rs:1150-1156`
- **根因**:通知文案「AI 循环未收敛**已暂停,等待继续或停止指令**」——该出口已改为自动完成收尾(无暂停、无需指令),文案停留在旧暂停态语义。
- **修复方案**:文案改「已达最大轮次,已自动收尾」。
- **验证**:触达上限看系统通知。
### 代理线发现(2026-10-02 深夜收口时补:三路走查已全部完成,中断说明作废)
> 原记「三次中断不再重派」已被超越:续跑成功,rev-backend/rev-frontend/rev-crates 三份完整报告已返回并落盘。
> **全文见 `docs/05-代码审查/aichat三路走查报告-2026-10-02.md`**(含批次归并建议),docs/todo.md 已登记可执行项。精华:
- **后端 P0-1**:ai_chat_compress_context conv_id 分裂(chat.rs:1734 起 cid 切 active)——压缩后台会话 100% 触发:目标 conv compressing 标志永久卡死 + active 会话被插入他人摘要(跨会话污染入库)。**主代理已亲核实锤**。存量缺陷非本批回归,批次2首位(约 4 行)。
- **crates P0-1**:V21 迁移 COUNT 幂等窗口——批次中途崩溃后重跑 COUNT>0 跳过剩余迁移但版本号照样补齐,ai_messages 永久残缺被掩盖(代理核验)。批次2 并入。
- **后端 P1×3**:remote_bridge 6 处 AiError 单路 emit(miniapp 错误全盲)/只读结果缓存无写失效(写后读回过期)/conv delete 不停 in-flight loop(zombie 复活+审批吊死,与已登记 conv 删除族同域)。
- **前端 P1×2**:后台审批/授权事件被 isCurrent 丢弃(挂死,与 V2/V1 不同源)/switchConversation 兜底分支漏置 activeId(视图/高亮/发送目标三重错配)。
- **crates P1×12**:OpenAI 流式 error 帧被 choices 必填整帧丢弃(无声截断)/serde 结构错误判 Retryable/usage 必填缺字段整响应失败/sanitize 合并丢多模态 parts(带图消息图隐身)/INSERT OR IGNORE 静默丢写/CAS 缺软删防护 等。
- **机械批**:remote_bridge 6 处+agentic 4 处裸 emit 换 emit_ai_chat_event;i18n searchFilesBlocked 死键裸 @ 转义。
- P2×45+P3 见报告;已核对无漂移面(迁移链/V43/anthropic SSE 栈)已注明免重查。
---
## 二、对既有报告「已修」声明的抽查结论
| # | 声明 | 抽查结论 | 证据(当前源码) |
|---|------|---------|-----------------|
| 1 | todo 第一批 P1「I1 停止窄窗」未修 | **漂移:已修(已提交)** | `ChatInput.vue:291-298` stopBtnState 读 `getConvState(conv)==='generating'`,注释明言补 conv_state 判定治工具执行期不可干预 |
| 2 | todo 第一批 P1「I2 编辑死链」未修 | **漂移:已修(04acc42 10-01)** | `MessageItem.vue:101-102` `canEdit` + `ai-msg-edit-btn` + emit('start-edit') |
| 3 | todo 第一批 P1「V2 审批黑洞」未修 | **漂移:聚合层已修** | `useAiToolEvents.ts:59-84` AiApprovalRequired 无 isCurrent 守卫、无条件进 pendingApprovals 且保留 conversationId(V5 条目来源显示/V6 计时器 per-conv 残留未逐项核) |
| 4 | todo 第一批 P1「V3 后台零通知」未修 | **漂移:已修(e1dd0bc)** | `notify.rs` 新模块 + `agentic/mod.rs` 6 处 notify_session_event 调用 |
| 5 | todo 第一批 P1「G2 破坏性回环熔断」未做 | **漂移:已实现(已提交)** | `helpers.rs:343+` detect_destructive_loop/normalize_loop_path/destructive_loop_sample + 采样接线注释 |
| 6 | 知识库走查「T4/T5 已修」 | **属实(行号漂移)** | begin_turn `chat.rs:810/2112`(报告写 651/1978)、sync_after_compress `context_lifecycle.rs:261`(报告写 278) |
| 7 | 09-29「R1/R2 已修(工作区待提交)」 | **属实** | `useToolCard.ts:741` TOOL_SUMMARIES;`ToolCardList.vue:131-136/162/286` touched + watch 幂等补齐 |
| 8 | 中断根治「P2-1 AiMaxRoundsReached 幽灵事件已修」 | **属实** | 全仓 grep 仅注释与 enum 定义残留,后端零 emit 点 |
| 9 | 中断根治进度「② helpers.rs 唯一率写间隔豁免已修」 | **漂移:无操作(=D1)** | `helpers.rs:290-311` 豁免对去重后分子零影响、分母未过滤;4 测试旧代码下同样通过 |
| 10 | todo 第一批「V1 awaiting_approval 徽标」 | **未修(与 todo 一致)** | `ConversationSidebar.vue` 无 approval/awaiting 相关代码 |
**抽查总结**:10 项声明中 6 项「报告说未修但实际已修」(todo.md 第一批 I1/I2/V2/V3/G2 回环 未销账),1 项「本批声明已修但实际无效」(D1),3 项属实。建议下一批开工前先做 todo.md 第一批销账核对,避免重复派工。
---
## 三、下一批次建议拆批方案
### 批次 1:新代码收口批(小,先行)
- **内容**:D1 唯一率豁免真实现 + 测试补鉴别力用例、D2 锁内复查、D5 尾部 user 分支、D3 incomplete 文案分流、D4 气泡路由、D6/D7 文案。
- **文件**:`helpers.rs` / `agentic/mod.rs` / `commands/chat.rs` / `useAiStream.ts` / `useAiLifecycleEvents.ts` / `i18n en+zh ai.ts` —— **与当前工作区 12+2 未提交文件完全重叠**。
- **约束**:必须并入当前「中断根治」批一起收尾(等用户通知一并提交),或紧随提交后第一个小批;**不可开 worktree 并行**(同文件互踩)。
### 批次 2:后端登记高危批
- **内容**:todo 2026-10-02 静默死亡 4 项(P0 run_agentic_loop 包 catch_unwind / P1 conv 删除类 6 处补终态事件 / P2 审批超时主动收敛 / F2 心跳兜底前移)+ 本走查后端代理新发现(待代理返回后并入)。
- **文件**:`agentic/mod.rs` / `stream_relay.rs` / `approval_timeout.rs` / `commands/chat.rs`。
- **约束**:与批次 1 共享 mod.rs/chat.rs → **等批次 1 提交后开工**;catch_unwind 属核心 loop 改动,按 memory `test-strategy-scenario-replay` 先落场景回放 fixture 再动手。
### 批次 3:前端体验批
- **内容**:09-29 第二批 P2(V8 草稿 per-conv / R3 长回复带量 / R4 代码块复制 / I5+9-28#7 错误分类 / I4+9-28#4 队列回填 / V7 排队时钟 / V4 浮窗二次确认)+ pendingMaxRounds/MaxRoundsCard 死链清理(后端已零 emit)+ 本走查前端代理新发现。
- **文件**:`composables/ai/*`、`components/ai/*`、`ChatInput.vue`。
- **约束**:与批次 2 无文件交集可并行;useAiStream/useAiLifecycleEvents 与批次 1 有少量交集 → 批次 1 先行或本批避开这两文件。9-28#17(图片上传外部域名)涉开源边界,单独决策不进批。
### 并行性总览
- 批次 2(后端)与批次 3(前端)可并行;批次 1 必须先行收口(同文件)。
- U 系(U1-U4,非 ai 页面)与 B1-B5(知识链路)与本规划三批文件零交集,可多线并行。
- todo.md 第一批销账核对(第二部分 #1-#5)随任意批次顺手完成,纯文档操作。
---
## 附:走查覆盖说明
- D 线(主代理):工作区 12+2 文件全量 diff 逐块审读 + 周边链路(guard.rs epoch 语义、try_continue_agent_loop、execute_with_heartbeat、useAiLifecycleEvents incomplete 分支)交叉核验。
- 代理线:rev-backend(src-tauri/src/commands/ai 静默失败/竞态/锁)、rev-frontend(composables/ai + components/ai 事件/残留/泄漏)、rev-crates(df-ai/df-storage 一致性/错误传播)——发现清单见上方「代理发现」段(会话重启后重派,结果合并中)。