f2245fb23d
对照 local-scripts/deisgn_agent 原型逐条比对后拉回五处分歧。原型 38 条 run 里 22 条走满 3 轮澄清,本仓库 GUI 实测一次都没到过第 3 轮。 1. header 从固定 8 字的轮号计数器「第N轮·关键决定」改回带主题的 「第N轮·当前要决定:<主题>」,决定台账的 topic 改从 header 取。12 字上限是 三条泳道共用的通用 user.input_request 常量,按 header 形状开策划分支——通用 问询今天能过的明天逐字照过,做游戏 / 做素材拿到的仍是 12 字。 2. 提问名额判据恢复原型三分法:「且影响首个可玩闭环」从「空白」的定义挪回提问 优先级,并恢复「空白或存疑」。此前的定义把「无从判断但不影响闭环」的字段整个 排除在空白之外,可问集合被闭合成 pillars + coreLoop 两项。 3. 有默认建议的字段从「一律先用默认建议,不占轮次」改回「优先用默认建议而不是 提问」——有默认不等于不能问。 4. 默认建议清单换回原型那五条(局长偏好、美术、成长、探索、构建)。摘掉 genre.fusion / targetUsers.coreUsers|preferences|referenceGames / outOfScope: 它们进清单等于把第三顺位「制作边界与 MVP」整条轴默认掉,出稿触发器③「剩余 空白都能由默认建议覆盖」随之在第 3 轮恒真。反幻觉那句按原型结构移到「低幻觉 与 GDD 约束」段,outOfScope 的兜底内容与该段已有的 MVP 范围句逐字重复。 5. 选项数 brief 写「2~3 个」(照通用常量生成)、Runtime 硬校验恰好 3、brief 下文 又写「固定三个选项」,三方打架。统一为恰好 3,裸 3 提成 PLAN_CLARIFICATION_OPTION_COUNT 让 brief 与解析器同源。label 分隔符集合按原型 ^A\s*[·•・::..\-] 从 4 个扩到 8 个,两处都只放宽不收紧。 守门两条:策划 header 的放宽只对「第N轮·」形状生效、同长度的通用 header 仍被 12 字 挡下;分隔符集合对着一份逐字来自原型正则的显式清单断言——遍历集合本身是空转的, 删一个就少测一个。 未动分歧 5/6/7(轴级封锁、内容级禁问、oneLiner 禁问)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
77 lines
2.2 KiB
TypeScript
77 lines
2.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
import { StrictMode } from 'react';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import type { AgentRuntimeUserInputRequest } from '../src/app/types';
|
|
import { AgentRuntimeUserInputCard } from '../src/features/agent-runtime/panels';
|
|
|
|
function clarificationRequest(): AgentRuntimeUserInputRequest {
|
|
return {
|
|
schemaVersion: 'agc.user_input_request.v1',
|
|
requestId: 'req-1',
|
|
agentId: 'project-supervisor',
|
|
taskId: 'task-1',
|
|
sessionId: 'session-1',
|
|
runId: 'run-1',
|
|
actionId: 'action-1',
|
|
status: 'pending',
|
|
questions: [
|
|
{
|
|
id: 'q1',
|
|
header: '第1轮·当前要决定:首版路线',
|
|
question: '这局游戏的重玩动力是什么?',
|
|
options: [
|
|
{ label: '分数驱动', description: '刷新纪录后重开' },
|
|
{ label: '成长驱动', description: '解锁新能力后重开' },
|
|
],
|
|
},
|
|
],
|
|
allowFreeform: true,
|
|
responseId: null,
|
|
requestedAt: 1,
|
|
updatedAt: 1,
|
|
};
|
|
}
|
|
|
|
describe('AgentRuntimeUserInputCard 澄清输入', () => {
|
|
it('手动输入自由回答不会让页面崩溃', () => {
|
|
render(
|
|
<StrictMode>
|
|
<AgentRuntimeUserInputCard
|
|
request={clarificationRequest()}
|
|
controlBusy={false}
|
|
/>
|
|
</StrictMode>,
|
|
);
|
|
|
|
const textarea = screen.getByLabelText(
|
|
'第1轮·当前要决定:首版路线 其他回答',
|
|
);
|
|
fireEvent.change(textarea, { target: { value: '玩家自己写的答案' } });
|
|
|
|
expect((textarea as HTMLTextAreaElement).value).toBe('玩家自己写的答案');
|
|
});
|
|
|
|
it('先选中选项再清空填充文本不会让页面崩溃', () => {
|
|
render(
|
|
<StrictMode>
|
|
<AgentRuntimeUserInputCard
|
|
request={clarificationRequest()}
|
|
controlBusy={false}
|
|
/>
|
|
</StrictMode>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('分数驱动'));
|
|
const textarea = screen.getByLabelText(
|
|
'第1轮·当前要决定:首版路线 其他回答',
|
|
) as HTMLTextAreaElement;
|
|
expect(textarea.value).toBe('分数驱动');
|
|
|
|
fireEvent.change(textarea, { target: { value: '' } });
|
|
expect(textarea.value).toBe('');
|
|
});
|
|
});
|