新增: F-15上下文管理增强设计+波1审查登记(CR-45)

This commit is contained in:
2026-06-16 23:49:24 +08:00
parent bebf2ec329
commit 44bc18294c
3 changed files with 563 additions and 5 deletions

View File

@@ -171,13 +171,27 @@
---
### CR-260616-44 P0 secret下沉+AiNode provider注入链(方案B FR-S1闭环) — 🟡 待审
### CR-260616-44 P0 secret下沉+AiNode provider注入链(方案B FR-S1闭环) — ✅ 已审(PASS)
- **范围**: 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。
- **复审结论(2026-06-16·CR-44 agent 亲跑 cargo+test+vue-tsc,485s)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
- **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-nodes` **79 passed; 0 failed; 1 ignored**(含 9 个 resolve_provider 新增测试)/ `npx vue-tsc --noEmit` EXIT 0。
- **① secret 下沉边界 PASS**:secret.rs:7-9 注释明确不下沉 build_provider_for(依赖 df-ai 会循环)。grep 0 处函数定义,5 处全注释(src-tauri/Cargo.toml:17/secret.rs:7/8)。src-tauri/secret.rs:20 pub use df_storage::secret::* 转发所有下沉函数 + :32 build_provider_for 保留全限定路径调 df_ai::build_provider(:37)。
- **② 转发壳 glob 兜底 PASS**:13 个实际调用点(排除注释行)全部经 crate::commands::ai::secret:: 或 super::secret:: 路径调用(commands.rs 4 处+agentic.rs 2 处+title.rs 1 处+knowledge_inject.rs 2 处+idea.rs 1 处+project.rs 2 处+lib.rs 1 处迁移调用)。编译期验证零断裂(cargo check EXIT 0)。
- **③ keyring feature 平台覆盖 PASS**:Cargo.toml workspace:24 统一声明 windows-native+apple-native+linux-native-async-persistent 三平台。df-storage/Cargo.toml:18 keyring = { workspace = true }。secret.rs:220 cfg(any(target_os="windows",target_os="macos")) 单测(仅桌面 OS 环境实测 keyring roundtrip,CI 任意 OS 安全)。
- **④ resolve_provider 三路径完备 PASS**:ai_node.rs:62-135 路径1 provider_id 优先→get_by_id(:76)→resolve_provider_secret(:143)→ensure_resolved_key(:144)/ 路径2 老明文兼容(:86-121,has_plain_base+has_plain_key 双判+:89-92 warn "老路径将在后续版本移除")/ 路径3 兜底(:124-134,list_all→find is_default→or_else 首条,友误 "未配置 AI Provider")。9 单测全覆盖(by_id/empty_key/unknown_id/legacy_plain/legacy_empty/fallback_default/no_providers/fallback_first/config_model_override)。
- **⑤ FR-S1 闭环 PASS**:api_key 仅存 ResolvedProvider 内存(ai_node.rs:45 明文字段,注释 "仅 AiNode 内存可见"),不进 config(schema L313/314 "已废弃过渡" 描述)/NodeOutput(L273-281 仅 text/model/usage 无 api_key)/schema(L316 required 改 ["provider_id"])。resolve_provider_secret(DB 优先→keyring)解析,build_provider(:235-240)传明文仅 AiNode 进程内存可见,不落库不外泄。
- **⑥ schema required 改 [provider_id] PASS**:ai_node.rs:316 required=["provider_id"] + :309 description "FR-S1密钥经 df_storage::secret 解析,不进 config" / AiSelfReviewNode :569 required=["task_id","provider_id"]。
- **⑦ 老明文兼容 warn 过渡期 PASS**:ai_node.rs:89-92 tracing::warn!("AiNode 明文 api_key/base_url 经 config 注入已废弃, 改用 provider_id (FR-S1). 老路径将在后续版本移除")。路径2 逻辑保留兼容(:86-121 has_plain_base+has_plain_key 双判)。
- **⑧ 迁移逻辑 FAILCOUNT SIDECAR PASS**:secret.rs:34-72 failcount_path/.devflow-keyring-failcount + read/write/record/clear_migration_failcount + migrate_secrets_to_keyring(:125)失败达阈值 3 升级 warn(:136-142 明文滞留风险+3 条排查建议:确认 OS 钥匙串可用/keyring 后端异常/手动重新保存)。lib.rs:28 启动调用非阻断(:29 warn "启动密钥迁移失败(非阻断)")。
- **⑨ df-nodes 显式导入 PASS**:ai_node.rs:17 use df_storage::secret::{ensure_resolved_key, resolve_provider_secret} 显式导入(非 glob)。resolve_from_record(:143-144)直接调用。ai_node.rs:14-15 也显式导入 AiProviderRepo/Database/AiProviderRecord。
- **⑩ testing 模板 PASS**:task_workflow_templates.rs:61 注册 "ai_self_review" 节点 + :72 edge(ai_self_review→human_review) + 单测(:123-129)验证节点类型+边方向+config 空。state.rs:269-270 注册 AiSelfReviewNode::new(review_db) 闭包捕获正确。
- **待修项回流 todo**: **无**(10 维度全 PASS,零问题)。
- **范围**: 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 保留全限定路径调)+ 13 调用点零改(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 兜底(13 调用点零改核验) ③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) ⑤⑥端到端联调实测。
- **审查 agent 待复审重点**: ①secret 下沉后 src-tauri 13 调用点经转发壳路径完整性(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) ⑤⑥端到端联调实测。
---
@@ -199,6 +213,16 @@
---
### CR-260616-45 波1 F-01阶段3厂商模型列表拉取 + UX-01错误气泡Fatal区分 + UX-03工具卡按轮次收起 — 🟡 待审
- **范围**: 波1 workflow(wef0eqdwp,2 agent 并行不同域)。①**F-01 阶段3**(107b848):df-ai 新建 model_fetch.rs(545 行)+ lib.rs:7 注册 pub mod。`fetch_model_names(provider_type,base_url,api_key)->Result<Vec<String>>` 协议分派(openai_compat GET /v1/models Bearer / anthropic_compat x-api-key+anthropic-version 双 header / 其他报错)+ `build_models_url` 智能拼接(尾 /、/v1→/models、/v4、/models 原样、chat/completions→models、/api→/models、根 base→/v1/models)+ `is_non_chat_model` 噪音过滤(13 类:dall-e/midjourney/sd/imagen/tts/whisper/audio/speech/voice/realtime/transcribe/moderation/-search;**embedding 按 §5.4 保留**)+ `filter_chat_models` 去重去空 trim+ `ModelsList` 反序列化(data/models alias + id/name alias + serde default 空兜底)+ `fetch_and_probe` 探测组合(probe 纯函数 map collect)+ 错误映射(timeout→超时提示 / 401/403→鉴权失败 / 404→端点不存在 / 5xx→厂商异常,全含 provider_type+url)。②**UX-01**(bebf2ec):AiChat.vue `canRetry(msg)`(L1940,errorType auth/provider_config=Fatal 隐藏「重试」按钮)+ notRetryable 提示 span(L413)+ v-if="canRetry(msg)" 守卫(L415)。③**UX-03**(bebf2ec):AiChat.vue 新增 `watch(agentRound)`(L1684-1690,cur>prev 收起上一轮工具卡)+ `buildActiveToolIds` 复用(L1644)+ ToolCardList.vue 注释说明 CSS 动画**不做**(flex gap:8px 对零高 item 仍贡献 gap 致双倍间距;display:none 才移出流无 gap;高度 auto→0 不可纯 CSS transition 需 `<Transition>` JS 钩子,本轮野人 lite 不重构 ToolCard 单卡级折叠)。④i18n 4 文件(zh-CN/en × ai.ts/aiChat.ts)notRetryable 等新增 key。
- **维度**: ①build_models_url 7 分支完备(尾//v1/v4/models/chat-completions/api/根 base,断言无 /v1/v1/models 重复) ②噪音过滤 13 类 + embedding 保留边界(§5.4) ③反序列化多 alias 兜底(data/models+id/name+serde default 空对象不 panic) ④协议分派(openai_compat Bearer/anthropic_compat 双 header) ⑤fetch_and_probe probe 兜底不中断整体 ⑥错误映射友好提示含上下文 ⑦UX-01 canRetry Fatal 分类(auth/provider_config;**边界:404/参数错映射 unknown 仍可重试**,受 error_type 分类粒度限制,后端下批补 retryable 字段后精确化) ⑧UX-03 agentRound watch 触发时机 + CSS 不动决策合理性(flex gap) ⑨i18n 转义(新增 key 无 @/|/裸 {}) ⑩主代亲跑 `cargo test -p df-ai model_fetch` **24 passed** + `vue-tsc` exit 0。
- **commit**: 107b848(F-01 阶段3) + bebf2ec(UX-01+UX-03)。
- **主代独立核查**: ✅ 全过(model_fetch.rs 545 行真实存在 + lib.rs:7 `pub mod model_fetch` 注册 / cargo test model_fetch **24 passed** 亲跑 EXIT 0 / 前 120 行结构清晰:公共入口 fetch_model_names/fetch_and_probe + 协议分派 fetch_openai_compat/fetch_anthropic_compat / AiChat.vue canRetry L1940 + notRetryable L413 + v-if L415 / agentRound watch L1684-1690(cur<=prev return 早退) / ToolCardList.vue 注释 flex gap 说明 CSS 不动决策 / vue-tsc exit 0 亲跑 EXIT 0)。
- **审查 agent 待复审重点**: ①model_fetch URL 拼接 7 分支边界(尤其根 base→/v1/models 对 GLM v4/DeepSeek/通义根 base 是否正确;`-search` 子串 contains 匹配是否误伤含 -search 的合法 chat 模型名) ②Anthropic 响应格式 GLM 订阅端点同构性(serde 仅取 id 容忍额外字段,留阶段5 IPC 实测) ③fetch_and_probe 无分页(Anthropic has_more 忽略,超大列表>500 可能截断) ④embedding 保留出现在 chat 下拉是否需前端分组(阶段6 前端按 capabilities 含 Embedding 折叠) ⑤UX-01 404/参数错走 unknown 仍可重试(后端补 AiStreamRetry/AiError retryable 字段后精确化,下批 df-ai 串行) ⑥真实厂商 API 联调(401/403/404/超时/解析失败各分支)留阶段5 IPC `ai_fetch_models`(Settings 点「测试连接」触发)端到端验证 ⑦UX-03 CSS 不做是否需后续补 `<Transition>` 动画(P3 体验项)。
---
## 已审归档
### CR-260616-34 batch58 F-260616-10 provider 默认值重启失效 bug — ⚠ 已审(PASS-WATCH)