From 1f5e6cce901a11cf619d482cfc3a8372f5f360ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Sat, 10 Oct 2026 00:21:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20system=20=E9=94=9A?= =?UTF-8?q?=E7=82=B9=E9=81=BF=E8=AE=A9=E6=82=AC=E7=A9=BA=20tool=20?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=A4=B4=E9=98=B2=20400?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - df-ai 新增 ContextManager::push_system_anchor 统一出口:末条为悬空 ToolCallHead 时插其前,否则尾部追加(不位移前缀) - 5 处 system 尾部 push 全量收口(压缩成功/兜底提交、警示单点、主题标记、续轮 nudge),从源头杜绝后台压缩摘要/警示楔入工具三元组致上游 400 insufficient tool messages - 单测 push_system_anchor 两态锁定;cargo test -p df-ai --lib context 155 passed / cargo check -p devflow EXIT 0 - 看板归档:全量完成态条目与段落迁 docs/07-项目管理/todo归档/2026-10-10.md(零措辞改动机械搬运,未完成项110条零丢失/完成项37条全入归档/行数守恒) --- crates/df-ai/src/context/manager_tests.rs | 34 ++++ crates/df-ai/src/context/mod.rs | 35 ++++ docs/07-项目管理/todo归档/2026-10-10.md | 179 +++++++++++++++++ docs/todo.md | 180 +----------------- .../commands/ai/agentic/context_lifecycle.rs | 4 +- src-tauri/src/commands/ai/agentic/helpers.rs | 2 +- src-tauri/src/commands/ai/agentic/mod.rs | 4 +- 7 files changed, 258 insertions(+), 180 deletions(-) create mode 100644 docs/07-项目管理/todo归档/2026-10-10.md diff --git a/crates/df-ai/src/context/manager_tests.rs b/crates/df-ai/src/context/manager_tests.rs index dd5848b..26c3e4d 100644 --- a/crates/df-ai/src/context/manager_tests.rs +++ b/crates/df-ai/src/context/manager_tests.rs @@ -1200,3 +1200,37 @@ fn eviction_no_user_text_no_summary() { ); assert_eq!(mgr.all_messages_clone().len(), 16, "裁剪污染了内存全量"); } + +// ── 源头守静(909):push_system_anchor 避开悬空 tool_call 头 ── + +#[test] +fn push_system_anchor_appends_on_normal_tail() { + let mut mgr = ContextManager::new(cfg(100_000)); + mgr.push(ChatMessage::user("问")); + mgr.push(ChatMessage::assistant("答")); + let avoided = mgr.push_system_anchor(ChatMessage::system("警示")); + assert!(!avoided, "常规尾部应走追加(不避让)"); + assert_eq!(mgr.len(), 3); + assert_eq!(mgr.messages_mut()[2].message.content, "警示", "system 应在尾部"); +} + +#[test] +fn push_system_anchor_avoids_dangling_tool_call_head() { + // [user] [assistant(tool_calls)] ← 末条为悬空头(其 tool 响应尚未回填) + let mut mgr = ContextManager::new(cfg(100_000)); + mgr.push(ChatMessage::user("跑工具")); + mgr.push(ChatMessage::assistant_with_tools( + "", + vec![ToolCall::new("c1".to_string(), "t", "{}")], + )); + let avoided = mgr.push_system_anchor(ChatMessage::system("## 摘要")); + assert!(avoided, "末条为悬空 tool_call 头应避让插入"); + // 序列应为 [user, system(摘要), assistant(tool_calls)] —— 头仍是末条,三元组不被劈开 + let msgs = mgr.all_messages_clone(); + assert_eq!(msgs.len(), 3); + assert_eq!(msgs[1].content, "## 摘要", "摘要应插在悬空头之前"); + assert!( + msgs[2].tool_calls.as_ref().is_some_and(|c| !c.is_empty()), + "悬空头应保持末条,其 tool 响应可紧随其后回填(协议不变式成立)" + ); +} diff --git a/crates/df-ai/src/context/mod.rs b/crates/df-ai/src/context/mod.rs index 1bb407f..87e9c5c 100644 --- a/crates/df-ai/src/context/mod.rs +++ b/crates/df-ai/src/context/mod.rs @@ -567,6 +567,41 @@ impl ContextManager { self.needs_full_rewrite = true; } + /// 追加一条 system 锚点消息(警示文本/压缩摘要),自动避开**悬空 tool_call 头**。 + /// + /// **源头守静(909)**:OpenAI/内部协议不变式是「assistant(tool_calls) 之后必须紧随 + /// 其 tool 响应」。主 loop 的顺序是 push 头(`finalize_round_output`)→ 释放锁 → + /// 执行工具 → 再取锁 push 结果;若在「头已入、结果未回」的窗口内从尾部追加 system + /// (后台压缩摘要/警示/nudge/主题标记),system 就楔进 head 与其响应之间 → 上游报 + /// 400「insufficient tool messages following tool_calls message」。 + /// + /// 本出口统一处置:末条非悬空头 → 常规尾部追加(不位移前缀,保自动前缀缓存,行为与 + /// 原 `push` 一致);末条是悬空头 → 改插在该头**之前**(index = len-1),不劈三元组, + /// 摘要/警示仍紧邻本轮内容对 LLM 可见。返回 `true` 表示走了避让插入(会置 + /// `needs_full_rewrite`,需 DB 全量重写),供调用方记日志。 + pub fn push_system_anchor(&mut self, message: ChatMessage) -> bool { + if self.ends_with_dangling_tool_call_head() { + let idx = self.messages.len() - 1; + self.insert_at(idx, message); + tracing::debug!( + idx, + "system 锚点避让悬空 tool_call 头插入(防楔入三元组致 400)" + ); + true + } else { + self.push(message); + false + } + } + + /// 末条是否为「悬空 tool_call 头」(assistant 带 tool_calls 且其 tool 响应尚未回填)。 + /// 供 [`Self::push_system_anchor`] 判定避让位置:末条即头,说明头已入而结果未回。 + fn ends_with_dangling_tool_call_head(&self) -> bool { + self.messages + .last() + .is_some_and(|m| m.group == MessageGroup::ToolCallHead) + } + /// 按淘汰单元分组消息范围(三元组原子性),供压缩定位/分段标记复用同一分组逻辑。 /// /// 返回每个单元的右开区间 end + token 总和,保证: diff --git a/docs/07-项目管理/todo归档/2026-10-10.md b/docs/07-项目管理/todo归档/2026-10-10.md new file mode 100644 index 0000000..ce0cc50 --- /dev/null +++ b/docs/07-项目管理/todo归档/2026-10-10.md @@ -0,0 +1,179 @@ +# todo 归档(2026-10-10) + +> 本文件由 2026-10-10 归档拆批迁移:docs/todo.md 中「完成态」条目/段落(编号 [x]、勾选 [x]、标题带 ✅ 且段内零未完成项)。拆分判据=块完成态机械判定(零措辞改动,纯行搬运)。主板保留全部 [ ]/[~]/[→] 未完成项与设计/指针段。 + +--- + +### ✅ 2026-06-20 BUG-260620-05 F-260619-03 工程内路径误弹窗(reload_allowed_dirs 丢 workspace_root·b22e9ae 回归) + +**现象**:AI Chat file_info 等文件工具访问工程内路径(如 docs/02-架构设计/单对话并行多轮-设计-2026-06-20.md)触发授权弹窗,用户称"本来就有访问权限,为什么还来申请"。 + +**根因**:`state.rs:586-619 reload_allowed_dirs` 合并 `kv_dirs(KV allowed_dirs)+ project_dirs(projects.bind_directory)` 为 `all_dirs`,**仅当 `all_dirs.is_empty()` 时(:618)才插入 workspace_root**。KV 配过 allowed_dirs 或有绑定项目 → all_dirs 非空 → **workspace_root 不入 persistent** → 工程内路径(在 workspace_root 下)`is_authorized`(:356 starts_with)失败 → `check_path_authorization` 返 `NeedsAuthorization` → 弹窗。 + +**注释承诺失配**::346-348 / :583 / :617 注释均称"KV 未配时保持 workspace_root 免授权(向后兼容)",但 :618 条件用 `all_dirs`(含 project_dirs),project_dirs 非空也丢 root。b22e9ae "reload 尊重用户 persistent" 引入的回归。 + +**用户政策指令**(2026-06-20):"默认直接访问,明确知道无权限才申请"。指向工程内(workspace_root 下)默认免授权,仅 workspace_root 外非白名单路径才弹窗。 + +**修法(层1·1 行确定性 bug)**:`state.rs:618` 去掉 `if all_dirs.is_empty()` 条件包裹,**无条件** `set.insert(workspace_root_path())`(工程内始终免授权,对齐用户政策 + 注释承诺)。不破坏现有能力:workspace_root 经 `set/get_allowed_dirs`(:664/680 filter root)本就不可见不可删,"用户删 root"语义未落地,保 root 无冲突。 + +**层2 待决策(政策范围)**:用户"默认直接访问"是否要求更宽语义——workspace_root 外的非黑名单路径也默认放行(翻转 `is_authorized` 为黑名单制,实质去白名单)?涉 P0 去固定根决策方向,见待决策.md。dev 自用场景层1 即满足。 + +**关联**:F-260619-03(todo :528 b22e9ae 回归源)/ 待决策 workspace_root 分发适配(方案b 用户项目绑定)/ memory [[devflow-project-path-binding]] +**状态**:✅ 已实施(层1 无条件插入 workspace_root + 层2 白名单制 persistent/session/once + 黑名单兼底,对齐 2026-06-28 用户决策) + +**✅ 2026-10-09 复核销账**:方案已演进为**更优的动态推导**(优于原「无条件插入 workspace_root」)——`state.rs:579` 注释实证「不再硬塞编译期 workspace_root(CARGO_MANIFEST_DIR)」,原因:编译机路径分发后失效反成脏白名单。工程根授权改由两途径:`projects.bind_directory`(绑项目自动授权)+ `Settings.allowed_dirs`(手动白名单),`is_authorized` 另有系统目录黑名单兜底(层2)。原 bug 根因(工程内路径因 `all_dirs.is_empty()` 条件丢 root 而误弹窗)已随该演进消除。**销账。** + +--- + +### ✅ aichat API Key 401 排查(2026-06-16·非 bug 已销账) + +> 用户报对话失败「调用失败: API Key 无效或无权限」+ 重设 key 无效。**排查结论:代码链路全对(保存 keyring✓ / 读取 resolve_provider_secret✓ / 鉴权头 openai=Bearer·anthropic=x-api-key✓ / URL 智能拼接✓),401 来自服务端,非 devflow bug**。根因四选一(key 无效 / provider_type 不匹配 / base_url 错 / model 名错)。详见 [aichat-apikey-401排查-2026-06-15.md](./09-问题排查/aichat-apikey-401排查-2026-06-15.md)。 + +> **✅ 2026-10-09 销账**:本条为**排查结论登记**(非代码缺陷)——已定性「非 devflow bug,401 来自服务端」。属环境/配置类问题,无代码修复动作。**销账(降级非 🔴)。** + + +### ✅ 架构坏味道全面扫描(2026-06-18·6路并行 Explore agent·57K行代码·报告类归档) + +> 来源: God文件/长函数 + 循环依赖/耦合 + DRY违反 + 错误处理不一致 + 命名API/类型安全 + 测试覆盖/死代码。6 维度全覆盖。 + +> **✅ 2026-10-09 降级销账**:本条为**扫描报告登记**(6 路 Explore agent 产出的改进清单),**非缺陷条目**。报告内 P1 项(SMELL-P1-6 / conditions 条件引擎)已各自独立销账(见下方 ✅ 标注),无遗留阻断项。**降级非 🔴(报告类归档)。** + +**🟡 P1 应该改进 (9项)**: + +✅ **SMELL-P1-6** — **[P1🟢 已决·待推进]** String 类型滥用替 newtype/enum —— execution_id/status/tool_type 等 5+ 处改强类型(b 路线:仅 Rust 内部 newtype) + +**🆕 sweep 派生登记(2026-06-18·workflow w2xkw4ybh data 抽出 + wo7d6zdqm 多维度 sweep)**: + +- ✅ **conditions 条件引擎** — ✅ 全部完成:引擎完备+feature flag 默认开+executor 集成+前端 UI(WorkflowDagDisplay.vue 可编辑)+i18n 补齐 + +### ✅ 2026-06-24 aichat 可靠性修复(消息重叠×5 + 授权弹窗卡死 + 工具执行心跳误报 · 诊断workflow→论证workflow) + +> 剩余低优 [ ](非阻断): +> - [x] BUG-260624-02 残留 ✅ 已销账(2026-08-15):ToolCard pending 态倒计时落地(`_approvalTimers` 升级携带 startedAt/timeoutMs + `getApprovalRemainingMs` 真值起点,expireSecs/expireLevel computed,≤60s `.is-urgent` 红;不限时回退正计时;i18n expireIn zh/en)+ detached 窗口核验无缺口(AiDetached→AiChat 独立 startListener,ai-chat-event app 级广播,主窗口 detach 时 stopListener 不竞态,结论注释 useAiWindow:107-115) +> - [x] F-09 根因:streaming/currentText 全局单例改 per-conv ✅ **2026-08-07 核验销账**(per_conv HashMap + 前端 convStates Map enum 轨 + AiConvStateChanged 事件 per-conv 路由,根治多会话串扰)(根治多会话串扰,见 docs/待决策.md) +> - [ ] CR-260622-01-P2-2 [P2低优·评估降级] — **HTML/markdown/JSON 等非代码文件无 session 级缓存**。**2026-06-24 评估**:已有 `TOOL_RESULT_COMPRESS_ENABLED`(mod.rs:98/1029-1078 view-only 摘要,>2KB tool_result 压缩,LLM 视图摘要非全文回灌 prompt)部分缓解;read_symbol 治代码文件(主场景,降 24.4x)。非代码文件完整 session 缓存(path→hash+content+patch失效)设计复杂 + LLM patch 后重读确认行为不确定(缓存命中提示可能不够 LLM 仍重读),归 B 路线 prompt 策略(约束 patch 后不重读)更合适,工具侧完整缓存低优暂缓。 + +--- + +### ✅ 2026-08-09 AI 自审/工作流推进链路缺陷(✅ P0×3 已修,2026-10-05 核验销账) + +> 用户「检查 AI self-review 遇到的问题」触发对工作流推进链路的深度审查。**原结论「三条推进链全部不可用」已过时——P0×3 全部已修**(task_id 注入 workflow.rs:340 + AiNode 自审落库含 agent 模式 + TaskDetail 审批 UI 随 2eea011 审批重设计批落地),核验详录迁 [07-项目管理/todo归档/2026-10-05.md](./07-项目管理/todo归档/2026-10-05.md)。以下原始诊断保留作档案,修复状态见归档勘误。 + +**涉及文件**: +- `src-tauri/src/commands/workflow.rs`(run_workflow_inner 核心) +- `crates/df-nodes/src/task_workflow_templates.rs`(三模板) +- `crates/df-nodes/src/ai_node.rs` / `ai_self_review_node.rs` / `ai_node_helpers.rs` / `human_node.rs` +- `crates/df-workflow/src/executor.rs` / `dag.rs` / `registry.rs` / `dag_def.rs` +- `src/views/TaskDetail.vue` / `src/stores/project/workflow.ts` / `src/views/ProjectDetail.vue` + +--- + +### P0 run_agentic_loop 包 catch_unwind ✅已修(2026-10-05 核验:loop 主体 unwind_to_result 包裹,panic 转 teardown_round 补终态,5 单测锁定 mod.rs:3135-3165) +- **位置**:`agentic/mod.rs` run_agentic_loop 主体(无 catch_unwind) +- **现状**:任一轮 prepare/stream/finalize/tools 段 panic → task 死亡 → GeneratingGuard::drop 只迁 Idle+emit AiConvStateChanged,**不 emit AiError/AiCompleted**,heartbeat 随作用域死亡——「工具完成后流断+无终止事件」的精确根因形状(前端探测式续跑已兜住后果,此为后端根治)。 +- **修复**:整体包 `std::panic::catch_unwind(AssertUnwindSafe(...))`,unwind 时 emit AiError{error_type:Internal} 再收敛;guard Drop 兜底保留。核心 loop 改动,需谨慎设计+场景回放测试。 + +- [x] P1[需拍板→已实施] 熔断无收尾轮 ✅ 2026-10-06:max_rounds 打满不再直接截断,补一次无工具(tools=None)收尾 LLM 调用——system 指令「输出最终任务总结报告」,成功则 push 报告+落库后走原收尾;失败(全候选耗尽/MidStream)静默落回原截断路径(报告是增益非闸门,零新增停靠)。预算:仅 1 轮 max_retries=0,无放大(agentic/mod.rs 达上限分支) +- [x] P1[需拍板→已实施] 报告被视图压缩吞掉 ✅ 2026-10-06:summarize_tool_results 加新近度豁免(RECENCY_EXEMPT_TOOL_RESULTS=2)——末尾 2 条 tool_result 恒不压缩(豁免在冻结决策之前,不进决策表),委托报告/末轮工具结果全文可见;单测 summarize_recency_exempt_keeps_last_two_tool_results 锁定行为(stall_detector.rs) +- [x] P2 namespace 10MB 全局 LRU 淘汰:报告蒸发为「重新调用」死路+多代理共池互踩 ✅设计完成(2026-10-04 [设计文档](./02-架构设计/专项设计/namespace淘汰治理-设计-2026-10-04.md)):pin 机制+EvictionLog 富提示(替代裸 EVICTED_PLACEHOLDER)+owner 软隔离三招,零 schema 变更;2 拍板点(pin 上限保护/报告外默认 pin 范围),约 0.5-1 天 ✅ 2026-10-06 实施完成 e0b29b4(pin+EvictionLog+owner 软隔离三招全落地,子代理报告出口 pin 收编;验收 cargo check 零 error) +### 新缺陷(2026-10-05 22:2x 用户截图实证,与 N/D 清单同级) +8. [x] **工具输出 ANSI 转义裸露**(乱码):run_command 类工具输出含 ANSI 码(\x1b[7m 等)原样透传前端显示为 ␛[7m。修:执行器统一 strip_ansi(或注入 TERM=dumb/--color=never)+前端渲染层兜底过滤;验收:cargo test 输出在面板无 ␛ 残留。 ✅ 2026-10-06 补销: 已随 70d6e98 修复(ANSI 源头抑制+strip 兜底), 当时漏销 +9. [x] **会话标题 content 泄漏**:会话标题显示原始正文片段(如 `
附:截图实证单会话 206 轮/1500 万 tokens——C 批次(上下文管理)紧迫性最强活证,随 C-r0 优先消化。 + +### ✅ 提交后跟进(2026-10-05 23:0x 走查发现,cb260ab 已入库但未修·10/11/12 已修销账) +10. [x] **loop_state 对新会话/编排会话静默失效(高)** ✅修复(2026-10-06,提交「修复: loop_state 懒创建继承与入口早退清理」):懒创建继承 running(conversation.rs)+入口早退清残留(agentic/mod.rs)+list_parked 消费方=TopBar「可恢复」分区(V49 读侧)。验收:cargo check EXIT 0+vue-tsc EXIT 0。 +11. [x] 【中】conversation_repo.rs:817 注释矛盾 ✅修复(2026-10-06 同批):update_full 剥离 loop_state 列,封 lost-update 窗口,注释与实现一致。 +12. [x] 【中】前端 i18n 双侧同缺 aiChat.reattachToMain 键 ✅修复(2026-10-06 同批):zh/en 补键。 +> 以上为 72 文件走查结论(报告要点 23:0x 已交群),提交规范 18 笔全合规,质量项按本清单跟进。 + +13. [x] **①过程流式** ✅ 2026-10-06 实施:run_stream_refresher 消费面扩容(AiTextDelta + AiAgentRound + AiToolCallStarted/Completed 四事件,conv 路由过滤共用)——过程流不刷屏:卡片改「头部一行摘要 + 正文增量」结构,头行=轮次 + ⚙当前工具执行中/已完成 N 步(历史步骤折叠为计数,决策价值过滤);过程事件标脏强制重刷(纯工具轮无文本也有"活着"信号,治 206 轮长任务卡片静止猜卡死);签名去重防同 tick 事件风暴空刷;超限保尾截断口径不变。4 单测锁格式契约(首轮无头行/工具执行中/轮间空档/截断)。验收:cargo check EXIT 0 + feishu 7 passed。参照 u-claw 进度卡治理;桌面过程视图同步对齐见「桌面端能力同步清单」。 +16. [x] **纠偏注入通道**(用户 2026-10-05 深夜点名,飞书对话补充消息目前只能排队不能注入运行中任务)✅ 2026-10-06 实施:引擎层 `inject_runtime_message`(chat.rs,活跃守卫 conv_states.is_active → 短锁 push per_conv.messages → save_conversation 增量落库,loop 下轮 build_for_request 自然带上,u-claw 补拉同构);飞书侧 handle_message 白名单后加注入旁路(chat_map 命中且 conv 活跃 → 注入 + 卡片确认「下一轮生效」,非活跃 Err 落回常规 spawn 路径零行为变更)。不改锁语义/不打断 loop;桌面任务卡补充入口待 14 异步委托批配。验收:cargo check EXIT 0 + chat 17 passed + feishu 3 passed(含注入前缀格式契约单测)。 +> **14 升级(用户拍板 2026-10-06 凌晨):桌面也要异步能力,不做双端分叉**——异步委托是引擎层能力,桌面与飞书都是它的前端视图。实现要点:①引擎层 spawn 即受理返回,完成走通知桥广播(oneshot 改事件+等待者订阅) ②桌面任务卡 UI:受理/进行中 N 轮/完成+系统通知,发消息后可切会话/最小化 ③飞书为同一语义投影(受理卡/完成原地更新) ④锁语义保守起步=单会话单在途+排队,多并发看用量再说 ⑤yml 开关灰度。依赖链:16 纠偏通道(受理后补充入口)→13 过程流式(进行中视图)→本项。验收:桌面发任务→切走→完成收系统通知;飞书同构;双端行为一致。 + +> **设计约束(用户拍板 2026-10-06 凌晨):任务会话高度复杂,代码设计一定要优雅**——①状态机显式化:受理/运行/暂停/完成/失败用枚举统一,禁散落 bool 拼凑 ②单一事实源:任务状态只在引擎层,桌面/飞书 UI 都是投影(订阅不轮询) ③事件驱动贯通:13/14/16 共用同一事件总线,不做渠道私有旁路 ④优雅优先于速度:宁可工期换结构,不做特判堆补丁(优雅架构铁律)。 + +> **桌面端能力同步清单**(与渠道对齐,2026-10-06):16 纠偏注入做在 AiSession 会话层桌面天然受益;13 过程流式桌面过程视图同步对齐;14 任务卡+系统通知已在升级注记;另两件桌面专属:①V49 可恢复入口——走查实锤 list_parked 读侧零消费,桌面侧栏补「可恢复」列表消费它(断点续跑闭环最后一块)✅已做 e0b29b4(分组展示+点击恢复) ②缺陷9 会话标题泄漏桌面同症随批修。 +18. [x] **渠道错误话术吞因**(09:51 实证)✅ 2026-10-06 修复:①spawn Err 分类话术——并发护栏拒绝(「正在生成」)→「⏳ 该会话已有任务进行中,本条将在任务完成后处理;可稍后重发,或先 /status 查看进度」;其余失败→附截断原始原因(不吞因)。②探活告警口径修正——inflight 计数点从 spawn 前提前到 handle_message 白名单通过处(RAII 覆盖占位卡 HTTP+per-chat 锁排队窗口,原该窗口任务在跑却报「无在途会话」);旧计数点删除防双重计数。验收:cargo check EXIT 0 + feishu 7 passed。 +> 附:09:48 桌面纠偏「不是 haogame 是 redtimer」被运行中 loop 下一轮直接接住——16 纠偏注入的桌面形态天然存在,飞书侧对齐即可。 + +- [x] G5 V49「可恢复」入口(桌面侧栏消费 list_parked,断点续跑闭环最后一块)✅ e0b29b4 可恢复分组+点击恢复已落地 +### ⚠️ 退回重处理(2026-10-06 用户令,G2/G3 治理标准首次执行) +- 三笔提交(cec4d11 委托引擎/84df9b5 租约/622507c UI)已 reset --mixed 退回(内容全在工作区,零丢失,未推远端无需处理远端) +- **重做要求**:①先修 🔴10 loop_state 失效(G2 门禁:🔴不清零不得提交新功能)②描述按 G3 如实:实际干了 14+16+事件广播三件须逐件列全 ③i18n reattachToMain 键随批补上 ④修完按主题重新分组提交,销账 [x] 附验收输出(G4) +19. [x] **飞书 outbound 统一出口**(11:23 实证)✅ 2026-10-06 收口:勘察确认对话/受理/完成/注入回执已统一走 deliver_result(降级/裁剪/卡片链内聚);病灶「挂机进度汇总独立发送路径」在当前 main 不存在(属退回的挂机实验代码)。本批防复发收口:①新增 push_to_chat pub(crate) 正典出口(主动推送形态——挂机进度汇总/巡检通知将来落地时禁再开独立路径,强制走降级/裁剪/卡片链) ②ACTIVE_CLIENT OnceLock 全局句柄(渠道启动就绪,推送复用长连接 client 不临时建连,未启动静默跳过)。验收:cargo check EXIT 0 + feishu 7 passed。 +> 附:挂机汇报自报 🔴10 已修验证通过+43 文件产出——挂机模式推进有效,路径修复别误伤它。 + +20. [x] **委托制伪受理断层**(13:38 实证):受理卡在引擎确认可启动之前发出——12:57 任务停滞 Generating 时,「进度」spawn 被引擎拒但受理卡已发,之后零通知零进度(比 09:51 立即报错更隐蔽)。修:受理前过 can_accept_request,不可启动→直接回「前任务在跑,已排队/稍后」;可启动→才发受理卡+watcher 接力。附带:委托制下 inflight 生命周期改跟任务走(handle_message 提前返回致 18 修正再次失效,13:18 误报复现)。验收:同场景复测,占用期发消息得到明确排队提示而非静默受理。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +> **符号语义规范(2026-10-06 用户反馈:受理卡「✅ 已受理」歧义,随 19 统一出口一并落地)**: +> - 🚀 受理/开工(任务开始才用,禁 ✅)|⏳ 进行中/排队/等待|✅ 仅限完成且成功|❌ 失败(必须附原因)|⚠️ 警告/需注意(附数据)|📎 附录/附加信息 +> - 原则:符号=状态的一眼判据,同一符号跨场景语义必须一致;受理卡改为「🚀 已受理,后台执行中」;完成推送才允许 ✅ 开头。 + +## 飞书交互面全量盘查(2026-10-06,11 条缺陷按恼人度降序,修复前话术避免引用 /status) +34. [x] 🔴 **过程推送散乱刷屏**(14:06 用户截图反馈「信息太散乱爆炸了」):push_to_chat(:797)每调用必新开独立卡片,通道无聚合语义——昨日调试把链路测试/修复上线/加固明细等 L3 过程逐条直推 4+ 条进群,与 U3 分级相悖。修双层:①机制=push_to_chat 增滚动汇总卡——同 chat 维护在途事务卡 message_id(内存即可,重启另起),进度用已有 update_card(:730)原地追加;首推需 send_markdown_card 返回新卡 id(现 :683 is_ok() 丢弃,比照 reply_markdown_card);文本以 ✅/❌ 开头=终态,更新后清句柄下条另起新卡;更新失败回退新发并重绑(比照 deliver_result 兜底)。②纪律=L3 过程默认不推群,落日志+进度文件;push 收敛「需决策/完成✅」两时机,调试确需的验证推也进汇总卡不新开。验收:同事务多步进度群时间线只占 1 张卡,过程类推送零直达。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +21. [x] 🔴 **/status 被话术推荐但不存在**(chat.rs:387/:486 两处):用户发了被当任务烧一轮 LLM。修:实现 /status(读 conv 状态返回进度摘要,配合委托制价值高)或先改话术删除引用。验收:发 /status 得进度卡而非 spawn。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +22. [x] 🔴 **35min 兜底回查会投出上一任务旧回答冒充本次结果**(feishu_channel.rs:483/:939):回查整 conv 最后 assistant 文本无任务边界。修:回查限定受理时间戳之后的 assistant,无则给真实状态话术。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +23. [x] 🔴 **watcher 首超时后永不弃守**(:486 continue 与注释"二次超时弃守"相反)+错误路径只发 AiError 无 AiAgentFinished→每 35min 重刷过期文案+任务泄漏。修:二次超时真弃守+AiError 也广播 Finished(status=error)。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +24. [x] **无取消/审批通路**:跑偏无法叫停(飞书零 stop 通道);审批挂起对飞书用户不可见(流式卡冻结无解释)。修:/cancel 对接 ai_stop_loop;审批事件上卡片(「需审批:xxx,回复 批准/拒绝」)。 /cancel 停止通路+watcher 收口+审批推送与批准/拒绝决议(路径类授权留原语差异), ✅ 2026-10-07 细项批(待提交) +25. [x] **非文本消息静默丢**(:243):发图/文件零反馈。修:回「暂仅支持文本,媒体能力规划中」(D7 前的兜底)。 非文本兜底已存在于基线(:255 回「暂仅支持文本」), ✅ 2026-10-07 核验补销 +26. [x] **⏳话术承诺排队实际不入队**——话术与行为不符。修:改话术为实况(「可稍后重发」)或实现真排队(与 20 受理断层同修)。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +27. [x] **快任务兜底死分支**(:449 elapsed>=3s 入场即查恒假,「秒级任务直投终版」不可达)。修:受理卡发送后计时或事件驱动判定。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +28. [x] **注入末轮竞态**:loop 已在末轮则补充指令无人消费静默丢失。修:注入后检查 loop 剩余轮次,末轮则自动续一轮。 注入末轮顺延轮次预算(共享余量+轮顶动态判定+封顶 8), 5 单测锁定, ✅ 2026-10-07 细项批(待提交) +29. [x] **push_to_chat 取 KV map 首条**(:792):多 chat 绑定可能发错会话。修:按 conv 反查 chat。 push_to_chat 按 conv 反查+多绑定取最近活跃(签名加 conv_hint 零涟漪), ✅ 2026-10-07 细项批(待提交) +30. [x] 细节批::389 反引号不闭合;/new 大小写与附言容错;/new 时旧任务在跑无提示;标点半全角混排。 反引号闭合//new 容错与旧任务提示/半全角归一(parse_command 3 单测), ✅ 2026-10-07 细项批(待提交) + +- [x] 13a-1 刷新可观测:refresher 的 update_card 失败从"静默跳过"改为 feishu_log 记录+连续失败计数(≥3 连败告警)——当前刷了还是没刷、刷败全不可知,排查无从下手。 ✅ 2026-10-07 实施(feishu_channel.rs 单文件):刷失败 feishu_log 落盘(连败次数/chat/msg/chars)+连败 ≥3 告警一次(REFRESH_FAIL_ALERT_THRESHOLD=3,== 阈值触发后继续累计不重复)+刷新恢复记「此前连败 N 次」+成功归零。验收:cargo check -p devflow EXIT 0 + cargo test --lib feishu 16 passed 0 failed。 +31. [x] (自动续作) **AI-Ops→devflow 派工通道**(多 bot 编排正统通道,2026-10-06 缺口实证):AI-Ops 无法直接触发 devflow 开工(飞书 bot 消息不互触发/WebChat 无/remote_bridge 走 tunnel 不外用)。方案二选一:①u-claw 侧新增 ask_devflow(与既有 ask_uclaw 对等,经 remote_bridge 协议下发启动指令) ②devflow 侧看板轮询引擎(定时扫 todo.md 新🔴/高优条目自动开工,CLAUDE.md 领单纪律的运行时化=B5b 同源)。建议 ② 与 B5b 合并实现(同一套自动续作引擎)。验收:AI-Ops 写看板后 15min 内 devflow 自主开工,零人工中转。 ✅ 2026-10-07 方案②实施并与 B5b 合并落地(unattended.rs 636 行,B5b+31 同一引擎):看板解析器(编号条目四态+🔴 判据=标记后首字符防误判)/G2 门禁领单选择器(🔴 未清零只领红 fail-closed)/轮询调度器(tokio 常驻,KV 热读配置 df-unattended-{enabled,poll-secs,claim-scope,todo-path},默认关+300s)/收敛回读钩子 notify_batch_done 接 agentic emit_ai_completed_once 单点/parked 自动恢复优先/连续 3 次 spawn 失败熔断/领单原子写+spawn 失败回滚/引擎独立后台会话 pin_active=false。验收证据:①接线 rg 实测 lib.rs:77 unattended::start + agentic/mod.rs 收敛钩子调用 ②`cargo test --lib unattended` = 9 passed 0 failed(本轮实测修正 parse_statuses_and_red_marker 断言:🔴 判据仅认标记后首字符,正文提及不误判,与实现注释语义对齐) ③**活证**=本条目自身即引擎产出:AI-Ops 写板 → 引擎改写 `31. [→](自动续作)` → 派发本会话执行,零人工中转,15min 上限实测远低于下限。 + +36. [x] **换装脚本失败:杀进程后 3s 即安装,Windows 文件锁延迟释放致 NSIS 静默伪成功**(17:48 实证 FAIL: exe not updated,installer exit=0)。✅ 2026-10-06 晚全链闭环:**脚本加固**(swap-install.ps1 v3,语法 0 错实测):①句柄释放探测(独占打开循环,30s) ②校验升级为构建产物 SHA256 强基准(mtime 判据作废,NSIS 保留源文件 mtime 必误报) ③校验失败自动重试一次 ④SYSTEM 触发时转投用户会话重入,四级 fallback,最坏落 swap-need-start.flag。**上线验证过**(18:44 版手动换装成功):运行 exe SHA256=构建产物=0C45683B,进程 12764 运行中,H1 行为实测通过(19:13 DevFlow 回复带「📎 git 实测」段)。 +38. [x] **飞书出站 md 语法裸露净化+交互样式对齐 u-claw**(AI-Ops 2026-10-06 晚实证落板):任务报告 `##`/`**` 等 md 标记在飞书裸露(20:53 分析报告截图实锤)。根因:飞书 lark_md 不支持标题/表格等全谱语法,LLM 产出未经出站转换;主链 send_markdown_card(feishu_channel.rs:716,注释自认「text 类型会符号裸露」)只保粗体/链接,im-gateway StreamText 链纯文本直出。修:①出站净化层——md 转飞书友好格式(标题行转粗体/表格转列表/代码块适配),双层防线=出站兜底转换+agent 产出 prompt 约束,移植 u-claw 投递链降级/裁剪经验(u-claw 仓为范本) ②首回受理卡样式对齐 u-claw 任务状态卡(计数 N/M+逐条状态符+当前动作一行,10-02 用户拍板「效果不错」);受理卡符号 🚀 已由 AI-Ops 修(449 行)。验收:同类分析报告在飞书零 md 裸露标记,受理卡样式与 u-claw 一致。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) +39. [x] 🔴 **提交冻结令被无人值守链路击穿**(10-07 用户查问「最近提交不符合要求」实证):看板顶部 🔒 冻结令(10-06 深夜用户令,期间只改不提)生效后,挂机链路 01:56-57 仍连提 4 笔(c5ba755 续作引擎 811 行/d0281cb 飞书取消审批 345 行/7ab25e5 换装判据/73fa7c2 看板销账),且 73fa7c2 看板内容自书「细项批(待提交)」与提交事实自相矛盾——冻结仅是文本纪律,引擎与挂机会话的提交步无机械闸。修:①引擎自动提交路径与挂机会话提交步在 commit 前必查冻结标志(看板顶部 🔒 行视为生效,KV 标志更佳),在场即拒绝提交、改动留工作区;禁用全局 pre-commit 拦截(误伤用户本人提交) ②引擎派发的会话启动 prompt 注入「冻结期禁 commit」硬约束 ③解冻=用户「攒批提交通知」后由会话摘除 🔒 行再提。验收:冻结令在场时引擎/挂机会话 commit 步零执行(实测),摘令后恢复;随 31 引擎同测。 ✅ 2026-10-07 实施(commit_freeze 三层闸,零提交全在工作区):①机械闸=crates/df-execute/src/commit_freeze.rs 新模块——静态冻结位 set_frozen(引擎 unattended::run_tick 每轮按看板 🔒 行+KV df-commit-frozen 覆盖同步)+execute/execute_streaming 入口 check(),git commit 变体(git -C/复合命令 ;&&|/引号包裹/多空格)spawn 前拒绝,改动留工作区;未挂全局 pre-commit,用户本人终端零影响。②任务书注入:claim_and_spawn 冻结在场时追加「提交冻结令硬约束」段(禁 git commit/push,执行层闸兜底)。③解冻=用户摘 🔒 行或 KV df-commit-frozen=false,下一轮轮询(≤300s)自动同步放行。验收实测:cargo test -p df-execute 26 passed 0 failed(18 lib 含 6 冻结闸新用例:commit 变体识别/只读命令放行/冻结拦截/解冻恢复/静态位切换;8 shell 集成)+ cargo test -p devflow --lib unattended 10 passed 0 failed(含冻结判据新用例:🔒 行/blockquote `> 🔒` 形态/KV 双向覆盖/默认不冻结)+ cargo check -p devflow --lib EXIT 0。冻结在场实测=单测断言 check() 对 `git commit` 返回 Err 且 `git diff` 放行,摘令后恢复=static_flag_toggle 用例复位断言;真实看板形态(`> 🔒` 引用行)经 commit_freeze_board_and_kv_judge 实测命中。 + +900. [x](自动续作) 🔴 测试条目 + +901. [x](自动续作) 🔴 提交工作区改动(转交实战,2026-10-07):将当前工作区改动按主题分组提交并推送 origin main。(21:4x AI-Ops 重置回队:前两轮引擎会话被 20:38-21:02 u-switch 故障窗口「网络中断不完整」腰斩,重入预算耗尽停 [→],网络已恢复)要求:①提交前 G4 前置走查(风格/明显缺陷/调试残留/注释规范,发现问题先处理再提交) ②分组建议:挂机模式 P1 总装一笔(UnattendedPanel/Settings/TopBar/TaskBoundaryCard/i18n/unattended 两 IPC/lib.rs/feishu_notify)+看板销账一笔(todo.md) ③swap-install.ps1 若有改动先审 diff 再定去留;删除 scripts/check_plaintext_key.py(一次性诊断脚本已完成使命) ④提交信息中文类型开头逐件列全,commit-msg hook 会拦截违规 ⑤完成后本条改 [x] 并附 git log --oneline -3 输出。验收(10-08 AI-Ops 补录,引擎 22:06 完成):git log --oneline -3 → 0aac14e 文档: 看板销账: 挂机模式 P1 总装与换装校验修复入库,901 销账 / 6bfcf01 修复: 换装校验同版本重装误判不通过 / 2441101 新增: 挂机模式 P1 总装: 设置面板+顶栏徽章+边界卡精简+监控告警推送;main=origin/main 已推,工作区清零。 + +904. [x](自动续作) 🔴(10-08 AI-Ops 定位) 停靠(parked)状态写侧缺失,停靠卡「继续」必报「AI 未在暂停态」:**根因**=全仓无任何业务路径写 loop_state=parked,`"parked"` 仅 3 处读取判定(unattended.rs:604/conversation.rs:341/chat.rs:2712),唯一写入源为应用重启时 V49 残留扫描(running→parked)。**实证**=G2 探索熔断(agentic/mod.rs:2288)与同类失败熔断(2265)均只调 emit_task_parked 发事件(前端渲染停靠卡+继续按钮)、漏写 DB,会话 6a5a3e04 实测 loop_state=running;点「继续」→ chat.rs:2703 查 parked 不匹配→报错。**副作用**=残留 running 使侧栏/引擎误判会话仍在跑。**修法(推荐根源解)**=停靠状态写入收敛进统一出口:emit_task_parked 升级为「写 parked + 发事件 + 通知」三件套(或新建 park_task),五路停靠调用点(熔断两路/审批超时/挂起取消/流致命)全部对齐;契约注释同步改写。**附:UI 观感**=熔断同轮双卡叠加(求助卡 AiHelpRequired + 停靠卡 AiTaskParked)语义打架且文案冗长,建议熔断只发停靠一张卡。 + + ✅ **2026-10-09 实施完成(根源解 + 附项一并落地)**: + - **① 写侧收敛进统一出口**:`emit_task_parked`(src-tauri/src/commands/ai/mod.rs:1435) 升级为「写 parked + 发事件 + 通知」三件套——新增 ① 步经 `app.state::().ai_conversations.set_loop_state(conv_id, Some("parked"))`(fire-and-forget spawn,失败仅 warn 不阻断事件/通知);②③ 步(AiTaskParked 事件 + NeedAction 通知)保留原位。8 个业务调用点(H2 三闸/熔断两路/挂起取消/max_rounds/midstream/fatal + 审批超时)全部经此单点,零遗漏(grep 实证 9 处调用全指向统一出口)。 + - **② 契约注释改写**:函数 doc 由「单一只读语义」改为「三件套」;调用方职责(保存现场 + conv_state 复位)边界不变。 + - **③ df-storage 支撑**:`impl_repo!` 宏生成的 Repo 结构体加 `#[derive(Clone)]`(`Arc>` 天然可克隆),使 `emit_task_parked` 能克隆 Repo 句柄进 spawn。 + - **④ 附项(熔断只发停靠一张卡)**:新增开关 `CIRCUIT_BREAKER_PARK_ONLY=true`(agentic/mod.rs:343);L1 同类失败熔断 / G2 探索熔断 / 破坏性回环熔断三处 `AiHelpRequired` 求助卡 emit 均包在 `if !CIRCUIT_BREAKER_PARK_ONLY` 内,求助卡关键信息(失败样本/路径)内联进 AiTaskParked 的 reason;开关置 false 即回退旧双卡行为。前端零改动(TaskParkedCard 词表已含 circuit_breaker/stall_breaker)。 + - **⑤ 回归测试**:新增 `set_loop_state_parked_roundtrip`(crates/df-storage/src/crud/conversation_repo.rs)锁死「写 parked → get_by_id 读回 parked / 清态读回 None」契约。 + - **与 V49 残留扫描无冲突**:`mark_running_as_parked` 的 SQL 为 `WHERE loop_state='running'`,不会覆盖已写 parked 的行(state.rs:459 启动扫描语义不变)。 + - **验收**:`cargo check -p df-storage` EXIT 0;`cargo check -p devflow` EXIT 0(10 warning 全为无关模块既有 dead_code);`cargo test -p df-storage` **200 passed + 12 passed, 0 failed**;`cargo test -p devflow --lib` **748 passed, 0 failed, 8 ignored**;`bunx vue-tsc --noEmit` EXIT 0。 + +905. [x](自动续作) 🔴 「@」文案 HTML 实体未解码(10-09 用户实测截图): 输入框 placeholder 与提及提示字面显示 `@` 而非 `@`。位置共 8 处——`src/i18n/zh-CN/aiChat.ts` 与 `src/i18n/en/aiChat.ts` 各 4 处(placeholderDefault / mentionHint / searchFilesBlocked / projectLabel)。根因:文案写了 HTML 实体,前端文本插值(`{{ }}`)不做 HTML 解码故字面显示。修:8 处 `@` → 直接写 `@`(纯文本字面量,无需转义)。 + ✅ 2026-10-09 修复完成。改动:zh-CN/aiChat.ts L139/144/236/330 + en/aiChat.ts L139/144/235/329 共 8 处 `@` → `@`。 + 验收证据:① `findstr /s /c:"@" *.ts *.vue *.tsx *.js`(src 目录) → 无匹配(exit 1),全仓零残留;② 回读 8 行确认字面 `@`(zh: "输入消息...(/ 触发技能,@ 引用实体)" / "@ 引用项目/任务" / "...请用 @[项目] 引用..." / "@项目";en 对应 4 处);③ `bunx vue-tsc --noEmit` → EXIT=0。 + +906. [x](自动续作) 🔴 停靠卡「总是弹出」频次治理(响应流中断路径,10-09 用户反馈): 流中途断(agentic/mod.rs:1345 `RoundOutcome::MidStreamSaved`)直接 `emit_task_parked` 停靠,无 Fatal 家族的 `ROUND_REENTRY_MAX` 重入预算——网络抖动一次即弹「响应流中断/任务已停靠」卡并进行中任务终止(10-09 u-switch 抖动期用户实测频繁弹)。修建议:①midstream 先加短重试/续传预算(对齐 Fatal 重入语义,耗尽才停靠)②前端同会话短时内停靠卡节流(避免重发-中断-再弹循环观感)。注:本条是打扰治理,与 904 写侧修复正交(904 让对方能点「继续」,本条降低「被迫点继续」频次)。 + + 【✅ 2026-10-09 实施完成 — 双管齐下:后端续传预算 + 前端停靠节流】 + + 【① 后端 MidStream 短重试/续传预算(stream_relay.rs)】新增 `MIDSTREAM_REENTRY_MAX=2` + `MIDSTREAM_REENTRY_BACKOFF_SECS=[5,15]` + `MIDSTREAM_CONTINUE_PROMPT`,对齐 Fatal 家族重入语义但采用**续传**而非重发(整轮重发会与前端已渲染 partial 重复): + - 原 candidate 循环包裹进 `'midstream: loop`:流中途断(`incomplete=true`)且用户未点停时,若预算未耗尽 → ①已接收 partial 落库为 assistant 消息 ②emit `AiAgentRound{round:0}` 触发前端 flush(partial 落独立气泡,续传渲染到新气泡,无重复) ③续传请求 = 原请求 + partial(assistant)+ 续传提示(user),模型从断点继续 ④退避 5s/15s 后重入 candidate 循环(全新 30s 流前预算)。 + - 续传成功 → 本轮转 `Continue`(走正常收尾,**不弹停靠卡**);预算耗尽 → 落回原保文+`emit_task_parked` 路径(系统提示文案区分「已自动续传重试仍未恢复」)。 + - 收尾保文修正:续传路径已落中间各段,终局只补「最后一段」(break 前未 push),无重叠不重复。 + + 【② 前端同会话停靠卡节流(useAiLifecycleEvents.ts)】`recordTaskParked` 增 `PARKED_REPARK_THROTTLE_MS=20000` 节流:同 conv 距上次停靠 <20s 且**卡仍在(未被用户处置)** → 保留原卡不刷新(压抖动期重复弹/重渲染);卡已被用户「继续/停止」摘除 → 仍放行新卡(不吞用户需决策的真实新终局)。安全边界:仅压「未处置的重复弹」,不吞「已处置后的真实新停靠」。 + + 【验收输出】 + - `cargo check -p devflow` → Finished dev profile(0 error,10 warning 全为无关模块既有 dead_code) + - `cargo test -p devflow` → **750 passed; 0 failed; 8 ignored**(含新增 `midstream_reentry_tests` 2 项:退避表长覆盖上限+单调递增+首退避 🔎 **2026-10-10 接手核查补注(devflow 侧)**: + > - **实现口径澄清**:01:0x 落地的是**「未邻接即剥除」**——sanitize.rs step2 判定由 `resolved_ids.contains` 收紧为 `adjacency.get(&i).contains`(`crates/df-ai/src/context/sanitize.rs:87-92`),openai_compat.rs `sanitize_openai_triplets` step2 同口径(`crates/df-ai/src/openai_compat.rs:401-434`);被 system/user 夹层隔开的 assistant 头连同其 tool 响应从**发送视图**剔除(step3/openai step3 按 orphaned_ids/head_ids 对称丢弃)。**此非设计 ① 所述「相邻性归位/只重排不删内容」**,而是判定口径收紧后走既有「未闭合→剥除」路径——安全(不 400)但**有损**(该轮工具交互对本轮 LLM 不可见;持久化 view-only 不动)。 + > - **代价评估**:夹层主因=后台压缩竞态(偶发)或用户插话(工具执行窗口内,纠偏 16 场景可复现)——后者每插话一次即丢一轮工具结果,模型可能重跑工具/误判。 + > - **独立复验**:`cargo test -p df-ai --lib` **567 passed / 0 failed**;`cargo check --workspace` **EXIT 0**;夹层回归用例 `sanitize_strips_calls_when_response_separated_by_interloper` 实测通过。 + > - **遗留未实施(原 ② 源头守静)**:后台压缩提交点仍无「序列静止」判据(持锁后末条为悬空 ToolCallHead 时不得 push 摘要),楔入仍会发生;设计 ① 的无损「归位」变体亦未实现——二者合并登记为 **909**。**落点已核**(2026-10-10 更正):`src-tauri/src/commands/ai/agentic/context_lifecycle.rs`(原条目行号引用准确)——摘要 push 点 `:269`(LLM 成功)/`:372`(`keyword_fallback_commit` 兜底),双取锁 `:312`→`:322`;`compress.rs` 仅产摘要文本,不改 ContextManager。 + +908. [x] 「settings.loading」i18n key 缺失**已带病入库**(a8c92e9,10-09 AI-Ops 走查发现): `src/components/settings/AboutPanel.vue:204` 用 `t('settings.loading')`,但 zh-CN/en `settings.ts` 均无此 key(grep 实证)→ 版本信息返回前渲染字面串 `settings.loading`。修:补 `loading` 键(两边)或改用已有键。同类走查发现:AboutPanel 曾用 `window.dispatchEvent('df-toast')`(全仓无监听)已随提交改动,复核确认现机制一致。 + + ✅ **2026-10-10 修复**:`src/i18n/zh-CN/settings.ts` / `src/i18n/en/settings.ts` 各补 `loading` 键(紧随 `descAppVersion`,与仓内 auditLog/common/fileExplorer/taskDetail 各命名空间自持 `loading` 的惯例一致)。验收:`grep -n "loading:" src/i18n/zh-CN/settings.ts src/i18n/en/settings.ts` → 各 1 命中;`bunx vue-tsc --noEmit` → EXIT 0。 diff --git a/docs/todo.md b/docs/todo.md index 03cf56e..569971a 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -14,6 +14,7 @@ > **2026-06-18 归档**: 已完成 `[x]` 与历史分析段已迁至 [07-项目管理/todo归档/2026-06-18.md](./07-项目管理/todo归档/2026-06-18.md)。 > **2026-06-27 归档**: 已完成 `[x]`/`✅` 项已迁至 [07-项目管理/todo归档/2026-06-27.md](./07-项目管理/todo归档/2026-06-27.md)。 > **2026-10-05 归档**: 7 个实测核验全完成的历史走查段(父子任务支持/对话上下文透明化/文件拆分升级/ToolCard UI 合并/灵感模块诊断/aichat 深度走查/TaskDetail 按钮走查)迁至 [07-项目管理/todo归档/2026-10-05.md](./07-项目管理/todo归档/2026-10-05.md),销账批注与滞后勘误见该文件。 +> **2026-10-10 归档**: 全量完成态条目/段落(编号 [x] 32 条 + 勾选 [x] + 标题带 ✅ 且段内零未完成项的历史段,含 907/908 与飞书交互面全量盘查批)迁至 [07-项目管理/todo归档/2026-10-10.md](./07-项目管理/todo归档/2026-10-10.md)。迁移为机械行搬运(零措辞改动),核对:未完成项 110 条零丢失、完成项 37 条全入归档、行数守恒。 > > **2026-08-05 编号碰撞登记**: `F-260619-03` 被两个特性复用(消息拆分存储=文档本体 + AI工具路径授权=复用)。子代理审查确认:改 18 处编号无实际价值有风险,**暂不改代码**,后续新增注释编号统一走日期+序号体系、避免复用已有编号。路径授权若后续单独立项再独立编号。 > **2026-08-15 编号碰撞登记**: `REFACTOR-260619-04` 被复用(父③③.2 审批状态机拆分=待办 vs ToolCard 拆分=已完成)。后续引用审批状态机拆分时用「审批状态机拆分」描述,不再沿用该编号。 @@ -184,27 +185,6 @@ graph TD > **📦 已完成项归档**: [07-项目管理/todo归档/2026-06.md](./07-项目管理/todo归档/2026-06.md) — 2026-06-18 拆分, 已完成 `[x]` 与历史分析段迁此。另有 [2026-06-18.md](./07-项目管理/todo归档/2026-06-18.md) / [2026-06-27.md](./07-项目管理/todo归档/2026-06-27.md) — 本次归档。 -### ✅ 2026-06-20 BUG-260620-05 F-260619-03 工程内路径误弹窗(reload_allowed_dirs 丢 workspace_root·b22e9ae 回归) - -**现象**:AI Chat file_info 等文件工具访问工程内路径(如 docs/02-架构设计/单对话并行多轮-设计-2026-06-20.md)触发授权弹窗,用户称"本来就有访问权限,为什么还来申请"。 - -**根因**:`state.rs:586-619 reload_allowed_dirs` 合并 `kv_dirs(KV allowed_dirs)+ project_dirs(projects.bind_directory)` 为 `all_dirs`,**仅当 `all_dirs.is_empty()` 时(:618)才插入 workspace_root**。KV 配过 allowed_dirs 或有绑定项目 → all_dirs 非空 → **workspace_root 不入 persistent** → 工程内路径(在 workspace_root 下)`is_authorized`(:356 starts_with)失败 → `check_path_authorization` 返 `NeedsAuthorization` → 弹窗。 - -**注释承诺失配**::346-348 / :583 / :617 注释均称"KV 未配时保持 workspace_root 免授权(向后兼容)",但 :618 条件用 `all_dirs`(含 project_dirs),project_dirs 非空也丢 root。b22e9ae "reload 尊重用户 persistent" 引入的回归。 - -**用户政策指令**(2026-06-20):"默认直接访问,明确知道无权限才申请"。指向工程内(workspace_root 下)默认免授权,仅 workspace_root 外非白名单路径才弹窗。 - -**修法(层1·1 行确定性 bug)**:`state.rs:618` 去掉 `if all_dirs.is_empty()` 条件包裹,**无条件** `set.insert(workspace_root_path())`(工程内始终免授权,对齐用户政策 + 注释承诺)。不破坏现有能力:workspace_root 经 `set/get_allowed_dirs`(:664/680 filter root)本就不可见不可删,"用户删 root"语义未落地,保 root 无冲突。 - -**层2 待决策(政策范围)**:用户"默认直接访问"是否要求更宽语义——workspace_root 外的非黑名单路径也默认放行(翻转 `is_authorized` 为黑名单制,实质去白名单)?涉 P0 去固定根决策方向,见待决策.md。dev 自用场景层1 即满足。 - -**关联**:F-260619-03(todo :528 b22e9ae 回归源)/ 待决策 workspace_root 分发适配(方案b 用户项目绑定)/ memory [[devflow-project-path-binding]] -**状态**:✅ 已实施(层1 无条件插入 workspace_root + 层2 白名单制 persistent/session/once + 黑名单兼底,对齐 2026-06-28 用户决策) - -**✅ 2026-10-09 复核销账**:方案已演进为**更优的动态推导**(优于原「无条件插入 workspace_root」)——`state.rs:579` 注释实证「不再硬塞编译期 workspace_root(CARGO_MANIFEST_DIR)」,原因:编译机路径分发后失效反成脏白名单。工程根授权改由两途径:`projects.bind_directory`(绑项目自动授权)+ `Settings.allowed_dirs`(手动白名单),`is_authorized` 另有系统目录黑名单兜底(层2)。原 bug 根因(工程内路径因 `all_dirs.is_empty()` 条件丢 root 而误弹窗)已随该演进消除。**销账。** - ---- - ### 💡 2026-06-16 新需求(已分析·待实施) > 功能需求,已走查定位链路 + 方案记录,待用户确认后实施。 @@ -213,13 +193,6 @@ graph TD > **✅ 2026-08-07 核验销账(F-09 B 阶段全部落地)**:三方独立核验(2 agent + 主代 grep 抽查)确认 06-19 草案所有改造点(§4.1-4.7)已落地,**且代码超出草案**:① AiSession 去单例 → `per_conv: HashMap`(mod.rs:593,顶层会话级字段全删)② ConvStateStore DashMap 无锁(治 guard.reset 锁竞争卡死连环)③ 三处退出校验改 conv 存在性(agentic/mod.rs 7 处,零旧 `active_conversation_id != conv_id` 残留)④ GeneratingGuard per-conv + loop_epoch owner token(F1 治同 conv 双 loop 竞态)⑤ last_heartbeat 心跳(F2 治卡死兜底误判)⑥ IPC 全加 conv_id + ConvStateStore 零锁读(switch 删 readonly / create 不杀旧 loop)⑦ 前端 convStates Map(enum 轨替代 bool Set)+ d2 多窗口(useAiWindow.ts `ai-detached-${convId}`)已落地。**剩余仅 B 路线 Phase3 单对话并行多轮**(`per_sub_flow` 占位未接,另线 [[aichat-b-route-parallel-multiround]])。c-1 决策已被用户「不设并发会话上限」推翻作废。 -### ✅ aichat API Key 401 排查(2026-06-16·非 bug 已销账) - -> 用户报对话失败「调用失败: API Key 无效或无权限」+ 重设 key 无效。**排查结论:代码链路全对(保存 keyring✓ / 读取 resolve_provider_secret✓ / 鉴权头 openai=Bearer·anthropic=x-api-key✓ / URL 智能拼接✓),401 来自服务端,非 devflow bug**。根因四选一(key 无效 / provider_type 不匹配 / base_url 错 / model 名错)。详见 [aichat-apikey-401排查-2026-06-15.md](./09-问题排查/aichat-apikey-401排查-2026-06-15.md)。 - -> **✅ 2026-10-09 销账**:本条为**排查结论登记**(非代码缺陷)——已定性「非 devflow bug,401 来自服务端」。属环境/配置类问题,无代码修复动作。**销账(降级非 🔴)。** - - ### 🟦 架构审查待修项(2026-06-16) > 纯架构层评估(边界/依赖/抽象/扩展性/状态管理),详见 [架构审查-2026-06-15.md](./05-代码审查/架构审查-2026-06-15.md)。共 🔴6 🟡6 ⚪4 + 亮点 6。与 06-14 三份报告去重(不重复 bug/性能 FR-*、aichat AR-*)。 @@ -230,20 +203,6 @@ graph TD ### P1 — 重要缺陷 -### ✅ 架构坏味道全面扫描(2026-06-18·6路并行 Explore agent·57K行代码·报告类归档) - -> 来源: God文件/长函数 + 循环依赖/耦合 + DRY违反 + 错误处理不一致 + 命名API/类型安全 + 测试覆盖/死代码。6 维度全覆盖。 - -> **✅ 2026-10-09 降级销账**:本条为**扫描报告登记**(6 路 Explore agent 产出的改进清单),**非缺陷条目**。报告内 P1 项(SMELL-P1-6 / conditions 条件引擎)已各自独立销账(见下方 ✅ 标注),无遗留阻断项。**降级非 🔴(报告类归档)。** - -**🟡 P1 应该改进 (9项)**: - -✅ **SMELL-P1-6** — **[P1🟢 已决·待推进]** String 类型滥用替 newtype/enum —— execution_id/status/tool_type 等 5+ 处改强类型(b 路线:仅 Rust 内部 newtype) - -**🆕 sweep 派生登记(2026-06-18·workflow w2xkw4ybh data 抽出 + wo7d6zdqm 多维度 sweep)**: - -- ✅ **conditions 条件引擎** — ✅ 全部完成:引擎完备+feature flag 默认开+executor 集成+前端 UI(WorkflowDagDisplay.vue 可编辑)+i18n 补齐 - ### 🟡 AI Chat 交互体验改进(2026-06-14 方案 → 待办化) > 来源:[aichat交互体验改进方案-2026-06-14.md](./02-架构设计/构想审查/aichat交互体验改进方案-2026-06-14.md)。7 大类 20 个改进项,按原文优先级分 4 批。**去重**:§1.3 复制 ⊂ F-260615-13/14(已写)、§3.4 新建中断 ⊂ B-260615-10(已做软复位,本文档加 confirm 增强)、§6.2 分离窗口同步 ⊂ CR-24/25(已写)。**状态: 待讨论→看板跟踪**。 @@ -300,15 +259,6 @@ graph TD **剩余开放项**:P3-B-260622 [P3·真机前置] — **manifest appid 占位**。`PLACEHOLDER` 无法真机调试,需微信公众平台注册的真 appid。— `apps/df-miniapp/src/manifest.json` -### ✅ 2026-06-24 aichat 可靠性修复(消息重叠×5 + 授权弹窗卡死 + 工具执行心跳误报 · 诊断workflow→论证workflow) - -> 剩余低优 [ ](非阻断): -> - [x] BUG-260624-02 残留 ✅ 已销账(2026-08-15):ToolCard pending 态倒计时落地(`_approvalTimers` 升级携带 startedAt/timeoutMs + `getApprovalRemainingMs` 真值起点,expireSecs/expireLevel computed,≤60s `.is-urgent` 红;不限时回退正计时;i18n expireIn zh/en)+ detached 窗口核验无缺口(AiDetached→AiChat 独立 startListener,ai-chat-event app 级广播,主窗口 detach 时 stopListener 不竞态,结论注释 useAiWindow:107-115) -> - [x] F-09 根因:streaming/currentText 全局单例改 per-conv ✅ **2026-08-07 核验销账**(per_conv HashMap + 前端 convStates Map enum 轨 + AiConvStateChanged 事件 per-conv 路由,根治多会话串扰)(根治多会话串扰,见 docs/待决策.md) -> - [ ] CR-260622-01-P2-2 [P2低优·评估降级] — **HTML/markdown/JSON 等非代码文件无 session 级缓存**。**2026-06-24 评估**:已有 `TOOL_RESULT_COMPRESS_ENABLED`(mod.rs:98/1029-1078 view-only 摘要,>2KB tool_result 压缩,LLM 视图摘要非全文回灌 prompt)部分缓解;read_symbol 治代码文件(主场景,降 24.4x)。非代码文件完整 session 缓存(path→hash+content+patch失效)设计复杂 + LLM patch 后重读确认行为不确定(缓存命中提示可能不够 LLM 仍重读),归 B 路线 prompt 策略(约束 patch 后不重读)更合适,工具侧完整缓存低优暂缓。 - ---- - ### 🔍 2026-08-01 走查剩余待办(P2/P3/专项·本会话登记) > 本次走查剩余项登记,未实施代码(守 session-role-diagnose-only)。专项已落 memory,此处留指针。 @@ -460,19 +410,6 @@ graph TD **P3(25 项)**: 见详单文档(MCP-7~16 / EX-1~4 / PRJ-2~4 / TUN-2~4 / RLY-2~5) > **全系统走查(阶段②)完成 ✅**——前端 13 模块 + 后端 9 命令 + 全部 crate 已走查登记。进入**阶段③批量打包推进 /advance**。 -### ✅ 2026-08-09 AI 自审/工作流推进链路缺陷(✅ P0×3 已修,2026-10-05 核验销账) - -> 用户「检查 AI self-review 遇到的问题」触发对工作流推进链路的深度审查。**原结论「三条推进链全部不可用」已过时——P0×3 全部已修**(task_id 注入 workflow.rs:340 + AiNode 自审落库含 agent 模式 + TaskDetail 审批 UI 随 2eea011 审批重设计批落地),核验详录迁 [07-项目管理/todo归档/2026-10-05.md](./07-项目管理/todo归档/2026-10-05.md)。以下原始诊断保留作档案,修复状态见归档勘误。 - -**涉及文件**: -- `src-tauri/src/commands/workflow.rs`(run_workflow_inner 核心) -- `crates/df-nodes/src/task_workflow_templates.rs`(三模板) -- `crates/df-nodes/src/ai_node.rs` / `ai_self_review_node.rs` / `ai_node_helpers.rs` / `human_node.rs` -- `crates/df-workflow/src/executor.rs` / `dag.rs` / `registry.rs` / `dag_def.rs` -- `src/views/TaskDetail.vue` / `src/stores/project/workflow.ts` / `src/views/ProjectDetail.vue` - ---- - ## 🔴 P0 缺陷(3 项,工作流推进 100% 失败) ### P0-A task_id 断裂:AiSelfReviewNode 必定失败 @@ -561,11 +498,6 @@ graph TD > 来源:中断根治批次全量走查(正常设计路径无漏 emit,风险集中四类)。P1-2/P0-1/P2-1 本批已修,以下为登记项。 -### P0 run_agentic_loop 包 catch_unwind ✅已修(2026-10-05 核验:loop 主体 unwind_to_result 包裹,panic 转 teardown_round 补终态,5 单测锁定 mod.rs:3135-3165) -- **位置**:`agentic/mod.rs` run_agentic_loop 主体(无 catch_unwind) -- **现状**:任一轮 prepare/stream/finalize/tools 段 panic → task 死亡 → GeneratingGuard::drop 只迁 Idle+emit AiConvStateChanged,**不 emit AiError/AiCompleted**,heartbeat 随作用域死亡——「工具完成后流断+无终止事件」的精确根因形状(前端探测式续跑已兜住后果,此为后端根治)。 -- **修复**:整体包 `std::panic::catch_unwind(AssertUnwindSafe(...))`,unwind 时 emit AiError{error_type:Internal} 再收敛;guard Drop 兜底保留。核心 loop 改动,需谨慎设计+场景回放测试。 - ### P1 conv 删除类静默退出族 6 处补终态事件 - **位置**:`agentic/mod.rs:417/:1316/:1379/:1437/:1593/:1423`、`stream_relay.rs:520-525`(MidStream 保文时 conv-deleted 跳过 teardown emit,最刺眼) - **现状**:生成期间会话被删,裸 return 仅 guard Drop 迁 Idle,无终止事件。 @@ -601,9 +533,6 @@ graph TD > 与上节 25 项零重叠(报告尾附排除对账)。修复批:四路走查修复收口后,确定性项先行,需拍板项进拍板集。 -- [x] P1[需拍板→已实施] 熔断无收尾轮 ✅ 2026-10-06:max_rounds 打满不再直接截断,补一次无工具(tools=None)收尾 LLM 调用——system 指令「输出最终任务总结报告」,成功则 push 报告+落库后走原收尾;失败(全候选耗尽/MidStream)静默落回原截断路径(报告是增益非闸门,零新增停靠)。预算:仅 1 轮 max_retries=0,无放大(agentic/mod.rs 达上限分支) -- [x] P1[需拍板→已实施] 报告被视图压缩吞掉 ✅ 2026-10-06:summarize_tool_results 加新近度豁免(RECENCY_EXEMPT_TOOL_RESULTS=2)——末尾 2 条 tool_result 恒不压缩(豁免在冻结决策之前,不进决策表),委托报告/末轮工具结果全文可见;单测 summarize_recency_exempt_keeps_last_two_tool_results 锁定行为(stall_detector.rs) -- [x] P2 namespace 10MB 全局 LRU 淘汰:报告蒸发为「重新调用」死路+多代理共池互踩 ✅设计完成(2026-10-04 [设计文档](./02-架构设计/专项设计/namespace淘汰治理-设计-2026-10-04.md)):pin 机制+EvictionLog 富提示(替代裸 EVICTED_PLACEHOLDER)+owner 软隔离三招,零 schema 变更;2 拍板点(pin 上限保护/报告外默认 pin 范围),约 0.5-1 天 ✅ 2026-10-06 实施完成 e0b29b4(pin+EvictionLog+owner 软隔离三招全落地,子代理报告出口 pin 收编;验收 cargo check 零 error) - [ ] P2 被拒事实父上下文仅报告尾段单副本,LLM 压缩 prompt 无保留指令[需拍板] - [ ] P2 完成卡(前端全文)与父上下文(压缩)双轨无「已消化」降级机制 ✅设计完成(2026-10-04 [设计文档](./02-架构设计/专项设计/子代理完成卡已消化降级-设计-2026-10-04.md)):勘察修正——重启回补已存在(aiShared.ts:677 ai_sub_agent_list 反查重建),设计聚焦 digested 标记(KV 持久)+卡降级交互+ai_sub_agent_trace 只读工具(LLM 追问自助通道,兼为 namespace 蒸发兜底);2 拍板点,约 1-1.5 天 - [ ] P2 子会话缺 maybe_auto_compress 层+预算恒 128k 默认[需拍板] @@ -830,32 +759,11 @@ graph TD - 飞书四修+流式(占位卡片 edit_card 原地更新/表格降级/clip_for_card 超长裁剪/启动日志)——已上线 - 知识库 691b351d/c142e6b2 已 published+high(c142e6b2 含权威源声明);9c848ab 确认零丢失(rescue 分支锚定) -### 新缺陷(2026-10-05 22:2x 用户截图实证,与 N/D 清单同级) -8. [x] **工具输出 ANSI 转义裸露**(乱码):run_command 类工具输出含 ANSI 码(\x1b[7m 等)原样透传前端显示为 ␛[7m。修:执行器统一 strip_ansi(或注入 TERM=dumb/--color=never)+前端渲染层兜底过滤;验收:cargo test 输出在面板无 ␛ 残留。 ✅ 2026-10-06 补销: 已随 70d6e98 修复(ANSI 源头抑制+strip 兜底), 当时漏销 -9. [x] **会话标题 content 泄漏**:会话标题显示原始正文片段(如 `
附:截图实证单会话 206 轮/1500 万 tokens——C 批次(上下文管理)紧迫性最强活证,随 C-r0 优先消化。 - -### ✅ 提交后跟进(2026-10-05 23:0x 走查发现,cb260ab 已入库但未修·10/11/12 已修销账) -10. [x] **loop_state 对新会话/编排会话静默失效(高)** ✅修复(2026-10-06,提交「修复: loop_state 懒创建继承与入口早退清理」):懒创建继承 running(conversation.rs)+入口早退清残留(agentic/mod.rs)+list_parked 消费方=TopBar「可恢复」分区(V49 读侧)。验收:cargo check EXIT 0+vue-tsc EXIT 0。 -11. [x] 【中】conversation_repo.rs:817 注释矛盾 ✅修复(2026-10-06 同批):update_full 剥离 loop_state 列,封 lost-update 窗口,注释与实现一致。 -12. [x] 【中】前端 i18n 双侧同缺 aiChat.reattachToMain 键 ✅修复(2026-10-06 同批):zh/en 补键。 -> 以上为 72 文件走查结论(报告要点 23:0x 已交群),提交规范 18 笔全合规,质量项按本清单跟进。 - ## 流畅性根本解三件(2026-10-05 深夜用户立项:1+2+3,④双通道搁置) -13. [x] **①过程流式** ✅ 2026-10-06 实施:run_stream_refresher 消费面扩容(AiTextDelta + AiAgentRound + AiToolCallStarted/Completed 四事件,conv 路由过滤共用)——过程流不刷屏:卡片改「头部一行摘要 + 正文增量」结构,头行=轮次 + ⚙当前工具执行中/已完成 N 步(历史步骤折叠为计数,决策价值过滤);过程事件标脏强制重刷(纯工具轮无文本也有"活着"信号,治 206 轮长任务卡片静止猜卡死);签名去重防同 tick 事件风暴空刷;超限保尾截断口径不变。4 单测锁格式契约(首轮无头行/工具执行中/轮间空档/截断)。验收:cargo check EXIT 0 + feishu 7 passed。参照 u-claw 进度卡治理;桌面过程视图同步对齐见「桌面端能力同步清单」。 14. [ ] **②任务式委托**:重任务(agentic loop 任务)受理即回任务卡(「已受理·预计 N 分钟」),用户可离开;完成后卡片原地更新+结果在卡。底座已有(30min 超时任务后台不丢),重构 handle_message 等待形态为受理/完成两段式。验收:发「走查代码」3s 内收到受理卡,完成后卡片变结果,全程无需等待。 15. [ ] **③C 批次关联**:本组两件的提速收益依赖 C 批次(prompt 71K→按需)——C-m1/m2 优先级顺位提升(单轮推理是 80% 耗时大头,见流畅性分析 2026-10-05)。 -16. [x] **纠偏注入通道**(用户 2026-10-05 深夜点名,飞书对话补充消息目前只能排队不能注入运行中任务)✅ 2026-10-06 实施:引擎层 `inject_runtime_message`(chat.rs,活跃守卫 conv_states.is_active → 短锁 push per_conv.messages → save_conversation 增量落库,loop 下轮 build_for_request 自然带上,u-claw 补拉同构);飞书侧 handle_message 白名单后加注入旁路(chat_map 命中且 conv 活跃 → 注入 + 卡片确认「下一轮生效」,非活跃 Err 落回常规 spawn 路径零行为变更)。不改锁语义/不打断 loop;桌面任务卡补充入口待 14 异步委托批配。验收:cargo check EXIT 0 + chat 17 passed + feishu 3 passed(含注入前缀格式契约单测)。 -> **14 升级(用户拍板 2026-10-06 凌晨):桌面也要异步能力,不做双端分叉**——异步委托是引擎层能力,桌面与飞书都是它的前端视图。实现要点:①引擎层 spawn 即受理返回,完成走通知桥广播(oneshot 改事件+等待者订阅) ②桌面任务卡 UI:受理/进行中 N 轮/完成+系统通知,发消息后可切会话/最小化 ③飞书为同一语义投影(受理卡/完成原地更新) ④锁语义保守起步=单会话单在途+排队,多并发看用量再说 ⑤yml 开关灰度。依赖链:16 纠偏通道(受理后补充入口)→13 过程流式(进行中视图)→本项。验收:桌面发任务→切走→完成收系统通知;飞书同构;双端行为一致。 - -> **设计约束(用户拍板 2026-10-06 凌晨):任务会话高度复杂,代码设计一定要优雅**——①状态机显式化:受理/运行/暂停/完成/失败用枚举统一,禁散落 bool 拼凑 ②单一事实源:任务状态只在引擎层,桌面/飞书 UI 都是投影(订阅不轮询) ③事件驱动贯通:13/14/16 共用同一事件总线,不做渠道私有旁路 ④优雅优先于速度:宁可工期换结构,不做特判堆补丁(优雅架构铁律)。 - -> **桌面端能力同步清单**(与渠道对齐,2026-10-06):16 纠偏注入做在 AiSession 会话层桌面天然受益;13 过程流式桌面过程视图同步对齐;14 任务卡+系统通知已在升级注记;另两件桌面专属:①V49 可恢复入口——走查实锤 list_parked 读侧零消费,桌面侧栏补「可恢复」列表消费它(断点续跑闭环最后一块)✅已做 e0b29b4(分组展示+点击恢复) ②缺陷9 会话标题泄漏桌面同症随批修。 17. [ ] **桌面会话内容同步飞书**(同会话,2026-10-06 凌晨拍板):绑定飞书 chat 的 conv(chat_map 映射既有,恢复机制在)——桌面端该 conv 的新消息(user/assistant)实时投影飞书卡片,用户离机后手机飞书可跟任务全程。实现:订阅 ai_event_bus 的 conv 内容事件→飞书投影,与 13 过程流式/14 任务卡共用单写卡者;按决策价值过滤防刷屏(完整历史在桌面,飞书给关键节点)。与 14 组合即完整闭环:桌面发任务→切走→手机看过程→收结果。验收:桌面 conv 发消息/收回复,绑定飞书 3s 内卡片更新关键节点。 -18. [x] **渠道错误话术吞因**(09:51 实证)✅ 2026-10-06 修复:①spawn Err 分类话术——并发护栏拒绝(「正在生成」)→「⏳ 该会话已有任务进行中,本条将在任务完成后处理;可稍后重发,或先 /status 查看进度」;其余失败→附截断原始原因(不吞因)。②探活告警口径修正——inflight 计数点从 spawn 前提前到 handle_message 白名单通过处(RAII 覆盖占位卡 HTTP+per-chat 锁排队窗口,原该窗口任务在跑却报「无在途会话」);旧计数点删除防双重计数。验收:cargo check EXIT 0 + feishu 7 passed。 -> 附:09:48 桌面纠偏「不是 haogame 是 redtimer」被运行中 loop 下一轮直接接住——16 纠偏注入的桌面形态天然存在,飞书侧对齐即可。 - ## 工作流治理升级(2026-10-06 用户拍板:根本性增强,源自提交质量检查三不达标) > 根因:任务传递靠软机制(看板摘要+群消息阅读),无结构化任务单与销账门禁——🔴被静默跳过/i18n 描述失实/描述与内容偏差,三层同源。 @@ -867,15 +775,8 @@ graph TD - [ ] **G4 销账带证据**:勾选 [x] 须附验收命令输出行,无证据视同未完成 ### 桌面能力同步(随本批) -- [x] G5 V49「可恢复」入口(桌面侧栏消费 list_parked,断点续跑闭环最后一块)✅ e0b29b4 可恢复分组+点击恢复已落地 - [ ] G6 缺陷9 标题泄漏桌面同症随批修 -### ⚠️ 退回重处理(2026-10-06 用户令,G2/G3 治理标准首次执行) -- 三笔提交(cec4d11 委托引擎/84df9b5 租约/622507c UI)已 reset --mixed 退回(内容全在工作区,零丢失,未推远端无需处理远端) -- **重做要求**:①先修 🔴10 loop_state 失效(G2 门禁:🔴不清零不得提交新功能)②描述按 G3 如实:实际干了 14+16+事件广播三件须逐件列全 ③i18n reattachToMain 键随批补上 ④修完按主题重新分组提交,销账 [x] 附验收输出(G4) -19. [x] **飞书 outbound 统一出口**(11:23 实证)✅ 2026-10-06 收口:勘察确认对话/受理/完成/注入回执已统一走 deliver_result(降级/裁剪/卡片链内聚);病灶「挂机进度汇总独立发送路径」在当前 main 不存在(属退回的挂机实验代码)。本批防复发收口:①新增 push_to_chat pub(crate) 正典出口(主动推送形态——挂机进度汇总/巡检通知将来落地时禁再开独立路径,强制走降级/裁剪/卡片链) ②ACTIVE_CLIENT OnceLock 全局句柄(渠道启动就绪,推送复用长连接 client 不临时建连,未启动静默跳过)。验收:cargo check EXIT 0 + feishu 7 passed。 -> 附:挂机汇报自报 🔴10 已修验证通过+43 文件产出——挂机模式推进有效,路径修复别误伤它。 - ## 无人值守可靠性专项(2026-10-06 swarm 立项,设计档:docs/02-架构设计/无人值守可靠性专项设计-20261006.md+temp/V49断点续跑设计-20261006.md) ## UI/UX 体验设计实施项(2026-10-06 设计档:temp/异步任务会话UIUX设计-20261006.md,依赖 13/14/16/17 机制先行) @@ -885,24 +786,6 @@ graph TD - [ ] U4 纠偏交互:桌面直发默认注入·下一轮生效(排队可切),回执+前缀逃生;飞书回复即注入保持 - [ ] U5 异常态话术公式:状态+真实原因+已保留什么+≥2条出路;禁自动判死静默任务(15min 重入退避是合法等待),给判断材料不替用户决策 - [ ] U6 跨端一致性:引擎唯一写者,双端不直同步,"实时"=秒级最终一致;断线恢复桌面走 DB 回放/飞书走 /status 回查 -20. [x] **委托制伪受理断层**(13:38 实证):受理卡在引擎确认可启动之前发出——12:57 任务停滞 Generating 时,「进度」spawn 被引擎拒但受理卡已发,之后零通知零进度(比 09:51 立即报错更隐蔽)。修:受理前过 can_accept_request,不可启动→直接回「前任务在跑,已排队/稍后」;可启动→才发受理卡+watcher 接力。附带:委托制下 inflight 生命周期改跟任务走(handle_message 提前返回致 18 修正再次失效,13:18 误报复现)。验收:同场景复测,占用期发消息得到明确排队提示而非静默受理。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -> **符号语义规范(2026-10-06 用户反馈:受理卡「✅ 已受理」歧义,随 19 统一出口一并落地)**: -> - 🚀 受理/开工(任务开始才用,禁 ✅)|⏳ 进行中/排队/等待|✅ 仅限完成且成功|❌ 失败(必须附原因)|⚠️ 警告/需注意(附数据)|📎 附录/附加信息 -> - 原则:符号=状态的一眼判据,同一符号跨场景语义必须一致;受理卡改为「🚀 已受理,后台执行中」;完成推送才允许 ✅ 开头。 - -## 飞书交互面全量盘查(2026-10-06,11 条缺陷按恼人度降序,修复前话术避免引用 /status) -34. [x] 🔴 **过程推送散乱刷屏**(14:06 用户截图反馈「信息太散乱爆炸了」):push_to_chat(:797)每调用必新开独立卡片,通道无聚合语义——昨日调试把链路测试/修复上线/加固明细等 L3 过程逐条直推 4+ 条进群,与 U3 分级相悖。修双层:①机制=push_to_chat 增滚动汇总卡——同 chat 维护在途事务卡 message_id(内存即可,重启另起),进度用已有 update_card(:730)原地追加;首推需 send_markdown_card 返回新卡 id(现 :683 is_ok() 丢弃,比照 reply_markdown_card);文本以 ✅/❌ 开头=终态,更新后清句柄下条另起新卡;更新失败回退新发并重绑(比照 deliver_result 兜底)。②纪律=L3 过程默认不推群,落日志+进度文件;push 收敛「需决策/完成✅」两时机,调试确需的验证推也进汇总卡不新开。验收:同事务多步进度群时间线只占 1 张卡,过程类推送零直达。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -21. [x] 🔴 **/status 被话术推荐但不存在**(chat.rs:387/:486 两处):用户发了被当任务烧一轮 LLM。修:实现 /status(读 conv 状态返回进度摘要,配合委托制价值高)或先改话术删除引用。验收:发 /status 得进度卡而非 spawn。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -22. [x] 🔴 **35min 兜底回查会投出上一任务旧回答冒充本次结果**(feishu_channel.rs:483/:939):回查整 conv 最后 assistant 文本无任务边界。修:回查限定受理时间戳之后的 assistant,无则给真实状态话术。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -23. [x] 🔴 **watcher 首超时后永不弃守**(:486 continue 与注释"二次超时弃守"相反)+错误路径只发 AiError 无 AiAgentFinished→每 35min 重刷过期文案+任务泄漏。修:二次超时真弃守+AiError 也广播 Finished(status=error)。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -24. [x] **无取消/审批通路**:跑偏无法叫停(飞书零 stop 通道);审批挂起对飞书用户不可见(流式卡冻结无解释)。修:/cancel 对接 ai_stop_loop;审批事件上卡片(「需审批:xxx,回复 批准/拒绝」)。 /cancel 停止通路+watcher 收口+审批推送与批准/拒绝决议(路径类授权留原语差异), ✅ 2026-10-07 细项批(待提交) -25. [x] **非文本消息静默丢**(:243):发图/文件零反馈。修:回「暂仅支持文本,媒体能力规划中」(D7 前的兜底)。 非文本兜底已存在于基线(:255 回「暂仅支持文本」), ✅ 2026-10-07 核验补销 -26. [x] **⏳话术承诺排队实际不入队**——话术与行为不符。修:改话术为实况(「可稍后重发」)或实现真排队(与 20 受理断层同修)。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -27. [x] **快任务兜底死分支**(:449 elapsed>=3s 入场即查恒假,「秒级任务直投终版」不可达)。修:受理卡发送后计时或事件驱动判定。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -28. [x] **注入末轮竞态**:loop 已在末轮则补充指令无人消费静默丢失。修:注入后检查 loop 剩余轮次,末轮则自动续一轮。 注入末轮顺延轮次预算(共享余量+轮顶动态判定+封顶 8), 5 单测锁定, ✅ 2026-10-07 细项批(待提交) -29. [x] **push_to_chat 取 KV map 首条**(:792):多 chat 绑定可能发错会话。修:按 conv 反查 chat。 push_to_chat 按 conv 反查+多绑定取最近活跃(签名加 conv_hint 零涟漪), ✅ 2026-10-07 细项批(待提交) -30. [x] 细节批::389 反引号不闭合;/new 大小写与附言容错;/new 时旧任务在跑无提示;标点半全角混排。 反引号闭合//new 容错与旧任务提示/半全角归一(parse_command 3 单测), ✅ 2026-10-07 细项批(待提交) - ## 上下文根治专项(2026-10-06 设计定稿:docs/02-架构设计/上下文根治设计-20261006.md,取代旧 C 发散条目) - [ ] P1(S):工具结果瘦身——超 2K 结果正文落盘+句柄,上下文留摘要+路径(与 D2a 同构,最大头先行) - [ ] P2(M):L1/L2/L3 分层组装器(build_for_request 改造:常驻+滑动窗口+检索召回) @@ -931,13 +814,10 @@ graph TD **补齐优先序:B5d 工具重试策略 → B5a 挂钟定级 → B5b 自动续作 → B5c 审批策略 → B2 预算闸 → 13 过程流式** ### 13a 飞书过程可见性加固(2026-10-06 用户实证"卡点到结束才回复",13 的最小件提前独立做,零新机制) -- [x] 13a-1 刷新可观测:refresher 的 update_card 失败从"静默跳过"改为 feishu_log 记录+连续失败计数(≥3 连败告警)——当前刷了还是没刷、刷败全不可知,排查无从下手。 ✅ 2026-10-07 实施(feishu_channel.rs 单文件):刷失败 feishu_log 落盘(连败次数/chat/msg/chars)+连败 ≥3 告警一次(REFRESH_FAIL_ALERT_THRESHOLD=3,== 阈值触发后继续累计不重复)+刷新恢复记「此前连败 N 次」+成功归零。验收:cargo check -p devflow EXIT 0 + cargo test --lib feishu 16 passed 0 failed。 - [ ] 13a-2 静默期心跳汇报:AiHeartbeat(20s 既有事件)接入 refresher——超 N 分钟无内容刷新→主动刷一行「仍在跑:第 X 轮 · 最近动作 yyy」(数据源 AiAgentRound/最后工具名),防静默期卡片冻结。 - [ ] 13a-3 阶段汇报:AiTaskProgress(每 5 轮,mod.rs:1119 已 emit)接入 refresher——长任务的阶段性进展对飞书用户可见(当前只有桌面消费)。 - [ ] 13a-4 受理→首刷衔接:受理卡到首个 delta/心跳之间的静默窗口检查(>60s 无首刷则心跳兜底)。 验收:206 轮级长任务期间,占位卡至少每 3-5 分钟有一次真实更新(心跳或阶段),失败可见于 feishu-channel.log。 -31. [x] (自动续作) **AI-Ops→devflow 派工通道**(多 bot 编排正统通道,2026-10-06 缺口实证):AI-Ops 无法直接触发 devflow 开工(飞书 bot 消息不互触发/WebChat 无/remote_bridge 走 tunnel 不外用)。方案二选一:①u-claw 侧新增 ask_devflow(与既有 ask_uclaw 对等,经 remote_bridge 协议下发启动指令) ②devflow 侧看板轮询引擎(定时扫 todo.md 新🔴/高优条目自动开工,CLAUDE.md 领单纪律的运行时化=B5b 同源)。建议 ② 与 B5b 合并实现(同一套自动续作引擎)。验收:AI-Ops 写看板后 15min 内 devflow 自主开工,零人工中转。 ✅ 2026-10-07 方案②实施并与 B5b 合并落地(unattended.rs 636 行,B5b+31 同一引擎):看板解析器(编号条目四态+🔴 判据=标记后首字符防误判)/G2 门禁领单选择器(🔴 未清零只领红 fail-closed)/轮询调度器(tokio 常驻,KV 热读配置 df-unattended-{enabled,poll-secs,claim-scope,todo-path},默认关+300s)/收敛回读钩子 notify_batch_done 接 agentic emit_ai_completed_once 单点/parked 自动恢复优先/连续 3 次 spawn 失败熔断/领单原子写+spawn 失败回滚/引擎独立后台会话 pin_active=false。验收证据:①接线 rg 实测 lib.rs:77 unattended::start + agentic/mod.rs 收敛钩子调用 ②`cargo test --lib unattended` = 9 passed 0 failed(本轮实测修正 parse_statuses_and_red_marker 断言:🔴 判据仅认标记后首字符,正文提及不误判,与实现注释语义对齐) ③**活证**=本条目自身即引擎产出:AI-Ops 写板 → 引擎改写 `31. [→](自动续作)` → 派发本会话执行,零人工中转,15min 上限实测远低于下限。 - ### 31 泛化路线:「看板即队列」模板资产化(2026-10-06 swarm 设计定稿) - **抽象**:看板=任务队列(优先级/状态三态/验收口径)+ 领单纪律=消费者协议 + 治理门禁=质量闸。任何 Claude Code 实例 + 一个看板 = 一个自主工作单元。 - **组件三件套模板**(提炼自 devflow 今日实践,新场景开箱即用): 1.看板格式规范 2.领单纪律 CLAUDE.md 模板 3.治理门禁包(cm v1.3.0+pre-commit hook) @@ -954,63 +834,13 @@ graph TD 35. [~] ~~H3 teardown 疑似过重~~ **真相改写(验装日志实锤)**:17:47 进程消失=agent 自杀式换装脚本杀宿主(swap-install.log:17:48:14 old process stopped),非 H3——H3 嫌疑解除。真正缺陷转 36。 > 关联:13a 过程可见性四件**置顶催办**——受理后静默的体感投诉今天第三次(09:51/13:38/17:47+),13a-1 刷新可观测是排查此类问题的前提。 -36. [x] **换装脚本失败:杀进程后 3s 即安装,Windows 文件锁延迟释放致 NSIS 静默伪成功**(17:48 实证 FAIL: exe not updated,installer exit=0)。✅ 2026-10-06 晚全链闭环:**脚本加固**(swap-install.ps1 v3,语法 0 错实测):①句柄释放探测(独占打开循环,30s) ②校验升级为构建产物 SHA256 强基准(mtime 判据作废,NSIS 保留源文件 mtime 必误报) ③校验失败自动重试一次 ④SYSTEM 触发时转投用户会话重入,四级 fallback,最坏落 swap-need-start.flag。**上线验证过**(18:44 版手动换装成功):运行 exe SHA256=构建产物=0C45683B,进程 12764 运行中,H1 行为实测通过(19:13 DevFlow 回复带「📎 git 实测」段)。 37. [ ] **监控轮询机制缺失(1004/1006 两次要求均未落地,全周响应性投诉的伴生缺口)**:10-04「持续不间断监控每 1 小时」+10-06 18:45「持续监控 10 分钟一轮」,两次均未建机制;18:45 断链实锤=传承摘要未继承该指令。根因:无头会话无定时能力(网关禁用 CronCreate)。方案二选一:①网关 cron(运维配 yml crons,周期拉 git/进程/日志快照+异常告警) ②devflow 侧自监控引擎(与 B5/31 看板轮询同源,顺带监控自身进程/会话活性+静默告警)。验收:监控自动轮询真实运转,devflow 静默/进程消失 1 个周期内被发现并通报。 -38. [x] **飞书出站 md 语法裸露净化+交互样式对齐 u-claw**(AI-Ops 2026-10-06 晚实证落板):任务报告 `##`/`**` 等 md 标记在飞书裸露(20:53 分析报告截图实锤)。根因:飞书 lark_md 不支持标题/表格等全谱语法,LLM 产出未经出站转换;主链 send_markdown_card(feishu_channel.rs:716,注释自认「text 类型会符号裸露」)只保粗体/链接,im-gateway StreamText 链纯文本直出。修:①出站净化层——md 转飞书友好格式(标题行转粗体/表格转列表/代码块适配),双层防线=出站兜底转换+agent 产出 prompt 约束,移植 u-claw 投递链降级/裁剪经验(u-claw 仓为范本) ②首回受理卡样式对齐 u-claw 任务状态卡(计数 N/M+逐条状态符+当前动作一行,10-02 用户拍板「效果不错」);受理卡符号 🚀 已由 AI-Ops 修(449 行)。验收:同类分析报告在飞书零 md 裸露标记,受理卡样式与 u-claw 一致。 ✅ 2026-10-06 c91b993(交互批八项合提,验收: cargo check 零 error+feishu 13/df-im-gateway 19/chat 17 passed+vue-tsc 0) -39. [x] 🔴 **提交冻结令被无人值守链路击穿**(10-07 用户查问「最近提交不符合要求」实证):看板顶部 🔒 冻结令(10-06 深夜用户令,期间只改不提)生效后,挂机链路 01:56-57 仍连提 4 笔(c5ba755 续作引擎 811 行/d0281cb 飞书取消审批 345 行/7ab25e5 换装判据/73fa7c2 看板销账),且 73fa7c2 看板内容自书「细项批(待提交)」与提交事实自相矛盾——冻结仅是文本纪律,引擎与挂机会话的提交步无机械闸。修:①引擎自动提交路径与挂机会话提交步在 commit 前必查冻结标志(看板顶部 🔒 行视为生效,KV 标志更佳),在场即拒绝提交、改动留工作区;禁用全局 pre-commit 拦截(误伤用户本人提交) ②引擎派发的会话启动 prompt 注入「冻结期禁 commit」硬约束 ③解冻=用户「攒批提交通知」后由会话摘除 🔒 行再提。验收:冻结令在场时引擎/挂机会话 commit 步零执行(实测),摘令后恢复;随 31 引擎同测。 ✅ 2026-10-07 实施(commit_freeze 三层闸,零提交全在工作区):①机械闸=crates/df-execute/src/commit_freeze.rs 新模块——静态冻结位 set_frozen(引擎 unattended::run_tick 每轮按看板 🔒 行+KV df-commit-frozen 覆盖同步)+execute/execute_streaming 入口 check(),git commit 变体(git -C/复合命令 ;&&|/引号包裹/多空格)spawn 前拒绝,改动留工作区;未挂全局 pre-commit,用户本人终端零影响。②任务书注入:claim_and_spawn 冻结在场时追加「提交冻结令硬约束」段(禁 git commit/push,执行层闸兜底)。③解冻=用户摘 🔒 行或 KV df-commit-frozen=false,下一轮轮询(≤300s)自动同步放行。验收实测:cargo test -p df-execute 26 passed 0 failed(18 lib 含 6 冻结闸新用例:commit 变体识别/只读命令放行/冻结拦截/解冻恢复/静态位切换;8 shell 集成)+ cargo test -p devflow --lib unattended 10 passed 0 failed(含冻结判据新用例:🔒 行/blockquote `> 🔒` 形态/KV 双向覆盖/默认不冻结)+ cargo check -p devflow --lib EXIT 0。冻结在场实测=单测断言 check() 对 `git commit` 返回 Err 且 `git diff` 放行,摘令后恢复=static_flag_toggle 用例复位断言;真实看板形态(`> 🔒` 引用行)经 commit_freeze_board_and_kv_judge 实测命中。 - 40. [ ] **桌面侧 u-relay 常驻连接管理器(设计命题,待综合评估后再规划设计)**(2026-10-07 用户议定「登记后处理」):ask_uclaw 现为每请求独立 WS 短连接(connect→hello→探活→request→progress/result→close,任务执行期内本就实时);用户提议改「全程一条常驻连接中转所有上下行(zhub 式单连接多路复用)」。**本项仅登记命题,具体方案待后续综合评估**——评估须回答:①当前唯一用例(ask_uclaw 15min 级委托)下常驻是否真有必要(握手 ~0.1s 可忽略、u-claw 侧单 channel 串行 worker 锁死并发=1)②push 型需求(u-claw 主动推事件/高频短指令/多 channel 并行/跨链路统一观测)何时兑现 ③是否与 10-05「推翻网关链路、定稿桌面直连」决策冲突(飞书直连接入-形态修正与问题记录-2026-10-05.md)。命中上述 push 型触发条件之一即启动评估;同构先例可参 df-im-gateway(一条常驻 tunnel 连接中转四端全部上下行)与 feishu_channel.rs。 -900. [x](自动续作) 🔴 测试条目 - -901. [x](自动续作) 🔴 提交工作区改动(转交实战,2026-10-07):将当前工作区改动按主题分组提交并推送 origin main。(21:4x AI-Ops 重置回队:前两轮引擎会话被 20:38-21:02 u-switch 故障窗口「网络中断不完整」腰斩,重入预算耗尽停 [→],网络已恢复)要求:①提交前 G4 前置走查(风格/明显缺陷/调试残留/注释规范,发现问题先处理再提交) ②分组建议:挂机模式 P1 总装一笔(UnattendedPanel/Settings/TopBar/TaskBoundaryCard/i18n/unattended 两 IPC/lib.rs/feishu_notify)+看板销账一笔(todo.md) ③swap-install.ps1 若有改动先审 diff 再定去留;删除 scripts/check_plaintext_key.py(一次性诊断脚本已完成使命) ④提交信息中文类型开头逐件列全,commit-msg hook 会拦截违规 ⑤完成后本条改 [x] 并附 git log --oneline -3 输出。验收(10-08 AI-Ops 补录,引擎 22:06 完成):git log --oneline -3 → 0aac14e 文档: 看板销账: 挂机模式 P1 总装与换装校验修复入库,901 销账 / 6bfcf01 修复: 换装校验同版本重装误判不通过 / 2441101 新增: 挂机模式 P1 总装: 设置面板+顶栏徽章+边界卡精简+监控告警推送;main=origin/main 已推,工作区清零。 - 902. [ ] 超时失败后的一键继续入口: 流式首字节超时(120s,服务收了不响应,不可自动重试)报「响应超时,请重试」后流程中断——轮次重入预算耗尽(3/3)即彻底停,用户只见裸文案。修:超时失败落停靠语义(复用 emit_task_parked),停靠卡带「继续」(ai_continue_loop parked 分流已修好)——错误后一键续跑而非中断。体验缺口实证 2026-10-07 晚 u-switch 故障窗口。 903. [ ] u-switch 20:38-21:02 故障窗口根因深查: 11 连败+120s 首字节超时风暴(多会话多 provider 同时),21:0x 自愈。查 moyu-ops 机 u-switch 日志定因(上游抖动/服务重启/连接池耗尽),根治或加监控告警。**【21:5x AI-Ops 定因已实锤,journalctl 实测】上游连环降级雪崩非服务故障:api.deepseek.com 402 余额不足(冷却10m)+enterprise.moyu.info 401 认证失效(冷却10m)+sensenova kimi-k3 429/SSE空响应体(30s冷却循环)+bigmodel glm 429/SSE截断缺 message_stop——多通道同时打光致无首字节,冷却走完自愈。剩余工作=根治项:①switch-gw 的 deepseek 系 4 通道确认摘除(用户指认已摘但配置仍在役)②429 风暴时客户端退避协同(30s 冷却对重试风暴无效)③故障窗口告警。**(10-08 已处置:moyu 令牌过期续期+主链切 ds-flash,见 900 区段外 905) -904. [x](自动续作) 🔴(10-08 AI-Ops 定位) 停靠(parked)状态写侧缺失,停靠卡「继续」必报「AI 未在暂停态」:**根因**=全仓无任何业务路径写 loop_state=parked,`"parked"` 仅 3 处读取判定(unattended.rs:604/conversation.rs:341/chat.rs:2712),唯一写入源为应用重启时 V49 残留扫描(running→parked)。**实证**=G2 探索熔断(agentic/mod.rs:2288)与同类失败熔断(2265)均只调 emit_task_parked 发事件(前端渲染停靠卡+继续按钮)、漏写 DB,会话 6a5a3e04 实测 loop_state=running;点「继续」→ chat.rs:2703 查 parked 不匹配→报错。**副作用**=残留 running 使侧栏/引擎误判会话仍在跑。**修法(推荐根源解)**=停靠状态写入收敛进统一出口:emit_task_parked 升级为「写 parked + 发事件 + 通知」三件套(或新建 park_task),五路停靠调用点(熔断两路/审批超时/挂起取消/流致命)全部对齐;契约注释同步改写。**附:UI 观感**=熔断同轮双卡叠加(求助卡 AiHelpRequired + 停靠卡 AiTaskParked)语义打架且文案冗长,建议熔断只发停靠一张卡。 - - ✅ **2026-10-09 实施完成(根源解 + 附项一并落地)**: - - **① 写侧收敛进统一出口**:`emit_task_parked`(src-tauri/src/commands/ai/mod.rs:1435) 升级为「写 parked + 发事件 + 通知」三件套——新增 ① 步经 `app.state::().ai_conversations.set_loop_state(conv_id, Some("parked"))`(fire-and-forget spawn,失败仅 warn 不阻断事件/通知);②③ 步(AiTaskParked 事件 + NeedAction 通知)保留原位。8 个业务调用点(H2 三闸/熔断两路/挂起取消/max_rounds/midstream/fatal + 审批超时)全部经此单点,零遗漏(grep 实证 9 处调用全指向统一出口)。 - - **② 契约注释改写**:函数 doc 由「单一只读语义」改为「三件套」;调用方职责(保存现场 + conv_state 复位)边界不变。 - - **③ df-storage 支撑**:`impl_repo!` 宏生成的 Repo 结构体加 `#[derive(Clone)]`(`Arc>` 天然可克隆),使 `emit_task_parked` 能克隆 Repo 句柄进 spawn。 - - **④ 附项(熔断只发停靠一张卡)**:新增开关 `CIRCUIT_BREAKER_PARK_ONLY=true`(agentic/mod.rs:343);L1 同类失败熔断 / G2 探索熔断 / 破坏性回环熔断三处 `AiHelpRequired` 求助卡 emit 均包在 `if !CIRCUIT_BREAKER_PARK_ONLY` 内,求助卡关键信息(失败样本/路径)内联进 AiTaskParked 的 reason;开关置 false 即回退旧双卡行为。前端零改动(TaskParkedCard 词表已含 circuit_breaker/stall_breaker)。 - - **⑤ 回归测试**:新增 `set_loop_state_parked_roundtrip`(crates/df-storage/src/crud/conversation_repo.rs)锁死「写 parked → get_by_id 读回 parked / 清态读回 None」契约。 - - **与 V49 残留扫描无冲突**:`mark_running_as_parked` 的 SQL 为 `WHERE loop_state='running'`,不会覆盖已写 parked 的行(state.rs:459 启动扫描语义不变)。 - - **验收**:`cargo check -p df-storage` EXIT 0;`cargo check -p devflow` EXIT 0(10 warning 全为无关模块既有 dead_code);`cargo test -p df-storage` **200 passed + 12 passed, 0 failed**;`cargo test -p devflow --lib` **748 passed, 0 failed, 8 ignored**;`bunx vue-tsc --noEmit` EXIT 0。 - -905. [x](自动续作) 🔴 「@」文案 HTML 实体未解码(10-09 用户实测截图): 输入框 placeholder 与提及提示字面显示 `@` 而非 `@`。位置共 8 处——`src/i18n/zh-CN/aiChat.ts` 与 `src/i18n/en/aiChat.ts` 各 4 处(placeholderDefault / mentionHint / searchFilesBlocked / projectLabel)。根因:文案写了 HTML 实体,前端文本插值(`{{ }}`)不做 HTML 解码故字面显示。修:8 处 `@` → 直接写 `@`(纯文本字面量,无需转义)。 - ✅ 2026-10-09 修复完成。改动:zh-CN/aiChat.ts L139/144/236/330 + en/aiChat.ts L139/144/235/329 共 8 处 `@` → `@`。 - 验收证据:① `findstr /s /c:"@" *.ts *.vue *.tsx *.js`(src 目录) → 无匹配(exit 1),全仓零残留;② 回读 8 行确认字面 `@`(zh: "输入消息...(/ 触发技能,@ 引用实体)" / "@ 引用项目/任务" / "...请用 @[项目] 引用..." / "@项目";en 对应 4 处);③ `bunx vue-tsc --noEmit` → EXIT=0。 - -906. [x](自动续作) 🔴 停靠卡「总是弹出」频次治理(响应流中断路径,10-09 用户反馈): 流中途断(agentic/mod.rs:1345 `RoundOutcome::MidStreamSaved`)直接 `emit_task_parked` 停靠,无 Fatal 家族的 `ROUND_REENTRY_MAX` 重入预算——网络抖动一次即弹「响应流中断/任务已停靠」卡并进行中任务终止(10-09 u-switch 抖动期用户实测频繁弹)。修建议:①midstream 先加短重试/续传预算(对齐 Fatal 重入语义,耗尽才停靠)②前端同会话短时内停靠卡节流(避免重发-中断-再弹循环观感)。注:本条是打扰治理,与 904 写侧修复正交(904 让对方能点「继续」,本条降低「被迫点继续」频次)。 - - 【✅ 2026-10-09 实施完成 — 双管齐下:后端续传预算 + 前端停靠节流】 - - 【① 后端 MidStream 短重试/续传预算(stream_relay.rs)】新增 `MIDSTREAM_REENTRY_MAX=2` + `MIDSTREAM_REENTRY_BACKOFF_SECS=[5,15]` + `MIDSTREAM_CONTINUE_PROMPT`,对齐 Fatal 家族重入语义但采用**续传**而非重发(整轮重发会与前端已渲染 partial 重复): - - 原 candidate 循环包裹进 `'midstream: loop`:流中途断(`incomplete=true`)且用户未点停时,若预算未耗尽 → ①已接收 partial 落库为 assistant 消息 ②emit `AiAgentRound{round:0}` 触发前端 flush(partial 落独立气泡,续传渲染到新气泡,无重复) ③续传请求 = 原请求 + partial(assistant)+ 续传提示(user),模型从断点继续 ④退避 5s/15s 后重入 candidate 循环(全新 30s 流前预算)。 - - 续传成功 → 本轮转 `Continue`(走正常收尾,**不弹停靠卡**);预算耗尽 → 落回原保文+`emit_task_parked` 路径(系统提示文案区分「已自动续传重试仍未恢复」)。 - - 收尾保文修正:续传路径已落中间各段,终局只补「最后一段」(break 前未 push),无重叠不重复。 - - 【② 前端同会话停靠卡节流(useAiLifecycleEvents.ts)】`recordTaskParked` 增 `PARKED_REPARK_THROTTLE_MS=20000` 节流:同 conv 距上次停靠 <20s 且**卡仍在(未被用户处置)** → 保留原卡不刷新(压抖动期重复弹/重渲染);卡已被用户「继续/停止」摘除 → 仍放行新卡(不吞用户需决策的真实新终局)。安全边界:仅压「未处置的重复弹」,不吞「已处置后的真实新停靠」。 - - 【验收输出】 - - `cargo check -p devflow` → Finished dev profile(0 error,10 warning 全为无关模块既有 dead_code) - - `cargo test -p devflow` → **750 passed; 0 failed; 8 ignored**(含新增 `midstream_reentry_tests` 2 项:退避表长覆盖上限+单调递增+首退避 🔎 **2026-10-10 接手核查补注(devflow 侧)**: - > - **实现口径澄清**:01:0x 落地的是**「未邻接即剥除」**——sanitize.rs step2 判定由 `resolved_ids.contains` 收紧为 `adjacency.get(&i).contains`(`crates/df-ai/src/context/sanitize.rs:87-92`),openai_compat.rs `sanitize_openai_triplets` step2 同口径(`crates/df-ai/src/openai_compat.rs:401-434`);被 system/user 夹层隔开的 assistant 头连同其 tool 响应从**发送视图**剔除(step3/openai step3 按 orphaned_ids/head_ids 对称丢弃)。**此非设计 ① 所述「相邻性归位/只重排不删内容」**,而是判定口径收紧后走既有「未闭合→剥除」路径——安全(不 400)但**有损**(该轮工具交互对本轮 LLM 不可见;持久化 view-only 不动)。 - > - **代价评估**:夹层主因=后台压缩竞态(偶发)或用户插话(工具执行窗口内,纠偏 16 场景可复现)——后者每插话一次即丢一轮工具结果,模型可能重跑工具/误判。 - > - **独立复验**:`cargo test -p df-ai --lib` **567 passed / 0 failed**;`cargo check --workspace` **EXIT 0**;夹层回归用例 `sanitize_strips_calls_when_response_separated_by_interloper` 实测通过。 - > - **遗留未实施(原 ② 源头守静)**:后台压缩提交点仍无「序列静止」判据(持锁后末条为悬空 ToolCallHead 时不得 push 摘要),楔入仍会发生;设计 ① 的无损「归位」变体亦未实现——二者合并登记为 **909**。另:原条目引用的 `context_lifecycle.rs:269/:312/:322/:372` **在当前树不存在**(压缩提交点位于 `src-tauri/src/commands/ai/compress.rs` 与 agentic 调用链),实施 909 前须重新定位。 - -908. [x] 「settings.loading」i18n key 缺失**已带病入库**(a8c92e9,10-09 AI-Ops 走查发现): `src/components/settings/AboutPanel.vue:204` 用 `t('settings.loading')`,但 zh-CN/en `settings.ts` 均无此 key(grep 实证)→ 版本信息返回前渲染字面串 `settings.loading`。修:补 `loading` 键(两边)或改用已有键。同类走查发现:AboutPanel 曾用 `window.dispatchEvent('df-toast')`(全仓无监听)已随提交改动,复核确认现机制一致。 - - ✅ **2026-10-10 修复**:`src/i18n/zh-CN/settings.ts` / `src/i18n/en/settings.ts` 各补 `loading` 键(紧随 `descAppVersion`,与仓内 auditLog/common/fileExplorer/taskDetail 各命名空间自持 `loading` 的惯例一致)。验收:`grep -n "loading:" src/i18n/zh-CN/settings.ts src/i18n/en/settings.ts` → 各 1 命中;`bunx vue-tsc --noEmit` → EXIT 0。 - -909. [ ] **907 源头守静 + 无损归位(夹层根治)**(2026-10-10 devflow 接手登记,源自 907 补注遗留):现 907 修复走「未邻接即剥除」——保证不 400 但有损(夹层场景丢一轮工具交互)。① **源头守静**:后台压缩/裁剪提交摘要前加「序列静止」判据(持锁后末条为悬空 `ToolCallHead` 则不得 push 摘要,推迟到本轮工具收尾);顺带把压缩失败路径的两次取锁合并为单锁(消并发二次压缩)。② **无损归位**:将 sanitize 的「剥除」升级为设计 ① 的「相邻性归位」(head 与响应间楔入的异类消息移到该组响应之后,只重排不删内容)+ 幂等单测——对**用户插话型夹层**(源头无法消除,纠偏 16 场景)是唯一无损解。实施前须重新定位压缩提交点(原条目 `context_lifecycle.rs:*` 路径在当前树不存在,实际在 `src-tauri/src/commands/ai/compress.rs` + agentic 调用链)。验收:构造楔入序列断言输出合规且 head 后紧随其响应、幂等;插话场景工具结果不丢。 +909. [~] **907 源头守静 + 无损归位(夹层根治)**(2026-10-10 devflow 接手登记,源自 907 补注遗留):现 907 修复走「未邻接即剥除」——保证不 400 但有损(夹层场景丢一轮工具交互)。 + - ✅ **① 源头守静(2026-10-10 实施完成,避让插入变体)**:df-ai 新增 `ContextManager::push_system_anchor`(context/mod.rs)——末条为悬空 `ToolCallHead` 时将 system 锚点插在**该头之前**(index=len-1),否则常规尾部追加;私有判定 `ends_with_dangling_tool_call_head`(`group==ToolCallHead`)。比原设计「推迟 push」更优:摘要/警示**不丢**,且楔入根本不产生。5 处尾部 push 全量收口(压缩成功 context_lifecycle:269 / 兜底 keyword_fallback_commit:372 / 警示 insert_system_notice helpers:44 / 主题标记 agentic/mod:1804 / 续轮 nudge :2399)。单测 `push_system_anchor_appends_on_normal_tail` + `push_system_anchor_avoids_dangling_tool_call_head` 锁定。验收:`cargo test -p df-ai --lib context` **155 passed/0 failed**;`cargo check -p devflow` EXIT 0。 + - [ ] **①-补 双取锁合并未做**:压缩失败路径 `set_compressing(false)` 与兜底提交仍两次取锁(context_lifecycle:312→:322),并发二次压缩窗口未消(低风险,留后续)。 + - [ ] **② 无损归位未做**:将 sanitize 的「剥除」升级为设计 ① 的「相邻性归位」(head 与响应间楔入的异类消息移到该组响应之后,只重排不删内容)+ 幂等单测——对**用户插话型夹层**(源头无法消除,纠偏 16 场景)是唯一无损解。落点:`src-tauri/src/commands/ai/agentic/context_lifecycle.rs:269`(LLM 成功 push)/`:372`(兜底 push)/`:312`→`:322`(双取锁)。验收:构造楔入序列断言输出合规且 head 后紧随其响应、幂等;插话场景工具结果不丢。 diff --git a/src-tauri/src/commands/ai/agentic/context_lifecycle.rs b/src-tauri/src/commands/ai/agentic/context_lifecycle.rs index 2447ed8..748f89e 100644 --- a/src-tauri/src/commands/ai/agentic/context_lifecycle.rs +++ b/src-tauri/src/commands/ai/agentic/context_lifecycle.rs @@ -266,7 +266,7 @@ pub(super) async fn maybe_auto_compress( // 前缀字节,使 DeepSeek 等自动前缀缓存的 provider 全部缓存失效 // (命中率掉到个位数)。尾部追加紧邻本轮内容 LLM 仍可见,前缀稳定 // 命中率最大化。JSON 卡片回填 WorkingContext,决策随压缩重锚定。 - conv.messages.push(ChatMessage::system(&summary.nl_summary)); + conv.messages.push_system_anchor(ChatMessage::system(&summary.nl_summary)); conv.messages.set_compressing(false); conv.working_context.sync_after_compress(&summary.json_card); } @@ -369,7 +369,7 @@ async fn keyword_fallback_commit( // 尾部追加(而非 insert_at(0) 头部插入):头部插入位移整条前缀字节, // 使 DeepSeek 等自动前缀缓存 provider 全部缓存失效(命中率掉到个位数)。 // 尾部追加紧邻本轮内容 LLM 仍可见,前缀稳定命中率最大化。 - conv.messages.push(ChatMessage::system(keyword_fallback)); + conv.messages.push_system_anchor(ChatMessage::system(keyword_fallback)); true } }; diff --git a/src-tauri/src/commands/ai/agentic/helpers.rs b/src-tauri/src/commands/ai/agentic/helpers.rs index 8084b01..445b51a 100644 --- a/src-tauri/src/commands/ai/agentic/helpers.rs +++ b/src-tauri/src/commands/ai/agentic/helpers.rs @@ -41,7 +41,7 @@ pub(crate) async fn insert_system_notice( let mut session = session_arc.lock().await; if session.per_conv.contains_key(conv_id) { let conv = session.conv(conv_id); - conv.messages.push(ChatMessage::system(text)); + conv.messages.push_system_anchor(ChatMessage::system(text)); tracing::info!(conv_id = %conv_id, "[ai] {}", log_tag); } } diff --git a/src-tauri/src/commands/ai/agentic/mod.rs b/src-tauri/src/commands/ai/agentic/mod.rs index fa103f5..53a4ec9 100644 --- a/src-tauri/src/commands/ai/agentic/mod.rs +++ b/src-tauri/src/commands/ai/agentic/mod.rs @@ -1801,7 +1801,7 @@ async fn prepare_round_inputs( let mut session = session_arc.lock().await; if session.per_conv.contains_key(conv_id) { let conv = session.conv(conv_id); - conv.messages.push(ChatMessage::system(&marker_text)); + conv.messages.push_system_anchor(ChatMessage::system(&marker_text)); tracing::info!( conv_id = %conv_id, iteration, @@ -2396,7 +2396,7 @@ async fn insert_goal_verify_nudge( let conv = session.conv(conv_id); // 尾部追加而非 insert_at(0):nudge 紧邻本轮内容语义不变,且不位移既有前缀字节 // (insert_at(0) 会把整条历史后移,自动前缀缓存/Anthropic 显式缓存全失效)。 - conv.messages.push(ChatMessage::system(&nudge_text)); + conv.messages.push_system_anchor(ChatMessage::system(&nudge_text)); tracing::info!( conv_id = %conv_id, iteration, verify_rounds, "[GOAL-VERIFY] 目标未验证,注入续轮 nudge(强制续轮)"