Files

9.3 KiB
Raw Permalink Blame History

发现记录

2026-07-29

  • 演示子代理线程 019f9160-6dde-7911-928c-925488ab00f9 的真实 rollout 中有 4 次 update_plan:依次推进三个步骤并最终全部完成。
  • 当前父会话持久化快照中,plan_card_demo 已保存 planProgress: { completed: 3, total: 3 },证明服务端至少在最终快照链路中拿到了计划摘要。
  • 用户界面始终未看到计划进度,根因应聚焦前端历史消息重建、collab tool 合并或实时 child update 的 DOM 刷新。
  • 旧的 subagent-plan-progress 任务已经实现服务端计划解析、状态持久化与基础卡片渲染,但真实使用仍暴露了覆盖不足的链路。
  • 当前不重启;先用真实 session 数据构造复现和回归。
  • codebase-memory 索引状态为 ready(4140 nodes / 8942 edges);关键前端入口仍为 mergeCollabAgentTools、upsertCollabAgentToolCall、applyCcwebMcpChildAgentUpdate。
  • 历史重建通过 renderToolCallsWithMergedCollab 合并全部 collab tool;实时 child update 则先更新缓存和 activeToolCalls,随后只调用 updateToolCall(toolUseId, tool.result, done),需要确认它是否用新 activeToolCalls 重新构造完整 tool,还是仅以 result 刷新旧 DOM。
  • upsertCollabAgentToolCall 把每个 tool id 保存在 DOM 节点的 __collabTools Map 中,然后整体重新合并;若相同 tool 的旧快照和新快照以不同 key 并存,或刷新未走该函数,旧状态可能覆盖计划字段。
  • collabAgentStateEntries 会显式规范化 planProgress / planCurrentStep,mergeCollabAgentTaskState 会 spread 保留这些字段,createCollabAgentToolElement 也会为存在的进度创建 .collab-agent-item-plan;基础数据/渲染链路从代码上看没有主动丢字段。
  • rememberCollabAgentStateFromChild 通过 { ...child } 把 child 计划字段写入缓存,所以实时事件接收阶段也能保留计划摘要。
  • 高风险点:updateToolCall 的 collab 分支忽略已有 domElement,固定在 getLatestAssistantToolScope() 返回的最新助手消息中调用 upsertCollabAgentToolCall。如果子代理卡片属于较早的消息,实时进度会更新到错误作用域或无法命中原卡片。
  • 独立计划审查已通过;建议的四个观测点(持久化、历史数据结构、实时 payload、DOM)已纳入定位。
  • 真实 session 的 plan_card_demo 工具呈现出关键形状:input.agentsStates 仍是启动时快照(无计划),更新后的 result JSON 才包含最终 planProgress: 3/3;历史重建必须正确以 result 覆盖 input 才能显示。
  • 同一工具的最新 result 还合并进了本轮计划审查子代理,说明长会话中的子代理状态持续复用旧 tool id;实时 DOM 更新如果只查最新消息,尤其容易错位。
  • 现有 subagent-card-metadata 回归虽然调用 applyCcwebMcpChildAgentUpdate,但测试夹具中的 updateToolCall() 是空函数,完全没有验证实时更新是否命中原卡片 DOM;这是目前最明确的覆盖缺口。
  • rememberToolCallTarget 能保存 tool 到 DOM 的引用,但 applyCcwebMcpChildAgentUpdate 会用新对象覆盖 activeToolCalls 条目,因此历史引用不会被保留。
  • buildMsgElement 重建历史消息时直接用合并后的 tool 创建元素,既不调用 rememberToolCallTarget,也没有给元素设置 data-collab-merged / __collabTools;后续实时更新无法把它识别为可增量合并的原卡片。
  • 修复需要同时处理两个点:实时更新优先按 tool id 找原卡片作用域;历史重建的 collab 卡片需保存合并标记和原始 tool Map,避免更新时重复插入或错位。
  • 真实会话顺序为:消息 29 启动演示子代理并承载卡片,消息 30 才是用户反馈“没变化”;因此实时计划阶段原卡片确实位于最近的助手消息中。仅“最新作用域漂移”不足以解释第一次完全看不到,还需核对服务端是否逐次广播计划事件。
  • 服务端关键路径已确认:processCcwebMcpChildNotification 解析计划,sendCcwebMcpChildAgentUpdate 同时更新活动工具、持久化工具并向父会话 WebSocket 发送 child update;下一步核验实际调用时机和 payload。
  • 服务端对每个成功解析的计划通知都会立即返回 changed: true,随后直接调用 sendCcwebMcpChildAgentUpdate;代码中没有只在完成时发送的节流逻辑。
  • 广播目标优先取当前活动 turn 的 WebSocket,否则取正在查看该 session 的 WebSocket;若事件已正确路由,前端理论上应逐次收到。
  • findCodexAppRouteByRuntime 能根据 child thread id 从 ccwebMcpChildThreads 恢复父会话路由;还需用日志或回归验证真实 plan 通知没有在这一步变成 unrouted。
  • PM2 当前 ccweb 状态为 online;现有 stdout/stderr 不记录具体 child plan 广播,也没有该演示线程或 plan notification 的可追溯日志,无法靠历史日志区分“未路由”与“前端未渲染”。因此回归必须直接覆盖 payload→DOM 全链路。
  • 新回归使用真实快照形状:启动态 input 无计划,更新态 result 才有 2/3 与当前步骤;同时要求历史卡片保存 merged 标记和原始 tool Map。
  • 修复前定向回归按预期失败在“实时子代理更新应先定位原卡片”,证明覆盖点有效。
  • 历史消息重建现在给合并卡片写入 data-collab-merged、child ids 与原始 tool Map;result-only 计划快照可继续接收实时更新。
  • 实时 updateToolCall 先按 tool id / tool Map 在全消息区查原卡片,并优先使用其父作用域;applyCcwebMcpChildAgentUpdate 同时保留已有 DOM 引用。
  • 增强后的定向回归不仅断言代码契约,还实际把历史卡片挂入测试 DOM,验证能按原始 tool id 找回;当前已通过。
  • 在线 http://127.0.0.1:8002/app.js 已包含 markMergedCollabAgentElement 和原卡片定位调用,返回 200 且 Cache-Control: no-store, max-age=0;静态文件修改无需重启进程。
  • PM2 中 ccweb 仍为 online,pid 241273,整个修复过程未重启服务。
  • 用户刷新后提供的真实截图显示:plan_progress_live_demo 卡片处于“进行中”,但没有任何进度计数、进度点或当前步骤;这是明确的失败验收,不能再以代理调用成功代替 UI 成功。
  • 同一子代理 019fab4a-e95c-7353-8a37-6cdaab89188e 在 session 快照中同时出现在消息 33(旧 plan_visibility_plan_review tool,id call_XLR...)与消息 35(自身 tool,id call_19L...);两处最终都有 4/4,强烈指向实时 plan update 曾路由到旧 tool/card,而用户看到的是当前 tool/card。
  • 计划最终更新时间 00:35:45Z 早于子代理返回时间 00:37:10Z 约 85 秒,排除“计划只在完成瞬间落盘、用户来不及看到”的解释。
  • 当前环境没有可用 Playwright/浏览器控制工具;真实 UI 验收必须以用户截图为准,代码回归只能作为辅助证据。
  • 服务端根因已定位:syncCcwebMcpChildAgentsFromCollabItem 对已有 child 仅在 spawnToolId 为空时赋 item.id。真实 subAgentActivity started 到达新卡片 call_19L... 时,child 已错误持有旧卡片 call_XLR...,因此不会重绑。
  • 后续每次 plan notification 都按旧 child.spawnToolId 调用 findCcwebMcpChildTargetToolInMessages,于是 4/4 被合并到消息 33 的旧卡片;用户正在看的消息 35 当前卡片没有计划字段,和截图完全一致。
  • findCcwebMcpChildTargetToolInMessages 在给定 spawn tool id 找不到精确匹配时仍回退到最新 collab tool,会进一步放大跨卡片污染风险;回归需确保有明确 id 时不更新其他卡片。
  • 计划审查通过;建议明确 child id → tool call id → message/card 映射,并同时断言目标卡片更新、非目标卡片不变,真实验收只接受用户截图或明确确认。
  • 父 rollout 证实 call_19LK... 就是 plan_progress_live_demo 的 spawn_agent / sub_agent_activity started 事件 ID,child id 为 019fab4a...;call_XLR... 明确属于更早的计划审查代理。两者不是同一卡片。
  • 当前回归 assertCodexAppChildToolFallbackContract 明确断言“缺失的 spawnToolId 应回退到最新 collab tool”,这正是跨卡片污染的错误契约,需要反转为:给定非空 id 未精确命中时不得修改任何其他卡片。
  • 新双卡片回归覆盖:正确 tool id 只更新当前卡片、旧卡片不出现当前 child、非空未知 id 返回 null 且两张卡片都不变、subAgentActivity started 将已有 child 从旧 id 重绑到当前 id。
  • 修复前回归稳定失败在“非空未知 spawn tool id 不得回退”,修改服务端后已通过。
  • 全量集成回归揭示嵌套 V2 场景:孙代理的内部 activity id 不在根会话可见 toolCalls 中,原先依赖模糊回退把孙代理合并进父子代理卡片。
  • 最终路由规则改为:根子代理使用自己的 canonical activity/tool id;嵌套子代理继承最近可见父子代理的 spawnToolId,同时保留自己的 parentThreadId。这样可以精确命中可见卡片而无需跨卡回退。
  • 更新后 subagent-card-routing、JS 语法、diff check 和全量 npm run regression 全部通过;全量回归也重新覆盖了 nested V2 child result。