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

14 KiB
Raw Blame History

下一批次规划走查(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 一致性/错误传播)——发现清单见上方「代理发现」段(会话重启后重派,结果合并中)。