修复: 流式重试N+1气泡根治(UX-260618-15第一批方案A)
- stream_recv: InitFailed加error字段,5处重试路径移除AiError emit(retryable交agentic统一emit) - agentic: 重试loop只emit AiStreamRetry,Fatal/全candidate耗尽emit最终AiError(单气泡聚合) - useAiEvents: AiStreamRetry改首次创建+后续更新(N+1气泡消除) - 注释残留修正(stream_recv :251/:299 旧"emit AiError"→"返回InitFailed") N+1独立气泡降为「1重试气泡+1最终错误气泡」
This commit is contained in:
@@ -343,7 +343,7 @@
|
||||
|
||||
- [x] ✅(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 回显;**C** 删 `tool_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`
|
||||
|
||||
- [ ] **UX-260618-15 [P1]** — **流式失败重试(1/4 2/4 3/4)每轮独立气泡**。后端每轮重试 emit AiError(`stream_recv.rs:200/258/298/358/398` 5 处 InitFailed 必 emit),前端 `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`
|
||||
- [x] ✅(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:200/258/298/358/398` 5 处 InitFailed 必 emit),前端 `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`
|
||||
|
||||
- [x] ✅(主代串行·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`
|
||||
|
||||
|
||||
38
docs/待审查.md
38
docs/待审查.md
@@ -625,7 +625,7 @@
|
||||
1. `Knowledge.vue submitCreate`(:438)/ `ProjectDetail.vue handleApproval`(:417) 缺显式 `if(submitting) return` 入口 guard(其余 5 处有)。实际防并发由模板 `:disabled="submitting"` 兜住(按钮点不动·无重入),IPC 路径单调用,风险极低。可选补 guard 对齐范式一致性。
|
||||
2. `ConnectionPanel.vue saveConn`(:152) 是同步函数(无 await),submitting try/finally 同步置位即复位,此处 submitting 实际无防双击语义(纯占位/视觉)。无功能影响,可选移除或改 async。
|
||||
|
||||
### CR-260619-01 UX-14 advance_task渲染缺任务名 + UX-16 time.ts销账 + UX-17 handleApprovalMulti防双击(workflow wwchro468 UX-14实施 + 主代串行 UX-16/17·主代兜底核验) — 🟡 待审
|
||||
### CR-260619-01 UX-14 advance_task渲染缺任务名 + UX-16 time.ts销账 + UX-17 handleApprovalMulti防双击(workflow wwchro468 UX-14实施 + 主代串行 UX-16/17·主代兜底核验) — ✅ 已审(PASS·巡检 2026-06-19 独立 grep/read 核验)
|
||||
|
||||
- **范围**:
|
||||
- **UX-260618-14 advance_task 渲染缺任务名**(workflow wwchro468 general-purpose agent 实施 6 文件):前端 `ToolCard.vue` advance_task 渲染取 `r.title||r.id`(:474 formatToolResult / :891 toolResultSummary)+ 新增 `TASK_ID_TOOL_ARG` 映射(:799 advance_task→id / run_workflow→task_id)+ `displayArgValue` 接入 task_id→title(:834)+ i18n zh/en `aiTool.ts`(advancedTask/advancedTaskWithRounds 改 {title} + taskLabel/taskIdNotFound);后端 `audit.rs` 新增 `resolve_task_label`(查 tasks 表 :151 三臂 Ok(Some)/Ok(None)/Err 对齐 resolve_project_label)+ `build_approval_reason` advance_task 分支(:173 id 走 task 解析)+ `tool_registry.rs` 删 `tool_display_hint` 的 advance_task 项(:1575 让 :1549 模板「推进任务状态:{} → {}」生效)。
|
||||
@@ -635,6 +635,42 @@
|
||||
- **审查要点(供审查 agent)**:① UX-14 行为变更——审批 reason 文案(advance_task 从静态串「推进任务状态」→ 含任务标题+target_status)+ audit resolve 查 tasks 表(原误查 projects 恒报「项目已不存在」);② resolve_task_label 三臂语义对齐 resolve_project_label(空 id/Ok(None)/Err 分流);③ tool_display_hint 删 advance_task 后 run_workflow 保留轻量命中(模板 task_id 走通用裸显,本批不动);④ 跨项目任务 taskNameById 查 projectStore.tasks 查不到降级「任务不在当前列表」(非硬错);⑤ UX-17 handleApprovalMulti submitting 对齐 handleApproval 防双击。
|
||||
- **关联**:todo UX-260618-14/16/17 销账。
|
||||
|
||||
**复审结论(2026-06-19·巡检独立 grep/read 核验当前源码形态·commit 1dc8b12 已落地·不信声明)**: ✅ **PASS** — 🔴0 🟡0 ⚪1
|
||||
|
||||
**逐项核验表(file:line 佐证 + 判定)**:
|
||||
|
||||
| 项 | 核验点 | 佐证 | 判定 |
|
||||
|---|---|---|---|
|
||||
| A-audit | resolve_task_label 三臂对齐 resolve_project_label | `audit.rs:151-163`:空 id→空串 / Ok(Some)→「标题」(id=x) / Ok(None)→任务已不存在 / Err→warn+裸 id。与 `resolve_project_label:131-144` 同构 | ✅ |
|
||||
| A-build_reason | advance_task 的 id 特化走 task 表 | `audit.rs:194` `"id" if tool_name=="advance_task" => resolve_task_label(db,&val)`。其余 `"id"\|"project_id"` 仍走 resolve_project_label(:195) | ✅ |
|
||||
| A-tool_registry | 删 tool_display_hint advance_task 轻量命中 | `tool_registry.rs:1575-1576` 注释「advance_task 不在此轻量映射」;`:1549` display_hint_for_tool 模板「推进任务状态:{id}→{target_status}」生效 | ✅ |
|
||||
| A-TaskRecord | 返回值含 title(非假修复) | `df-storage/models.rs:54-57` TaskRecord `pub title:String`;`task_advance_node.rs:43-47` advance_task_atomic→Result<TaskRecord>;serde 默认序列化 title,前端 r.title 可读 | ✅ |
|
||||
| B-formatToolResult | advance_task 优先显 title 回退 id | `ToolCard.vue:474-478` formatToolResult `r.title\|\|r.id`;review_rounds>0 走 advancedTaskWithRounds,=0 走 advancedTask | ✅ |
|
||||
| B-toolResultSummary | header 摘要与 body 对齐 | `ToolCard.vue:921-924` 同 formatToolResult 双路径覆盖 | ✅ |
|
||||
| B-displayArgValue | task_id→title 无 project 冲突 | `ToolCard.vue:824` TASK_ID_TOOL_ARG{advance_task:'id',run_workflow:'task_id'};`:811-815` PROJECT_ID_TOOL_ARG **不含 advance_task** → :835 projectArgKey 为 undefined 跳过 → :845 taskArgKey 命中;`:806` taskNameById 查 projectStore.tasks 降级 taskIdNotFound | ✅ |
|
||||
| B-i18n | 中英对称 + message 无特殊字符 | `zh-CN/aiTool.ts:63-104` + `en/aiTool.ts:62-102` advancedTask/advancedTaskWithRounds/taskLabel/taskIdNotFound/workflowTriggered 全对称;message 仅 `{title}{status}{n}{id}{exec}` 标准插值,无 `@/`/裸 `{}`(prod runtime 盲点安全) | ✅ |
|
||||
| C-time | formatDate .value(CR-24/25 闭环) | `time.ts:44` `(i18n as any).global.locale.value === 'en'`。composition 模式 ref 读 .value,en locale 现生效 | ✅ |
|
||||
| D-ProjectDetail | handleApprovalMulti submitting 防双击 | `ProjectDetail.vue:431-440` async + try/finally(submitting=true :433 / finally=false :440);模板 `:198 :disabled="submitting\|\|multiDecisions.length===0"` 联动;对齐单选 handleApproval `:417-425` | ✅ |
|
||||
|
||||
**对抗核验印证**:
|
||||
- **run_workflow 审批 reason 不含任务名?** ✅ 非遗漏:`tool_display_hint:1577` run_workflow 轻量命中「触发工作流」(动词级,在 `build_approval_reason:184` 优先于 display_hint_for_tool 模板返回),根本不进模板分支,故 task_id 无裸显问题。review 要点③已声明「run_workflow 本批不动」,advance_task 是 UX-14 唯一范围。
|
||||
- **advance_task(Medium risk)是否走审批触发 build_approval_reason?** Medium 既有审批阈值逻辑非本批改动;无论审批卡触发与否,前端 formatToolResult/toolResultSummary(执行结果渲染)双路径均消费 title,UX-14 修复在审批卡 + 结果卡两处均生效。
|
||||
- **i18n message prod 编译盲点?** ✅ memory [[i18n-message-compile-blindspot]] 教训落实:grep message 值全部标准 `{var}` 插值,无字面 `@/`/`|`/裸 `{}`。
|
||||
- **PROJECT_ID_TOOL_ARG / advance_task key 冲突?** ✅ advance_task 不在 PROJECT_ID_TOOL_ARG(:811-815 仅 4 个 *_project),displayArgValue 项目分支短路跳过,无 advance_task.id 被误判为 project_id 风险。
|
||||
|
||||
- **⚪ WATCH-1**: run_workflow 审批 reason 走轻量「触发工作流」动词(不含 task 名/target_status),UX-14 范围外;若后续需 run_workflow reason 显任务名,需同时从 tool_display_hint :1577 移除 run_workflow(让 display_hint_for_tool :1550 模板生效)+ build_approval_reason task_id key 特化走 resolve_task_label。当前接受动词级。
|
||||
- **待修项回流 todo**: **无** 🔴/🟡 项
|
||||
|
||||
### CR-260619-02 UX-260618-15 第一批 流式重试N+1气泡根治·方案A(agent ux15-batch1 实施 + 主代兜底核验 + 注释修正) — 🟡 待审
|
||||
|
||||
- **范围**(3 文件 + 主代注释修正):
|
||||
- **stream_recv.rs**:`InitFailed` 变体加 `error: String` 字段(:125);5 处重试路径 AiError emit 全移除(idle timeout :203 / stream-error :262 / chunk Err :303 / 流尽无 finished :362 / provider.stream() Err :402),retryable=true 重试路径 emit 权交 agentic,retryable=false Fatal 也交 agentic 统一 emit。**主代修正注释残留** :251/:299(旧"emit AiError"→"返回 InitFailed")。
|
||||
- **agentic.rs**:重试 loop 解构 `InitFailed{retryable,error}`;Fatal 分支(:726)+ 全 candidate 耗尽(:748)新增 emit AiError(单气泡聚合,Network 分类);保留 ensure_resolved_key Auth(:401)/压缩失败(:618)/try_continue provider 缺失(:1058)emit。
|
||||
- **useAiEvents.ts**:`AiStreamRetry`(:177)改首次创建+后续更新(末条非 isError 创建新错误气泡 / 是 isError 更新 content);AiError push 保留(耗尽/Fatal 最终)。
|
||||
- **验证(主代兜底,不信 agent 自报)**:cargo check -p devflow EXIT 0 + cargo test -p df-ai **119 passed** + vue-tsc EXIT 0 + grep 核验 5 处 emit 移除判定 + 注释修正。
|
||||
- **审查要点(供审查 agent)**:① N+1 根治——重试过程不再每轮 push AiError,改 AiStreamRetry 单气泡(首次创建+更新),耗尽/Fatal emit 1 最终 AiError(N+1→1重试+1最终);② stream_recv 5 处移除判定(retryable=true 重试路径移除 / Fatal 交 agentic);③ agentic Fatal/耗尽 emit 单气泡(Network 分类);④ **riskNote error_type 统一 Network**——401 鉴权 Fatal 原本 error_type=None(前端 canOpenSettings 只认 {auth,provider_config} 本不显「去设置」),现 Network 也不显,**不恶化既有行为**(记 low WATCH:理想 401 显 auth 按钮需单独做);⑤ InitFailed 枚举加 error 字段 pub(crate) 内部无破坏。
|
||||
- **关联**:todo UX-260618-15 第一批销账;第二批(方案C 聚合气泡显示错误原因)可选增强待定。
|
||||
|
||||
---
|
||||
|
||||
## 已审归档
|
||||
|
||||
Reference in New Issue
Block a user