立项策划:落地 M1C-0 用户修订 lineage 分类

新增 UserRevisionRequested durable 状态并保持未知状态 serde fail-closed

区分用户修订、澄清 continuation 与质量返工的 lineage 计数及深度门

补充连续修订、真实 agent.delegate、32-hop 和历史兼容回归

同步 Fast GDD、Runtime 文档与 M1C-0 决策记录
This commit is contained in:
2026-08-14 04:02:53 +00:00
parent 09c7d7af8d
commit 7db5118e27
5 changed files with 475 additions and 10 deletions
@@ -37,6 +37,7 @@ pub(crate) enum StaticDelegateContractStatus {
EvidenceReady,
NeedsRepair,
NeedsUserInput,
UserRevisionRequested,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
@@ -1090,7 +1091,7 @@ pub(crate) fn game_chat_art_delivery_gap_at(
/// 下一跳(若存在)应当被归类为澄清 continuation,而不是质量返工。
/// `validate_static_delegate_repair_request_at` 与
/// `validate_static_delegate_clarification_continuation_at` 共用这一个判据源,
/// 避免两处口径漂移。
/// 避免两处口径漂移;用户修订状态另由下方判据单独识别
fn static_delegate_original_is_awaiting_clarification(
delivery: &StaticDelegateDeliveryRecord,
) -> bool {
@@ -1099,12 +1100,26 @@ fn static_delegate_original_is_awaiting_clarification(
})
}
/// 唯一权威判据:某条 delivery 是否由用户审批的「修改」动作标记为待修订。
///
/// 该状态只由后续审批工作包写入;本包只让 lineage 重放认识它,不能自行生成或
/// 把其它状态静默映射成它。
fn static_delegate_original_is_user_revision_requested(
delivery: &StaticDelegateDeliveryRecord,
) -> bool {
delivery.structured_result.as_ref().is_some_and(|result| {
result.contract_status == StaticDelegateContractStatus::UserRevisionRequested
})
}
/// 沿 repair_of_delegation_id 反向重放整条链,现算目标 delivery 的
/// (repair_depth, clarification_round)。两个维度都是运行时派生值,故意不落盘:
/// - R3链根repair_of_delegation_id 为 None):depth = 0round = 0。
/// - R1澄清跳父节点处于 awaiting-clarification):round = parent.round + 1
/// - 链根repair_of_delegation_id 为 None):depth = 0round = 0。
/// - 澄清跳父节点处于 awaiting-clarification):round = parent.round + 1
/// depth 原样继承(不重置),否则可以插一次澄清洗掉返工深度,变成无限返工。
/// - R2(返工跳:父节点不处于 awaiting-clarification):depth = parent.depth + 1
/// - 用户修订跳(父节点为 UserRevisionRequested):depth/round 都原样继承
/// 因为该跳由用户显式触发,不属于 runaway-agent 质量返工。
/// - 质量返工跳(父节点不处于上述两种状态):depth = parent.depth + 1
/// round 重置为 0,因为返工后策划节点重新开工,不能因为返工吃掉预设的澄清轮次预算。
///
/// 防环 / 防越界:反向重放阶段一旦遇到重复 id、缺失的上游节点,或跳数超出
@@ -1140,6 +1155,8 @@ pub(crate) fn static_delegate_lineage_counters(
for parent in &chain[..chain.len().saturating_sub(1)] {
if static_delegate_original_is_awaiting_clarification(*parent) {
round += 1;
} else if static_delegate_original_is_user_revision_requested(*parent) {
// 用户明确触发的修订不是 runaway-agent 返工;保留两个运行时派生计数。
} else {
depth += 1;
round = 0;
@@ -1215,6 +1232,12 @@ pub(crate) fn validate_static_delegate_repair_request_at(
if original_clarification_round >= clarification_round_limit {
return Err("静态委派澄清轮次已达上限".to_string());
}
} else if static_delegate_original_is_user_revision_requested(original) {
// 用户修订不消耗 repair_depth/clarification_round,但链上重放的 fail-closed
// 哨兵仍不能被绕过;否则损坏或成环的 durable lineage 可能被误放行。
if original_repair_depth == u32::MAX || original_clarification_round == u32::MAX {
return Err("静态委派谱系计数无效,拒绝继续".to_string());
}
} else if original_repair_depth >= 1 {
return Err("静态委派返工深度最多为 1".to_string());
}
@@ -2575,4 +2598,288 @@ mod tests {
fs::remove_dir_all(root).ok();
}
fn claimed_static_delegate_for_lineage_test(
parent_run_id: &str,
delegation_id: &str,
repair_of_delegation_id: Option<&str>,
contract_status: StaticDelegateContractStatus,
) -> StaticDelegateDeliveryRecord {
let mut delivery = new_static_delegate_delivery_with_contract(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-lineage-parent-session",
parent_run_id,
&format!("{delegation_id}-action"),
delegation_id,
"design-director",
&format!("{delegation_id}-session"),
&format!("{delegation_id}-run"),
&[],
&[],
repair_of_delegation_id,
);
let mut result = StaticDelegateStructuredResult::default();
result.contract_status = contract_status;
delivery.status = StaticDelegateDeliveryStatus::ClaimedByParent;
delivery.terminal_status = Some("completed".to_string());
delivery.result_summary = Some("M1C-0 lineage test".to_string());
delivery.structured_result = Some(result);
delivery.claimed_by_action_id = Some(format!("{delegation_id}-claim-action"));
delivery
}
#[test]
fn static_delegate_user_revision_preserves_counters_and_bypasses_depth_gate() {
let root = std::env::temp_dir().join(format!(
"genarrative-static-user-revision-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time after unix epoch")
.as_nanos()
));
init_local_game_project_at(&root, "m1c0-user-revision", "M1C-0 用户修订分类测试")
.expect("project init");
let parent_run_id = "m1c0-user-revision-parent-run";
let root_delivery = claimed_static_delegate_for_lineage_test(
parent_run_id,
"m1c0-user-revision-d0",
None,
StaticDelegateContractStatus::NeedsRepair,
);
write_static_delegate_delivery_at(&root, &root_delivery).expect("write root delivery");
// 先证明做游戏链路的普通返工仍然受 depth=1 门限制。
let mut first_revision = claimed_static_delegate_for_lineage_test(
parent_run_id,
"m1c0-user-revision-d1",
Some(&root_delivery.delegation_id),
StaticDelegateContractStatus::NeedsRepair,
);
write_static_delegate_delivery_at(&root, &first_revision)
.expect("write first repair delivery");
let ordinary_repair_error = validate_static_delegate_repair_request_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
parent_run_id,
"m1c0-user-revision-d2-ordinary",
"design-director",
&[],
&[],
Some(&first_revision.delegation_id),
)
.expect_err("ordinary repair at depth=1 must remain blocked");
assert!(ordinary_repair_error.contains("深度最多为 1"));
// 同一节点被用户审批标记为修订后,第一次用户修订不应再被当作质量返工。
first_revision
.structured_result
.as_mut()
.expect("first revision structured result")
.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
write_static_delegate_delivery_at(&root, &first_revision)
.expect("rewrite first delivery as user revision");
let deliveries = list_static_delegate_deliveries_at(&root).expect("list deliveries");
assert_eq!(
static_delegate_lineage_counters(&deliveries, &first_revision.delegation_id),
(1, 0),
"用户修订跳的父节点已有一次普通返工,两个计数仍应保持 (1,0)"
);
validate_static_delegate_repair_request_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
parent_run_id,
"m1c0-user-revision-d2",
"design-director",
&[],
&[],
Some(&first_revision.delegation_id),
)
.expect("user revision must bypass the depth=1 quality-repair gate");
// 连续第二次用户修订仍挂在同一 lineage 上,不能把任一计数重新解释成返工。
let second_revision = claimed_static_delegate_for_lineage_test(
parent_run_id,
"m1c0-user-revision-d2",
Some(&first_revision.delegation_id),
StaticDelegateContractStatus::UserRevisionRequested,
);
write_static_delegate_delivery_at(&root, &second_revision)
.expect("write second user revision delivery");
let deliveries = list_static_delegate_deliveries_at(&root).expect("list deliveries");
assert_eq!(
static_delegate_lineage_counters(&deliveries, &second_revision.delegation_id),
(1, 0),
"连续用户修订不能增加 repair_depth,也不能重置 clarification_round"
);
validate_static_delegate_repair_request_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
parent_run_id,
"m1c0-user-revision-d3",
"design-director",
&[],
&[],
Some(&second_revision.delegation_id),
)
.expect("a second consecutive user revision must remain admissible");
fs::remove_dir_all(root).ok();
}
#[test]
fn static_delegate_user_revision_preserves_existing_clarification_round() {
let mut quality_root = new_static_delegate_delivery(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-counter-session",
"m1c0-counter-run",
"m1c0-counter-d0-action",
"m1c0-counter-d0",
"design-director",
"m1c0-counter-d0-session",
"m1c0-counter-d0-run",
);
let mut quality_result = StaticDelegateStructuredResult::default();
quality_result.contract_status = StaticDelegateContractStatus::NeedsRepair;
quality_root.structured_result = Some(quality_result);
let mut clarification = new_static_delegate_delivery_with_contract(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-counter-session",
"m1c0-counter-run",
"m1c0-counter-d1-action",
"m1c0-counter-d1",
"design-director",
"m1c0-counter-d1-session",
"m1c0-counter-d1-run",
&[],
&[],
Some("m1c0-counter-d0"),
);
let mut clarification_result = StaticDelegateStructuredResult::default();
clarification_result.contract_status = StaticDelegateContractStatus::NeedsUserInput;
clarification.structured_result = Some(clarification_result);
let mut user_revision = new_static_delegate_delivery_with_contract(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-counter-session",
"m1c0-counter-run",
"m1c0-counter-d2-action",
"m1c0-counter-d2",
"design-director",
"m1c0-counter-d2-session",
"m1c0-counter-d2-run",
&[],
&[],
Some("m1c0-counter-d1"),
);
let mut user_revision_result = StaticDelegateStructuredResult::default();
user_revision_result.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
user_revision.structured_result = Some(user_revision_result);
let deliveries = vec![quality_root, clarification, user_revision];
assert_eq!(
static_delegate_lineage_counters(&deliveries, "m1c0-counter-d2"),
(1, 1),
"用户修订必须继承此前的 depth=1 与 clarification_round=1"
);
}
#[test]
fn static_delegate_lineage_boundary_and_status_deserialization_fail_closed() {
let mut deliveries = Vec::new();
let mut ids = Vec::new();
for index in 0..33 {
ids.push(format!("m1c0-lineage-boundary-{index}"));
}
for index in 0..33 {
let parent = (index > 0).then(|| ids[index - 1].as_str());
let mut delivery = new_static_delegate_delivery(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-boundary-session",
"m1c0-boundary-run",
&format!("{}-action", ids[index]),
&ids[index],
"design-director",
&format!("{}-session", ids[index]),
&format!("{}-run", ids[index]),
);
delivery.repair_of_delegation_id = parent.map(str::to_string);
let mut result = StaticDelegateStructuredResult::default();
result.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
delivery.structured_result = Some(result);
deliveries.push(delivery);
}
assert_eq!(
static_delegate_lineage_counters(&deliveries[..32], &ids[31]),
(0, 0),
"32 条 delivery31 跳)仍应在安全阀内"
);
assert_eq!(
static_delegate_lineage_counters(&deliveries, &ids[32]),
(u32::MAX, u32::MAX),
"超过 32-hop 安全阀必须 fail closed"
);
let encoded = serde_json::to_value(StaticDelegateContractStatus::UserRevisionRequested)
.expect("serialize user revision status");
assert_eq!(encoded, serde_json::json!("user-revision-requested"));
assert_eq!(
serde_json::from_value::<StaticDelegateContractStatus>(encoded)
.expect("round-trip user revision status"),
StaticDelegateContractStatus::UserRevisionRequested
);
let unknown = serde_json::from_value::<StaticDelegateContractStatus>(serde_json::json!(
"future-contract-status"
))
.expect_err("unknown durable contract status must fail closed");
assert!(unknown.to_string().contains("unknown variant"));
// 额外钉住 durable sidecar 读取入口:未知状态不能在 JSON sidecar 层被吞掉或
// 降级成 Default(NeedsRepair)。
let root = std::env::temp_dir().join(format!(
"genarrative-static-unknown-status-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time after unix epoch")
.as_nanos()
));
init_local_game_project_at(&root, "m1c0-unknown-status", "未知静态委派状态解析测试")
.expect("project init");
let mut record = new_static_delegate_delivery(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-unknown-status-session",
"m1c0-unknown-status-run",
"m1c0-unknown-status-action",
"m1c0-unknown-status-delivery",
"design-director",
"m1c0-unknown-status-target-session",
"m1c0-unknown-status-target-run",
);
record.status = StaticDelegateDeliveryStatus::Ready;
record.terminal_status = Some("completed".to_string());
record.result_summary = Some("unknown status fixture".to_string());
let mut result = StaticDelegateStructuredResult::default();
result.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
record.structured_result = Some(result);
write_static_delegate_delivery_at(&root, &record).expect("write known status fixture");
let delivery_path = root
.join(STATIC_DELEGATE_DELIVERY_DIR)
.join(format!("{}.json", record.delegation_id));
let mut raw = serde_json::to_value(&record).expect("serialize status fixture");
raw["structuredResult"]["contractStatus"] = serde_json::json!("future-contract-status");
fs::write(
&delivery_path,
serde_json::to_vec(&raw).expect("serialize unknown status fixture"),
)
.expect("overwrite unknown status fixture");
let sidecar_error = read_static_delegate_delivery_at(&root, &record.delegation_id)
.expect_err("unknown durable status must fail at sidecar read");
assert!(
sidecar_error.contains("解析") && sidecar_error.contains("静态委派 delivery"),
"unexpected sidecar parse error: {sidecar_error}"
);
fs::remove_dir_all(root).ok();
}
}
@@ -3780,6 +3780,151 @@ fn alternating_repair_and_clarification_hop_blocks_second_repair_at_depth_two()
fs::remove_dir_all(root).ok();
}
#[test]
fn static_delegate_user_revision_requested_continuation_passes_real_delegate_gate_after_depth_one() {
// 这里手工把 delivery 状态切到 UserRevisionRequested,是对 M1C-1 未来审批写入的
// durable fixture;委派本身仍走真实 observe_agent_runtime_agent_delegate 生产路径。
let root = unique_project_path();
init_local_game_project_at(
&root,
"project-user-revision-real-gate",
"用户修订真实委派门测试",
)
.expect("project init");
let run_id = "project-supervisor-user-revision-real-gate-run";
let state = start_game_creator_agent_runtime_task_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"验收策划子 Agent 并处理用户修订",
run_id,
"agent-chat",
"等待策划回执",
vec!["完成用户修订后的方案".to_string()],
)
.expect("start supervisor run");
let acceptance_criteria = vec!["必须交付策划产物".to_string()];
let expected_artifacts = vec!["game/user-revision-real-gate.md".to_string()];
let target_agent_id = "design-director";
let target_lock = try_acquire_game_creator_agent_runtime_task_lock(&root, target_agent_id)
.expect("acquire target lane")
.expect("target lane available");
let d0_id = "user-revision-real-gate-d0";
create_dispatched_static_delegate_delivery(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
&state.session_id,
run_id,
"user-revision-real-gate-d0-action",
d0_id,
target_agent_id,
"user-revision-real-gate-d0-session",
"user-revision-real-gate-d0-run",
&acceptance_criteria,
&expected_artifacts,
None,
);
mark_and_claim_static_delegate_needs_repair(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
"user-revision-real-gate-d0-session",
"user-revision-real-gate-d0-run",
d0_id,
&expected_artifacts,
"user-revision-real-gate-d0-claim",
);
let d1_action = "user-revision-real-gate-d1-repair-action";
let d1_id = agent_runtime_delegation_id(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
d1_action,
);
let d1_observation = dispatch_static_delegate_plain_repair(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
"补齐用户修订前的策划产物",
&acceptance_criteria,
&expected_artifacts,
d0_id,
d1_action,
);
assert_eq!(
d1_observation.status, "ok",
"首次质量返工必须放行:{d1_observation:?}"
);
let d1_delivery = read_static_delegate_delivery_at(&root, &d1_id)
.expect("read D1")
.expect("D1 exists");
mark_and_claim_static_delegate_needs_repair(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
&d1_delivery.target_session_id,
&d1_delivery.target_run_id,
&d1_id,
&expected_artifacts,
"user-revision-real-gate-d1-claim",
);
let mut d1_user_revision = read_static_delegate_delivery_at(&root, &d1_id)
.expect("read claimed D1")
.expect("claimed D1 exists");
d1_user_revision
.structured_result
.as_mut()
.expect("D1 structured result")
.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
write_static_delegate_delivery_at(&root, &d1_user_revision)
.expect("persist simulated approval revision status");
// D1 的链上 depth 已经是 1;只有 UserRevisionRequested 分支能让这次真实委派继续。
let d2_action = "user-revision-real-gate-d2-revision-action";
let d2_id = agent_runtime_delegation_id(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
d2_action,
);
let d2_observation = dispatch_static_delegate_plain_repair(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
run_id,
target_agent_id,
"按用户审批修改方案",
&acceptance_criteria,
&expected_artifacts,
&d1_id,
d2_action,
);
assert_eq!(
d2_observation.status, "ok",
"UserRevisionRequested 不能被 depth=1 质量返工门误拒:{d2_observation:?}"
);
let d2_delivery = read_static_delegate_delivery_at(&root, &d2_id)
.expect("read D2")
.expect("D2 exists");
let deliveries = list_static_delegate_deliveries_at(&root).expect("list deliveries");
assert_eq!(
static_delegate_lineage_counters(&deliveries, &d2_id),
(1, 0),
"用户修订续跑应保留已有 depth=1 且不重置此前 round"
);
assert_eq!(
d2_delivery.repair_of_delegation_id.as_deref(),
Some(d1_id.as_str())
);
drop(target_lock);
fs::remove_dir_all(root).ok();
}
#[test]
fn quality_rework_resets_clarification_round_budget() {
let root = unique_project_path();
@@ -1,5 +1,13 @@
# 决策记录
## 2026-08-14 M1C-0:用户修订状态进入静态委派 lineage 分类
- 落地:`StaticDelegateContractStatus` 新增 `UserRevisionRequested`serde durable 值为 `user-revision-requested`)。`static_delegate_lineage_counters` 现在按三类传播:`NeedsUserInput` 只增加 `clarification_round``UserRevisionRequested` 原样继承 `repair_depth``clarification_round`;其它状态继续按质量返工增加 `repair_depth` 并重置 `clarification_round`
- 门禁:父 delivery 为 `UserRevisionRequested` 时,后续同链续跑不再被 `repair_depth=1` 的质量返工门误拒,因此连续用户修订可以继续通过;链上 `(u32::MAX, u32::MAX)` fail-closed 哨兵仍拒绝,不得借用户修订分支绕过 32-hop、成环或缺失上游保护。普通做游戏链路没有该状态,`repair_depth≤1` 结论未被推翻。
- 范围:本包没有任何审批状态写入方、`gdd-approval` pending、receipt 或前端状态;`UserRevisionRequested` 仍由后续 `M1C-1` 的审批命令与 receipt 同步写入。本包不 bump `STATIC_DELEGATE_DELIVERY_SCHEMA_VERSION`,未知 durable status 由 serde 直接报错,禁止静默降级为 `NeedsRepair`
- 回归:新增连续用户修订、保留已有澄清轮次、普通 depth=1 拒绝、32-hop fail-closed、`user-revision-requested` serde round-trip 与未知 variant fail-closed 覆盖;无新状态的历史记录继续按原质量返工分类。
- 关联文档:`docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md` 第 23.7、23.8 节;后续审批写入依赖 `M1B-2``M1C-0`
## 2026-08-13 M1A-2planning 子 Agent 两层工具面与角色 brief 注入
- 落地范围:在 `M1A-1``project-supervisor-plan` source 基础上,收口两层工具面。Supervisor 根 run 继续使用 `standard` 的现役工具面;`project-planning` 只接受 `source=agent-delegate``profile=standard`、父 Agent 为 `project-supervisor` 的静态委派身份。
@@ -1060,7 +1060,7 @@ static delivery 在 V1.16 的 parent/target Agent、Session、run、action、del
```text
structuredResult = {
contractStatus: evidence-ready | needs-repair,
contractStatus: evidence-ready | needs-repair | needs-user-input | user-revision-requested,
artifacts: [{ path, sha256 }],
missingExpectedArtifacts: [path],
verificationRequired: boolean,
@@ -1070,6 +1070,11 @@ structuredResult = {
}
```
其中 `needs-user-input` 是静态委派澄清中转状态,`user-revision-requested` 是 Fast GDD
审批扩展预留的用户修订状态;后者不由通用 Runtime 自动生成,且不得被反序列化失败静默
降级为 `needs-repair`。用户修订的 lineage 计数语义与 M1C-0/M1C-1 边界以
`【技术方案】立项策划AgentFast GDD-2026-08-10.md` 第 23.7 节为准。
`artifacts``missingExpectedArtifacts` 必须恰好分割全部 `expectedArtifacts`,路径保持合同中的精确相对路径;存在的普通文件在形成终态回执时重新计算 SHA-256。`evidence``error` 只允许有界、已清洗的安全摘要、项目内相对路径和哈希,不得保存凭据、绝对路径、私有 observation 正文或 Provider payload。旧空合同 delivery 可以继续没有 `structuredResult`,且不得为了升级格式改写原 sidecar;新带合同 delivery 进入 ready/claimed 时必须有完整 `structuredResult`
### 客观门禁与语义验收
@@ -1,9 +1,9 @@
# 立项策划 AgentFast GDD)技术方案
- 日期:2026-08-10
- 状态:2026-08-12 **M0 代码工作包全部完成**`M0A-2``M0B-1``M0B-2` 已合入 M0 集成分支并通过各自门禁,见第 23.4 节);同日 **D6 作废、拓扑改变**`M0A-1` 交付的文档基线随之失效,需以工作包 `M0A-3` 修订,**修订完成前 M0 不计完整完成**(见第 1.1 节)。2026-08-13 **D9 二次作废、D10 作废,由 D11 取代**:立项策划节点改为 Project Supervisor 通过 `agent.delegate` 发起的静态委派子 Agent,问询复用 PR #165 中转链路(见第 1.1 节「D11 新拓扑」);D11 依赖 WP1(静态委派澄清轮次与返工深度拆分)为强制前置,**该前置已于 2026-08-13 落地并合入**`WP1` 生产代码 + `WP2` 回归,完成状态与门禁见第 23.5 节),澄清轮次上限现为 3game-chat source 仍为 1)。随后 `M1A-1``M1A-2``M1A-3` 已分别落地:`M1A-2` 仅收口两层工具面、`project-planning` role brief 注入和 fail-closed 拒绝边界;`plan.submit_gdd``.agent/planning` 存储、提交/审批闭环仍未实现,见第 23.6、23.8 节。后续执行计划见第 23.6 节。
- 状态:2026-08-12 **M0 代码工作包全部完成**`M0A-2``M0B-1``M0B-2` 已合入 M0 集成分支并通过各自门禁,见第 23.4 节);同日 **D6 作废、拓扑改变**`M0A-1` 交付的文档基线随之失效,需以工作包 `M0A-3` 修订,**修订完成前 M0 不计完整完成**(见第 1.1 节)。2026-08-13 **D9 二次作废、D10 作废,由 D11 取代**:立项策划节点改为 Project Supervisor 通过 `agent.delegate` 发起的静态委派子 Agent,问询复用 PR #165 中转链路(见第 1.1 节「D11 新拓扑」);D11 依赖 WP1(静态委派澄清轮次与返工深度拆分)为强制前置,**该前置已于 2026-08-13 落地并合入**`WP1` 生产代码 + `WP2` 回归,完成状态与门禁见第 23.5 节),澄清轮次上限现为 3game-chat source 仍为 1)。随后 `M1A-1``M1A-2``M1A-3``M1C-0` 已分别落地:`M1A-2` 仅收口两层工具面、`project-planning` role brief 注入和 fail-closed 拒绝边界;`M1C-0` 仅新增用户修订状态及 lineage 分类,不包含审批写入方;`plan.submit_gdd``.agent/planning` 存储、提交/审批闭环仍未实现,见第 23.6、23.8 节。后续执行计划见第 23.6 节。
- 适用范围:AI 游戏创作独立 App、Project Supervisor、Agent Runtime、本地项目策划 sidecar 与后续完整构建准入
- 当前实现边界:本文件是后续详细设计与实现的仓库内阶段基线;M0 工作包冻结 Fast GDD 合同并修复现有 owner 验证、game-chat retry 与前端投影边界,`M1A-1``M1A-3` 已提供 plan source、两层工具面角色 brief 的 Runtime 基础,但不代表立项策划入口、GDD/`.agent/planning` 持久化、`plan.submit_gdd` 提交、审批 UI 或构建绑定已经可用
- 当前实现边界:本文件是后续详细设计与实现的仓库内阶段基线;M0 工作包冻结 Fast GDD 合同并修复现有 owner 验证、game-chat retry 与前端投影边界,`M1A-1``M1A-3``M1C-0` 已提供 plan source、两层工具面角色 brief 及用户修订 lineage 分类的 Runtime 基础,但不代表立项策划入口、GDD/`.agent/planning` 持久化、`plan.submit_gdd` 提交、审批 UI 或构建绑定已经可用
## 1. 背景与目标
@@ -1871,7 +1871,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`、`M1A-2`、`M1A-3`
**完成状态**:上述门禁全部通过,`WP1` 生产代码与 `WP2` 回归已合入本分支。已知遗留:`project_supervisor_concurrent_repair_dispatch_creates_exactly_one_delivery` 是 Barrier 同步的并发用例,在本机 Windows 上间歇失败;经与改造前基线对照(同一用例各跑 6 次,改造前后同为 1 次失败)确认为既有抖动,不是本工作包引入,不作为回归处理。
### 23.6 后续执行计划(2026-08-13 起)
### 23.6 后续执行计划(2026-08-13 起2026-08-14 状态更新
本节记录 D11 之后的执行顺序与各步状态,避免「设计结论持续更新、但做到哪了无人维护」。
@@ -1882,7 +1882,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`、`M1A-2`、`M1A-3`
| 三 | `project-planning` 的 agentCatalog 登记 | **已完成**(机制冻结见第 3.1 节;**代码亦已落地**2026-08-13manifest、prompt bundle、runtime adapter、`prompt.rs` 角色合成分支及四处 needs_change 全部合入) |
| 四 | `M0A-3` 批二:拓扑与工具面部分 | **已完成**2026-08-13),拆解见下 |
| 四之余 | schema 与 golden vector 收口 | **已完成**2026-08-13),拆解见下 |
| 五 | M1 本体:策划闭环功能实现 | **`M1A-1``M1A-2``M1A-3` 已落地**`M1B-1` 及之后仍未开始。M1A-2 只交付工具面、brief 注入和拒绝边界,不包含 GDD 提交/审批。合入门见第 23.8 节 |
| 五 | M1 本体:策划闭环功能实现 | **`M1A-1``M1A-2``M1A-3``M1C-0` 已落地**`M1B-1``M1B-2` 及之后仍未开始。`M1A-2` 只交付工具面、brief 注入和拒绝边界,`M1C-0` 只交付用户修订 lineage 分类,均不包含 GDD 提交/审批。合入门见第 23.8 节 |
批二在 2026-08-13 拆成两半,因为其中一半在 M1 代码存在之前**做不完**:
@@ -1963,7 +1963,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`、`M1A-2`、`M1A-3`
| `M1A-3` | plan 根 run 强判据函数 + retry 保源(本节下方「`M1A-3` 的由来」,规格见第 4.1 节第 5、7 段) | `M1A-1` | **已落地**source 保源与强判据)。`kind=plan-root-retry-identity-unsupported`gui/cli 兜底不变。第 4.1 节第 7 段里依赖 plan-session / `gddId` / `gdd-approval` kind 的部分仍后置。必须早于 `M1C-2a` |
| `M1B-1` | `.agent/planning/` 存储层、strict schema、typed 指纹、版本链;含 `.agent/planning/**` 只挡写判据 | `M1A-2` | **第 9.1 节 golden vector 逐字节相等且指纹相等**(先于其它测试);create-only 与等前缀不可变 |
| `M1B-2` | `plan.submit_gdd` 原生工具与提交点 | `M1B-1` | 全部拒绝分支;提交点前后强杀恢复;同 submissionId replay 不产生 vN+1 |
| `M1C-0` | 新增 `StaticDelegateContractStatus::UserRevisionRequested` 与分类分支 | `M1A-1` | **本 PR 无写入方、是惰性路径,行为零变化**;做游戏链路返工仍在 `depth=1` 被拒;无该变体的历史记录分类不变 |
| `M1C-0` | 新增 `StaticDelegateContractStatus::UserRevisionRequested` 与分类分支 | `M1A-1` | **已落地**:无审批写入方、是惰性路径;用户修订跳不增 `repair_depth` 也不重置 `clarification_round`,连续修订可通过;做游戏链路返工仍在 `depth=1` 被拒;历史记录与未知状态均保持 fail-closed |
| `M1C-1` | `gdd-approval` pending、审批命令、receiptreceipt 写入上述 status | `M1B-2``M1C-0` | 三动作全通;版本+指纹竞态防护;两窗口并发;**连续多次修订均可通过且 `repair_depth` 不变** |
| `M1C-2a` | Goal Contract 接线:turn 1 冻结、固定验收图、审批前置门取证 | `M1C-1``M1A-3` | turn 1 只能是一个 `agent.goal_contract``acceptanceNodes` 不接受自定义;**第 13.0 节审批前置门:取证未通过时不出现审批卡、而是产生返工委派**(取证顺序是协议时序问题,归本包而非 `M1C-1` |
| `M1C-2b` | 澄清中转接线、轮次派生、预算注入 | `M1C-2a` | 3 轮上限;continuation 重放幂等不增加轮次;答案绑定冲突被拒 |