修复AGC持续集成断言与空快照竞态

同步原生HTTP权限检查与当前更新器边界。
保持活动回合空快照引用稳定,修正异步测试与窗口发布断言。
补齐图集生成测试的显式切片参数及工具schema断言。
更新技术方案与空快照测试排障记录。
This commit is contained in:
2026-09-17 20:37:18 +08:00
parent b07483fc86
commit 17eb8b4e6a
8 changed files with 32 additions and 14 deletions
@@ -734,6 +734,9 @@ async fn background_agent_runtime_can_generate_platform_art_asset() {
"imageSize": "1K",
"assetKind": "art-spritesheet",
"assetLabel": "游戏首版核心美术素材",
"sliceMode": "grid",
"gridX": 2,
"gridY": 2,
"replaceExisting": false
}
}
@@ -934,6 +937,9 @@ async fn background_agent_runtime_can_generate_platform_art_asset() {
request_header(generation_request, "idempotency-key").expect("generation idempotency key");
assert!(uuid::Uuid::parse_str(&generation_idempotency_key).is_ok());
assert!(generation_request.contains(r#""source":"ai-game-creator-client""#));
assert!(generation_request.contains(r#""sliceMode":"grid""#));
assert!(generation_request.contains(r#""gridX":2"#));
assert!(generation_request.contains(r#""gridY":2"#));
assert_eq!(
canvas_requests
.iter()
@@ -6916,7 +6916,11 @@ fn agent_native_function_catalog_exposes_each_runtime_tool_with_core_schemas() {
"imageSize",
"assetKind",
"assetLabel",
"replaceExisting"
"replaceExisting",
"sliceMode",
"gridX",
"gridY",
"sliceCount"
])
);
assert_eq!(
@@ -45,7 +45,7 @@ export function useDirectActiveTurns({
* 换掉数组身份:所有依赖 `activeTurns` 的 effect 都会跟着重跑(窗口标题栏的活动项目
* 面板就是这么被反复重发布的)。这里只在内容真的变了才更新状态。
*/
const lastSnapshotSignatureRef = useRef<string>('');
const lastSnapshotSignatureRef = useRef<string>('[]');
const retryTimerRef = useRef<number | null>(null);
useEffect(() => {
@@ -112,7 +112,7 @@ export function useDirectActiveTurns({
useEffect(() => {
if (!enabled || !invoke) {
lastSnapshotSignatureRef.current = '';
lastSnapshotSignatureRef.current = '[]';
// 空态也要保持引用稳定:已经空了就不要再换一个新数组。
setActiveTurns((current) => (current.length === 0 ? current : []));
setSnapshotReadFailed((current) => (current ? false : current));
@@ -51,19 +51,23 @@ describe('useDirectActiveTurns', () => {
});
it('clears to a stable empty snapshot when the hook is disabled', async () => {
const invoke = vi.fn(
async () => [] as GameCreatorDirectActiveTurn[],
) as never;
let resolveSnapshot!: (turns: GameCreatorDirectActiveTurn[]) => void;
const snapshot = new Promise<GameCreatorDirectActiveTurn[]>((resolve) => {
resolveSnapshot = resolve;
});
const invoke = vi.fn(() => snapshot) as never;
const { result, rerender } = renderHook(
({ enabled }: { enabled: boolean }) =>
useDirectActiveTurns({ invoke, enabled, pollIntervalMs: 60_000 }),
{ initialProps: { enabled: true } },
);
await waitFor(() => {
expect(result.current.activeTurns).toEqual([]);
});
const emptySnapshot = result.current.activeTurns;
await act(async () => {
resolveSnapshot([]);
await snapshot;
});
expect(result.current.activeTurns).toBe(emptySnapshot);
rerender({ enabled: false });
expect(result.current.activeTurns).toBe(emptySnapshot);
});
@@ -84,8 +84,8 @@ it('真实窗口与工作台状态同步收敛,回调读取最新处理器且
await act(async () => {
await Promise.resolve();
});
// 无原生 invoke 时 active-turn Hook 会把初始快照归一为空数组一次
expect(publications).toHaveLength(2);
// 无原生 invoke 时保持初始快照,不额外发布相同状态
expect(publications).toHaveLength(1);
expect(cleanups).toBe(0);
expect(new Set(publications.map((item) => item.onOpenProject)).size).toBe(
1,
@@ -94,13 +94,13 @@ it('真实窗口与工作台状态同步收敛,回调读取最新处理器且
const latestOpen = vi.fn(async () => undefined);
homeProjectOverride.openProject = latestOpen;
rendered.rerender(view('更改显示名'));
expect(publications).toHaveLength(2);
expect(publications).toHaveLength(1);
act(() => openProject('/tmp/window-latest-project'));
expect(latestOpen).toHaveBeenCalledWith(
'/tmp/window-latest-project',
'open',
);
expect(publications).toHaveLength(2);
expect(publications).toHaveLength(1);
expect(cleanups).toBe(0);
rendered.unmount();
expect(cleanups).toBe(1);
@@ -1,5 +1,9 @@
# 踩坑与排障记录
## AGC 空快照测试必须等待请求完成
`waitFor(() => expect(activeTurns).toEqual([]))` 在 Hook 初始状态就能成功,不能证明首次异步读取已经完成。引用稳定性回归应显式控制 Promise 完成,并同时检查首次空响应与禁用后的引用;快照签名初值必须与初始空数组一致。窗口同步测试应验证未变化状态不重复发布,不能依赖一次多余的空态更新。
## 2026-09-17 AGC 输入盒的「推理档」弹层被祖先裁切:要放开裁切而不是挪弹层
- **现象**:窄窗口下(视口 ≤1000px 时右侧对话面板只有 280px 宽)点开输入盒右下角的「推理档」,弹层是个**空盒子**:档位文字(默认 / 低 / 中 / 高 / 最高)整片看不见,只剩一个方框。
@@ -12,6 +12,7 @@
## 资源画布交互与工作台状态同步
- 活动回合初始空快照、首次成功读取的空结果及禁用后的空态保持同一数组引用;快照签名初值与空态重置值均为 `[]`。无原生 invoke 的窗口测试只期待首次状态发布,异步空结果测试显式控制请求完成,不以初始空数组作为请求已完成的证据。
- 工作台向窗口标题栏发布正在运行的项目时,输入未变化不得形成重复发布与清理的渲染循环;打开项目动作始终使用当前工作台处理逻辑,退出工作台后清除其标题栏状态。
- 资源子画布(含「所有资源」)保留空白处左键框选、资源卡左键选中/拖动、触摸板双指平移及捏合缩放;右键按住空白处或资源卡拖动时平移画布,不改变资源选择与布局。中键和空格抓手继续可用。总览保留既有左键平移,并支持右键平移。
- 画布接管的右键手势不弹出原生菜单;输入框、媒体操作、工具条和独立浮层不被画布抢占。指针取消、捕获丢失或窗口失焦后终止平移,不能继续跟随指针。
-1
View File
@@ -2582,7 +2582,6 @@ function assertAiGameCreatorShellUserDevBoundary() {
JSON.stringify([
{ url: 'https://dev.genarrative.world/api/*' },
{ url: 'https://www.genarrative.world/api/*' },
{ url: 'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/*' },
{ url: 'https://*/api/*' },
{ url: 'http://localhost:*/*' },
{ url: 'http://127.0.0.1:*/*' },