优化: 推进链阶段3代码层闭环(P0 secret下沉+注入链⑥待实测)+F-07c方案B落地+CR-44

This commit is contained in:
2026-06-16 23:09:28 +08:00
parent 84a6fc41d1
commit d3e6f80d2b
3 changed files with 24 additions and 7 deletions

View File

@@ -59,6 +59,12 @@
- [ ] B-260616-11 [P2] — **AI 消息 `-`/`+` 列表/diff 无视觉效果**。marked 实测两个出口:①裸 `-`/`+` 混用 → GFM 要求同列表标记一致marked 拆成**两个独立 `<ul>`** 且 `-`/`+` 被 eat 当 marker用户看到的符号消失两个分离单项列表②```diff 代码块 → `<pre><code class="language-diff">` **无 hljs 高亮**ai-md.css 未集成 highlight.js`-`/`+` 字面纯文本无红绿。根因层 marked 行为合规,缺的是 **diff 高亮集成 + 裸 diff 文本识别**。改动(待定):①集成 hljs 覆盖 ```diff关联已知待办「aichat交互体验改进方案-2026-06-14」2.2 代码块高亮)②裸 `-`/`+` diff 文本预处理识别。— src/composables/useMarkdown.ts + src/styles/ai-md.css。**②子项已修(第㉖批 2026-06-16·主代理直修)**:useMarkdown.ts L40-41 import diff + L63 registerLanguage('diff', diff)(16→17 语言),```diff 代码块经 highlightCode getLanguage('diff') 命中 → hljs-addition/deletion token class → github-dark.css 红绿。**①子项仍②类待复现**:裸 -/+ 文本预处理识别(需确认 AI 实际输出格式)。**batch58 可行性评估结论(2026-06-16·workflow w2dw65maz·Agent C 仅评估未改码)**:推荐**方案A**(renderMd/parseBlockNoCache 前 `wrapNakedDiff` 正则检测连续 ≥2 行 ±开头→包裹 ```diff 围栏,复用已就绪 hljs diff 红绿样式零额外 CSS);方案B(CSS ::before 注入)**否决**(marked 已 eat 掉原始 ±符号,li 内文本信息丢失无法区分);方案C(marked custom tokenizer)**否决**(侵入 marked 内部收益不优于 A)。守卫:纯 `-` 列表(普通 ul)不触发+必须含 `+` 行混排。**暂缓实施**:需求前提(AI 实际输出格式)未验证,不同 LLM 输出 diff 格式差异大(围栏/裸行/自然语言描述),盲目做可能解决不存在的问题;确认需求后 0.5 批次(~40-60 行+测试)。vue-tsc exit 0。 - [ ] B-260616-11 [P2] — **AI 消息 `-`/`+` 列表/diff 无视觉效果**。marked 实测两个出口:①裸 `-`/`+` 混用 → GFM 要求同列表标记一致marked 拆成**两个独立 `<ul>`** 且 `-`/`+` 被 eat 当 marker用户看到的符号消失两个分离单项列表②```diff 代码块 → `<pre><code class="language-diff">` **无 hljs 高亮**ai-md.css 未集成 highlight.js`-`/`+` 字面纯文本无红绿。根因层 marked 行为合规,缺的是 **diff 高亮集成 + 裸 diff 文本识别**。改动(待定):①集成 hljs 覆盖 ```diff关联已知待办「aichat交互体验改进方案-2026-06-14」2.2 代码块高亮)②裸 `-`/`+` diff 文本预处理识别。— src/composables/useMarkdown.ts + src/styles/ai-md.css。**②子项已修(第㉖批 2026-06-16·主代理直修)**:useMarkdown.ts L40-41 import diff + L63 registerLanguage('diff', diff)(16→17 语言),```diff 代码块经 highlightCode getLanguage('diff') 命中 → hljs-addition/deletion token class → github-dark.css 红绿。**①子项仍②类待复现**:裸 -/+ 文本预处理识别(需确认 AI 实际输出格式)。**batch58 可行性评估结论(2026-06-16·workflow w2dw65maz·Agent C 仅评估未改码)**:推荐**方案A**(renderMd/parseBlockNoCache 前 `wrapNakedDiff` 正则检测连续 ≥2 行 ±开头→包裹 ```diff 围栏,复用已就绪 hljs diff 红绿样式零额外 CSS);方案B(CSS ::before 注入)**否决**(marked 已 eat 掉原始 ±符号,li 内文本信息丢失无法区分);方案C(marked custom tokenizer)**否决**(侵入 marked 内部收益不优于 A)。守卫:纯 `-` 列表(普通 ul)不触发+必须含 `+` 行混排。**暂缓实施**:需求前提(AI 实际输出格式)未验证,不同 LLM 输出 diff 格式差异大(围栏/裸行/自然语言描述),盲目做可能解决不存在的问题;确认需求后 0.5 批次(~40-60 行+测试)。vue-tsc exit 0。
- [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/数据变动。 - [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/数据变动。
### 🔧 2026-06-16 aichat 工具卡片重复渲染排查(仅分析·未实施)
> 排查性质session-role-diagnose-only走查定位根因 + 记录待办,未改代码。
- [ ] B-260616-21 [P2] — **aichat 工具卡片「重复读取同一文件」表象read_file 出现两条卡:一 0 行·7.1KB running、一 183 行·7.1KB completed**。用户报对话记录里 `读取 .../api/ai.ts` 出现两次。**根因分析**(已走查前端流转 + 后端 emit 点):①前端 `useAiEvents.ts:202-206` `AiToolCallStarted` 分支直接 `lastMsg.toolCalls.push(info)`**无 id 幂等守卫**(对比同文件 `startToolSlowTimer:58``_toolTimers.has(callId)` 守卫Started 漏了同款判重)②`findToolCall`(`aiShared.ts:41-49`) 按 `id` 命中**首个**返回,`AiToolCallCompleted`(`:213`) 只 update 第一张卡 → 同 id 若被 push 两次,第二张卡永远 running`ToolCard.vue:72` `parsed?.lines||0` 无 result 时显示 `0 行` ③后端 `audit.rs:538-549` `process_tool_calls` 对 tc_list 每 draft emit 一次 Startedread_file=Low risk(`:561`) **无去重**(去重仅 High`:553-556` 注释明确 Low 不进)。**两候选根因****(A 最贴合现象)** 同一 `tool_call_id` 被**重复 emit Started**(后端 `process_tool_calls` 重入 / `stream_recv.rs` 生成重复 id / `agentic.rs` loop 重 process 同轮)→ push 两张卡 → Completed 只更首张(183 行)→ 次张残留 running(0 行)**(B)** agent loop 多轮 LLM 真读两次(不同 id)→ 但应两卡皆 183 行与「0 行 + 183 行」现象不符,**排除为主因**。**待查**:后端同 id 重复 emit 的确切路径(`audit.rs:532` tc_list 来源 `tool_calls_acc` + `stream_recv.rs` id 生成 + `agentic.rs` 是否对同轮 tool_calls 重复 process)。**修复方向**(待定):①前端 Started push 前加 `findToolCall(event.id)` 幂等守卫(确定性低风险,对齐 startToolSlowTimer 守卫,治标兜底) ②后端查重复 emit 源治本(定位 stream_recv/process/loop 哪环生重复)。**关联** F-260616-05(agentic loop 工具去重缺失batch53 仅 High 去重read_file/search_files 等 Low 未去重;本现象若实为候选 A 则与 F-05 不同维度——F-05 是不同 id 重复调用,本条是同 id 重复 emit)。— src/composables/ai/useAiEvents.ts(:195-209 Started 分支) + src/composables/ai/aiShared.ts(:41 findToolCall) + src/components/ToolCard.vue(:72 lines 展示) + src-tauri/src/commands/ai/audit.rs(:538-549 emit Started)。
### 💡 2026-06-16 新需求(已分析·待实施) ### 💡 2026-06-16 新需求(已分析·待实施)
> 功能需求,已走查定位链路 + 方案记录,待用户确认后实施。 > 功能需求,已走查定位链路 + 方案记录,待用户确认后实施。
@@ -92,6 +98,7 @@
- [ ] UX-260616-01 [P2] — **工具调用失败时「重试」提示语义模糊**。用户场景:Run Command 执行失败(exit_code 255,`head` not recognized on Windows),UI 显示「⚠ 调用失败,正在重试(1/4)…」+「重试」按钮。**根因分析**:「正在重试(n/m)」是 `AiStreamRetry` 事件(F-260616-07 流式 LLM 重试),通过 `useAiEvents.ts:177-186` 更新错误气泡内容;工具执行失败(run_command 等 Low 风险工具)走 AR-6 路径(`audit.rs:640-652`)→ emit `AiToolCallCompleted`(result=错误信息)→ **不触发 AiError 不触发 AiStreamRetry**,错误包在 tool_result 回传 LLM 自行决策。**问题**:①用户看到的是「LLM 流重试」提示,非「工具执行失败」提示,语义错位 ②确定性失败(命令语法错/exit_code 非0)重试同命令必再败,「重试」按钮误导 ③工具实际结果(stdout/stderr/exit_code)已显示在 ToolCard 内,错误气泡的「重试」是消息级 regenerate(重新生成整条回复),非工具级重试。**改动方向**(待定):a)错误气泡区分两类——流式重试中显示「正在重试(n/m)…」(现有);工具执行失败不弹错误气泡(结果已在 ToolCard)或气泡文案改为「工具执行失败,查看上方结果」b)Fatal 类错误(4xx/鉴权/参数)气泡去掉「重试」按钮或改为「去设置」c)`AiStreamRetry` 事件更新气泡时附带 `retryable` 标识,前端据此显隐重试按钮。—— useAiEvents.ts(:177-186 AiStreamRetry case) + AiChat.vue(错误气泡 UX-03 操作栏) + i18n ai.aiStreamRetry - [ ] UX-260616-01 [P2] — **工具调用失败时「重试」提示语义模糊**。用户场景:Run Command 执行失败(exit_code 255,`head` not recognized on Windows),UI 显示「⚠ 调用失败,正在重试(1/4)…」+「重试」按钮。**根因分析**:「正在重试(n/m)」是 `AiStreamRetry` 事件(F-260616-07 流式 LLM 重试),通过 `useAiEvents.ts:177-186` 更新错误气泡内容;工具执行失败(run_command 等 Low 风险工具)走 AR-6 路径(`audit.rs:640-652`)→ emit `AiToolCallCompleted`(result=错误信息)→ **不触发 AiError 不触发 AiStreamRetry**,错误包在 tool_result 回传 LLM 自行决策。**问题**:①用户看到的是「LLM 流重试」提示,非「工具执行失败」提示,语义错位 ②确定性失败(命令语法错/exit_code 非0)重试同命令必再败,「重试」按钮误导 ③工具实际结果(stdout/stderr/exit_code)已显示在 ToolCard 内,错误气泡的「重试」是消息级 regenerate(重新生成整条回复),非工具级重试。**改动方向**(待定):a)错误气泡区分两类——流式重试中显示「正在重试(n/m)…」(现有);工具执行失败不弹错误气泡(结果已在 ToolCard)或气泡文案改为「工具执行失败,查看上方结果」b)Fatal 类错误(4xx/鉴权/参数)气泡去掉「重试」按钮或改为「去设置」c)`AiStreamRetry` 事件更新气泡时附带 `retryable` 标识,前端据此显隐重试按钮。—— useAiEvents.ts(:177-186 AiStreamRetry case) + AiChat.vue(错误气泡 UX-03 操作栏) + i18n ai.aiStreamRetry
- [ ] UX-260616-02 [P3] — **「全部收起」与搜索/技能区合并一行**。当前 ToolCardList.vue 有两个独立行:①batch-approve 栏(line 4-11,pendingCount>0 时显示)②global-toggle 栏(line 14-17,collapsibleGroupCount>0 时显示「▾ 全部收起」)。用户要求将「全部收起」与附近的操作元素(search files 搜索文件/技能触发等)放到同一行,减少垂直空间占用。**需确认**:「search files」具体指哪个 UI 元素(i18n `ai.searchFiles` 渲染位置需定位,可能在输入框上方 skill 栏或工具卡区域)。**改动方向**:global-toggle 从独占行改为 inline 元素,与相邻操作栏 flex 同行。—— ToolCardList.vue(:13-17 template + :278-295 CSS .ai-tool-global-toggle) - [ ] UX-260616-02 [P3] — **「全部收起」与搜索/技能区合并一行**。当前 ToolCardList.vue 有两个独立行:①batch-approve 栏(line 4-11,pendingCount>0 时显示)②global-toggle 栏(line 14-17,collapsibleGroupCount>0 时显示「▾ 全部收起」)。用户要求将「全部收起」与附近的操作元素(search files 搜索文件/技能触发等)放到同一行,减少垂直空间占用。**需确认**:「search files」具体指哪个 UI 元素(i18n `ai.searchFiles` 渲染位置需定位,可能在输入框上方 skill 栏或工具卡区域)。**改动方向**:global-toggle 从独占行改为 inline 元素,与相邻操作栏 flex 同行。—— ToolCardList.vue(:13-17 template + :278-295 CSS .ai-tool-global-toggle)
- [ ] UX-260616-03 [P2] — **对话内容输出时自动收起旧工具卡片分组**。需求:当 AI 输出新内容(流式 delta / 新工具调用 / 新轮次)滚动到下方时,上方已完成的旧消息中的工具卡片分组自动收起,保持视野聚焦当前内容。**现状**:机制已存在——`ToolCardList.collapseInactive(activeIds)`(:191-199)供父组件调用,`AiChat.vue:1657-1661` 已有 watch 调用(refs 数组逐实例 collapseInactive)。**增强方向**:a)触发时机扩展——当前可能仅在特定时机调用,可扩展到:`AiTextDelta` 新消息开始时 + `AiAgentRound` 新轮次时 + 用户滚动接近底部时(跟随阅读位置自动收起已读内容)b)平滑过渡——收起加 CSS transition(高度动画 200ms)避免内容突然消失跳变c)可选:记忆用户手动展开的分组不自动收起(expandedCards Set 区分用户主动展开 vs 默认态)。—— ToolCardList.vue(collapseInactive + CSS transition) + AiChat.vue(watch 触发时机扩展) - [ ] UX-260616-03 [P2] — **对话内容输出时自动收起旧工具卡片分组**。需求:当 AI 输出新内容(流式 delta / 新工具调用 / 新轮次)滚动到下方时,上方已完成的旧消息中的工具卡片分组自动收起,保持视野聚焦当前内容。**现状**:机制已存在——`ToolCardList.collapseInactive(activeIds)`(:191-199)供父组件调用,`AiChat.vue:1657-1661` 已有 watch 调用(refs 数组逐实例 collapseInactive)。**增强方向**:a)触发时机扩展——当前可能仅在特定时机调用,可扩展到:`AiTextDelta` 新消息开始时 + `AiAgentRound` 新轮次时 + 用户滚动接近底部时(跟随阅读位置自动收起已读内容)b)平滑过渡——收起加 CSS transition(高度动画 200ms)避免内容突然消失跳变c)可选:记忆用户手动展开的分组不自动收起(expandedCards Set 区分用户主动展开 vs 默认态)。—— ToolCardList.vue(collapseInactive + CSS transition) + AiChat.vue(watch 触发时机扩展)
- [ ] UX-260616-04 [P1] — **审批通过后工具卡片仅显示裸 JSON**。用户实测:审批前 `pending_approval` 态渲染友好(参数键值对+风险提示+审批按钮),审批通过后 `completed` 态 body 仅显示一段 JSON 数据。**根因**:`ToolCard.vue` 模板渲染链对 `read_file`/`list_directory`/`write_file` 有专门 UI 分支,其余工具全部走通用兜底(L110 `v-else-if tc.result && tc.status === 'completed'`)→ `formatToolResult(tc)` → 兜底 `return formatJson(r)` 裸 JSON 美化输出。**代码核对**:①`formatToolResult`(L270-289)只特化 `write_file`,其余全 `formatJson``toolResultSummary`(L476-498,header 摘要)覆盖 `list_tasks`/`list_projects`/`list_ideas`/`create_project`/`create_task`/`create_idea`/`update_project`/`delete_project`/`restore_project`/`purge_project`/`run_workflow` 共 11 工具,但 body 的 `formatToolResult` 不共享此逻辑 ③两函数覆盖范围严重不一致(header 有摘要但 body 展开仍裸 JSON)。**后端返回值核对**(tool_registry.rs):`update_task``{id,field,updated}` / `delete_task``{deleted,id}` / `delete_file``{path,deleted,...}` / `run_command``{command,exit_code,stdout,stderr,...}`(可能很大) / `patch_file``{path,changed,size_diff,...}` / `append_file``{path,bytes_written,new_size}` / `rename_file``{from,to,renamed,...}` / `search_files``{path,pattern,results,total,...}` / `file_info``{path,exists,size,lines,...}` / `bind_directory``{id,path,stack,bound}` / `advance_task``{...}` ——全部走裸 JSON。**修复方向**:统一 `formatToolResult` 的 switch 覆盖全部工具产出人类可读摘要(复用 `toolResultSummary` 已有 i18n key + 补缺失 key 如 `commandOk`/`commandFailed`/`patchedFile`/`appendedTo`/`renamed`/`foundN` 等),同时补 `toolResultSummary` 缺失 case(`update_task`/`delete_task`/`delete_file`/`run_command`/`patch_file`/`append_file`/`rename_file`/`search_files`/`file_info`/`bind_directory`/`advance_task`)。—— `src/components/ToolCard.vue`(`formatToolResult` L270 + `toolResultSummary` L476) + `src/i18n/{zh-CN,en}/aiTool.ts`(补 key)
- [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 释放锁再裁剪。低收益优化。— agentic.rs:251-253。 - [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 释放锁再裁剪。低收益优化。— agentic.rs:251-253。
@@ -136,14 +143,14 @@
- **实施顺序**:④-1→②-1→②-5→②-2→②-3→②-4→②-6→①-1/①-3。**最小里程碑**(④-1+②-1+②-2+②-3):run_workflow 单 task_advance 节点 DAG 端到端推进 todo→in_progress - **实施顺序**:④-1→②-1→②-5→②-2→②-3→②-4→②-6→①-1/①-3。**最小里程碑**(④-1+②-1+②-2+②-3):run_workflow 单 task_advance 节点 DAG 端到端推进 todo→in_progress
- [ ] F-260616-07 阶段3 AI 执行闭环 — **F-03 收口三件 ✅ 本批完成**(batch64), AiNode 自审闸门 ⏳ 待后续批 - [ ] F-260616-07 阶段3 AI 执行闭环 — **F-03 收口三件 ✅ 本批完成**(batch64), AiNode 自审闸门 ⏳ 待后续批
- [x] ✅(batch64·2026-06-16·workflow wii1u1lnm) **F-03 收口三件** — ①advance_task 注册 AI 工具(tool_registry.rs:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic` 与 IPC `commands::task::advance_task:165` 同源) ②run_workflow 注册 AI 工具(:428,handler 架构约束无 AppHandle/State 报错引导走 IPC,ToolDefinition+审批文案 L1260-1261/1286-1287 注册让 LLM 可产出 tool_call) ③update_task handler guard 拒 status(L374 `field=="status"` bail,schema 通用 field/value 故 guard 拦非 schema 改)+ df-storage tasks 白名单移 status+review_rounds(crud.rs:331-347,advance_status_atomic CAS L848 独立路径不经白名单)。同步落地防 AI 工具行为不一致。**主代独立核查全过**:cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试(含新 `update_field_rejects_tasks_status`)。文件锁:tool_registry.rs + crud.rs + project_soft_delete.rs。**审查登记 CR-260616-41**(待审查.md 当前队列) - [x] ✅(batch64·2026-06-16·workflow wii1u1lnm) **F-03 收口三件** — ①advance_task 注册 AI 工具(tool_registry.rs:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic` 与 IPC `commands::task::advance_task:165` 同源) ②run_workflow 注册 AI 工具(:428,handler 架构约束无 AppHandle/State 报错引导走 IPC,ToolDefinition+审批文案 L1260-1261/1286-1287 注册让 LLM 可产出 tool_call) ③update_task handler guard 拒 status(L374 `field=="status"` bail,schema 通用 field/value 故 guard 拦非 schema 改)+ df-storage tasks 白名单移 status+review_rounds(crud.rs:331-347,advance_status_atomic CAS L848 独立路径不经白名单)。同步落地防 AI 工具行为不一致。**主代独立核查全过**:cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试(含新 `update_field_rejects_tasks_status`)。文件锁:tool_registry.rs + crud.rs + project_soft_delete.rs。**审查登记 CR-260616-41**(待审查.md 当前队列)
- [x] ✅ AiNode 自审闸门(阶段3 核心,决策a) — **阶段1迁移+方案✅(539b5ed+25409e5) + 阶段2②③④⑤✅(c10adaf ai 侧+741b0b9 前端)**,⑥联调阻塞 P0 provider 注入(F-260616-07c 待决)。cargo df-nodes 73 passed + workspace EXIT 0 + vue-tsc EXIT 0 - [x] ✅ AiNode 自审闸门(阶段3 核心,决策a) — **阶段1迁移+方案✅(539b5ed+25409e5) + 阶段2②③④⑤✅(c10adaf+741b0b9) + P0 secret下沉+注入链✅(766f235+84a6fc4)**。⑥联调代码层解锁(待实测 tauri dev)。cargo df-nodes 79 passed + workspace EXIT 0 + vue-tsc EXIT 0
- [x] ✅(539b5ed) ①df-storage: TaskRecord 加 `output_json: Option<String>` + V17 迁移(幂等补列) + crud 白名单+SELECT/INSERT/UPDATE 全链路 - [x] ✅(539b5ed) ①df-storage: TaskRecord 加 `output_json: Option<String>` + V17 迁移(幂等补列) + crud 白名单+SELECT/INSERT/UPDATE 全链路
- [x] ✅(c10adaf) ②df-nodes ai_node.rs: AiNode 持 db(Arc<Database>)+execute 后 config[task_id] 存在则 update_field 落 output_json - [x] ✅(c10adaf) ②df-nodes ai_node.rs: AiNode 持 db(Arc<Database>)+execute 后 config[task_id] 存在则 update_field 落 output_json
- [x] ✅(c10adaf) ③df-nodes ai_node.rs: AiSelfReviewNode 独立节点(prompt 四维度+parse_review_json 兜底 verdict=unknown+写回 review 子字段)+ state.rs 注册 + testing 模板 ai→ai_self_review - [x] ✅(c10adaf) ③df-nodes ai_node.rs: AiSelfReviewNode 独立节点(prompt 四维度+parse_review_json 兜底 verdict=unknown+写回 review 子字段)+ state.rs 注册 + testing 模板 ai→ai_self_review
- [ ] ④df-workflow: run_workflow 注入 task 数据到 NodeContext(④-1 deep_merge 就绪,**但 provider 配置注入阻塞 P0 F-260616-07c**) - [x] ✅(84a6fc4) ④df-nodes ai_node.rs: resolve_provider 注入链(provider_id 优先/老明文兼容 warn/空兜底 is_default)+ execute 经 df_storage::secret 解析 api_key 不进 config(FR-S1)+ schema required [provider_id]。run_workflow 注入 task_id 到 NodeContext(④-1 deep_merge 就绪),模板 config 空走 AiNode 兜底
- [x] ✅(c10adaf) human_review: 经 DAG inputs["ai_self_review"].data 透传 verdict/summary(HumanNode 零改动,方案④更优路径) - [x] ✅(c10adaf) human_review: 经 DAG inputs["ai_self_review"].data 透传 verdict/summary(HumanNode 零改动,方案④更优路径)
- [x] ✅(741b0b9) ⑤前端: TaskDetail.vue 展示 output_json(parsedOutput+review 红绿标+产出 markdown)+ types.ts+i18n 双语 - [x] ✅(741b0b9) ⑤前端: TaskDetail.vue 展示 output_json(parsedOutput+review 红绿标+产出 markdown)+ types.ts+i18n 双语
- [ ] ⑥端到端联调 — **阻塞 P0 provider 注入**(F-260616-07c secret 下沉待决)+ 实测类待用户(tauri dev) - [ ] ⑥端到端联调 — **代码层完成**(P0 secret 下沉+注入链✅ 766f235+84a6fc4),实测类待用户(tauri dev 验证 testing 模板 ai_self_review→human_review 闭环)
- [ ] run_workflow handler 注入 AppState(当前报错引导走 IPC) — 后续批扩展 build_ai_tool_registry 签名注入 AppState 句柄让 AI 直驱 - [ ] run_workflow handler 注入 AppState(当前报错引导走 IPC) — 后续批扩展 build_ai_tool_registry 签名注入 AppState 句柄让 AI 直驱
- [x] ⏸️(待决策.md已决b暂缓·2026-06-16) F-260616-08 阶段4 Git 集成kind+git闸门+worktree - [x] ⏸️(待决策.md已决b暂缓·2026-06-16) F-260616-08 阶段4 Git 集成kind+git闸门+worktree
- [x]**CR-260616-01 代码审查完成** → 审查登记已迁 [待审查.md](./待审查.md)(职责分离:审查队列独立,不进 todo)。结论 🔴0 🟡6 ⚪4 质量优,8 维度全过。**待修项 CR-01-A~I 见下方推进区**。 - [x]**CR-260616-01 代码审查完成** → 审查登记已迁 [待审查.md](./待审查.md)(职责分离:审查队列独立,不进 todo)。结论 🔴0 🟡6 ⚪4 质量优,8 维度全过。**待修项 CR-01-A~I 见下方推进区**。

View File

@@ -148,9 +148,9 @@
- a: 完整下沉 secret.rs → df-storage(新建 df-storage/src/secret.rs,src-tauri 改调 df-storage::secret) - a: 完整下沉 secret.rs → df-storage(新建 df-storage/src/secret.rs,src-tauri 改调 df-storage::secret)
- b: df-storage 加薄 secret 查询方法(AiProviderRepo::resolve_api_key(id) 封装 mask/env 解析,df-nodes 调此) - b: df-storage 加薄 secret 查询方法(AiProviderRepo::resolve_api_key(id) 封装 mask/env 解析,df-nodes 调此)
- c: provider_id 注入 config + df-nodes 直查 ai_providers.api_key(不走 secret.rs,df-nodes 自实现解析) - c: provider_id 注入 config + df-nodes 直查 ai_providers.api_key(不走 secret.rs,df-nodes 自实现解析)
- **推荐**:**待 secret 下沉调研方案定**(workflow wf_94cf4cad 产出 docs/02-架构设计/secret下沉与provider注入方案-2026-06-16.md) - **推荐**:**方案 B**(df-storage 加薄 secret 查询,纯密钥逻辑下沉不含 build_provider_for 避循环依赖)
- **关联**:todo F-260616-07 阶段3(⑥联调阻塞此) / secret.rs / FR-S1 / memory FR-S1 api_key IPC mask - **关联**:todo F-260616-07 阶段3(⑥联调) / secret.rs / FR-S1 / memory FR-S1 api_key IPC mask
- **状态**:🟡 待决(2026-06-16)— secret 下沉方向待调研方案定。AiNode 自审闭环功能通(②③④⑤),⑥阻塞此 P0 安全债 - **状态**:✅ 已决+实施(2026-06-16)— **方案 B 落地**:①secret 纯密钥逻辑下沉 df-storage/src/secret.rs(766f235,避循环依赖 build_provider_for 留 src-tauri 转发壳,12 调用点零改)②AiNode provider_id 注入链(84a6fc4,resolve_provider 三路径:provider_id 优先/老明文兼容 warn/空兜底 is_default)。**FR-S1 闭环**:api_key 经 df_storage::secret 解析 AiNode 内存内,不进 config/NodeOutput/schema。⑥联调代码层解锁,待实测(tauri dev)
--- ---

View File

@@ -154,7 +154,17 @@
- **维度**: ①AiSelfReviewNode prompt 四维度+JSON 兜底完备性(parse_review_json 各路径 verdict 覆盖,代码块 fence 剥离重试) ②写回 output_json review 子字段(读现有合并不覆盖 text,reviewed_at/reviewer_model) ③DAG 透传(NodeOutput.data 平铺,human_review 经 inputs 读,HumanNode 零改动核验) ④state.rs 注册+工厂闭包 move db.clone() ⑤testing 模板节点类型对齐 ⑥前端 schema 契约一致(types.ts 与 ai_node.rs 写回 schema 字段名对齐 verdict/summary/suggestions) ⑦i18n 特殊字符转义 ⑧**P0 安全债登记**(api_key 明文 config 注入违背 FR-S1,跨 crate 阻塞,F-260616-07c 待决) ⑨主代亲跑 cargo df-nodes 73 passed + workspace EXIT 0 + vue-tsc EXIT 0。 - **维度**: ①AiSelfReviewNode prompt 四维度+JSON 兜底完备性(parse_review_json 各路径 verdict 覆盖,代码块 fence 剥离重试) ②写回 output_json review 子字段(读现有合并不覆盖 text,reviewed_at/reviewer_model) ③DAG 透传(NodeOutput.data 平铺,human_review 经 inputs 读,HumanNode 零改动核验) ④state.rs 注册+工厂闭包 move db.clone() ⑤testing 模板节点类型对齐 ⑥前端 schema 契约一致(types.ts 与 ai_node.rs 写回 schema 字段名对齐 verdict/summary/suggestions) ⑦i18n 特殊字符转义 ⑧**P0 安全债登记**(api_key 明文 config 注入违背 FR-S1,跨 crate 阻塞,F-260616-07c 待决) ⑨主代亲跑 cargo df-nodes 73 passed + workspace EXIT 0 + vue-tsc EXIT 0。
- **commit**: 539b5ed + c10adaf + 741b0b9。 - **commit**: 539b5ed + c10adaf + 741b0b9。
- **主代独立核查**: ✅ 全过(human_node.rs 零改动 git diff 确认 / state.rs L262 AiNode::new(ai_db)+L269-270 AiSelfReviewNode::new(review_db) / templates L61 ai_self_review+L72 edge / ai_node.rs AiNode 持 db@L119+execute 写@L184+AiSelfReviewNode@L292+parse_review_json@L258 / cargo test -p df-nodes 73 passed 亲跑 / cargo check --workspace EXIT 0 / vue-tsc EXIT 0 / 前端 schema 对齐)。 - **主代独立核查**: ✅ 全过(human_node.rs 零改动 git diff 确认 / state.rs L262 AiNode::new(ai_db)+L269-270 AiSelfReviewNode::new(review_db) / templates L61 ai_self_review+L72 edge / ai_node.rs AiNode 持 db@L119+execute 写@L184+AiSelfReviewNode@L292+parse_review_json@L258 / cargo test -p df-nodes 73 passed 亲跑 / cargo check --workspace EXIT 0 / vue-tsc EXIT 0 / 前端 schema 对齐)。
- **审查 agent 待复审重点**: ①AiSelfReviewNode.execute 完整 mock LlmProvider 单测(本次降级 parse_review_json+update_field 单测,P3 待补) ②parse_review_json 兜底各路径(verdict=unknown 覆盖非 Object/数组/缺 verdict/JSON 解析失败/fence 剥离) ③写回 output_json 读现有合并的并发安全(同一 task 多次自审竞态) ④human_review 审批卡 description 拼自审摘要未实现(P2,透传链通但卡片未拼,HumanNode 只读 config 不读 inputs) ⑤P0 api_key config 注入(F-260616-07c 阻塞⑥联调)。 - **审查 agent 待复审重点**: ①AiSelfReviewNode.execute 完整 mock LlmProvider 单测(本次降级 parse_review_json+update_field 单测,P3 待补) ②parse_review_json 兜底各路径(verdict=unknown 覆盖非 Object/数组/缺 verdict/JSON 解析失败/fence 剥离) ③写回 output_json 读现有合并的并发安全(同一 task 多次自审竞态) ④human_review 审批卡 description 拼自审摘要未实现(P2,透传链通但卡片未拼,HumanNode 只读 config 不读 inputs) ⑤P0 api_key config 注入(F-260616-07c,✅ 已由 CR-44 secret 下沉+注入链解决)。
---
### CR-260616-44 P0 secret下沉+AiNode provider注入链(方案B FR-S1闭环) — 🟡 待审
- **范围**: F-260616-07c P0 安全闭环(3 commit:36f70b3 方案 + 766f235 下沉 + 84a6fc4 注入链)。①**secret 下沉**(766f235):df-storage 新建 src/secret.rs move 纯密钥逻辑(get/set/delete/resolve/ensure/migrate+failcount,**不含 build_provider_for** 避循环)+ keyring workspace 平台 feature(windows/apple/linux-native-async-persistent)+ src-tauri secret.rs 瘦身转发壳(pub use df_storage::secret::* + build_provider_for 保留全限定路径调)+ 12 调用点零改(glob 兜底)+ keyring 单测 cfg-gate。②**AiNode 注入链**(84a6fc4):ai_node.rs resolve_provider(db,config)->ResolvedProvider 三路径(provider_id 优先 get_by_id→resolve_provider_secret→ensure / 老明文兼容 warn / 空兜底 list_all find is_default)+ AiNode/AiSelfReviewNode execute 同构经 resolve_provider 拿 provider + schema required [provider_id] + 模板 config 空走兜底。
- **维度**: ①secret 下沉边界(纯密钥逻辑无 df-ai 依赖,build_provider_for 留 src-tauri,无循环) ②转发壳 glob 兜底(12 调用点零改核验) ③keyring feature 平台覆盖(Linux linux-native-async-persistent) ④resolve_provider 三路径完备(provider_id/老明文 warn/兜底 is_default/无 provider 友好错误) ⑤**FR-S1 闭环**(api_key 仅 ResolvedProvider 内存,不进 config/NodeOutput/schema required) ⑥schema required 改 [provider_id] ⑦老明文兼容 warn 过渡期 ⑧主代亲跑 cargo df-nodes 79 passed(9 新增 resolve_provider 测试)+ workspace EXIT 0。
- **commit**: 36f70b3 + 766f235 + 84a6fc4。
- **主代独立核查**: ✅ 全过(df-storage secret.rs 无 build_provider 函数定义,grep 5 处全注释 / src-tauri 转发壳 pub use+build_provider_for 全限定路径 / keyring workspace feature 三平台 / resolve_provider 三路径 L74/L89/L123 / schema required L316+L569 / api_key 仅 ResolvedProvider L45/238/464 调 build_provider 不进 NodeOutput / cargo test df-nodes 79 passed 亲跑 / workspace check EXIT 0)。
- **审查 agent 待复审重点**: ①secret 下沉后 src-tauri 12 调用点经转发壳路径完整性(lib.rs/commands.rs/agentic.rs + build_provider_for 6 处) ②keyring Linux CI(linux-native-async-persistent 后端依赖 zbus/secret-service) ③resolve_provider 老明文兼容路径后续移除时机 ④FR-S1 端到端验证(实测 tauri dev:testing 模板 ai_self_review 经 resolve_provider 拿 provider→LLM→写 output_json) ⑤⑥端到端联调实测。
--- ---