收紧策划 continuation 游标防止旧 delivery 越权

新增 planning session latestDelegationId 直接后继校验,阻止旧 delivery 另起分支

保留用户修订周期内质量返工的 user_revision 语义并更新提交门注释

补充 continuation 边界回归与 Fast GDD 技术决策文档
This commit is contained in:
2026-08-28 07:46:32 +00:00
parent fa3b60777c
commit 52894d2e8a
4 changed files with 79 additions and 11 deletions
@@ -25,6 +25,25 @@ fn plan_coordinator_error(kind: &str, detail: impl AsRef<str>) -> String {
format!("{kind}: {}", detail.as_ref())
}
fn validate_plan_continuation_parent<'a>(
latest_delegation_id: &str,
delivery: &'a StaticDelegateDeliveryRecord,
) -> Result<&'a str, String> {
let original_id = delivery.repair_of_delegation_id.as_deref().ok_or_else(|| {
plan_coordinator_error(
"PLAN_ACTIVE_RUN_EXISTS",
"已有 planning session 时不能创建第二条根 delegation",
)
})?;
if latest_delegation_id != original_id {
return Err(plan_coordinator_error(
"PLAN_NEEDS_RECONCILIATION",
"planning continuation 必须直接继承当前 session 的 latest delegation",
));
}
Ok(original_id)
}
fn plan_session_successor_base(previous: &PlanSessionV1) -> Result<PlanSessionV1, String> {
let mut next = previous.clone();
next.session_revision = previous.session_revision.checked_add(1).ok_or_else(|| {
@@ -230,6 +249,45 @@ mod option_label_delimiter_tests {
}
}
#[cfg(test)]
mod planning_continuation_parent_tests {
use super::*;
fn delivery(repair_of_delegation_id: Option<&str>) -> StaticDelegateDeliveryRecord {
new_static_delegate_delivery_with_contract(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"supervisor-session",
"supervisor-run",
"delegate-action",
"current-delivery",
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
"planning-session",
"planning-run",
&[],
&[],
repair_of_delegation_id,
)
}
#[test]
fn continuation_must_extend_the_session_cursor() {
let continuation = delivery(Some("older-delivery"));
let error = validate_plan_continuation_parent("current-delivery", &continuation)
.expect_err("older delivery must not become the current planning branch");
assert!(error.contains("latest delegation"));
}
#[test]
fn continuation_accepts_the_current_session_cursor() {
let continuation = delivery(Some("current-delivery"));
assert_eq!(
validate_plan_continuation_parent("current-delivery", &continuation)
.expect("current delivery is a valid continuation"),
"current-delivery"
);
}
}
fn plan_option_label_matches_answer(label: &str, normalized_answer: &str) -> bool {
label == normalized_answer
}
@@ -584,12 +642,13 @@ pub(crate) fn ensure_plan_session_for_planning_child_task_at_locked(
{
return Ok(true);
}
let original_id = delivery.repair_of_delegation_id.as_deref().ok_or_else(|| {
plan_coordinator_error(
"PLAN_ACTIVE_RUN_EXISTS",
"已有 planning session 时不能创建第二条根 delegation",
)
})?;
// `latest_delegation_id` is the planning session's single continuation
// cursor. A new child must extend that cursor directly; otherwise a
// Supervisor can select an older claimed delivery and make an unrelated
// branch look like the current session. Keep this check here, after the
// exact-task replay fast path above, so replaying an already projected
// child remains idempotent.
let original_id = validate_plan_continuation_parent(&previous.latest_delegation_id, &delivery)?;
let deliveries = list_static_delegate_deliveries_at(root)?;
if static_delegate_lineage_contains_unknown_contract_status(
&deliveries,
@@ -1189,9 +1189,10 @@ fn validate_current_session_cas(
Ok(())
}
/// `user_revision` 只证明审批意见,不证明澄清。首次 collecting、澄清续跑和提交前
/// 质量返工的 session 都没有 revise/reject `lastDecisionRef`;合法续跑会把该引用
/// 带到新的 collecting successor 上。replay 不走这里。
/// `user_revision` 只证明审批意见,不证明澄清。首次 collecting、澄清续跑和没有待处理
/// 用户修订的普通质量返工 session 都没有 revise/reject `lastDecisionRef`;用户修订周期
/// 内的 continuation(包括其质量返工)会把该引用带到新的 collecting successor 上。
/// continuation 的直接父边由 planning coordinator 校验,replay 不走这里。
fn validate_user_revision_requires_approval_decision(
session: &PlanSessionV1,
input: &PlanSubmitGddInputV1,