修复 HEAD 既存红:planning session 恢复块给双锚缺失诊断让路

committed_plan_submit_without_either_anchor_is_publicly_recoverable_and_fails_closed
在 HEAD 上确定性失败(Windows 与 WSL 干净基线一致,与本轮审查修复无关):
phase 确实是 needs-reconciliation,但 error 不是「恢复锚点同时缺失」。

M1C-2b 把 planning session 恢复块插进了同一个循环,位置在
reconcile_missing_plan_submit_anchors_at 之前。该 fixture 用裸
start_game_creator_agent_runtime_task_at 建策划任务,task record 上没有
parentAgentId/delegationId,于是 exact_plan_child_identity_at 判定身份不符、
返回类型化 Err,session 块随即落 needs-reconciliation 并 continue,更具体的双锚
缺失诊断永远轮不到跑。两条都写 needs-reconciliation,谁先写谁赢,而先写的恰恰是
信息量更少的那条。

不是判错,是插在前面的门改变了哪条诊断胜出——与 M1C-1 那五条 CI 失败、与 P4
同一形状。

- session 恢复块加让路条件:该 run 是双锚缺失候选时跳过。本块只服务「将要继续」的
  continuation,而双锚缺失的 run 根本不会继续,修投影没有意义
- 选让路而不是调换两块顺序:调换会让锚点探测对所有 Agent 提前,正是「前移改作用域」
  那个坑本身。探测器只对 project-planning + agent-delegate + standard 返回 Some,
  且对已按双锚缺失收敛过的状态返回 None,因此这道让路既窄又幂等
- recovery 族 105 passed/2 failed -> 106 passed/1 failed,planning_ 150/1、
  session 87/1;剩下的唯一红是 planning_recovery_contains_session_identity_conflict
  _before_provider(另一条既存红,下一个提交处理)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 05:34:22 +00:00
parent c1a70cb37d
commit 73816bd512
@@ -1049,8 +1049,18 @@ pub(in crate::agent) fn resume_game_creator_agent_background_tasks_unredacted_at
// and reacquire in project -> execution order, re-read the task under
// the new lock set, project exactly once, then release the project lock
// before any later path can enter the Session lane.
//
// 但双锚缺失的 run 要让路。本块修 session 投影,是为了让**将要继续**的
// continuation 不带着过期决定去发第一个 Provider 请求;而 `plan.submit_gdd`
// 的 pending/batch 恢复锚点同时丢失的 run 根本不会继续——下面
// `reconcile_missing_plan_submit_anchors_at` 会把它判失败并要求人工核对。
// 若不让路,本块会先落一条泛化的「未命中 planning session 协调器」,把那条
// 精确得多的双锚缺失诊断永久挡在后面:两者都写 needs-reconciliation,谁先
// 写谁赢,而先写的那条恰恰是信息量更少的。探测器自身很便宜,且对「已按双锚
// 缺失收敛过」的状态返回 None,所以这道让路不会反复触发。
let runtime_lock = if agent_id == GAME_CREATOR_PROJECT_PLANNING_AGENT_ID
&& read_recoverable_runnable_game_creator_agent_runtime_task(root, &agent_id)?.is_some()
&& missing_plan_submit_anchor_candidate_at(root, &agent_id)?.is_none()
{
drop(runtime_lock);
let project_lock = match acquire_game_creator_agent_runtime_project_write_lock_with_wait(