批准 GDD 预填建造指令
Project CI / Repository checks (pull_request) Successful in 3m21s
Project CI / Frontend tests (pull_request) Successful in 3m48s
Project CI / Backend tests (pull_request) Failing after 4m46s
Project CI / Native shell tests (pull_request) Failing after 5m59s

新增固定 GDD 建造说明并与参考附件联合预填
保持点击做成游戏不自动创建项目或启动 Agent
补充首页提交链路回归测试并同步技术方案与决策记录
This commit is contained in:
2026-08-30 09:05:49 +00:00
parent c1b27c9f71
commit 94bc96c610
7 changed files with 89 additions and 21 deletions
@@ -78,7 +78,8 @@ export function WorkspaceLauncherShell({
setAgentResults: setActiveProjectAgentResults,
resetLauncherHomeDraft,
homePrefillAttachments,
clearHomePrefillAttachments,
homePrefillPrompt,
clearHomePrefill,
prepareHomeGameFromApprovedGdd,
createHomeDraftAutomatically,
openProject,
@@ -263,7 +264,7 @@ export function WorkspaceLauncherShell({
currentUser={currentUser}
onLogout={() => {
resetLauncherHomeDraft();
clearHomePrefillAttachments();
clearHomePrefill();
accountWallet.resetWalletBalance();
onLogout();
}}
@@ -298,7 +299,8 @@ export function WorkspaceLauncherShell({
recentProjectRows={recentProjectRows}
onCreateDraftAutomatically={createHomeDraftAutomatically}
initialAttachments={homePrefillAttachments}
onInitialAttachmentsApplied={clearHomePrefillAttachments}
initialPrompt={homePrefillPrompt}
onInitialPrefillApplied={clearHomePrefill}
onProjectsOpen={() => setLauncherView('projects')}
onProjectOpen={(path) => {
setProjectPath(path);
@@ -49,6 +49,14 @@ type UseHomeProjectCreationOptions = {
rememberRecentWorkspace: (projectPath: string) => void;
};
const APPROVED_GDD_HOME_PROMPT = [
'请按照附件中的已批准 GDD 开始建造这款游戏。',
'',
'这份 GDD 已覆盖游戏定位与一句话概念、类型与美术方向、游戏支柱、核心循环、目标用户、平台与输入事实、MVP 系统、暂不纳入范围、创作者提示和原型验证项。',
'',
'请先阅读并理解附件中的 fast_gdd.md,以它作为本次建造的主要依据,优先实现其中 MVP 范围内的可运行游戏原型。',
].join('\n');
export function useHomeProjectCreation({
setStatus,
setLauncherView,
@@ -84,6 +92,7 @@ export function useHomeProjectCreation({
const [homePrefillAttachments, setHomePrefillAttachments] = useState<
HomeAttachmentDraft[]
>([]);
const [homePrefillPrompt, setHomePrefillPrompt] = useState('');
function validateProjectPath(nextProjectPath: string) {
const trimmedProjectPath = nextProjectPath.trim();
@@ -124,6 +133,7 @@ export function useHomeProjectCreation({
resetLauncherHomeDraft();
setHomeCreationType('game');
setHomePrefillPrompt(APPROVED_GDD_HOME_PROMPT);
setHomePrefillAttachments([
{
id: `approved-gdd-${Date.now().toString(36)}`,
@@ -644,7 +654,11 @@ export function useHomeProjectCreation({
pendingNonEmptyProject,
resetLauncherHomeDraft,
homePrefillAttachments,
clearHomePrefillAttachments: () => setHomePrefillAttachments([]),
homePrefillPrompt,
clearHomePrefill: () => {
setHomePrefillAttachments([]);
setHomePrefillPrompt('');
},
prepareHomeGameFromApprovedGdd,
createHomeDraft,
createHomeDraftAutomatically,
@@ -29,7 +29,8 @@ type RichInputAreaProps = {
onChange: (value: Draft) => void;
onEnter: () => void;
initialAttachments?: readonly HomeAttachmentDraft[];
onInitialAttachmentsApplied?: () => void;
initialPrompt?: string;
onInitialPrefillApplied?: () => void;
children?: React.ReactNode;
};
const INSERT_ATTACHMENTS_COMMAND: LexicalCommand<HomeAttachmentDraft[]> =
@@ -107,13 +108,18 @@ function EditorPlugins({
onChange,
onEnter,
initialAttachments = [],
onInitialAttachmentsApplied,
initialPrompt = '',
onInitialPrefillApplied,
}: Pick<
RichInputAreaProps,
'onChange' | 'onEnter' | 'initialAttachments' | 'onInitialAttachmentsApplied'
| 'onChange'
| 'onEnter'
| 'initialAttachments'
| 'initialPrompt'
| 'onInitialPrefillApplied'
>) {
const [editor] = useLexicalComposerContext();
const initialAttachmentsAppliedRef = useRef(false);
const initialPrefillAppliedRef = useRef(false);
useEffect(
() =>
@@ -198,16 +204,29 @@ function EditorPlugins({
useEffect(() => {
if (
initialAttachmentsAppliedRef.current ||
initialAttachments.length === 0
initialPrefillAppliedRef.current ||
(initialPrompt.trim().length === 0 && initialAttachments.length === 0)
) {
return;
}
initialAttachmentsAppliedRef.current = true;
editor.dispatchCommand(INSERT_ATTACHMENTS_COMMAND, [...initialAttachments]);
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());
onInitialAttachmentsApplied?.();
}, [editor, initialAttachments, onChange, onInitialAttachmentsApplied]);
onInitialPrefillApplied?.();
}, [
editor,
initialAttachments,
initialPrompt,
onChange,
onInitialPrefillApplied,
]);
return (
<OnChangePlugin
@@ -281,7 +300,8 @@ export default function RichInputArea(props: RichInputAreaProps) {
onChange={props.onChange}
onEnter={props.onEnter}
initialAttachments={props.initialAttachments}
onInitialAttachmentsApplied={props.onInitialAttachmentsApplied}
initialPrompt={props.initialPrompt}
onInitialPrefillApplied={props.onInitialPrefillApplied}
/>
</LexicalComposer>
);
@@ -105,7 +105,8 @@ type HomeViewProps = {
onProjectOpen: (path: string) => void;
onProjectPick: () => void;
initialAttachments?: readonly HomeAttachmentDraft[];
onInitialAttachmentsApplied?: () => void;
initialPrompt?: string;
onInitialPrefillApplied?: () => void;
};
export default function HomeView({
@@ -117,7 +118,8 @@ export default function HomeView({
onProjectOpen,
onProjectPick,
initialAttachments = [],
onInitialAttachmentsApplied,
initialPrompt = '',
onInitialPrefillApplied,
}: HomeViewProps) {
const homeCreationType = useLauncherHomeDraftStore(
(state) => state.creationType,
@@ -240,7 +242,8 @@ export default function HomeView({
placeholder={activeCreationType.placeholder}
onChange={setHomeRichText}
initialAttachments={initialAttachments}
onInitialAttachmentsApplied={onInitialAttachmentsApplied}
initialPrompt={initialPrompt}
onInitialPrefillApplied={onInitialPrefillApplied}
onEnter={() => {
void createFromHome();
}}
@@ -61,7 +61,8 @@ function ApprovedGddHomeBridgeHarness({
onProjectOpen: () => undefined,
onProjectPick: () => undefined,
initialAttachments: controller.homePrefillAttachments,
onInitialAttachmentsApplied: controller.clearHomePrefillAttachments,
initialPrompt: controller.homePrefillPrompt,
onInitialPrefillApplied: controller.clearHomePrefill,
});
}
@@ -1575,6 +1576,14 @@ export function registerHomeProjectCreationTests() {
);
await screen.findByText('fast_gdd.md');
await waitFor(() => {
expect(screen.getByLabelText('创作想法').textContent).toContain(
'请按照附件中的已批准 GDD 开始建造这款游戏。',
);
});
expect(screen.getByLabelText('创作想法').textContent).toContain(
'这份 GDD 已覆盖游戏定位与一句话概念、类型与美术方向、游戏支柱、核心循环、目标用户、平台与输入事实、MVP 系统、暂不纳入范围、创作者提示和原型验证项。',
);
const creationTypes = screen.getByRole('group', { name: '创作类型' });
expect(
within(creationTypes)
@@ -1598,6 +1607,20 @@ export function registerHomeProjectCreationTests() {
'chat_with_game_creator_direct_codex',
expect.anything(),
);
fireEvent.click(screen.getByRole('button', { name: '开启创作' }));
await waitFor(() => {
expect(onCreateDraftAutomatically).toHaveBeenCalledTimes(1);
});
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 () => {
@@ -7835,3 +7835,9 @@ 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;用户主动提交后,固定文字与附件才作为现有做游戏链路的输入。
@@ -1518,8 +1518,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` 参考附件预填到“做游戏”输入框该动作不创建新项目、不发送首页需求、不启动 `autonomous-game-build`
- 用户随后主动提交首页输入时,才沿现有做游戏链路创建新的自动项目、导入附件并启动 Direct Codex。这里的附件是参考材料,不把原项目的 `approvedGddRef` 复制到新项目。
- approved 后显示“做成游戏”。点击后读取当前有效的 `game/fast_gdd.md`,回到首页并把它作为 `text/markdown` 参考附件预填到“做游戏”输入框,同时预填固定的建造指令:说明 GDD 覆盖的栏目,并要求 Agent 先阅读附件、按 GDD 的 MVP 范围开始建造;这段文字不根据具体 GDD 内容生成。该动作不创建新项目、不发送首页需求、不启动 `autonomous-game-build`
- 用户随后主动提交首页输入时,才沿现有做游戏链路创建新的自动项目、导入附件并启动 Direct Codex。这里的附件和预填文字都是本次建造的输入,不把原项目的 `approvedGddRef` 复制到新项目。
### 18.2 GDD 审批卡