审批消费测试改用生产写法收口策划子 Run,补上幂等键冲突的覆盖
approval_receipt_consumes_submit_batch_and_folds_final_provider_usage_once 本该覆盖上一个提交修掉的那条链路,却一直是绿的。原因在夹具:它手搓 child_completed 再用 append_game_creator_agent_runtime_task 写终态,而这个入口 不往记录里写 actionId。生产侧走的是 ensure_project_planning_submit_child_completion_at,它用 append_game_creator_agent_runtime_task_projection_once 把 actionId 一起盖进去。 差别正好落在被测的那把幂等键上。磁盘上没有 (runId, actionId, phase=completed) 这一行,receipt 消费时的投影就找不到冲突对象,顺利追加——于是一条 100% 必现的 线上故障在单测里完全看不见。 改成调用生产入口本身,并按它的前置补齐 durable delivery(沿用同模块 2400 行 一带的既有写法,结果文案与生产的 last_response 一致,避免 publish 时身份不符)。 A/B 验证过测试确实抓得住:临时撤掉上一个提交的修复,此测试报 assertion failed: !first.recovery_pending,正是线上症状;装回修复即通过。 同批 planning_submit 并发跑有 6 条失败,逐条单跑全过,是本机 agent.db 文件锁 竞争,与本改动无关。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4484,21 +4484,49 @@ mod tests {
|
||||
context.action_id = pending.action_id.clone();
|
||||
context.action_fingerprint = pending.action_fingerprint.clone();
|
||||
|
||||
execute_plan_submit_gdd(&root, &context, &input).expect("commit GDD");
|
||||
let submit_result = execute_plan_submit_gdd(&root, &context, &input).expect("commit GDD");
|
||||
assert_eq!(
|
||||
fold_plan_provider_usage_into_session_at_locked(&root)
|
||||
.expect("submit batch must defer final usage"),
|
||||
PlanProviderUsageFoldOutcome::Deferred
|
||||
);
|
||||
// Close the child through the production writer rather than a
|
||||
// hand-rolled terminal state. It stamps the task projection with the
|
||||
// submit `actionId`, which is what makes the receipt consumption below
|
||||
// collide on the (runId, actionId, phase) idempotency key. A plain
|
||||
// `append_game_creator_agent_runtime_task` leaves no keyed row, so the
|
||||
// collision never happens and this test passes over a lane that fails
|
||||
// 100% of the time in production.
|
||||
let delivery = new_static_delegate_delivery(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
"root-session-001",
|
||||
&context.root_run_id,
|
||||
"parent-action-001",
|
||||
&context.delegation_id,
|
||||
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
|
||||
&context.session_id,
|
||||
&context.created_by_run_id,
|
||||
);
|
||||
create_or_read_static_delegate_delivery_at(&root, &delivery)
|
||||
.expect("create planning delivery");
|
||||
mark_static_delegate_delivery_ready_at(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
|
||||
&context.session_id,
|
||||
&context.created_by_run_id,
|
||||
&context.delegation_id,
|
||||
"completed",
|
||||
&format!("Fast GDD v{} 已提交。", submit_result.gdd_ref.version),
|
||||
)
|
||||
.expect("close planning delivery");
|
||||
let mut child_completed = child_runtime.clone();
|
||||
child_completed.status = "completed".to_string();
|
||||
child_completed.phase = "completed".to_string();
|
||||
child_completed.current_action = "Fast GDD 已提交".to_string();
|
||||
child_completed.pending_tool_action = Some(pending.summary());
|
||||
append_game_creator_agent_runtime_task(&root, &child_completed)
|
||||
.expect("append completed planning child task");
|
||||
write_game_creator_agent_runtime_state(&root, &child_completed)
|
||||
.expect("persist completed planning child state");
|
||||
ensure_project_planning_submit_child_completion_at(
|
||||
&root,
|
||||
&mut child_completed,
|
||||
&pending,
|
||||
&submit_result,
|
||||
)
|
||||
.expect("close planning child at the submit point");
|
||||
|
||||
let gdd = read_plan_gdd_chain(&root)
|
||||
.expect("read submitted GDD")
|
||||
|
||||
Reference in New Issue
Block a user