完成M1D-2入口分流与阶段进度
Project CI / Repository checks (pull_request) Successful in 1m28s
Project CI / Frontend tests (pull_request) Failing after 2m4s
Project CI / Native shell tests (pull_request) Failing after 2m58s
Project CI / Backend tests (pull_request) Successful in 3m45s

游戏新项目默认进入立项策划并保留直接开建路径

项目总控页面挂载GDD审批卡与阶段进度

同步策划展示名称、定向测试与开发日志
This commit is contained in:
2026-08-18 09:11:38 +00:00
parent 9bad7121b5
commit bf2185fba0
15 changed files with 296 additions and 24 deletions
+30 -7
View File
@@ -33,6 +33,7 @@ import {
CONVERSATION_INITIAL_VISIBLE_COUNT,
CONVERSATION_VISIBLE_STEP,
PROJECT_SUPERVISOR_AGENT_ID,
PROJECT_SUPERVISOR_PLAN_SOURCE,
seedManifest,
} from './app/constants';
import { useEscapeToClose } from './app/dialogs';
@@ -6008,12 +6009,31 @@ export function App({
if (!sessionId || localProjectPathRef.current !== nextProjectPath) {
return;
}
const submissionRunProfile =
workspaceProjectKind === 'godot' ||
(supervisorChatOnly && !gameChatOnly)
const runtimeAtSubmission = projectSupervisorRuntimeRef.current;
const planningEntry =
!gameChatOnly &&
workspaceProjectKind === 'web' &&
runtimeAtSubmission?.source === PROJECT_SUPERVISOR_PLAN_SOURCE;
const submissionRunProfile = planningEntry
? 'standard'
: workspaceProjectKind === 'godot' ||
(supervisorChatOnly && !gameChatOnly)
? 'standard'
: 'autonomous-game-build';
const runtimeAtSubmission = projectSupervisorRuntimeRef.current;
const submissionSource = planningEntry
? PROJECT_SUPERVISOR_PLAN_SOURCE
: gameChatOnly
? 'project-supervisor-game-chat'
: 'project-supervisor-gui';
if (
planningEntry &&
runtimeAtSubmission &&
runtimeAtSubmission.source === PROJECT_SUPERVISOR_PLAN_SOURCE &&
!isAgentRuntimeTerminalState(runtimeAtSubmission)
) {
setProjectSupervisorRuntimeError('请先完成当前立项策划步骤');
return;
}
const steerRuntime = gameChatOnly
? matchingAgentRuntimeForSteer(
[runtimeAtSubmission],
@@ -6077,9 +6097,7 @@ export function App({
prompt,
runtime: runtimeAtSubmission,
runProfile: submissionRunProfile,
source: gameChatOnly
? 'project-supervisor-game-chat'
: 'project-supervisor-gui',
source: submissionSource,
});
const runtimeResult = submission.runtimeResult;
const acceptedRunId = submission.acceptedRunId.trim();
@@ -11324,6 +11342,11 @@ export function App({
visibleMessages={visibleMessages}
visibleProfessionalAgentCards={visibleProfessionalAgentCards}
workspaceStatus={workspaceStatus}
planGddState={planGddState}
planGddHydrateBusy={planGddHydrateBusy || planGddDecisionBusy}
planGddError={planGddError}
onPlanGddRefresh={() => void hydratePlanGddState()}
onPlanGddDecision={decidePlanGdd}
runtime={projectSupervisorRuntime}
error={projectSupervisorRuntimeError}
runtimeByAgentId={agentRuntimeById}
@@ -12,6 +12,7 @@ export const CONVERSATION_INITIAL_VISIBLE_COUNT = 20;
export const CONVERSATION_VISIBLE_STEP = 20;
export const AGENT_CHAT_SCROLL_BOTTOM_THRESHOLD = 48;
export const PROJECT_SUPERVISOR_AGENT_ID = 'project-supervisor';
export const PROJECT_SUPERVISOR_PLAN_SOURCE = 'project-supervisor-plan';
export const launcherNotifications: Array<{
label: string;
detail: string;
@@ -52,6 +52,8 @@ export type LauncherImportedAttachment = {
export type LocalProjectKind = 'web' | 'godot';
export type ProjectStartMode = 'planning' | 'direct-build';
export type LauncherProjectContext = {
projectPath: string;
projectName: string;
@@ -76,6 +78,7 @@ export type PendingNonEmptyProject =
kind: 'home-create';
projectPath: string;
mode: HomeAgentMode;
startMode: ProjectStartMode;
prompt: string;
attachments: HomeAttachmentDraft[];
}
@@ -1537,6 +1537,9 @@ export function projectSupervisorCollaboratingAgentRuntimes(
}
export function projectProfessionalAgentLabel(agentId: string) {
if (agentId === 'project-planning') {
return '立项策划 Agent';
}
if (agentId === 'design-foundation') {
return '玩法策划 Agent';
}
@@ -63,6 +63,7 @@ export function WorkspaceLauncherShell({
setAgentResults: setActiveProjectAgentResults,
resetLauncherHomeDraft,
createHomeDraft,
createHomeDraftDirectBuild,
createHomeDraftAutomatically,
openProject,
} = homeProject;
@@ -260,6 +261,7 @@ export function WorkspaceLauncherShell({
homeAgentModeItems={homeAgentModeItems}
recentProjectRows={recentProjectRows}
onCreateDraft={createHomeDraft}
onCreateDraftDirectBuild={createHomeDraftDirectBuild}
onCreateDraftAutomatically={createHomeDraftAutomatically}
onProjectsOpen={() => setLauncherView('projects')}
onProjectOpen={(path) => {
@@ -10,7 +10,10 @@ import type {
GameCreationAppManifest,
GameCreationAppPreviewState,
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
import { seedManifest } from '../../app/constants';
import {
PROJECT_SUPERVISOR_PLAN_SOURCE,
seedManifest,
} from '../../app/constants';
import { useEscapeToClose } from '../../app/dialogs';
import { resolveTauriInvoke } from '../../app/tauri';
import type {
@@ -20,6 +23,7 @@ import type {
LocalGameProjectRevisionStatus,
LocalProjectDirectoryStatus,
PendingNonEmptyProject,
ProjectStartMode,
TauriInvoke,
UploadLocalAssetResult,
} from '../../app/types';
@@ -168,6 +172,7 @@ export function useHomeProjectCreation({
mode: HomeAgentMode,
prompt: string,
attachments: HomeAttachmentDraft[],
startMode: ProjectStartMode,
) {
const importedAttachments = await importHomeAttachments(
invoke,
@@ -187,7 +192,10 @@ export function useHomeProjectCreation({
sessionId,
prompt: buildHomeConversationContent(mode, prompt, attachments),
runtime: null,
runProfile: 'autonomous-game-build',
runProfile:
startMode === 'planning' ? 'standard' : 'autonomous-game-build',
source:
startMode === 'planning' ? PROJECT_SUPERVISOR_PLAN_SOURCE : undefined,
});
enterProjectDevelopment({
projectPath: result.projectPath,
@@ -218,6 +226,7 @@ export function useHomeProjectCreation({
mode: HomeAgentMode,
prompt: string,
attachments: HomeAttachmentDraft[],
startMode: ProjectStartMode,
skipNonEmptyCheck = false,
) {
const trimmedProjectPath = validateProjectPath(nextProjectPath);
@@ -241,6 +250,7 @@ export function useHomeProjectCreation({
kind: 'home-create',
projectPath: trimmedProjectPath,
mode,
startMode,
prompt,
attachments,
});
@@ -263,6 +273,7 @@ export function useHomeProjectCreation({
mode,
prompt,
attachments,
startMode,
);
setStatus('已创建项目并交给项目总控 Agent');
} catch (error) {
@@ -285,6 +296,7 @@ export function useHomeProjectCreation({
mode: HomeAgentMode,
prompt: string,
attachments: HomeAttachmentDraft[],
startMode: ProjectStartMode,
) {
const invoke = resolveTauriInvoke();
if (!invoke) {
@@ -292,7 +304,14 @@ export function useHomeProjectCreation({
}
setStatus('正在启动项目总控 Agent');
try {
await enterCreatedHomeProject(invoke, result, mode, prompt, attachments);
await enterCreatedHomeProject(
invoke,
result,
mode,
prompt,
attachments,
startMode,
);
setStatus('已自动创建工作区并开始工作');
return '已自动创建工作区并开始工作';
} catch (error) {
@@ -476,6 +495,7 @@ export function useHomeProjectCreation({
pendingProject.mode,
pendingProject.prompt,
pendingProject.attachments,
pendingProject.startMode,
true,
);
return;
@@ -513,6 +533,28 @@ export function useHomeProjectCreation({
draft.mode,
draft.prompt,
draft.attachments,
draft.mode === 'game' ? 'planning' : 'direct-build',
);
}
async function createHomeDraftDirectBuild(draft: HomeDraft) {
const invoke = resolveTauriInvoke();
if (!invoke) {
throw new Error('需要在 Tauri App 内运行');
}
const selectedPath = await invoke<string | null>(
'pick_local_project_directory',
projectPath.trim() ? { initialPath: projectPath.trim() } : undefined,
);
if (!selectedPath) {
return '已取消';
}
return createHomeProjectFromDirectory(
selectedPath,
draft.mode,
draft.prompt,
draft.attachments,
'direct-build',
);
}
@@ -530,6 +572,7 @@ export function useHomeProjectCreation({
draft.mode,
draft.prompt,
draft.attachments,
draft.mode === 'game' ? 'planning' : 'direct-build',
);
}
@@ -619,6 +662,7 @@ export function useHomeProjectCreation({
pendingNonEmptyProject,
resetLauncherHomeDraft,
createHomeDraft,
createHomeDraftDirectBuild,
createHomeDraftAutomatically,
openProject,
pickAndOpenProject,
@@ -40,7 +40,7 @@ export const commonProjectInternalReadDrafts = [
] as const;
export const taskGroupLabels: Record<GameCreationAppAgentGroup, string> = {
design: '策划组',
design: '设计实现组',
art: '美术组',
code: '程序组',
balance: '数值组',
@@ -25,6 +25,39 @@ const stateLabels: Record<PlanGddStateViewV1['state'], string> = {
rejected: '已退回',
};
export function PlanGddStageProgress({
state,
active = false,
}: {
state: PlanGddStateViewV1 | null;
active?: boolean;
}) {
if (!state || (!active && state.state === 'not_started')) {
return null;
}
const latestVersion =
state.displayGdd?.version ??
state.versions[state.versions.length - 1]?.gddRef.version ??
null;
const clarificationRound = state.session?.clarificationRound ?? 0;
return (
<section className="plan-gdd-stage-progress" aria-label="立项策划阶段进度">
<div className="plan-gdd-stage-progress__header">
<strong></strong>
<span>{stateLabels[state.state]}</span>
</div>
<div className="plan-gdd-stage-progress__meta">
<span>{`轮次 ${clarificationRound}/3`}</span>
<span>
{latestVersion === null
? '当前版本:草稿'
: `当前版本:v${latestVersion}`}
</span>
</div>
</section>
);
}
const decisionStateLabels = {
confirmed: '已确认',
default_pending: '待确认默认项',
@@ -11,6 +11,8 @@ import type {
AgentStatusCard,
ChatMessage,
PendingUiConfirmation,
PlanGddDecisionAction,
PlanGddStateViewV1,
} from '../../app/types';
import {
projectProfessionalAgentLabel,
@@ -21,6 +23,7 @@ import {
} from '../agent-runtime';
import { formatAgentCardRuntimeStatus } from '../project-summary/agentPresentation';
import { taskStatusLabels } from '../project-summary/projectSummary';
import { GddApprovalCard, PlanGddStageProgress } from './GddApprovalCard';
type RuntimePanelProps = ComponentProps<typeof ProjectSupervisorRuntimePanel>;
@@ -39,6 +42,14 @@ type ProjectSupervisorViewProps = RuntimePanelProps & {
visibleMessages: ChatMessage[];
visibleProfessionalAgentCards: AgentStatusCard[];
workspaceStatus: string;
planGddState: PlanGddStateViewV1 | null;
planGddHydrateBusy: boolean;
planGddError: string | null;
onPlanGddRefresh: () => void;
onPlanGddDecision: (
action: PlanGddDecisionAction,
comment: string | null,
) => Promise<void>;
};
export function ProjectSupervisorView({
@@ -56,11 +67,29 @@ export function ProjectSupervisorView({
visibleMessages,
visibleProfessionalAgentCards,
workspaceStatus,
planGddState,
planGddHydrateBusy,
planGddError,
onPlanGddRefresh,
onPlanGddDecision,
...runtimePanelProps
}: ProjectSupervisorViewProps) {
return (
<section className="project-supervisor-surface" aria-label="项目总控对话">
<div className="project-supervisor-conversation">
<PlanGddStageProgress
state={planGddState}
active={
runtimePanelProps.runtime?.source === 'project-supervisor-plan'
}
/>
<GddApprovalCard
state={planGddState}
busy={planGddHydrateBusy}
error={planGddError}
onRefresh={onPlanGddRefresh}
onDecision={onPlanGddDecision}
/>
<div
className="message-list project-supervisor-message-list"
aria-label="项目总控消息"
@@ -47,7 +47,7 @@ import {
taskGroupLabels,
taskStatusLabels,
} from '../project-summary/projectSummary';
import { GddApprovalCard } from './GddApprovalCard';
import { GddApprovalCard, PlanGddStageProgress } from './GddApprovalCard';
import {
pendingCommandDetail,
pendingCommandTitle,
@@ -363,6 +363,12 @@ export function ProjectWorkspaceChatPane({
</button>
</div>
</header>
<PlanGddStageProgress
state={planGddState}
active={
projectSupervisorRuntime?.source === 'project-supervisor-plan'
}
/>
<GddApprovalCard
state={planGddState}
busy={planGddHydrateBusy}
+53
View File
@@ -3272,6 +3272,59 @@ textarea {
background: #f8fbff;
}
.plan-gdd-stage-progress {
display: grid;
gap: 6px;
padding: 10px 12px;
border: 1px solid #dbe4f1;
border-radius: 8px;
background: #fff;
color: #526173;
font-size: 12px;
}
.plan-gdd-stage-progress__header,
.plan-gdd-stage-progress__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px 14px;
}
.plan-gdd-stage-progress__header strong {
color: #27364a;
}
.plan-gdd-stage-progress__header span {
padding: 3px 8px;
border-radius: 999px;
background: #eef6ff;
color: #1f6feb;
}
.home-creation-actions {
display: flex;
align-items: center;
gap: 8px;
}
.home-direct-build-button {
min-height: 28px;
padding: 0 9px;
border: 1px solid var(--platform-surface-border);
border-radius: 999px;
background: var(--platform-button-secondary-fill);
color: var(--platform-button-secondary-text);
font-size: 11px;
cursor: pointer;
}
.home-direct-build-button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.gdd-approval-card__header {
display: flex;
align-items: flex-start;
@@ -47,6 +47,7 @@ type HomeViewProps = {
homeAgentModeItems: readonly HomeAgentModeItem[];
recentProjectRows: readonly HomeProjectRow[];
onCreateDraft: (draft: HomeDraft) => Promise<string>;
onCreateDraftDirectBuild: (draft: HomeDraft) => Promise<string>;
onCreateDraftAutomatically: (draft: HomeDraft) => Promise<string>;
onProjectsOpen: () => void;
onProjectOpen: (path: string) => void;
@@ -60,6 +61,7 @@ export default function HomeView({
homeAgentModeItems,
recentProjectRows,
onCreateDraft,
onCreateDraftDirectBuild,
onCreateDraftAutomatically,
onProjectsOpen,
onProjectOpen,
@@ -183,14 +185,33 @@ export default function HomeView({
>
<div className="grid grid-cols-[1fr_auto] items-center gap-2.5 text-[12px] text-(--platform-text-soft)">
<UploadButton />
<button
className="grid size-6 cursor-pointer place-items-center rounded-full border-0 bg-(image:--platform-button-primary-fill) p-0 text-(--platform-button-primary-text) shadow-(--platform-profile-action-shadow) disabled:cursor-not-allowed disabled:opacity-55"
type="submit"
aria-label="开启创作"
disabled={homeCreationBusy}
>
<Plus size={16} aria-hidden="true" />
</button>
<div className="home-creation-actions">
{homeAgentMode === 'game' ? (
<button
className="home-direct-build-button"
type="button"
disabled={homeCreationBusy}
onClick={() =>
void createFromHome(
onCreateDraftDirectBuild,
'正在创建工作区',
)
}
>
</button>
) : null}
<button
className="grid size-6 cursor-pointer place-items-center rounded-full border-0 bg-(image:--platform-button-primary-fill) p-0 text-(--platform-button-primary-text) shadow-(--platform-profile-action-shadow) disabled:cursor-not-allowed disabled:opacity-55"
type="submit"
aria-label={
homeAgentMode === 'game' ? '进入立项策划' : '开启创作'
}
disabled={homeCreationBusy}
>
<Plus size={16} aria-hidden="true" />
</button>
</div>
</div>
</RichInputArea>
</form>
@@ -1,6 +1,7 @@
import { describe, expect, test, vi } from 'vitest';
import { createGameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
import { PROJECT_SUPERVISOR_PLAN_SOURCE } from '../src/app/constants';
import type {
AgentRuntimeEventRecord,
AgentRuntimeResponseStream,
@@ -618,6 +619,51 @@ describe('Game Chat stream identity and source', () => {
);
});
test('planning entry forwards the exact standard profile and trusted plan source', async () => {
const runtimeResult = {
state: {
schemaVersion: 'game-creator-agent-runtime.v1',
agentId: 'project-supervisor',
taskId: 'project-supervisor',
sessionId: 'session-plan-entry',
runId: 'plan-run',
source: PROJECT_SUPERVISOR_PLAN_SOURCE,
runProfile: 'standard' as const,
status: 'running',
phase: 'planning',
currentTask: '整理游戏立项需求',
currentAction: '',
plan: [],
observations: [],
allowedTools: [],
lastResponse: null,
error: null,
updatedAt: 1,
},
sessionPath: 'session.json',
eventPath: 'events.jsonl',
} satisfies AgentRuntimeResult;
const invoke = vi.fn(async () => runtimeResult);
await submitProjectSupervisorRuntimeTask({
invoke,
projectPath: '/tmp/plan-entry',
sessionId: 'session-plan-entry',
prompt: '做一个双摇杆动作游戏',
runtime: null,
runProfile: 'standard',
source: PROJECT_SUPERVISOR_PLAN_SOURCE,
});
expect(invoke).toHaveBeenCalledWith(
'start_game_creator_supervisor_runtime_task',
expect.objectContaining({
runProfile: 'standard',
source: PROJECT_SUPERVISOR_PLAN_SOURCE,
}),
);
});
test('game-chat hydration keeps identical text from a different runtime response identity', () => {
const history = [
{
@@ -1,5 +1,13 @@
# 决策记录
## 2026-08-18 M1D-2 隔离工作树实现:入口分流与阶段进度
- **隔离范围**:在 `codex/genarrative-isolated`、基线 `5b11a0530` 上开工;只接入口分流、阶段进度和实际项目总控页面的现有审批卡挂载,不接 M2 `approvedGddRef` 构建绑定、完整构建按钮或 M1E 端到端故障注入。
- **入口合同**:游戏新项目默认以 `standard + project-supervisor-plan` 启动;“直接开建”显式走既有 `autonomous-game-build` 路径。做素材/做方案入口保持原有自动构建行为;空目录确认会携带同一 start mode,不因二次确认改变路径。项目打开/项目页新建不注入 start mode,保持老项目行为。
- **页面接线**`ProjectSupervisorView` 现在复用 M1D-1 的 hydrate、GDD 审批卡和阶段进度;阶段进度只消费 `plan-gdd-state-view.v1`,显示 `轮次 x/3`、当前版本与状态徽章。`project-planning` 显示为“立项策划 Agent”,设计组展示名改为“设计实现组”。
- **Runtime 路由**:项目总控聊天提交在规划入口或已读到 `project-supervisor-plan` 时继续使用 `standard + project-supervisor-plan`;规划 Run 尚未终态时不另起通用聊天 Run,要求先完成当前策划步骤。直接开建及非规划入口保留原提交 profile/source。
- **自审边界与验证**:只接受有完整触发链路的问题;本包不改变 Runtime source 门禁、审批命令、构建准入或老项目 direct-build 语义。改动文件 ESLint、Shell TypeScript 两套 typecheck、`agentRuntimeModel.test.ts`29 passed)、编码检查与 `git diff --check` 通过;appSurface UI suite 受仓库现有缺失 Tauri plugin 依赖阻断,未把该基线失败归因于本包。自审未发现测试失败或修复边界明确的 M1D-2 之外缺陷;M2 approved-GDD 构建绑定与 M1E 跨层故障注入保留。
## 2026-08-18 M1D-1 隔离工作树实现:hydrate/read model 与 GDD 审批卡
- **隔离基线与范围**:在 `codex/genarrative-isolated`、基线 `14c00017c` 上开工;只实现 M1D-1 的前端 hydrate/read model 与 GDD 审批卡,不接 M1D-2 入口分流、完整构建按钮、构建准入或 M1E 下游链路。
@@ -3,7 +3,7 @@
- 日期: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-4` 已分别落地:`M1A-2` 仅收口两层工具面、`project-planning` role brief 注入和 fail-closed 拒绝边界,`M1A-4` 收窄 plan 根 run 的子 Agent 创建面。**2026-08-14 `M1B-1` 已通过门禁并合入本分支**:已落地 `.agent/planning` storage module、strict schema/typed 指纹/canonical parser、GDD 版本链、session 原子恢复、Runtime 写入身份及只挡写门禁;golden vector 与 11 个定向 storage 测试通过,writer/index/recovery 门禁已完成。**2026-08-15 `M1B-2` 工作包已通过本包门禁并以 `27c3eb847` 合入本分支**:已落地 `plan.submit_gdd`、exact planning Provider binding/structured injection、专用提交点与崩溃恢复;本包不包含 `gdd-approval` planning pending、审批等待、receipt、审批命令或 UI。**2026-08-14 `M1C-0` 已合回本分支**:仅新增用户修订状态及 lineage 分类,不包含审批写入方。**2026-08-15 `M1C-0b` 已通过定向门禁并完成**:只改静态委派 durable status 的前向兼容读路径,未知字符串显式保留为 `Unknown(raw)` 并最大化阻塞;不含审批写入方。`M1C-1``M1C-2a` 已提供审批核心、固定 Goal Contract、完整分页 `file.read` evidence、claim 后三态 acceptance gate、审批 pending 恢复及 completion/finalization 门。**`M1C-2b` 已完成本包实现并通过门禁,现已快进合回 `feat/five_min_design`**planning 澄清中转、确定性 continuation/session 投影、审批后用户修订谱系、Provider 活跃时间预算与末次 submit usage fold 已实现,11 条 `planning_clarification_*` 回归及关联 Rust 门禁通过。审批 UI、hydrate、构建准入与下游完整构建仍后置,M1 整体不可交付(见第 23.6、23.8 节)。
- 适用范围:AI 游戏创作独立 App、Project Supervisor、Agent Runtime、本地项目策划 sidecar 与后续完整构建准入
- 当前实现边界:本文件是后续详细设计与实现的仓库内阶段基线;M0 工作包冻结 Fast GDD 合同并修复现有 owner 验证、game-chat retry 与前端投影边界,`M1A-1``M1A-4` 已提供 plan source、两层工具面、角色 brief 与子 Agent 创建面收窄的 Runtime 基础,`M1C-0` 已提供用户修订 lineage 分类,已合入的 `M1B-1` 提供 storage 基础与写入隔离;`M1B-2` 已提供提交点与恢复,`M1C-0b` 已补齐静态委派未知 durable status 的前向兼容读路径,`M1C-1` 已提供 receipt/审批核心、投影恢复和 plan 根完成门,`M1C-2a` 已补齐固定 Goal Contract、验收图证据与生产 acceptance gate。`M1C-2b` 已实现 planning 澄清中转、三轮确定性 session/continuation 投影、审批后修订谱系和 Provider 活跃时间预算,并已合回 `feat/five_min_design`审批 UI、hydrate、构建绑定和正式入口仍不可用
- 当前实现边界:本文件是后续详细设计与实现的仓库内阶段基线;M0 工作包冻结 Fast GDD 合同并修复现有 owner 验证、game-chat retry 与前端投影边界,`M1A-1``M1A-4` 已提供 plan source、两层工具面、角色 brief 与子 Agent 创建面收窄的 Runtime 基础,`M1C-0` 已提供用户修订 lineage 分类,已合入的 `M1B-1` 提供 storage 基础与写入隔离;`M1B-2` 已提供提交点与恢复,`M1C-0b` 已补齐静态委派未知 durable status 的前向兼容读路径,`M1C-1` 已提供 receipt/审批核心、投影恢复和 plan 根完成门,`M1C-2a` 已补齐固定 Goal Contract、验收图证据与生产 acceptance gate。`M1C-2b` 已实现 planning 澄清中转、三轮确定性 session/continuation 投影、审批后修订谱系和 Provider 活跃时间预算,并已合回 `feat/five_min_design``M1D-1` 已提供 hydrate/read model 与审批卡,`M1D-2` 已接入新项目入口分流、阶段进度和实际项目总控页面挂载;approved GDD 构建绑定与完整下游仍留给 M2/M1E
## 1. 背景与目标
@@ -483,7 +483,7 @@ exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不
**轮次计数。** 本轮是第几轮由委派链上的 `clarification_round` 派生值决定(沿 `repair_of_delegation_id` 上溯推断,语义见第 23.5 节),上限 3;session 不再自累加 `roundsUsed`。达到上限后 Runtime 在下一轮子 run 的上下文里注入「必须出稿」,若子 Agent 仍输出信封则拒绝该信封并要求改为 `plan.submit_gdd`
当前实现边界:第四轮违规信封已能在建立 Supervisor pending 前失败关闭,但失败后目前进入 reconciliation,尚未形成“同一子 run 被强制改为 `plan.submit_gdd`”的自动闭环。该差距不影响三轮、重放、身份、锁序与预算门禁,但涉及更宽的 Provider/终态状态机,留待后续单列,不将 M1C-2b 的“第四轮不建 pending”回归夸大为已完成强制 submit。
当前实现边界:第四轮澄清委派在 `agent.delegate` 工具边界按持久化血缘上限被硬拒,并以普通 failed observation 返回 Supervisor;正常路径不进入 reconciliationSupervisor 可据此改走 `plan.submit_gdd``planning_coordinator` 的超三轮 reconciliation 分支仅用于损坏或篡改血缘的纵深防御;本包不把它扩展成自动 submit 状态机
**Provider batch 规则。** exact planning 子 Agent 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不走现役「少于两项则 NotNeeded」的优化,`plan.submit_gdd` 因此是唯一 v4 member。Supervisor 侧的澄清 `user.input_request` 由 Runtime parent-wake 从 delivery 直接投影,不是 Supervisor Provider action、也不创建 v4 batch;该 pending 的 exactly-once 由 delivery/request/answer 指纹、pending identity 与 planning session revision 共同保证。exact planning 的 `final-reply``context-compaction``final-reply-context-compaction` 均无 action batch但仍写 v3 lifecycle/bindingplanning idle compaction 不支持。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。
@@ -2023,8 +2023,8 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1``M1A-4`、`M1B-1`
| `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_*` **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 observationcoordinator 的超三轮 reconciliation 仅用于损坏血缘纵深防御。审批 UI、hydrate、构建准入和下游完整构建不在本包范围 |
| `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` | **实现与门禁完成,已由 `6e4bd9703` 合回 `feat/five_min_design`**Runtime 已实现 A/B/固定第三项校验、B 不再生成 `default_pending``answerSummary` 逐字保真;非法 C/缺项 fail-closedA/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` | **隔离工作树实现完成,待提交/合回**:新增严格 `{projectPath}` hydrate command、`plan-gdd-state-view.v1` Rust read model、审批卡与独立 GDD 正文详情弹层;页面只消费 hydrate,决定 responseId 按审批请求/动作复用,`recoveryPending` 仅提供恢复重试。已修正“仅 session awaiting 即露出审批动作”及未审批 GDD 缺 session fail-closed 边界;定向 hydrate 空项目回归、offline all-targets cargo check、格式、编码与 diff 门禁通过。未接 M1D-2 入口分流、完整构建按钮或 M1E 下游链路 |
| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | 「直接开建」跳过路径与现状零差异 |
| `M1D-1` | 前端 hydrate 与 GDD 审批卡;决策卡按第 23.9 节实现(label 动态渲染、默认焦点 A、Other 槽不变) | `M1C-2b` | **已完成并合入 `feat/five_min_design``5b11a0530`**:新增严格 `{projectPath}` hydrate command、`plan-gdd-state-view.v1` Rust read model、审批卡与独立 GDD 正文详情弹层;页面只消费 hydrate,决定 responseId 按审批请求/动作复用,`recoveryPending` 仅提供恢复重试;审批前置 pending 与错绑 session 继续 fail-closed |
| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | **隔离工作树实现完成,待提交**:游戏新项目默认 `standard + project-supervisor-plan`,显式“直接开建”保持 `autonomous-game-build`;阶段进度显示轮次 x/3、当前版本和状态徽章;实际项目总控页面挂载 hydrate/审批卡,并将 `project-planning` / 设计组展示名收口。未接 M2 approved-GDD 构建绑定或完整构建按钮。 |
| `M1E` | 端到端与故障注入收口 | `M1D-2` | 第 21 节测试矩阵中跨层场景 |
**2026-08-14 `M1B-1` 合入验收快照**:已合入实现集中在 `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs`,并由 `runtime_protocol.rs` 注册。已具备 `plan-gdd.v1``plan-gdd-index.v1``plan-session.v1``plan.submit_gdd` input 的 strict serde 形状校验、文本/ID/时间/枚举边界、typed serde fingerprint、canonical JSON(重复键、BOM、尾空白、字段顺序)解析、GDD 连续版本链、session `revision + 1` / `previousFingerprint` 链、create-only durable writer、session 原子替换与受限 recovery,以及 `project-planning / agent-delegate / standard / project-supervisor` writer identity。通用 `file.write``file.patch``file.delete``project.patchset` 与 checkpoint restore 对 `.agent/planning/**``game/fast_gdd.md` 只挡写,planning 的 `file.read` / `file.list` 仍可读;M1B-1 合入时没有注册或执行 `plan.submit_gdd`,也没有实现 approval pending、receipt、UI 或构建准入。