收口M1C-2c决策卡A/B语义
更新策划决策卡的A/B平行方案与原型验证合同 收紧Runtime选项校验和决定状态映射并保留用户原文 补充必要的自由填写与非法信封回归 同步更新提示词、技术方案与项目决策日志
This commit is contained in:
@@ -11,11 +11,12 @@
|
||||
|
||||
- 除非用户明确说“直接出稿”,最多进行 3 轮关键澄清;每轮是新 run、同一 session。你看得到自己的历史,但用户答案以 Supervisor 委派任务中的转述为准,缺失信息不能臆造。
|
||||
- 优先顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。每轮最多问一个主要决定;达到第 3 轮、剩余问题不影响首个可玩闭环或 Runtime 提示接近活跃预算时,直接整理并提交。
|
||||
- 决策卡的 header 固定为“第N轮·关键决定”,正文以“当前要决定:”开头,必须说明为什么现在问、推荐方案、好处和代价;选项固定为“接受推荐”“暂按推荐”“需要原型验证”,自由输入按用户原话处理。
|
||||
- 决策卡的 header 固定为“第N轮·关键决定”,正文以“当前要决定:”开头,只问尚未由平台事实或 MVP 规则排除的真实产品取舍,并说明为什么现在问;每张卡固定提供三个选项:A 是你的推荐方案(label 以 `A ·`、`A:` 或 `A-` 开头并写明推荐、好处和代价),B 是形状不同且真实可行的平行备选(label 以 `B ·`、`B:` 或 `B-` 开头并写明后果和代价),第三项逐字为“需要原型验证”,description 必须给出 30~90 分钟微型原型、试玩对象、观察信号和通过标准。自由输入按用户原话处理。
|
||||
|
||||
## 低幻觉与 GDD 约束
|
||||
|
||||
- 用户明确提供或接受的内容标 `confirmed`;推荐但未确认的内容标 `default_pending`;需要靠手感、节奏、可读性或重玩行为证明的内容标 `prototype_pending`,并给出 30~90 分钟微型原型、观察信号和通过标准。
|
||||
- A、B 或自由填写得到的用户决定标 `confirmed`;用户选择“需要原型验证”标 `prototype_pending`;只有未提问、由你按默认建议填写的字段才标 `default_pending`,其 `answerSource=default`、`round=0`。不要把用户选择的 B 当成默认项。
|
||||
- 用户在后续自由填写中推翻已确认决定时,保留用户答案原文,紧接被推翻的决定注明“已被第 N 轮回答推翻,以后者为准”,并在新决定 topic 中写明关系;不得改写、拆分或搬动原文。
|
||||
- 只定义一个完整可玩闭环。MVP 不含多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情或全量内容,除非用户明确改变范围。
|
||||
- GDD 至少覆盖:游戏名称与类型、一句话描述、2~4 条游戏支柱、核心循环、目标用户、美术方向、3~6 个最小 MVP 系统、先做/暂缓/验证/扩展条件、决定状态和审批请求。不要把 Runtime 注入的身份、时间、指纹、审批 receipt 或平台事实当作 Provider 输入字段。
|
||||
- 平台事实由 Runtime 固定注入为自包含 Web、desktop/mobile 双视口、keyboard/touch 双输入、本地 HTTP 预览;不得修改、删减或向用户询问。
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
需要等待专业 Agent 时不得调用 respond_to_user;Runtime 会通过 delegate/all-join 完成屏障保持同一父 run,取得 readyDelegateReceipts 或 readyIsolatedJoins 后直接整合结果。readyDelegateReceipts 中 contractStatus=evidence-ready 只说明终态、产物和验证等客观证据齐全,你仍须按 acceptanceCriteria 判断语义是否满足;needs-repair 不得当作成功。contractStatus=needs-user-input 时,Runtime 会按原 delivery 逐一发起 user.input_request;每个请求答案收齐后,为对应原 delivery 仅创建一次 continuation 委派,repairOfDelegationId 与 continuationOfDelegationId 都指向该原 delivery,并提交 observation 给出的 questionsSha256、answersSha256;Runtime 自动派生稳定 continuation identity,禁止跨 delivery 混用指纹。客观或语义不满足时可以发起一次新 agent.delegate,并把 repairOfDelegationId 指向已认领原 delivery;不得对返工再返工或为同一原 delivery 创建第二个返工。专业结果冲突且无法依据用户目标裁决时,合并问题后用一次 user.input_request 询问用户。只有实现路径、产品取舍或缺失事实会实质改变结果时才调用 user.input_request;项目内可读取事实、权限确认和工具失败不得伪装成用户问题。
|
||||
|
||||
对 `project-planning` 的澄清 continuation,必须按 A/B/“需要原型验证”三项合同原样转述;B 是用户确认的 `confirmed/user_option`,不能转成默认建议。若用户后续自由填写推翻已确认决定,保留用户答案原文逐字不改写、不拆分、不搬轮次,并在被推翻决定后注明“已被第 N 轮回答推翻,以后者为准”,在新决定 topic 中写明推翻关系。
|
||||
|
||||
只在所有必要回执已认领、manifest 正式任务图已经完成、所有必要返工也已认领、项目副作用已验证且没有待确认动作或待回答请求时给用户最终回复。不要向用户暴露内部 task/event、工具计划、动态 child ID 或调试状态。
|
||||
|
||||
@@ -868,6 +868,11 @@ mod tests {
|
||||
);
|
||||
assert!(planning.contains("立项策划 Agent"));
|
||||
assert!(planning.contains("AGC_NEEDS_USER_INPUT_V1"));
|
||||
assert!(planning.contains("A ·"));
|
||||
assert!(planning.contains("B ·"));
|
||||
assert!(planning.contains("需要原型验证"));
|
||||
assert!(planning.contains("平台事实或 MVP 规则排除"));
|
||||
assert!(!planning.contains("暂按推荐"));
|
||||
assert!(game_creator_agent_runtime_role_overlay_prompt(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
None,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ use platform_llm::LlmFunctionTool;
|
||||
|
||||
const AGENT_RUNTIME_COMPLETION_BLOCKER_TOOL_PLAN_PROTOCOL: &str = "通用完成阻断规则:如果最新 observation 的 tool 为 runtime.autonomous_completion 且 status 为 blocked,本轮禁止调用 respond_to_user;必须先读取该 observation.detail 的 nextRequiredAction,并据此调用合适的读取、修复和验证工具。只有完成要求的动作、取得后续可信 observation 且完成门禁不再阻断后,才能给最终回复;不得反复提交 final response,也不得按项目正文硬编码某一种 blocker 的处理方式。";
|
||||
|
||||
const GAME_CREATOR_PROJECT_PLANNING_FINAL_REPLY_SYSTEM_PROMPT: &str = "你是 Genarrative 的立项策划 Agent final-reply 收束器。你只能依据当前请求中明确提供的后台任务、运行中用户追加指令、收束摘要和已获准工具 observation 作答;不得使用通用角色聊天人格,也不得补充这些材料之外的项目事实。没有对应成功 observation 时,不要声称已经写入文件、提交 GDD、获得审批、生成素材、构建或验证完成;不要声称调用了未出现在 observation 中的工具,也不要把建议当成用户确认。若 observation 明确返回 plan.submit_gdd 成功,只能如实报告已提交的 GDD 版本、指纹摘要和待审批状态,不得把提交当成批准。若收束摘要或 observation 中已有 AGC_NEEDS_USER_INPUT_V1 终态信封,必须保留其首行和下一行严格 JSON 问题信封(只去除外围空白),不得改写、翻译、包装成普通中文或追加解释。若当前需要用户决定而尚无完整信封,只能输出 AGC_NEEDS_USER_INPUT_V1 首行,下一行输出 Runtime 可解析的严格 {\"questions\":[...]} JSON;不得输出 markdown、代码围栏或第三行正文。没有用户输入需求时,只简洁总结已观察到的策划结论、confirmed/default_pending/prototype_pending 状态、未完成事项和下一步,明确审批或构建尚未发生。回复保持中文。";
|
||||
const GAME_CREATOR_PROJECT_PLANNING_FINAL_REPLY_SYSTEM_PROMPT: &str = "你是 Genarrative 的立项策划 Agent final-reply 收束器。你只能依据当前请求中明确提供的后台任务、运行中用户追加指令、收束摘要和已获准工具 observation 作答;不得使用通用角色聊天人格,也不得补充这些材料之外的项目事实。没有对应成功 observation 时,不要声称已经写入文件、提交 GDD、获得审批、生成素材、构建或验证完成;不要声称调用了未出现在 observation 中的工具,也不要把建议当成用户确认。若 observation 明确返回 plan.submit_gdd 成功,只能如实报告已提交的 GDD 版本、指纹摘要和待审批状态,不得把提交当成批准。若收束摘要或 observation 中已有 AGC_NEEDS_USER_INPUT_V1 终态信封,必须保留其首行和下一行严格 JSON 问题信封(只去除外围空白),不得改写、翻译、包装成普通中文或追加解释。若当前需要用户决定而尚无完整信封,只能输出 AGC_NEEDS_USER_INPUT_V1 首行,下一行输出 Runtime 可解析的严格 {\"questions\":[...]} JSON;不得输出 markdown、代码围栏或第三行正文。决策卡必须是 A/B/需要原型验证三项合同:B 必须是真实可行的平行方案,不能把用户选择的 B 说成默认建议;用户改口时保留原文并注明被哪一轮推翻。没有用户输入需求时,只简洁总结已观察到的策划结论、confirmed/default_pending/prototype_pending 状态、未完成事项和下一步,明确审批或构建尚未发生。回复保持中文。";
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum AgentBackgroundContextMode {
|
||||
|
||||
+30
-42
@@ -2,8 +2,8 @@ use super::*;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
const PLAN_OPTION_ACCEPT_RECOMMENDATION: &str = "接受推荐";
|
||||
const PLAN_OPTION_TEMPORARY_RECOMMENDATION: &str = "暂按推荐";
|
||||
const PLAN_OPTION_A_PREFIX: char = 'A';
|
||||
const PLAN_OPTION_B_PREFIX: char = 'B';
|
||||
const PLAN_OPTION_PROTOTYPE_VALIDATION: &str = "需要原型验证";
|
||||
const PLAN_QUESTION_PREFIX: &str = "当前要决定:";
|
||||
|
||||
@@ -127,6 +127,19 @@ fn plan_question_topic(question: &AgentRuntimeUserInputQuestion) -> Result<Strin
|
||||
normalize_plan_text(topic, "plan question topic", 1, 80).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn plan_option_label_has_prefix(label: &str, prefix: char) -> bool {
|
||||
let Some(remainder) = label.strip_prefix(prefix).map(str::trim_start) else {
|
||||
return false;
|
||||
};
|
||||
let Some(delimiter) = remainder.chars().next() else {
|
||||
return false;
|
||||
};
|
||||
if !matches!(delimiter, '·' | ':' | '-') {
|
||||
return false;
|
||||
}
|
||||
!remainder[delimiter.len_utf8()..].trim().is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn validate_exact_plan_clarification_question(
|
||||
questions: &[AgentRuntimeUserInputQuestion],
|
||||
round: u32,
|
||||
@@ -154,21 +167,14 @@ pub(crate) fn validate_exact_plan_clarification_question(
|
||||
format!("plan question header 必须精确等于 {expected_header}"),
|
||||
));
|
||||
}
|
||||
let expected_labels = [
|
||||
PLAN_OPTION_ACCEPT_RECOMMENDATION,
|
||||
PLAN_OPTION_TEMPORARY_RECOMMENDATION,
|
||||
PLAN_OPTION_PROTOTYPE_VALIDATION,
|
||||
];
|
||||
if question.options.len() != expected_labels.len()
|
||||
|| question
|
||||
.options
|
||||
.iter()
|
||||
.zip(expected_labels)
|
||||
.any(|(option, expected)| option.label != expected)
|
||||
{
|
||||
let valid_shape = question.options.len() == 3
|
||||
&& plan_option_label_has_prefix(&question.options[0].label, PLAN_OPTION_A_PREFIX)
|
||||
&& plan_option_label_has_prefix(&question.options[1].label, PLAN_OPTION_B_PREFIX)
|
||||
&& question.options[2].label == PLAN_OPTION_PROTOTYPE_VALIDATION;
|
||||
if !valid_shape {
|
||||
return Err(plan_coordinator_error(
|
||||
"PLAN_INVALID_CLARIFICATION",
|
||||
"plan question 的三个选项标签或顺序不符合固定合同",
|
||||
"plan question 必须恰好提供 A、B、需要原型验证三个选项",
|
||||
));
|
||||
}
|
||||
plan_question_topic(question)?;
|
||||
@@ -188,32 +194,14 @@ fn build_plan_clarification_decision_projection(
|
||||
let question = &answer.question;
|
||||
let topic = plan_question_topic(question)?;
|
||||
let decision_id = question.id.replace('_', "-");
|
||||
let (state, answer_source, answer_summary) = match normalized_answer.as_str() {
|
||||
PLAN_OPTION_ACCEPT_RECOMMENDATION => (
|
||||
"confirmed",
|
||||
"user_option",
|
||||
format!(
|
||||
"{PLAN_OPTION_ACCEPT_RECOMMENDATION}:{}",
|
||||
question.options[0].description
|
||||
),
|
||||
),
|
||||
PLAN_OPTION_TEMPORARY_RECOMMENDATION => (
|
||||
"default_pending",
|
||||
"default",
|
||||
format!(
|
||||
"{PLAN_OPTION_TEMPORARY_RECOMMENDATION}:{}",
|
||||
question.options[1].description
|
||||
),
|
||||
),
|
||||
PLAN_OPTION_PROTOTYPE_VALIDATION => (
|
||||
"prototype_pending",
|
||||
"user_option",
|
||||
format!(
|
||||
"{PLAN_OPTION_PROTOTYPE_VALIDATION}:{}",
|
||||
question.options[2].description
|
||||
),
|
||||
),
|
||||
_ => ("confirmed", "user_freeform", normalized_answer),
|
||||
let (state, answer_source) = if normalized_answer == question.options[0].label
|
||||
|| normalized_answer == question.options[1].label
|
||||
{
|
||||
("confirmed", "user_option")
|
||||
} else if normalized_answer == PLAN_OPTION_PROTOTYPE_VALIDATION {
|
||||
("prototype_pending", "user_option")
|
||||
} else {
|
||||
("confirmed", "user_freeform")
|
||||
};
|
||||
let decision = PlanDecisionSummary {
|
||||
id: decision_id.clone(),
|
||||
@@ -221,7 +209,7 @@ fn build_plan_clarification_decision_projection(
|
||||
state: state.to_string(),
|
||||
answer_source: answer_source.to_string(),
|
||||
round,
|
||||
answer_summary,
|
||||
answer_summary: normalized_answer.clone(),
|
||||
};
|
||||
let prototype_validation_item =
|
||||
(state == "prototype_pending").then(|| PlanPrototypeValidationItem {
|
||||
|
||||
@@ -486,6 +486,10 @@ struct AnsweredPlanningClarification {
|
||||
awaiting_session: PlanSessionV1,
|
||||
}
|
||||
|
||||
const PLAN_TEST_OPTION_A: &str = "A · 采用当前推荐方案(推荐)";
|
||||
const PLAN_TEST_OPTION_B: &str = "B · 采用另一条可行路线";
|
||||
const PLAN_TEST_OPTION_PROTOTYPE: &str = "需要原型验证";
|
||||
|
||||
fn planning_clarification_question_body(round: u32) -> (String, String) {
|
||||
let question_id = format!("round_{round}_decision");
|
||||
let body = serde_json::json!({
|
||||
@@ -495,16 +499,16 @@ fn planning_clarification_question_body(round: u32) -> (String, String) {
|
||||
"question": format!("当前要决定:第{round}轮核心取舍。现在确认后才能继续收敛 Fast GDD。"),
|
||||
"options": [
|
||||
{
|
||||
"label": "接受推荐",
|
||||
"description": "采用当前推荐方案继续收敛。"
|
||||
"label": PLAN_TEST_OPTION_A,
|
||||
"description": "采用当前推荐方案继续收敛,代价是优先投入这条路线的验证。"
|
||||
},
|
||||
{
|
||||
"label": "暂按推荐",
|
||||
"description": "先按推荐推进,后续仍可调整。"
|
||||
"label": PLAN_TEST_OPTION_B,
|
||||
"description": "采用另一条可行路线,代价是放弃当前推荐的部分确定性。"
|
||||
},
|
||||
{
|
||||
"label": "需要原型验证",
|
||||
"description": "用小型原型验证后再定稿。"
|
||||
"label": PLAN_TEST_OPTION_PROTOTYPE,
|
||||
"description": "用 30~90 分钟微型原型让三名测试者试玩,记录取舍行为并按至少两次符合预期判定。"
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -3875,9 +3879,13 @@ fn clarification_continuation_chain_supports_multiple_rounds() {
|
||||
fn planning_clarification_three_rounds_project_session_and_structured_injection() {
|
||||
let mut fixture = planning_clarification_fixture("three-rounds");
|
||||
let expected_decisions = [
|
||||
("接受推荐", "confirmed", "user_option"),
|
||||
("暂按推荐", "default_pending", "default"),
|
||||
("需要原型验证", "prototype_pending", "user_option"),
|
||||
(PLAN_TEST_OPTION_A, "confirmed", "user_option"),
|
||||
(PLAN_TEST_OPTION_B, "confirmed", "user_option"),
|
||||
(
|
||||
PLAN_TEST_OPTION_PROTOTYPE,
|
||||
"prototype_pending",
|
||||
"user_option",
|
||||
),
|
||||
];
|
||||
|
||||
for (index, (answer, expected_state, expected_source)) in
|
||||
@@ -3947,6 +3955,7 @@ fn planning_clarification_three_rounds_project_session_and_structured_injection(
|
||||
assert_eq!(decision.state, expected_state);
|
||||
assert_eq!(decision.answer_source, expected_source);
|
||||
assert_eq!(decision.round, round);
|
||||
assert_eq!(decision.answer_summary, *answer);
|
||||
if round == 3 {
|
||||
assert!(session
|
||||
.prototype_validation_items
|
||||
@@ -3976,6 +3985,69 @@ fn planning_clarification_three_rounds_project_session_and_structured_injection(
|
||||
cleanup_planning_clarification_fixture(fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn planning_clarification_freeform_answer_is_confirmed_and_preserves_original_text() {
|
||||
let mut fixture = planning_clarification_fixture("freeform");
|
||||
let freeform = "改成双路线并让玩家在十分钟内完成一次取舍";
|
||||
let answered = answer_planning_clarification_round(&mut fixture, 1, freeform, "freeform");
|
||||
dispatch_answered_planning_continuation(&mut fixture, &answered, "freeform");
|
||||
|
||||
let session = read_plan_session_with_recovery(&fixture.root)
|
||||
.expect("read freeform planning session")
|
||||
.expect("freeform planning session exists");
|
||||
let decision = session
|
||||
.decisions_summary
|
||||
.iter()
|
||||
.find(|decision| decision.id == answered.question_id.replace('_', "-"))
|
||||
.expect("freeform decision exists");
|
||||
assert_eq!(decision.state, "confirmed");
|
||||
assert_eq!(decision.answer_source, "user_freeform");
|
||||
assert_eq!(decision.answer_summary, freeform);
|
||||
|
||||
cleanup_planning_clarification_fixture(fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn planning_clarification_rejects_non_ab_option_envelope_before_pending() {
|
||||
let mut fixture = planning_clarification_fixture("invalid-option-shape");
|
||||
let current = fixture.current_delivery.clone();
|
||||
let (_, valid_body) = planning_clarification_question_body(1);
|
||||
let invalid_body = valid_body.replace(PLAN_TEST_OPTION_B, "C · 另一条路线");
|
||||
mark_and_claim_static_delegate_needs_user_input(
|
||||
&fixture.root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&fixture.supervisor.run_id,
|
||||
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
|
||||
¤t.target_session_id,
|
||||
¤t.target_run_id,
|
||||
¤t.delegation_id,
|
||||
&fixture.expected_artifacts,
|
||||
&invalid_body,
|
||||
"planning-invalid-option-shape-claim",
|
||||
);
|
||||
let claimed = claimed_static_delegate_deliveries_at(
|
||||
&fixture.root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&fixture.supervisor.run_id,
|
||||
)
|
||||
.expect("read invalid option-shape delivery");
|
||||
let error =
|
||||
ensure_static_delegate_user_input_wait_at(&fixture.root, &mut fixture.supervisor, &claimed)
|
||||
.expect_err("non A/B option envelope must fail closed");
|
||||
assert!(error.contains("PLAN_INVALID_CLARIFICATION"), "{error}");
|
||||
assert!(
|
||||
!game_creator_agent_runtime_pending_tool_action_path(
|
||||
&fixture.root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&fixture.supervisor.run_id,
|
||||
)
|
||||
.exists(),
|
||||
"非法选项信封不得建立 pending"
|
||||
);
|
||||
|
||||
cleanup_planning_clarification_fixture(fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn planning_clarification_user_revision_after_answer_preserves_round_for_revise_and_reject() {
|
||||
for action in ["revise", "reject"] {
|
||||
@@ -3983,7 +4055,7 @@ fn planning_clarification_user_revision_after_answer_preserves_round_for_revise_
|
||||
let answered = answer_planning_clarification_round(
|
||||
&mut fixture,
|
||||
1,
|
||||
"接受推荐",
|
||||
PLAN_TEST_OPTION_A,
|
||||
&format!("user-{action}"),
|
||||
);
|
||||
let submitted_delivery = dispatch_answered_planning_continuation(
|
||||
@@ -4154,7 +4226,8 @@ fn planning_clarification_user_revision_after_answer_preserves_round_for_revise_
|
||||
#[test]
|
||||
fn planning_clarification_answer_and_continuation_replay_are_idempotent() {
|
||||
let mut fixture = planning_clarification_fixture("replay");
|
||||
let answered = answer_planning_clarification_round(&mut fixture, 1, "接受推荐", "replay");
|
||||
let answered =
|
||||
answer_planning_clarification_round(&mut fixture, 1, PLAN_TEST_OPTION_A, "replay");
|
||||
let request_id = answered
|
||||
.original_delivery
|
||||
.clarification_request_id
|
||||
@@ -4239,7 +4312,7 @@ fn planning_clarification_answer_and_continuation_replay_are_idempotent() {
|
||||
fn planning_clarification_conflicting_answer_fails_without_projection() {
|
||||
let mut fixture = planning_clarification_fixture("answer-conflict");
|
||||
let answered =
|
||||
answer_planning_clarification_round(&mut fixture, 1, "接受推荐", "answer-conflict");
|
||||
answer_planning_clarification_round(&mut fixture, 1, PLAN_TEST_OPTION_A, "answer-conflict");
|
||||
let request_id = answered
|
||||
.original_delivery
|
||||
.clarification_request_id
|
||||
@@ -4299,8 +4372,12 @@ fn planning_clarification_conflicting_answer_fails_without_projection() {
|
||||
fn planning_clarification_fourth_round_is_rejected_before_pending() {
|
||||
let mut fixture = planning_clarification_fixture("fourth-round");
|
||||
for round in 1..=3 {
|
||||
let answered =
|
||||
answer_planning_clarification_round(&mut fixture, round, "接受推荐", "fourth-round");
|
||||
let answered = answer_planning_clarification_round(
|
||||
&mut fixture,
|
||||
round,
|
||||
PLAN_TEST_OPTION_A,
|
||||
"fourth-round",
|
||||
);
|
||||
dispatch_answered_planning_continuation(
|
||||
&mut fixture,
|
||||
&answered,
|
||||
@@ -4369,7 +4446,8 @@ fn planning_clarification_fourth_round_is_rejected_before_pending() {
|
||||
#[test]
|
||||
fn planning_clarification_session_recovery_projects_existing_child_once_without_provider() {
|
||||
let mut fixture = planning_clarification_fixture("recovery");
|
||||
let answered = answer_planning_clarification_round(&mut fixture, 1, "接受推荐", "recovery");
|
||||
let answered =
|
||||
answer_planning_clarification_round(&mut fixture, 1, PLAN_TEST_OPTION_A, "recovery");
|
||||
let continuation =
|
||||
dispatch_answered_planning_continuation(&mut fixture, &answered, "recovery-first");
|
||||
let continuation_task = read_latest_game_creator_agent_runtime_task_by_delegation_id(
|
||||
@@ -4537,7 +4615,7 @@ fn planning_clarification_answer_prepared_recovery_releases_execution_before_pro
|
||||
&pending,
|
||||
&request.request_id,
|
||||
response_id,
|
||||
BTreeMap::from([(question_id, "接受推荐".to_string())]),
|
||||
BTreeMap::from([(question_id, PLAN_TEST_OPTION_A.to_string())]),
|
||||
)
|
||||
.expect_err("answer must stop after durable answer-prepared");
|
||||
assert!(injected.contains("answer-prepared"), "{injected}");
|
||||
@@ -4888,7 +4966,7 @@ fn planning_clarification_recovery_treats_concurrent_answer_as_obsolete_candidat
|
||||
&pending,
|
||||
&request.request_id,
|
||||
"planning-recovery-obsolete-response",
|
||||
BTreeMap::from([(question_id, "接受推荐".to_string())]),
|
||||
BTreeMap::from([(question_id, PLAN_TEST_OPTION_A.to_string())]),
|
||||
&project_lock,
|
||||
)
|
||||
.expect("advance answer while old recovery waits");
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-08-18 `M1C-2c` 隔离工作树开工:A/B 决策卡语义与信封合同收口
|
||||
|
||||
- **隔离基线**:在 `codex/genarrative-isolated` 上从 `0199fb6e4` 开工;`M1C-2b` 已合回 `feat/five_min_design`,本包不回改其三轮上限、continuation 幂等、答案绑定或预算折叠。
|
||||
- **本包范围**:把 planning 决策卡从“同一推荐的三种采纳程度”改为 A/B 平行方案 + 固定“需要原型验证”;Runtime 按 label 形状校验并确定性映射 `state` / `answerSource` / `answerSummary`,同步更新 planning role brief、final-reply 收束提示与定向回归。
|
||||
- **冻结映射**:A、B → `confirmed / user_option`;固定第三项 → `prototype_pending / user_option`;自由填写 → `confirmed / user_freeform`;`default_pending / default / round=0` 只允许由未提问默认项产生。`answerSummary` 必须逐字等于用户选中的 label 或自由填写原文。
|
||||
- **信封合同**:每张 planning 卡必须恰好三项;第一项 label 以 `A` + `·`/`:`/`-` 开头,第二项同形以 `B` 开头,第三项逐字为“需要原型验证”;形状不符 fail-closed,不建立 pending,不改变 session。
|
||||
- **提示词纪律**:B 必须是真实、形状不同且说明代价的平行路线;第三项 description 要给出可执行的 30~90 分钟微型原型验证;平台事实和 MVP 已排除项不提问;改口保留用户原文并标注被哪一轮推翻。
|
||||
- **纠正旧记录**:此前 M1C-2b 条目把“第四轮”写成正常进入 reconciliation;代码核查确认正常路径在 `agent.delegate` 边界硬拒并返回 failed observation,`planning_coordinator` 的超三轮 reconciliation 仅是损坏血缘的纵深防御,后续文档收口时一并更正。
|
||||
- **当前进度**:Runtime 映射、role brief、Supervisor playbook/final-reply 提示、A/B/自由填写/非法信封回归已落地;`planning_clarification_*` **13 passed / 0 failed**,`project_planning` prompt 定向回归 **5 passed / 0 failed**,`planning_submit` 定向回归通过,prompt bundle、`cargo fmt --check`、离线 `cargo check --offline --all-targets --target-dir target-m1c2c`、`npm run check:encoding`(5406 files)及 `git diff --check` 均通过。实现仍在 `codex/genarrative-isolated`,尚未合回原分支。
|
||||
|
||||
## 2026-08-17 M1C-2b 隔离工作树实现完成:策划澄清中转、链路派生与预算注入
|
||||
|
||||
- **当前基线**:`M1C-1`、`M1C-2a` 已合回 `feat/five_min_design`;本隔离分支开工后又以 merge commit `9f12d8467` 合入原分支截至 `a8215a599` 的全部已提交改动,包含 P4 的 Fast GDD 识别顺序修复与 P5 的委派栅栏 detail 等价性锁定。原工作树未提交的 `planning_approval.rs` 不属于该合并且未触碰。M1C-2a 的固定 Goal Contract、验收图与审批前置门作为既有前置,不在本包回改。
|
||||
@@ -14,7 +24,7 @@
|
||||
- **本轮已修的明确缺陷**:plan 回答读取曾把 opaque `taskId` 误与 Supervisor `agentId` 比较,会令第一轮 continuation 必然失败。现改为读取同一 parent run 的 Supervisor root task,并精确核对 taskId、agentId、sessionId、runId、source、requestId、questionsSha256 与 answersSha256;回答 sidecar 的共用 payload 校验保持完整,不降低普通 user-input 的身份校验。
|
||||
- **终审修复**:完成至少一轮澄清后,审批 `revise/reject` 会保留 `appliedAnswers`,但新修订 delivery 的身份不再等于最后回答 continuation;旧纯 session 判据会把合法修订 successor 固定拒成 `PLAN_IDENTITY_CONFLICT`。现把独立 schema 校验收窄为“不得回退到已消费问题 delivery”,并在 session 新值、已有 primary/previous、发布后回读及普通读取边界读取真实 static-delivery 谱系:从 latest 回到最后回答 continuation 的**每一条边**都必须由父 delivery 的 `UserRevisionRequested` 状态授权,且 root/agent/session 身份一致、无 Unknown、缺节点或循环;质量返工边不得借路径中其它用户修订继续保留旧回答。正向回归同时覆盖 `revise/reject` 后 continuation、轮次/回答/决定保留与 Provider 注入;负向回归证明混入质量返工边时,即使重算合法 session fingerprint 仍失败关闭。
|
||||
- **门禁中修复的测试缺陷**:并发整组首次复跑时,锁序测试把“回答线程开始”误当成“已得到调度”,180ms 内未观察到 project lock 竞争而失败;同用例精确复跑通过。测试只将调度观察窗口放宽到 2 秒,断言仍要求真实 project lock 竞争发生后才释放被占用的 execution lane,未改变生产锁序或放松结果判据。
|
||||
- **保留观察**:第四轮违规澄清信封当前会在建立 pending 前失败关闭并进入 reconciliation,而方案目标是第三轮后由 planning 子 Agent 转入 submit。现有回归明确只证明“不建立第四轮 pending”,未证明强制 submit;修复会扩到更宽的 Provider/终态状态机,当前也未引起本包测试失败,按缺陷处置规则留待后续单列,不在 M1C-2b 收口中顺手扩修。
|
||||
- **纠正旧观察**:第四轮澄清委派在 `agent.delegate` 工具边界就按持久化血缘轮次上限硬拒,返回普通 failed observation,正常路径不会进入 reconciliation;`planning_coordinator` 的 `clarification_round > 3` 分支仅是损坏/篡改血缘的纵深防御。Supervisor 可据失败 observation 改走 `plan.submit_gdd`,不需要在 M1C-2b 另加自动 submit 状态机。
|
||||
- **最终门禁证据**:`planning_clarification_*` **11 passed / 0 failed**(原 9 条之外新增真实 main-loop 释放 execution lane 后 parent-wake 回归,以及已回答后 `revise/reject` 修订回归);`tests::collaboration::static_deliveries::*` **44 passed**;planning storage **13 passed**(含重新计算 fingerprint 的混合质量返工谱系负例);`planning_submit` **53 passed**;`planning_provider_usage` **4 passed**;真实末次 submit usage receipt 回归 **1 passed**;`barrier_detail_*` **3 passed**。`cargo fmt --check`、`cargo check --offline --all-targets --target-dir target-m1c2b`、`npm run check:encoding`(7810 files)及整个工作树 `git diff --check` 均通过。**M1C-2b 本包实现及门禁已完成,并已快进合回 `feat/five_min_design`;审批 UI、hydrate、构建准入和下游完整构建仍后置。**
|
||||
|
||||
## 2026-08-17 立项策划决策卡改为 A/B 平行方案:`default_pending` 收回为未提问默认项唯一来源,改口不改合同,立包 `M1C-2c`
|
||||
|
||||
@@ -417,24 +417,25 @@ Runtime 注入并强校验以下精确结构:
|
||||
|
||||
### 5.2 决策卡
|
||||
|
||||
> **2026-08-17 拟定修订(见第 23.9 节,由 `M1C-2c` 落地)**:选项语义改为「方案 A(推荐)/ 方案 B(平行备选)/ 固定『需要原型验证』」(仍恒定三项),A、B 均记 `confirmed / user_option`,`default_pending` 收回为未提问默认项的唯一来源。本节正文是已合回的 `M1C-2b` 的实现依据(固定三选项的校验与映射已冻结在其 planning coordinator 里);`M1C-2c` 落地时按第 23.9 节改写本节,`M1D-1` 前端决策卡直接按第 23.9 节实现。
|
||||
> **2026-08-18 `M1C-2c` 隔离实现已完成(见第 23.9 节)**:选项语义为「方案 A(推荐)/ 方案 B(平行备选)/ 固定『需要原型验证』」(恒定三项),A、B 均记 `confirmed / user_option`,`default_pending` 只来自未提问默认项。Runtime 已按本节收口 label 形状、状态来源和 `answerSummary` 保真;`M1D-1` 前端决策卡直接复用本节语义。本包当前仍在 `codex/genarrative-isolated`,尚未合回原分支。
|
||||
|
||||
每次 `user.input_request` 固定只含一题:
|
||||
|
||||
- header:固定为 `第{N}轮·关键决定`,N 为 1~3,始终不超过现有 12 scalar 上限;
|
||||
- question:以 `当前要决定:{主题}` 开头,再依次说明为什么现在问、推荐方案、好处、代价;
|
||||
- 选项固定为 `接受推荐`、`暂按推荐`、`需要原型验证`;
|
||||
- 选项固定为三项:第一项 label 以 `A` 加 `·` / `:` / `-` 开头并说明推荐方案,第二项以 `B` 加同类分隔符开头并说明真实平行备选,第三项 label 逐字为 `需要原型验证`;每项 description 都要写清选择后果与代价,第三项还要给出可执行的微型原型验证方式;
|
||||
- 自由填写由现有 Other 输入槽承载,placeholder 为 `改成:……`。
|
||||
|
||||
映射:
|
||||
|
||||
| 用户行为 | decision state | answer source |
|
||||
| --- | --- | --- |
|
||||
| 接受推荐 | `confirmed` | `user_option` |
|
||||
| 暂按推荐 | `default_pending` | `default` |
|
||||
| 选择 A 或 B | `confirmed` | `user_option` |
|
||||
| 需要原型验证 | `prototype_pending` | `user_option` |
|
||||
| 自由填写 | `confirmed` | `user_freeform` |
|
||||
|
||||
`default_pending / default / round=0` 只允许出现在未提问、由 Agent 按默认建议补齐的决定中。
|
||||
|
||||
选择“需要原型验证”必须新增一条与该 decision 使用相同 ID 的 30~90 分钟微型原型验证项,包含问题、最小原型、可观察信号和明确通过标准;不允许只写“试玩后再看”。
|
||||
|
||||
exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不新增第五个 action tool。它继续使用现役 strict input,且 `questions` 必须恰好一题:
|
||||
@@ -447,8 +448,8 @@ exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不
|
||||
"header": "第1轮·关键决定",
|
||||
"question": "当前要决定:路线重玩……",
|
||||
"options": [
|
||||
{ "label": "接受推荐", "description": "……" },
|
||||
{ "label": "暂按推荐", "description": "……" },
|
||||
{ "label": "A · 方案 A(推荐)", "description": "说明推荐理由、收益与代价。" },
|
||||
{ "label": "B · 方案 B", "description": "说明真实平行路线的后果与代价。" },
|
||||
{ "label": "需要原型验证", "description": "……" }
|
||||
]
|
||||
}
|
||||
@@ -456,11 +457,11 @@ exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不
|
||||
}
|
||||
```
|
||||
|
||||
三个 option 的标签与顺序必须逐字等于上表;plan question ID 在现役 snake_case 规则上进一步限制为最多 32 个 ASCII 字符,并且不能映射成 `initial-request`。Runtime 确定性令 `decisionId = questionId.replace('_', '-')`,因此该 ID 必然满足第 8.3 节 GDD decision ID 合同,Provider 不能另选身份。D11/M1C-2b 下 Supervisor 的 `user.input_request` 不是 Supervisor Provider tool-plan action:它由 Runtime 在认领 `NeedsUserInput` delivery 后、释放父 run execution lane,再经 planning parent-wake 直接投影为唯一 pending,因此不创建 Supervisor v4 Provider action batch。exact planning 子 Agent 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不能走现役“少于两项则 NotNeeded”的优化;`plan.submit_gdd` 仍是该 batch 的唯一 member。exact planning 的 `final-reply`、`context-compaction`、`final-reply-context-compaction` 无 action、不创建 batch,但仍写第 12 节 v3 lifecycle/binding。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。
|
||||
三个 option 的数量与 A/B/第三项形状必须符合上表;A/B 的短语可动态生成,分隔符允许 `·`、`:` 或 `-`,第三项 label 必须逐字等于 `需要原型验证`。plan question ID 在现役 snake_case 规则上进一步限制为最多 32 个 ASCII 字符,并且不能映射成 `initial-request`。Runtime 确定性令 `decisionId = questionId.replace('_', '-')`,因此该 ID 必然满足第 8.3 节 GDD decision ID 合同,Provider 不能另选身份。D11/M1C-2b 下 Supervisor 的 `user.input_request` 不是 Supervisor Provider tool-plan action:它由 Runtime 在认领 `NeedsUserInput` delivery 后、释放父 run execution lane,再经 planning parent-wake 直接投影为唯一 pending,因此不创建 Supervisor v4 Provider action batch。exact planning 子 Agent 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不能走现役“少于两项则 NotNeeded”的优化;`plan.submit_gdd` 仍是该 batch 的唯一 member。exact planning 的 `final-reply`、`context-compaction`、`final-reply-context-compaction` 无 action、不创建 batch,但仍写第 12 节 v3 lifecycle/binding。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。
|
||||
|
||||
> **2026-08-13 按 D11 重写本节后半(原 D10「Runtime 直投」状态机整段作废)。** 原文描述的链路是:策划节点持续存活于同一 run,自己调 `user.input_request`,Runtime 在同一 run 内截获、写 `activeQuestion` session checkpoint,回答后再发一次 `plan-decision-checkpoint` 专用 Provider 请求取得设计解释,并以新 session primary 作为线性化点。D11 下这条链路的每一环都换了承载物,且**不是换实现是换机制**——用的是 PR #165 已发布、已有回归覆盖的静态委派澄清中转,不再自造状态机。
|
||||
|
||||
**决策卡的形状不变,承载物变了。** 上面那张映射表、三个固定选项、每次恰好一题、`decisionId = questionId.replace('_', '-')` 的确定性映射,全部继续有效;变的是这张卡由谁产生、经谁送达:
|
||||
**决策卡的承载物不变,选项语义已由 `M1C-2c` 收口。** 上面那张映射表、A/B/固定第三项、每次恰好一题、`decisionId = questionId.replace('_', '-')` 的确定性映射,全部继续有效;变的是这张卡由谁产生、经谁送达:
|
||||
|
||||
1. **产生**:策划子 Agent **不能**调 `user.input_request`(委派子 Agent 带 parent 身份,执行层 `validate_user_input_action_owner` 直接拒绝)。它以 `AGC_NEEDS_USER_INPUT_V1` **终态信封退出本轮 run**,信封内是同一个规范化单题对象。Runtime 据此形成 `contract_status=NeedsUserInput` 的 delivery,问题原文与 `questionsSha256` 一并落在 delivery 里。
|
||||
2. **权威载体**:**delivery 就是「已问出、未回答」这一状态的权威**,不再需要 session 复制一份 `activeQuestion`。这消掉了 D10 里「batch 已有而 activeQuestion 未落」「activeQuestion 已落而 standalone pending 未落」等一整族需要逐格对账的中间态——它们的存在前提是同一份问题被复制到两处。
|
||||
@@ -517,12 +518,12 @@ MVP 明确排除多人、商城、服务器、开放世界、赛季、复杂社
|
||||
【快速追问规则】
|
||||
- 除非用户说“直接出稿”,否则先澄清。最多 3 个主动问题,每轮只有 1 个主要决定。
|
||||
- 优先顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。
|
||||
- 每轮以 AGC_NEEDS_USER_INPUT_V1 终态信封输出一张决策卡后停止,不要再调任何工具。header 固定为“第N轮·关键决定”;正文以“当前要决定:…”开头,并包含为什么现在问、我的推荐、好处、代价;三个选项固定为“接受推荐”“暂按推荐”“需要原型验证”。自由填写按用户明确输入处理。
|
||||
- 每轮以 AGC_NEEDS_USER_INPUT_V1 终态信封输出一张决策卡后停止,不要再调任何工具。header 固定为“第N轮·关键决定”;正文以“当前要决定:…”开头,只问未被平台事实或 MVP 规则排除的真实取舍,并说明为什么现在问;三个选项固定为 A(推荐方案)、B(真实平行备选)和逐字固定的“需要原型验证”,每个 description 写清后果与代价,第三项给出可执行的微型原型验证方式。自由填写按用户明确输入处理。
|
||||
- 信封只放规范化后的问题本身,不预填用户尚未给出的决定解释。你对上一轮回答的设计解释,写在**下一轮 run 的第一个普通回合**里,与「下一题还是出稿」的判断一并给出。
|
||||
- 用户说“直接出稿”、第 3 轮已经完成、剩余问题不影响首个可玩闭环,或 Runtime 提示接近 240 秒 Agent 活跃预算时,立即整理并提交。
|
||||
|
||||
【低幻觉规则】
|
||||
- 用户明确提供或接受的内容标 confirmed;推荐但未确认的内容标 default_pending。
|
||||
- A、B 或自由填写得到的用户决定标 confirmed;只有未提问、由子 Agent 按默认建议填写的字段标 default_pending(answerSource=default、round=0)。
|
||||
- 需要靠手感、节奏、镜头、可读性或重玩行为证明的内容标 prototype_pending,并给出 30~90 分钟微型原型、观察信号和通过标准。
|
||||
- Runtime 注入的 initial-request 和既有 decisionsSummary 是来源事实;提交 GDD 时必须逐项保留,不能把默认建议改成 confirmed 或伪造回答来源/轮次。
|
||||
- 不得编造具体游戏的机制、数值、销量、团队规模、研究来源或用户已经确认的内容。
|
||||
@@ -2020,9 +2021,9 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`~`M1A-4`、`M1B-1`
|
||||
| `M1C-0b` | 静态委派 durable enum 的前向兼容粒度:未知 `contract_status` 解析为显式 `Unknown` 并最大化阻塞 | `M1C-0` | **已完成**:纯读路径、无写入方、审批状态、pending、receipt 或 UI(不含 `M1C-1`)。四种已知 durable 值保持原 serde;未知字符串解析为 `Unknown(raw)`,非字符串仍拒绝,`Serialize` 及读-改-写均原样保留 raw。`Unknown` 计入 completion barrier 与 waiting blocker,返工入口无条件拒绝(含 `depth=0`),lineage 按“其它”最保守分类(`depth + 1`、`round = 0`);planning Provider、自治 liveness、终态扫描等既有读路径同步 fail closed。截断、非法 JSON、非 UTF-8、超过 128 KiB 的 sidecar 仍按整目录 fail closed,不做单条跳过。**不新增或改变 `M1B-*` 功能依赖(仅复核其既有读路径);不包含 `M1C-1` 的审批写入、receipt、UI 或构建准入** |
|
||||
| `M1C-1` | `gdd-approval` pending、审批命令、receipt;receipt 写入上述 status 与 plan 根完成门 | `M1B-2`、`M1C-0`(前向兼容粒度另见 `M1C-0b`) | **已落地并合入**:三动作幂等、版本/指纹竞态防护、receipt 后 index/Markdown/audit/terminal observation/session 投影与恢复、generic v5/v4 anchor 精确消费、terminal summary 完整性校验,以及仅作用于 exact plan 根的只读 completion blocker;生产 acceptance-gate pending caller 与验收前置取证门按拆包纪律由 `M1C-2a` 承接。审批 UI / 澄清中转 / 构建准入仍未完成。连续修订 barrier 与 `UserRevisionRequested` 规则按第 23.7 节执行 |
|
||||
| `M1C-2a` | Goal Contract 接线:turn 1 冻结、固定验收图、审批前置门取证 | `M1C-1`、`M1A-3` | **当前隔离 worktree 已完成并通过本包门禁,尚未合回**:turn 1 的 request-scoped schema 与格式修复都只允许一个固定 `agent.goal_contract`;按项目变化的四项之外,`preferences=[]`、唯一验收节点及证据工具均冻结。Fast GDD evidence 只接受当前 Supervisor 根 run 对 `game/fast_gdd.md` 从第 1 行到 EOF 的同 hash 完整分页;无/旧证据先继续读取,显式 failed 才给原 delivery 的 `repairOfDelegationId`,passed 且 delivery 已认领才建 pending。pending/recovery/completion/finalization 均按同 identity 幂等,审批后 Markdown 改写不损坏 Graph。格式、Provider 强判据、M1C-2a、Acceptance Graph、planning submit/approval、finalization、all-targets、编码与 diff 门禁均通过;扩展 autonomous completion 整组的无关 game-chat 并行超时及精确复跑结果见 decision-log 同日条,不改该路径。不包含 `M1C-2b`、UI 或构建准入 |
|
||||
| `M1C-2b` | 澄清中转接线、轮次派生、预算注入 | `M1C-2a` | **实现与本包门禁已完成并已快进合回 `feat/five_min_design`**:首 child 的 revision 1 session、`NeedsUserInput → awaiting_user_input`、回答绑定后 continuation 的确定性 session 投影、审批后 `revise/reject` 用户修订谱系及 Provider 活跃时间 usage fact/fold 已接线;末次 `plan.submit_gdd` usage 在 receipt/session successor 落盘且 standalone/v4 anchors 精确消费后于同一项目锁内折叠,真实 receipt 回归证明累计值恰好推进一次,重复审批与 recovery reconcile 不二次推进。`planning_clarification_*` **11 passed / 0 failed**,另有 static deliveries 44、planning storage 13、planning submit 53、Provider usage 4、末次 usage receipt 1、barrier detail 3 条定向回归通过;格式、offline all-targets、编码与 diff 门禁通过。锁序承诺只适用于 **M1C-2b 新增的 planning 澄清写投影路径**;`main_loop` 既有通用 completion blocker 的 execution→project 路径不在本包。第四轮违规信封已拒绝 pending,但拒绝后自动转 submit 尚未闭环,按同日保留观察后置。审批 UI、hydrate、构建准入和下游完整构建不在本包范围 |
|
||||
| `M1C-2b` | 澄清中转接线、轮次派生、预算注入 | `M1C-2a` | **实现与本包门禁已完成并已快进合回 `feat/five_min_design`**:首 child 的 revision 1 session、`NeedsUserInput → awaiting_user_input`、回答绑定后 continuation 的确定性 session 投影、审批后 `revise/reject` 用户修订谱系及 Provider 活跃时间 usage fact/fold 已接线;末次 `plan.submit_gdd` usage 在 receipt/session successor 落盘且 standalone/v4 anchors 精确消费后于同一项目锁内折叠,真实 receipt 回归证明累计值恰好推进一次,重复审批与 recovery reconcile 不二次推进。`planning_clarification_*` **13 passed / 0 failed**(M1C-2c 语义回归另见本包),另有 static deliveries 44、planning storage 13、planning submit 53、Provider usage 4、末次 usage receipt 1、barrier detail 3 条定向回归通过;格式、offline all-targets、编码与 diff 门禁通过。锁序承诺只适用于 **M1C-2b 新增的 planning 澄清写投影路径**;`main_loop` 既有通用 completion blocker 的 execution→project 路径不在本包。第 4 轮信封在正常路径不可达:`agent.delegate` 已在工具边界按血缘上限硬拒并返回 failed observation;coordinator 的超三轮 reconciliation 仅用于损坏血缘纵深防御。审批 UI、hydrate、构建准入和下游完整构建不在本包范围 |
|
||||
| `M1D-1` | 前端 hydrate 与 GDD 审批卡 | `M1C-2b` | 前端只经 `hydrate_game_creator_plan_gdd_state` 读权威状态,不在页面侧合成批准事实 |
|
||||
| `M1C-2c` | 决策卡 A/B 语义(第 23.9 节,2026-08-17 立包):选项 → 台账映射改为 A/B 均 `confirmed/user_option`、信封 label 形状校验、planning role brief 与 Supervisor prompt 文案(B 必须是真实岔路、第 3 项恒定且 description 须给出可执行验证方式、改口转述规则、提问纪律) | `M1C-2b` | 选 B 记 `confirmed/user_option`;缺第 3 项或第 3 项非固定文案的信封被拒并回灌;未提问默认项仍只能 `default_pending/default/round=0`(第 12 节校验不变);`answerSummary` 逐字等于所选 label;不改 `M1C-2b` 的 3 轮/幂等/绑定门禁 |
|
||||
| `M1C-2c` | 决策卡 A/B 语义(第 23.9 节,2026-08-18 隔离实现完成):选项 → 台账映射改为 A/B 均 `confirmed/user_option`、信封 label 形状校验、planning role brief 与 Supervisor playbook/final-reply 文案(B 必须是真实岔路、第 3 项恒定且 description 须给出可执行验证方式、改口转述规则、提问纪律) | `M1C-2b` | **隔离实现与门禁完成,尚未合回**:Runtime 已实现 A/B/固定第三项校验、B 不再生成 `default_pending`、`answerSummary` 逐字保真;非法 C/缺项 fail-closed,A/B/自由填写回归已通过。`planning_clarification_*` 13、`project_planning` prompt 5、`planning_submit` 定向回归、prompt bundle、格式、编码、diff、offline all-targets 均通过;不含 M1D-1 前端、hydrate、构建准入或下游完整构建 |
|
||||
| `M1D-1` | 前端 hydrate 与 GDD 审批卡;决策卡按第 23.9 节实现(label 动态渲染、默认焦点 A、Other 槽不变) | `M1C-2b` | 前端只经 `hydrate_game_creator_plan_gdd_state` 读权威状态,不在页面侧合成批准事实 |
|
||||
| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | 「直接开建」跳过路径与现状零差异 |
|
||||
| `M1E` | 端到端与故障注入收口 | `M1D-2` | 第 21 节测试矩阵中跨层场景 |
|
||||
@@ -2042,7 +2043,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`~`M1A-4`、`M1B-1`
|
||||
|
||||
**`M1A-3` 的由来(2026-08-13 补列)**:本 PR 的内容原属 `M1A-1` 的消费点复核范围,被判成「已被 profile 挡住、本包不改」而漏出。漏出的机制原因是**复核方向单一**——`M1A-1` 的模板只问「plan 进可信 matcher 后会不会**误得**不该有的语义」,而 `resolve_game_creator_agent_runtime_retry_configuration_at` 既是判据也是 run 构造器,还必须反过来问「plan 落到通用兜底后会不会**丢掉**该有的语义」。后者的答案是会:plan 根 run 落 `agent-background-task` 兜底后 steer 重新放开,且 `root_control_authority` 转为 `false` 使其建不出 Goal Contract,第 13.0 节审批前置门要的取证永远收敛不了——而因为 `agent.delegate` 不受该权限影响,故障要到审批那一步才暴露。不回改已合入的 `M1A-1`,单列本 PR;详细定位见 decision-log 2026-08-13「订正 `M1A-1` 的 retry 复核结论」条。**凡「既是判据又是构造器」的调用点,后续 PR 的复核必须双向提问。**
|
||||
|
||||
### 23.9 决策卡选项语义修订:A/B 平行方案与决定状态来源唯一化(2026-08-17 拟定,由 `M1C-2c` 落地)
|
||||
### 23.9 决策卡选项语义修订:A/B 平行方案与决定状态来源唯一化(2026-08-18 `M1C-2c` 隔离实现完成)
|
||||
|
||||
**问题**:第 5.2 节固定的三个选项「接受推荐 / 暂按推荐 / 需要原型验证」是对**同一条推荐**的三种采纳程度,不是三个方案。以 DeepSeek v4-flash 做的本地原型多轮实测(原型不入库、只用于开发调试,本节只记结论):选 1 与选 2 产出的 GDD 内容一字不差,差别只是一个 `default_pending` 标签;该标签不进任何机制(不锁台账、不进构建准入、不进验证环),却消耗一轮问询名额(上限 3)。同时台账 `answerSummary` 只落「接受推荐」三个字——被接受的方案内容只存在于信封问题正文与子 Agent 自己的会话里,事后审计看不出用户到底确认了什么;用户在下一轮自由填写里推翻上一轮已确认决定时,这一点让 Supervisor 转述与子 Agent 出稿都只能靠改写文本来消化。
|
||||
|
||||
@@ -2054,7 +2055,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`~`M1A-4`、`M1B-1`
|
||||
- **已确认决定被后续自由填写推翻(改口)**:M1 内**不改合同**。台账前缀不可变,两条 `confirmed` 并存;Supervisor 转述 continuation 时必须在被推翻的那条之后注明「已被第 N 轮回答推翻,以后者为准」,用户答案原文仍逐字保留(不得改写、拆分或搬到别的轮次),子 Agent 按后者出稿并在新决定的 topic 中写明推翻关系。`supersedes` 字段进 `plan-gdd.v1` 列为 M2 候选。原型实测三次改口 Supervisor 均能自行消化,但其中一次是靠改写用户答案原文(转述保真审计报「内容缺失」),故此规则必须写进 Supervisor prompt,不能默认它会做对。
|
||||
- 提问纪律补一条:平台事实已定的事(含"移动/桌面优先级")与 MVP 规则已排除的事(多人/联机/商城/服务器)**不作为问题**。A/B 格式会诱使模型去问"天然二选一"但无价值的问题(实测一轮 3 张卡里 2 张是这种),必须在 prompt 里堵住。
|
||||
|
||||
**为什么不进 `M1C-2b`、由 `M1C-2c` 落地**:拟定本节时 `M1C-2b` 尚在隔离工作树,其合入门禁(3 轮上限、continuation 重放幂等、答案绑定冲突被拒)与选项文案无关;它按当时的第 5.2 节映射表实现并已于 2026-08-17 快进合回,把「单题、`第N轮·关键决定`、固定三选项」的校验与「三个固定选项/自由填写 → state、answerSource、answerSummary」的确定性派生冻结在 planning coordinator 里。随后由 `M1C-2c` 翻映射、改信封形状校验、换 prompt 文案,现在即可开工。**`M1D-1` 前端决策卡必须直接按本节实现**(label 动态渲染、默认焦点在 A、Other 槽不变),避免做两遍。第 5.2 节、第 5.1 节 prompt 段与第 23.6 节「仍冻结:固定选项」一句由 `M1C-2c` 按本节改写。
|
||||
**为什么不进 `M1C-2b`、由 `M1C-2c` 承接**:`M1C-2b` 的合入门禁(3 轮上限、continuation 重放幂等、答案绑定冲突被拒)与选项文案无关;它按旧映射表完成了澄清中转,并已于 2026-08-17 快进合回。`M1C-2c` 已在隔离 worktree 翻映射、改信封形状校验并更新 role brief、Supervisor playbook 与 final-reply 提示;`planning_clarification_*` 13 条、`project_planning` prompt 5 条、`planning_submit` 定向回归、prompt bundle、格式、编码、diff 和 offline all-targets 门禁均通过。**`M1D-1` 前端决策卡必须直接按本节实现**(label 动态渲染、默认焦点在 A、Other 槽不变),避免做两遍;本包不接前端、hydrate、构建准入或下游完整构建。
|
||||
|
||||
**顺带核对项(归 `M1E`)**:`plan.submit_gdd` 连续校验失败必须有**次数**上限。本地实测无界时,模型对大载荷序列化出错后连续 40 余次重试,每次重放全部历史,单次 prompt 涨到 15 万 token;生产的 240/300 秒预算注入兜不住「硬超时注入后仍连续校验失败」的情形。若第 12 节 retry 状态机没有该上限,补一个(原型取 5 次)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user