diff --git a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx index 33c664552..501eca0b1 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx +++ b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx @@ -77,10 +77,7 @@ export function WorkspaceLauncherShell({ activeProjectAgentResults, setAgentResults: setActiveProjectAgentResults, resetLauncherHomeDraft, - homePrefillAttachments, - homePrefillPrompt, - clearHomePrefill, - prepareHomeGameFromApprovedGdd, + startGameFromApprovedGdd, createHomeDraftAutomatically, openProject, } = homeProject; @@ -264,7 +261,6 @@ export function WorkspaceLauncherShell({ currentUser={currentUser} onLogout={() => { resetLauncherHomeDraft(); - clearHomePrefill(); accountWallet.resetWalletBalance(); onLogout(); }} @@ -298,9 +294,6 @@ export function WorkspaceLauncherShell({ onStatusChange={setStatus} recentProjectRows={recentProjectRows} onCreateDraftAutomatically={createHomeDraftAutomatically} - initialAttachments={homePrefillAttachments} - initialPrompt={homePrefillPrompt} - onInitialPrefillApplied={clearHomePrefill} onProjectsOpen={() => setLauncherView('projects')} onProjectOpen={(path) => { setProjectPath(path); @@ -359,7 +352,7 @@ export function WorkspaceLauncherShell({ setActiveProjectAgentRuntimeSummaries } onAgentResultsChange={setActiveProjectAgentResults} - onMakeGameFromApprovedGdd={prepareHomeGameFromApprovedGdd} + onMakeGameFromApprovedGdd={startGameFromApprovedGdd} /> } /> diff --git a/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts b/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts index a8dba6259..366ddbf6f 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts +++ b/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts @@ -49,7 +49,7 @@ type UseHomeProjectCreationOptions = { rememberRecentWorkspace: (projectPath: string) => void; }; -const APPROVED_GDD_HOME_PROMPT = [ +const APPROVED_GDD_BUILD_PROMPT = [ '请按照附件中的已批准 GDD 开始建造这款游戏。', '', '这份 GDD 已覆盖游戏定位与一句话概念、类型与美术方向、游戏支柱、核心循环、目标用户、平台与输入事实、MVP 系统、暂不纳入范围、创作者提示和原型验证项。', @@ -57,6 +57,19 @@ const APPROVED_GDD_HOME_PROMPT = [ '请先阅读并理解附件中的 fast_gdd.md,以它作为本次建造的主要依据,优先实现其中 MVP 范围内的可运行游戏原型。', ].join('\n'); +function createTextAttachmentFile(content: string) { + const file = new File([content], 'fast_gdd.md', { + type: 'text/markdown', + lastModified: Date.now(), + }); + if (typeof file.arrayBuffer !== 'function') { + Object.defineProperty(file, 'arrayBuffer', { + value: async () => new TextEncoder().encode(content).buffer, + }); + } + return file; +} + export function useHomeProjectCreation({ setStatus, setLauncherView, @@ -86,13 +99,7 @@ export function useHomeProjectCreation({ const resetLauncherHomeDraft = useLauncherHomeDraftStore( (state) => state.reset, ); - const setHomeCreationType = useLauncherHomeDraftStore( - (state) => state.setCreationType, - ); - const [homePrefillAttachments, setHomePrefillAttachments] = useState< - HomeAttachmentDraft[] - >([]); - const [homePrefillPrompt, setHomePrefillPrompt] = useState(''); + const approvedGddStartInFlightRef = useRef(false); function validateProjectPath(nextProjectPath: string) { const trimmedProjectPath = nextProjectPath.trim(); @@ -107,41 +114,48 @@ export function useHomeProjectCreation({ return trimmedProjectPath; } - async function prepareHomeGameFromApprovedGdd(nextProjectPath: string) { + async function startGameFromApprovedGdd(nextProjectPath: string) { + if (approvedGddStartInFlightRef.current) { + return; + } + approvedGddStartInFlightRef.current = true; const invoke = resolveTauriInvoke(); - if (!invoke) { - throw new Error('需要在陶泥儿客户端内运行'); - } - const projectPath = nextProjectPath.trim(); - if (!projectPath) { - throw new Error('当前项目路径无效'); - } + try { + if (!invoke) { + throw new Error('需要在陶泥儿客户端内运行'); + } + const projectPath = nextProjectPath.trim(); + if (!projectPath) { + throw new Error('当前项目路径无效'); + } - setStatus('正在读取已批准 GDD'); - const result = await invoke( - 'read_local_project_file', - { - projectPath, - relativePath: 'game/fast_gdd.md', - commandId: 'file.read', - }, - ); - const file = new File([result.content], 'fast_gdd.md', { - type: 'text/markdown', - lastModified: Date.now(), - }); + setStatus('正在读取已批准 GDD'); + const result = await invoke( + 'read_local_project_file', + { + projectPath, + relativePath: 'game/fast_gdd.md', + commandId: 'file.read', + }, + ); + const file = createTextAttachmentFile(result.content); - resetLauncherHomeDraft(); - setHomeCreationType('game'); - setHomePrefillPrompt(APPROVED_GDD_HOME_PROMPT); - setHomePrefillAttachments([ - { - id: `approved-gdd-${Date.now().toString(36)}`, - file, - }, - ]); - setLauncherView('home'); - setStatus('已回到做游戏入口'); + await createHomeDraftAutomatically( + { + creationType: 'game', + prompt: APPROVED_GDD_BUILD_PROMPT, + attachments: [ + { + id: `approved-gdd-${Date.now().toString(36)}`, + file, + }, + ], + }, + 'direct-build', + ); + } finally { + approvedGddStartInFlightRef.current = false; + } } function enterProjectDevelopment(context: LauncherProjectContext) { @@ -653,13 +667,7 @@ export function useHomeProjectCreation({ projectBusy: projectAction !== null, pendingNonEmptyProject, resetLauncherHomeDraft, - homePrefillAttachments, - homePrefillPrompt, - clearHomePrefill: () => { - setHomePrefillAttachments([]); - setHomePrefillPrompt(''); - }, - prepareHomeGameFromApprovedGdd, + startGameFromApprovedGdd, createHomeDraft, createHomeDraftAutomatically, openProject, diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx index d914c7ee2..5a470dd39 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx @@ -221,7 +221,7 @@ export function PlanGddStageProgress({ .finally(() => setMakingGame(false)); }} > - {makingGame ? '正在准备' : '做成游戏'} + {makingGame ? '正在启动' : '做成游戏'} ) : null} diff --git a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx index d6997be68..1d627d7dc 100644 --- a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx +++ b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx @@ -28,9 +28,6 @@ type RichInputAreaProps = { placeholder: string; onChange: (value: Draft) => void; onEnter: () => void; - initialAttachments?: readonly HomeAttachmentDraft[]; - initialPrompt?: string; - onInitialPrefillApplied?: () => void; children?: React.ReactNode; }; const INSERT_ATTACHMENTS_COMMAND: LexicalCommand = @@ -107,19 +104,8 @@ function selectEditableEndWhenNeeded() { function EditorPlugins({ onChange, onEnter, - initialAttachments = [], - initialPrompt = '', - onInitialPrefillApplied, -}: Pick< - RichInputAreaProps, - | 'onChange' - | 'onEnter' - | 'initialAttachments' - | 'initialPrompt' - | 'onInitialPrefillApplied' ->) { +}: Pick) { const [editor] = useLexicalComposerContext(); - const initialPrefillAppliedRef = useRef(false); useEffect( () => @@ -202,32 +188,6 @@ function EditorPlugins({ [editor], ); - useEffect(() => { - if ( - initialPrefillAppliedRef.current || - (initialPrompt.trim().length === 0 && initialAttachments.length === 0) - ) { - return; - } - initialPrefillAppliedRef.current = true; - if (initialPrompt.length > 0) { - editor.dispatchCommand(INSERT_CLIPBOARD_TEXT_COMMAND, initialPrompt); - } - if (initialAttachments.length > 0) { - editor.dispatchCommand(INSERT_ATTACHMENTS_COMMAND, [ - ...initialAttachments, - ]); - } - onChange(editor.getEditorState()); - onInitialPrefillApplied?.(); - }, [ - editor, - initialAttachments, - initialPrompt, - onChange, - onInitialPrefillApplied, - ]); - return ( { @@ -296,13 +256,7 @@ export default function RichInputArea(props: RichInputAreaProps) { /> {props.children} - + ); } diff --git a/apps/ai-game-creator-shell/src/view/home/index.tsx b/apps/ai-game-creator-shell/src/view/home/index.tsx index 7416e02e1..b76dfec30 100644 --- a/apps/ai-game-creator-shell/src/view/home/index.tsx +++ b/apps/ai-game-creator-shell/src/view/home/index.tsx @@ -20,7 +20,6 @@ import { } from './components/RichInputArea/richTextToPrompt'; import InspirationGallery from './InspirationGallery'; import { - type HomeAttachmentDraft, type HomeCreationType, type HomeDraft, useLauncherHomeDraftStore, @@ -104,9 +103,6 @@ type HomeViewProps = { onProjectsOpen: () => void; onProjectOpen: (path: string) => void; onProjectPick: () => void; - initialAttachments?: readonly HomeAttachmentDraft[]; - initialPrompt?: string; - onInitialPrefillApplied?: () => void; }; export default function HomeView({ @@ -117,9 +113,6 @@ export default function HomeView({ onProjectsOpen, onProjectOpen, onProjectPick, - initialAttachments = [], - initialPrompt = '', - onInitialPrefillApplied, }: HomeViewProps) { const homeCreationType = useLauncherHomeDraftStore( (state) => state.creationType, @@ -241,9 +234,6 @@ export default function HomeView({ value={homeRichText} placeholder={activeCreationType.placeholder} onChange={setHomeRichText} - initialAttachments={initialAttachments} - initialPrompt={initialPrompt} - onInitialPrefillApplied={onInitialPrefillApplied} onEnter={() => { void createFromHome(); }} diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts index 852c86e45..520ff4666 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts @@ -1,9 +1,7 @@ import { PROJECT_SUPERVISOR_PLAN_SOURCE } from '../../src/app/constants'; -import type { ProjectStartMode } from '../../src/app/types'; import type { ProjectSupervisorComponentProps } from '../../src/features/app-shell/model'; import { useHomeProjectCreation } from '../../src/features/app-shell/useHomeProjectCreation'; import { WorkspaceLauncherShell } from '../../src/features/app-shell/WorkspaceLauncher'; -import HomeView, { type HomeDraft } from '../../src/view/home'; import type { LauncherView } from '../../src/view/layout'; import { act, @@ -31,15 +29,8 @@ import { within, } from './harness'; -function ApprovedGddHomeBridgeHarness({ - onCreateDraftAutomatically, -}: { - onCreateDraftAutomatically: ( - draft: HomeDraft, - startMode: ProjectStartMode, - ) => Promise; -}) { - const [launcherView, setLauncherView] = React.useState( +function ApprovedGddStartHarness() { + const [, setLauncherView] = React.useState( 'project-development', ); const [, setStatus] = React.useState(''); @@ -51,19 +42,12 @@ function ApprovedGddHomeBridgeHarness({ rememberRecentWorkspace: () => undefined, }); - if (launcherView === 'home') { - return React.createElement(HomeView, { - hasPromo: false, - onStatusChange: setStatus, - recentProjectRows: [], - onCreateDraftAutomatically, - onProjectsOpen: () => undefined, - onProjectOpen: () => undefined, - onProjectPick: () => undefined, - initialAttachments: controller.homePrefillAttachments, - initialPrompt: controller.homePrefillPrompt, - onInitialPrefillApplied: controller.clearHomePrefill, - }); + if (controller.currentProjectContext) { + return React.createElement( + 'p', + { 'aria-label': '已进入自动游戏项目' }, + controller.currentProjectContext.initialPrompt, + ); } return React.createElement( @@ -71,9 +55,9 @@ function ApprovedGddHomeBridgeHarness({ { type: 'button', onClick: () => - void controller.prepareHomeGameFromApprovedGdd('/tmp/planning-project'), + void controller.startGameFromApprovedGdd('/tmp/planning-project'), }, - '把已批准 GDD 带到做游戏', + '直接用已批准 GDD 开始建造', ); } @@ -1552,75 +1536,71 @@ export function registerHomeProjectCreationTests() { ); }); - it('returns to the game entry with the approved GDD without starting creation', async () => { - const onCreateDraftAutomatically = vi.fn(async () => 'unexpected'); + it('starts an automatic game project from the approved GDD', async () => { + const automaticProjectPath = '/tmp/approved-gdd-game'; + const manifest = createGameCreationAppManifest( + 'approved-gdd-game', + '已批准 GDD 游戏', + ); + const gddContent = '# Fast GDD\n\n批准后的方案内容'; const invoke = vi.fn(async (command: string) => { if (command === 'read_local_project_file') { return { path: 'game/fast_gdd.md', absolutePath: '/tmp/planning-project/game/fast_gdd.md', - content: '# Fast GDD\n\n批准后的方案内容', + content: gddContent, + }; + } + if (command === 'create_automatic_local_game_project') { + return { + projectPath: automaticProjectPath, + manifestPath: `${automaticProjectPath}/.agent/manifest.json`, + manifest, + }; + } + if (command === 'upload_local_asset') { + return { + id: 'approved-gdd-asset', + localPath: 'assets/uploads/fast_gdd.md', + absolutePath: `${automaticProjectPath}/assets/uploads/fast_gdd.md`, + manifestPath: `${automaticProjectPath}/.agent/manifest.json`, }; } throw new Error(`unexpected invoke ${command}`); }); window.__TAURI__ = { core: { invoke } }; - render( - React.createElement(ApprovedGddHomeBridgeHarness, { - onCreateDraftAutomatically, - }), - ); + render(React.createElement(ApprovedGddStartHarness)); fireEvent.click( - screen.getByRole('button', { name: '把已批准 GDD 带到做游戏' }), + screen.getByRole('button', { name: '直接用已批准 GDD 开始建造' }), + ); + fireEvent.click( + screen.getByRole('button', { name: '直接用已批准 GDD 开始建造' }), ); - await screen.findByText('fast_gdd.md'); await waitFor(() => { - expect(screen.getByLabelText('创作想法').textContent).toContain( - '请按照附件中的已批准 GDD 开始建造这款游戏。', - ); + expect( + invoke.mock.calls.filter( + ([command]) => command === 'create_automatic_local_game_project', + ), + ).toHaveLength(1); }); - expect(screen.getByLabelText('创作想法').textContent).toContain( - '这份 GDD 已覆盖游戏定位与一句话概念、类型与美术方向、游戏支柱、核心循环、目标用户、平台与输入事实、MVP 系统、暂不纳入范围、创作者提示和原型验证项。', + expect(screen.getByLabelText('已进入自动游戏项目').textContent).toContain( + '请按照附件中的已批准 GDD 开始建造这款游戏。', ); - const creationTypes = screen.getByRole('group', { name: '创作类型' }); - expect( - within(creationTypes) - .getByRole('button', { name: '做游戏' }) - .getAttribute('aria-pressed'), - ).toBe('true'); - expect(onCreateDraftAutomatically).not.toHaveBeenCalled(); expect(invoke).toHaveBeenCalledWith('read_local_project_file', { projectPath: '/tmp/planning-project', relativePath: 'game/fast_gdd.md', commandId: 'file.read', }); - expect(invoke).not.toHaveBeenCalledWith( - 'create_automatic_local_game_project', - ); - expect(invoke).not.toHaveBeenCalledWith( - 'start_game_creator_supervisor_runtime_task', - expect.anything(), - ); - expect(invoke).not.toHaveBeenCalledWith( - 'chat_with_game_creator_direct_codex', - expect.anything(), - ); - - fireEvent.click(screen.getByRole('button', { name: '开启创作' })); await waitFor(() => { - expect(onCreateDraftAutomatically).toHaveBeenCalledTimes(1); + expect(invoke).toHaveBeenCalledWith('upload_local_asset', { + projectPath: automaticProjectPath, + fileName: 'fast_gdd.md', + mediaType: 'text/markdown', + bytes: Array.from(new TextEncoder().encode(gddContent)), + }); }); - const submittedDraft = onCreateDraftAutomatically.mock.calls[0]?.[0] as - | HomeDraft - | undefined; - expect(submittedDraft?.prompt).toContain( - '请按照附件中的已批准 GDD 开始建造这款游戏。', - ); - expect(submittedDraft?.attachments.map(({ file }) => file.name)).toEqual([ - 'fast_gdd.md', - ]); }); it('keeps the home composer out of chat mode while automatic project creation is pending', async () => { diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 63b19733e..efdc42c8a 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -14,12 +14,12 @@ - 关联文档:相关 PRD、技术文档、提交或 Issue ``` -## 2026-08-30 批准 GDD 通过首页做游戏入口回填参考附件 +## 2026-08-30 批准 GDD 直接进入做游戏链路 -- 背景:立项策划 GDD 批准后需要给用户一个进入做游戏的自然出口,但用户明确要求点击按钮时不要直接创建项目或启动完整制作流程。 -- 决策:批准态 GDD 交付行提供“做成游戏”按钮。点击后读取当前项目的权威 `game/fast_gdd.md`,回到首页并预选“做游戏”、把该文件作为 `text/markdown` 参考附件放入输入框;只有用户随后主动提交首页输入,才沿现有自动建项、附件导入和 Direct Codex 链路继续。该动作不复制原项目的 `approvedGddRef`、planning sidecar 或 approval receipt。 -- 影响范围:AGC 前端 GDD 交付行、WorkspaceLauncher 首页预填状态、首页 RichInputArea 一次性附件注入;不新增 HTTP API、SpacetimeDB schema、迁移、OpenAPI 或正式构建绑定。 -- 验证方式:批准态按钮、首页预填附件、回填过程不触发创建项目/启动 Runtime/Direct Codex 的 appSurface 回归;类型检查、编码检查和 `git diff --check` 通过。 +- 背景:立项策划 GDD 批准后需要给用户一个进入做游戏的自然出口,产品决策改为点击按钮后直接开始建造。 +- 决策:批准态 GDD 交付行提供“做成游戏”按钮。点击后读取当前项目的权威 `game/fast_gdd.md`,直接创建自动游戏工作区、导入 `text/markdown` 参考附件,并以固定建造指令自动启动 Direct Codex;不再回首页等待用户二次提交。该动作不复制原项目的 `approvedGddRef`、planning sidecar 或 approval receipt。 +- 影响范围:AGC 前端 GDD 交付行与现有自动建项/附件导入/Direct Codex 链路;移除首页 RichInputArea 的 GDD 一次性预填链路;不新增 HTTP API、SpacetimeDB schema、迁移、OpenAPI 或正式构建绑定。 +- 验证方式:批准态按钮直接创建工作区、导入附件、携带固定首条指令进入项目工作台且重复点击不重复创建的 appSurface 回归;类型检查、编码检查和 `git diff --check` 通过。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`。 --- @@ -7835,9 +7835,3 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - `autonomous-game-build` 中,manifest `dependencies` 只作为上下文,不阻塞 ready;代码、设计、美术、音频和发布任务允许并行启动,child 不依赖固定回执顺序或固定 run 身份才能推进。 - 任务最终状态不再提前绑定平台画布、preview、static smoke 或发布产物检查;这些内容不参与该档位的完成判定,也不会因缺失而重置已完成任务。父 run 在任务图进入终态后直接收束并回复。 - 本档位仍沿用现有项目根和工具权限边界;本次调整只解除流程编排与平台产物验收前置,不新增第二套任务系统。 - -## 2026-08-30 批准 GDD 回填建造指令 - -- “做成游戏”回首页时,除了把当前有效的 `game/fast_gdd.md` 作为 `text/markdown` 参考附件预填,还要在首页输入框预填固定建造指令。 -- 固定指令只说明 GDD 覆盖的栏目(定位、类型与美术、支柱、核心循环、目标用户、平台与输入、MVP 系统、范围边界、创作者提示、原型验证),不读取具体 GDD 做总结或缩写。 -- 预填动作仍不创建项目、不发送首页需求、不启动 Agent;用户主动提交后,固定文字与附件才作为现有做游戏链路的输入。 diff --git a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md index c6ba3a37e..02b245051 100644 --- a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md +++ b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md @@ -180,7 +180,7 @@ D9/D10 描述的「manifest ready-task 调度器在 Supervisor 下游启动策 | 构建引用 | `approvedGddRef {gddId, version, fingerprint}` | | 现有 design 组 UI 名 | `设计实现组`,替代原“策划 Agent”卡片名称 | | 新组件名 | `GDD 审批卡` | -| 固定入口动作 | 当前客户端为 `做成游戏`:回首页预填 `game/fast_gdd.md` 参考附件,不自动创建项目或启动 Runtime;用户再次提交首页输入后才进入做游戏链路 | +| 固定入口动作 | 当前客户端为 `做成游戏`:读取 `game/fast_gdd.md`,直接创建自动游戏工作区、导入参考附件并以固定建造指令启动 Direct Codex;不再回首页等待用户二次提交 | `approvalRequestId` 与 `responseId` 是两个不同的持久 ID:前者由 Runtime 在 GDD 提交前生成、进入不可变 GDD,一张审批卡终身不变;后者由 UI 在用户执行一次决定时生成,并在传输重试中复用。不得继续用含义不明的单个 `requestId` 同时承担两种职责。 @@ -278,8 +278,7 @@ flowchart TD SPLAN -->|"在自己的 runtime/session 上建 pending,向用户提问"| U SPLAN -->|"answersSha256 绑回 delivery,再发 continuation 委派
(最多 3 轮,受 clarification_round 上限约束,见第 23.5 节)"| PLANAGENT PLANAGENT --> GDD["不可变 GDD + approve receipt"] - GDD -->|"用户动作:做成游戏;回首页预填 game/fast_gdd.md"| HOME["首页做游戏入口
参考附件:fast_gdd.md
不自动创建/启动"] - HOME -->|"用户再次提交首页输入"| BUILD + GDD -->|"用户动作:做成游戏;读取并导入 game/fast_gdd.md"| BUILD["自动创建游戏工作区
参考附件:fast_gdd.md
固定建造指令"] U -.->|"直接开建"| BUILD BUILD --> DAG["现行 16 任务 DAG"] U --> CHAT @@ -1518,8 +1517,8 @@ type PlanningBaselineInput = - 仅首页“做方案”新建项目提交 `standard + project-supervisor-plan`(2026-08-13 按 D11 更正,旧值 `project-supervisor-plan-chat` 作废);“做游戏”和“做素材”保持 `autonomous-game-build` 直接开建。前端只提交 Supervisor 根 run 的身份,**不提交也不感知策划子 Agent**——后者由 Supervisor 在服务端通过 `agent.delegate` 派生,页面侧不得直接创建或引用它。 - 项目页新建、打开既有项目和 Godot 导入不新增“进入立项策划”入口,保持现行构建/打开语义;只有已存在 planning sidecar 或 active plan lineage 的项目恢复原有策划链路。 - 策划阶段聊天输入属于当前 run:有活跃决策卡/审批卡时,输入回到该卡片对应 action;无 active run 时才可创建新的 plan continuation。 -- approved 后显示“做成游戏”。点击后读取当前有效的 `game/fast_gdd.md`,回到首页并把它作为 `text/markdown` 参考附件预填到“做游戏”输入框,同时预填固定的建造指令:说明 GDD 覆盖的栏目,并要求 Agent 先阅读附件、按 GDD 的 MVP 范围开始建造;这段文字不根据具体 GDD 内容生成。该动作不创建新项目、不发送首页需求、不启动 `autonomous-game-build`。 -- 用户随后主动提交首页输入时,才沿现有做游戏链路创建新的自动项目、导入附件并启动 Direct Codex。这里的附件和预填文字都是本次建造的输入,不把原项目的 `approvedGddRef` 复制到新项目。 +- approved 后显示“做成游戏”。点击后读取当前有效的 `game/fast_gdd.md`,直接沿现有自动做游戏链路创建新的工作区、导入 `text/markdown` 参考附件,并以固定建造指令作为 `initialSupervisorMessage` 自动启动 Direct Codex;固定指令只说明 GDD 覆盖的栏目,不根据具体 GDD 内容生成总结。该动作不回首页等待二次提交,不把原项目的 `approvedGddRef` 复制到新项目。 +- 用户仍可在项目工作台继续补充需求;普通首页“做游戏”入口的手动提交行为保持不变。 ### 18.2 GDD 审批卡 @@ -1705,7 +1704,7 @@ receipt 永远压过 stale pending:一旦该版本存在有效 receipt,pendi | agent.db | 专用幂等 helper;same key conflict;日志达到普通容量、尾部截断与压缩后仍能补齐并保留决定记录 | | source/security | durable exact identity;三个 action tool(`file.read` / `file.list` / `plan.submit_gdd`)广告与执行;MCP 空且 webSearchEnabled=false;control functions 单列;tool-plan/batch/ledger/context/repair/completion 全部跳过 collaboration;plan retry 保留 source/profile;除 Runtime-owned submit 外的副作用工具拒绝 | | Prompt | **2026-08-13 按 D11 改写**:不新增 composition,Supervisor 根 run 沿用现役 supervisor composition、策划子 Agent 复用 `runtime` composition(见第 4.2 节);`decision-checkpoint` 请求 kind 随 D10 作废(见第 5.1 节)。仍冻结:3 轮/单题/固定选项;回答后设计解释与 prototype item;平台事实;恢复摘要;direct-out 条件 | -| frontend | 首页“做方案”目录提交与 Enter 自动创建均为 `standard + project-supervisor-plan`;“做游戏/做素材”均保持 `autonomous-game-build`;项目页新建/打开不首次注入 planning;stable approvalRequestId/responseId;busy;stale card;hydrate strict input/view;无目录空态;receipt 隐藏 stale pending;corrupt authority typed error;project open/reload/resume/submit/decision 刷新;recovery pending;批准 GDD 后“做成游戏”回首页预填 `fast_gdd.md` 且不启动创建/Runtime;用户再次提交后沿现有附件导入链路执行 | +| frontend | 首页“做方案”目录提交与 Enter 自动创建均为 `standard + project-supervisor-plan`;“做游戏/做素材”均保持 `autonomous-game-build`;项目页新建/打开不首次注入 planning;stable approvalRequestId/responseId;busy;stale card;hydrate strict input/view;无目录空态;receipt 隐藏 stale pending;corrupt authority typed error;project open/reload/resume/submit/decision 刷新;recovery pending;批准 GDD 后“做成游戏”直接创建自动工作区、导入 `fast_gdd.md` 并自动启动 Direct Codex;重复点击不重复创建;普通首页链路不回归 | | M2 integration | explicit approved/direct mode;锁内重验 receipt;ref 贯穿 task/run/completion/context;无 ref 非回归;恢复不换稿 | 关键强杀点逐项覆盖: @@ -1996,7 +1995,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`~`M1A-4`、`M1B-1` | `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、构建准入和下游完整构建不在本包范围 | | `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-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` | **已完成并合入 `feat/five_min_design`(落地 `0052a80da`,其后 ESLint 修正 `5b11a0530`)**:新增严格 `{projectPath}` hydrate command、`plan-gdd-state-view.v1` Rust read model、审批卡与独立 GDD 正文详情弹层;页面只消费 hydrate,决定 responseId 按审批请求/动作复用,`recoveryPending` 仅提供恢复重试;审批前置 pending 与错绑 session 继续 fail-closed。 | -| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | **2026-08-19 更正 `bf2185fba` 的错误入口映射**:仅首页“做方案”新项目以 `standard + project-supervisor-plan` 启动;“做游戏/做素材”保持 `autonomous-game-build`,不再展示额外“直接开建”按钮;项目页新建、打开和 Godot 导入不新增策划入口,仅恢复已有 planning lineage。阶段进度显示轮次 x/3、当前版本和状态徽章;实际项目总控页面挂载 hydrate/审批卡,并将 `project-planning` / 设计组展示名收口。**2026-08-30 补充:批准 GDD 后的“做成游戏”只回首页预填 `game/fast_gdd.md` 参考附件,不自动创建项目或启动 Runtime;仍未接入正式 `approvedGddRef` 构建绑定。** | +| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | **2026-08-19 更正 `bf2185fba` 的错误入口映射**:仅首页“做方案”新项目以 `standard + project-supervisor-plan` 启动;“做游戏/做素材”保持 `autonomous-game-build`,不再展示额外“直接开建”按钮;项目页新建、打开和 Godot 导入不新增策划入口,仅恢复已有 planning lineage。阶段进度显示轮次 x/3、当前版本和状态徽章;实际项目总控页面挂载 hydrate/审批卡,并将 `project-planning` / 设计组展示名收口。**2026-08-30 变更:批准 GDD 后的“做成游戏”直接创建自动工作区、导入 `game/fast_gdd.md` 并自动启动 Direct Codex;仍未接入正式 `approvedGddRef` 构建绑定。** | | `M1E` | 端到端与故障注入收口 | `M1D-2` | **已完成**:planning 覆盖审计与 submit 拒绝上限收口完成。`PLAN_INVALID_REQUEST` / Provider input 或候选 GDD 的 `PLAN_SIZE_LIMIT` 每 child run 最多 5 次 rejected observation,第 5 次在 observation durable 后终态失败;counter durable,重启不清零。若在第五条 rejected observation 落盘与终态失败之间崩溃,恢复入口会按 durable counter 直接终态失败,不请求第六次 Provider tool-plan。既有不可变 GDD/receipt 的超限读取及 lineage 版本已耗尽均改走 reconciliation,不误耗 Provider 重试额度。第 21 节已存在的三轮、续跑、receipt/replay、投影恢复及 hydrate 回归复核通过;不为“拼接已有单测”新增脆弱大 E2E。 | **2026-08-18 M1D 审查修复快照**:对 `14c00017c..bf2185fba` 做规格对照审查后,修复三条决定链路缺陷并补齐回归。① `decidePlanGdd` 的失败分支原来不 hydrate,命中后端任一 `PLAN_STALE_APPROVAL` 分支后卡片会停在已失效的 pending 身份上、`recoveryPending` 永不翻真导致「重试恢复」入口不渲染,现已按第 18.3 节在失败分支同样重灌(顺序钉死:`hydratePlanGddState` 入口会清空错误,必须先 hydrate 再写决定错误)。② responseId 复用键原为 `approvalRequestId:action`,不含 comment,违反第 13.2 节「改变 action/comment 必须换新 responseId」,现改为比对 `{action, comment}` 完整意图,判据方向为宁可多换不可少换。③ 第 18.2 节「`recoveryPending` 时不允许提交决定」原来只作用于三个触发按钮,已打开的评论弹层仍可提交,现已同门控并保留用户已输入内容。回归位于 `tests/appSurface/plan-gdd.suite.ts`,三条均经变异验证(逆转对应修复即变红);`appSurface.test.ts` 381 passed,`agc:typecheck`、ESLint、编码检查通过。其中锁错误回传绝对路径、阶段进度轮次差一格与 design 组展示名收口三条已于同日补修(见 decision-log 同日两条);仅 hydrate 身份校验与落盘投影修复的顺序一条单列后续,未并入。