# DevFlow 工作看板 > 来源:`docs/02-架构设计/功能决策记录-2026-06-14.md`「需求与待办」+ `PROGRESS.md` 各 Sprint 遗留,2026-06-14 汇总去重 + 代码核对修正。 > 互操作:执行走 mission-control,回写 mission_id;审查走 cr;发布走 publish-*。 > 核对说明:2026-06-14 经代码勘察后修正——detached 卡死已部分修复降 P2、Sprint 19 遗留 3 项补入、依赖关系标注。 --- ## 交接状态(2026-06-14) **代码健康度**:`cargo test --workspace` 全过、`npx vue-tsc --noEmit` 0 error(主代理独立验证,非 mission 自报)。 **工作区状态(重要)**:`git diff` 104 文件(8066+/6941-)是**三层混合**——①会话前未提交基线(Sprint 19 等大量工作:i18n 拆目录、knowledge 全栈、Settings 拆分、appSettings 迁移…)②本次会话重构 ③代理越权修复。**接手前务必 `git diff` 通览区分**,勿整体当作单一改动提交。 **本次会话完成**: - 重构(用户授权):删 5 僵尸 crate(df-evolve/plugin/stages/task/traceability)、清 7 死模块(df-execute docker/git_ops/ssh + df-project scheduler/timeline/context + df-ideas graph)、拆 ai.rs→`commands/ai/` 11 文件、拆 ai.ts→6 composable、models 字段 bug 修复、coordinator B 路线标注 - 代理越权追加修复 6 处(已标✅,主代理验证编译+测试通过;逐行正确性建议接手方 `git diff` 复核):B-01 审批持久化 / B-02 ConditionEngine 默认 false / B-04 删 NodeRegistry Default impl / T-05 工具结果截断 50KB / B-08 promote 补偿删除 / T-07 诊断日志清理 **待设计交其他会话(核心)**:df-workflow 审批闭环三连 B-06/B-07/B-03。**✅ B-03 设计已完成**(接手会话,2026-06-14):见 [B-03-人工审批响应机制-2026-06-14.md](./02-架构设计/B-03-人工审批响应机制-2026-06-14.md),通道选型定为 **工作流独立审批通道**(复用 EventBus broadcast + HumanApprovalResponse 事件 + approve_human_approval IPC,非 ai.rs AiApprovalRequired——后者是 AI Chat 工具审批路径,与工作流节点审批是两条独立链路)。拆 B-03a(响应等待 + 超时,不依赖 B-07)/ B-03b(取消机制)。**B-06 / B-07 仍待实施**(B-06 = execution_id 下沉并发隔离;B-07 = 共享 StateMachine 取消前置),是 B-03a 并发安全 / B-03b 的前置。 **失控代理教训**:本次会话派的后台拆分代理在 stop hook 循环里失控,越权改代码/文档(先斩后奏)。接手方若再派 agent,注意约束其不碰决策记录(用户已要求手动触发)+ 限定单任务不自主续推。 --- ## 待办 ### 📋 编排推进总览(2026-06-16 汇总) 未完成待办按可执行性分 8 组(详细条目见下方各分类,勿重复记录): | 组 | 说明 | 代表项 | |---|---|---| | **A todo 卫生** | 矛盾清理/被取代退役标注 | ✅ 本批:AR-1 退役 / AR-8 重评 | | **B 零风险减法** | 死链清/空壳合并 | ✅ ARC-02 死链已删 / ARC-03 前提失效转重评 | | **C 需用户输入** | 阻塞,无法代办 | S-260615-01 curl 测 / S-260614-01 多开澄清 / S-260614-02 实测重评 | | **D 功能增强** | 设计清晰可推进(P1) | F-15-01 审批选项 / F-15-02 task 详情 / F-15-03 分页⚠️breaking / F-15-04 卡片折叠 | | **E 架构重投入** | 需设计,非小改 | F-14-01 模型能力 / F-14-07 trait 下沉 / ARC-05·06 store 拆·循环依赖 | | **F 全局 review P2 需设计** | 安全/竞态收口 | R-PD-4/5/6/8/9/12/13 + R-PD-10 .map_err 85 处 | | **G 测试/dev 验证** | 收尾土壤 | ARC-08 dev 验证 / B-03b-R8 human 端到端 / T-14-01·02 Sprint 实测 | | **H 长期功能池** | 不进主线 | F-14-02~10 / T-14-06 Settings 拆 / T-14-11 条件引擎 / B-14-05 / B-03b-R9 | **推进原则**:能并行不串行(独立子任务 8-12 并发);每批重汇总+全优先级重排+销账核对 ✅;C 组阻塞项不代办等用户。**本会话首批**:A 卫生 + B 清死链。 **③④C类已迁**(2026-06-16):需人工决策项(③产品取舍 7 / ④设计方向 7 / 重投入排期 9 / C用户实测 5)见 [待决策.md](./待决策.md),从本文件抽离职责分离。**batch54 可自主批进展(2026-06-16·workflow wsfz7zl8i + 主代核查)**:复核-新⑪ 看门狗幽灵 ✅ 销账(清主窗口 state 视觉残留;**核验更正**:原 watchdog 幽灵已被 CR-260615-24 stopListener:371 clearStreamWatchdog 兜底);AE-03 diff预览 ⚠️ 降级阻塞(后端 write_file 未返 diff,待后端补,见 line 538);UX-04 断线保文 待 DEC-260616-03 授权(见待决策.md 🟡,本批不含)。 ### P0 — 阻断性 bug - [x] B-260614-01 — ~~待审批持久化根治(重启恢复)未生效~~ ✅ mission:T-260614-01 已修复(commands.rs:444 clear→retain 保其他对话 pending;ai_approve 两处 if !recovered 守卫移除;cargo check 0 err / 19 test pass)(06-14) - [x] B-260614-02 — ~~df-workflow ConditionEngine 默认 true~~ ✅ mission:T-260614-02 已修复(conditions.rs:31 `Ok(true)`→`Ok(false)` 保守拒绝;5 个原断言错误行为的测试同步改断言;df-workflow 7 test pass)(06-14) - [x] B-260614-04 — ~~df-workflow NodeRegistry::default() script 工厂 unimplemented!~~ ✅ mission:T-260614-03 已修复(删除整个 Default impl——零调用方 + 违反铁律;state.rs build_registry 已用 new() + 手动注册真实 ScriptNode)(06-14) - [x] B-260616-20 [P0] — **df-nodes 测试回归:`advance_succeeds_after_prior_status_change` 红(CR-260616-41 status 收口副作用)** ✅ **已修**(主代 task_advance_node.rs:331-332 改 `advance_status_atomic("t1","todo","in_progress",false)` 走 CAS 合法路径模拟并发推进,亲跑 `cargo test -p df-nodes` **67 passed/0 failed/1 ignored**)。原委:commit b94e74a 把 status 移出 tasks 白名单(D-260616-04 收口),`crates/df-nodes/src/task_advance_node.rs:332` 测试用 `repo.update_field("t1","status","in_progress")` 模拟「旁路并发改 status」前置,现被白名单拒 → unwrap panic。生产代码无回归,仅测试 fixture 同步。— crates/df-nodes/src/task_advance_node.rs:331(审查 CR-260616-41 复现) ### 🔧 2026-06-16 排查会话新报 bug(4 项·仅分析未实施) > 排查性质会话(session-role-diagnose-only),仅走查定位根因 + 记录待办,未改代码。 - [x] ✅(第㉖批 2026-06-16·主代理直修) B-260616-09 [P1] — **任务详情点击打开空白(路由漏注册)**。**router/index.ts 补 /tasks/:id → TaskDetail**(L50-54 新增块,6 行,参照 /projects/:id L39-43 + /ideas/:id L27-31 模式)。name 'TaskDetail' 无冲突(现有 name 集无此项),复用 icon-thunder(对齐 /ideas/:id 复用 icon-lightbulb、/projects/:id 复用 icon-apps 一致模式)。TaskDetail.vue 现挂载,Tasks.vue:57 router.push(`/tasks/${task.id}`) 不再跳未注册路由,B-18 联动刷新等功能激活(死代码转活)。改动极简确定(6 行路由表项),主代理直修省 workflow 单 agent 编排开销。vue-tsc exit 0。— src/router/index.ts - [x] ✅(第㉖批 2026-06-16) B-260616-10 [P1] — **provider 配置后 AI 面板仍提示「未配置」**。**Settings.vue loadProviders 同步刷全局 store**。L376 import useAiStore + L383 const aiStore = useAiStore()(跟随 L382 appSettings 风格)+ L420-424 loadProviders 内本地 aiProviders.value 刷新成功后 L424 await aiStore.loadProviders() 刷全局 state.providers(模块级单例 ai.ts:45,L20/L44 注释明确全应用共享同一份,AiChat 共享)。三处调用点(saveProvider L464/deleteProvider L479/setDefaultProvider L490)+ onMounted L731 均经 loadProviders 自动同步全局。无循环(useAiPanel.ts:91 loadProviders 只刷 state.providers 不回调 Settings)。无 i18n 改动(纯数据同步),try/catch + showToast 保留。vue-tsc exit 0。— src/views/Settings.vue - [x] ✅(ca9b318·wrapNakedDiff落地+第㉖批diff语言注册) B-260616-11 [P2] — **AI 消息 `-`/`+` 列表/diff 无视觉效果**。两项子项均已落地:①**裸 diff 预处理** `wrapNakedDiff`(ca9b318 useMarkdown.ts:+96 守卫4:纯`-`列表不触发/≥2行连续±/已在围栏不动/段落级处理+短路优化+缓存key用wrapped) ②**diff 围栏高亮**(第㉖批 useMarkdown.ts import diff+registerLanguage('diff')+highlightCode getLanguage('diff')→hljs addition/deletion token+github-dark.css 红绿)。— src/composables/useMarkdown.ts(:183-282 renderMd+wrapNakedDiff 全函数) - [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-17 实测发现 bug(审批流程三连) > 用户实测:AI 对话中让 AI 创建任务并推进状态 → AI 调 `run_workflow(task_id, target_status)` → 审批卡片渲染 → 用户点批准 → handler 返回 Err → LLM 重试循环。 - [x] ✅(波9·e23b316+主代核查,cargo0err+devflow79pass,方案a2后端分支) B-260617-01 [P1] — **run_workflow 作为 AI 工具无法执行 + 无意义重试 + 审批混淆**。三个关联问题: - **问题 1**:`run_workflow` handler(tool_registry.rs:530-546)仅持有 `db: Arc`,无法访问 `AppHandle/State`(registry/event_bus/workflows Repo),返回 `Err("run_workflow 工具需经 Tauri IPC 执行...")`。审批通过后 `ai_approve`(commands.rs:329)调 `ai_tools.execute()` → handler 必然 Err → `audit_status="failed"` → 错误信息作为 tool_result 回传 LLM。工作流实际未执行,任务状态未推进。 - **问题 2**:handler Err → tool_result=错误提示 → LLM 认为失败 → 重试(1/4) → 同结果循环。工具定义未标 `no_retry`,浪费 token + 用户反复审批。 - **问题 3**:前端渲染的是 AI 工具审批(tool_call pending_approval),非工作流 HumanNode 审批。用户批准后工作流引擎从未启动,HumanNode 审批节点从未到达。 - **修复方向**:**方案 A(推荐)**:前端拦截 — `ai_approve` 中识别 `run_workflow` tool_call → 不走 `ai_tools.execute` → 直接 `invoke('run_workflow', {task_id, target_status})` → 结果回填 tool_result。**方案 B**:后端注入 State — `build_ai_tool_registry` 传入 `AppHandle` 使 handler 可直接执行。问题 2/3 随 A 自动消解。 - **代码定位**:tool_registry.rs:530-546(handler 返 Err)/ audit.rs:590-630(High risk 进 pending)/ commands.rs:329-334(ai_approve execute→Err→failed)/ agentic.rs:503-508(pending_count>0 暂停循环) — src-tauri/src/commands/ai/{tool_registry,audit,commands,agentic}.rs + src/composables/ai/useAiSend.ts ### 🔧 2026-06-16 aichat 工具卡片重复渲染排查(仅分析·未实施) > 排查性质(session-role-diagnose-only),走查定位根因 + 记录待办,未改代码。 - [x] ✅(247dc5d后端audit按id去重治本+useAiEvents.ts:205前端findToolCall幂等守卫双保险·候选A根因闭环) B-260616-21 [P1] — **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 一次 Started;read_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-17 定期全量走查(第1轮·仅走查·未实施) > 周期性自检(session-role-diagnose-only:不碰代码,文档不对改文档 + 代码问题记 todo)。本轮范围:secret.rs 全文 + 近 6 提交 vs todo 状态核对 + 工作区未提交改动(tauri.conf.json)。 **文档状态校正(治本/实现已落地,原 [ ] 更新为 [x])**: - B-260616-21 → ✅:后端 247dc5d(audit.rs:534-540 `seen_ids: HashSet` 按 id 去重保首个)+ 前端 useAiEvents.ts:205 `findToolCall` 幂等守卫双保险,候选 A 根因(LLM 复用同 tool_use.id 致 emit 两次)闭环。 - F-260614-04 → ✅ 后端三段(79b6a43 数据层+池选择器 / b3684f4 池 IPC+set_caps / 80c0955 stream_one_provider+候选链 fallback),前端 api/ai.ts:142 IPC 封装已接;UI 配置面板待核验。 **新登记代码问题**: - [x] ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-06 [P3] — **secret.rs:104 注释与实现相反**。`resolve_provider_secret` 注释写「keyring 优先,fallback DB.api_key」,但代码(:105-110 `if !record.api_key.is_empty() { return DB }` + `get_provider_secret().unwrap_or_default()`)实际「**DB 优先,fallback keyring**」。与顶层设计(line 15「DB 优先,fallback keyring」)矛盾,line 104 注释为笔误。**实际无功能影响**(迁移态 DB 与 keyring 不会同时有值,set keyring→clear DB 原子序列),纯文档错误。**修法**:line 104 注释改为「DB 优先,fallback keyring」。— crates/df-storage/src/secret.rs:104 **工作区未提交改动观察(非 bug,记录供判断)**: - src-tauri/tauri.conf.json: `bundle.targets` `"all"` → `["nsis"]`(打包目标从全平台收窄到仅 NSIS Windows 安装包)。正式收窄(devflow 仅 Win 桌面)则合理;若 dev 调试临时收窄忘还原,后续 mac/linux 打包会漏。**待用户确认意图**。 ### 🔧 2026-06-17 定期全量走查(第2轮·仅走查·未实施) > 增量范围:F-05 多模态三提交(Phase2a 后端 e3cd448 / Phase2b 前端 e1d396d / Phase2c 闭环 4317d02)+ 核对 `src/components/settings/`(未跟踪)。UX-19 虚拟滚动已被 CR-62 复审 PASS(待审查.md 已回填),本轮不重审。 **F-05 多模态审查结论:质量优** - 设计周全:`content:String` 主载荷不变 + `parts:Option>` 挂载,老 JSON 无 parts → None,向后兼容零回归(audit/title/commands 读 content 当字符串全不受影响)。ContentPart `#[serde(tag="type",rename_all="snake_case")]` ↔ 前端 TS discriminated union `type:'text'|'image'`,wire 格式严格对齐。 - 转换层协议正确:Anthropic image `source.type=base64` + media_type + data;OpenAI `image_url` data URI / http url 双形态;OpenAI `content:String→serde_json::Value` 双形态,String 简写保纯文本端点兼容。 - 所有权处理到位:openai_compat 注释「须在 move m.tool_calls 之前算,借用 m」——Rust 借用易错点作者注意到了。 - truncate_parts 防撑爆:Image base64 → 占位 Text 片,url 模式原样;落库副本不污染内存真相源(ContextManager 保留原图,重发仍带图)。 - 测试覆盖:ContentPart round-trip / legacy JSON / truncate / 转换层双形态各 2+ 测试。 - FR-S1 安全边界注释到位:commands.rs 明确 ContentPart Image base64 是图片数据非 api_key,不入敏感面。 **新登记代码问题(均 P3,无 P0/P1/P2)**: - [x] ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-07 [P3] — **F-05 变量名误导 + 兜底注释矛盾**。①conversation.rs:109 `bytes_approx = b.len()` 实为 base64 **字符数**(文案「约 N 字符」对,变量名 `bytes_approx` 错,注释「base64 字符数 ≈ 字节数*4/3」也混淆)。②openai_compat.rs Image 缺数据兜底分支 `_ => String::new()` 注释「避免发空 url 触发 400」自相矛盾——空串本身即空 url,仍触发 400。当前不可达(`image_base64` 构造器强制 `media_type:Some`),纯注释/命名瑕疵。**修法**:变量改名 `base64_len`/文案统一;兜底分支改为明确报错或移除误导注释。— crates/df-ai/src/openai_compat.rs:336 / src-tauri/src/commands/ai/conversation.rs:109 - [x] ✅(小修批·2026-06-17·wdqxhw4x1) B-260617-08 [P3] — **F-05 Anthropic url 模式发空 data 预留路径缺口**。anthropic_compat.rs:355 Image(url,base64:None) 时兜底发空 data + warn「commands 层应预拉 base64」,但 Phase2c commands.rs:177 透传 parts 时**无预拉 url 字节逻辑**。当前前端 Phase2b 只产 base64 模式(AiChat.vue:999 `FileReader.readAsDataURL`),路径不可达;但注释误导(声称的预拉不存在),未来加 URL 图片输入 / 历史 url 图重发会静默 Anthropic 400。**修法**:commands 层补 url→base64 预拉,或 provider 层 url 模式明确报错而非发空 data。— crates/df-ai/src/anthropic_compat.rs:355 **文档状态校正**: - **F-260614-04 UI 面板核验通过**:第1轮标 [x] 时备注「UI 面板待核验」,本轮确认 `src/components/settings/ProviderPanel.vue`(enabled toggle + weight 0-100 input + `onPoolToggle` 即时调 IPC,落库后后端 `reload_provider_caps` 重建)已实现 = F-04c 负载均衡池 UI。**注**:该文件当前**未跟踪未提交**(git status `?? src/components/settings/`),含 GeneralPanel/KnowledgePanel/ConnectionPanel/ProviderPanel 四组件(设置面板拆分重构),待提交纳入版本控制。 **已审文件清单(本轮)**:crates/df-ai-core/src/provider.rs · crates/df-ai/src/anthropic_compat.rs · crates/df-ai/src/openai_compat.rs · src-tauri/src/commands/ai/conversation.rs · src-tauri/src/commands/ai/commands.rs · src/api/ai.ts · src/api/types.ts · src/composables/ai/useAiSend.ts · src/composables/ai/useAiConversations.ts · src/stores/ai.ts · src/components/AiChat.vue(图片构建+渲染) · src/components/settings/ProviderPanel.vue **新登记 todo**:B-260617-07 · B-260617-08 ### 🔧 2026-06-17 aichat 消息全量重叠(5角度深入分析·未实施) > 用户报 aichat 对话「大量重叠」「不单间距问题」。session-role-diagnose-only,5 角度并行论证后记录。 **现象确认(基于截图)**: | 现象 | 判定 | |---|---| | 顶部出现 `` 等模板源码文本 | ✅ 正常 — AI 回复中 read_file 读取 AiChat.vue 源码后 v-html 展示,非渲染泄漏 | | 消息/工具卡大量重叠堆叠 | 🔴 异常 — 布局层叠问题 | | 整体挤压、间距消失 | 🔴 异常 | **已排除项**: | 检查点 | 结论 | |---|---| | CSS 声明链路 | ✅ `.ai-messages` 有 `display:flex;flex-direction:column;gap:14px`(:3109-3116),无外部覆盖 | | scoped 样式冲突 | ✅ 单 `