4c20e06a9a
现场:上游网关把流掐断在 response.created 之后,策划子 run 落到 needs-reconciliation,父 Supervisor 在 waiting-for-delegate-receipts 上静默 二十分钟,界面全程显示「正在启动处理」。前端认得 needs-reconciliation, 但它读的是父 run,而父 run 看上去一切正常。 成因是三件事叠起来: 1. 委派子 run 的其余每一条终态出口都会向父 run 发布结果—— finish_game_creator_agent_runtime_turn_at、fail_..._turn_at、 fail_..._budget_at 三条都调 publish_game_creator_agent_delegate_result_for_state ——唯独 needs-reconciliation 不发。它在 task_queue 的 outcome match 里从 `outcome => return outcome` 那条兜底臂返回,一个通知都没有。 2. delivery 于是永远停在 Dispatched,而 static_delegate_completion_barrier_at 无条件把 Dispatched 计入 waiting。 3. 父唤醒是一次性事件驱动的:drive_waiting_static_delegate_parent_wake_pass 一旦看到 has_waiting() 为真就永久 return,没有任何周期性复查。 那条出口拒绝**认领结果**是对的——Runtime 无法证明服务端副作用是否已经发生, 伪造一份 delivery 结果比卡住更糟。缺的是「这条委派不会再产生回执」这句话。 改法:在 outcome match 里给 NeedsReconciliation 补一条臂,把父 run 也标成 需要人工核对(复用既有的 mark_static_delegate_parent_wake_needs_reconciliation_at), 不伪造任何 delivery 结果。该原语自带门槛——父 run 的 run_id 不匹配、或父 run 不在 waiting-for-delegate-receipts 时直接返回 Ok(())——所以重复调用与竞态安全。 只覆盖立项策划子 Agent。做游戏与做素材的委派子 run 逐字保持既有行为; 它们那条链路上同一个死锁仍然存在,解开需要另行评估各自的父 run 语义。 守卫的四个前提拿现场 gameagent-75a9be8d 的落盘数据逐条验过:子 run phase=needs-reconciliation、parentAgentId=project-supervisor、parentRunId 与 父 runId 逐字相同、父 phase=waiting-for-delegate-receipts。这个修复会在那次 事故上真的触发。 三条新测试:策划子 run 卡死必须把父 run 抬起来;重复通知无副作用; design-director 子 run 同样卡死时父 run 一个字节都不变。 需要说明的是,被测的是 notify 助手本身,outcome match 那一行接线没有被覆盖 ——驱动一次真实的 NeedsReconciliation 需要 Provider。 delegated_child_reconciliation_tests 3/3、delegation::tests 19/19、 tool_plan 93/93、user_input 21/21、prompt::tests 32/32、 plan_envelope_repair_tests 6/6。tests::collaboration 仍是本机既有的 4 条失败, 已 stash 做 A/B 确认失败集合逐条相同。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>