Merge branch 'master' into fix/chat-status-lost
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled

This commit is contained in:
2026-09-18 00:31:44 +08:00
83 changed files with 6505 additions and 738 deletions
@@ -2,6 +2,20 @@
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。
## 2026-09-18 Provider 瞬态重试次数严格按设置执行(游戏开发 Agent 与策划 Agent 不再被档位收进区间)
- 背景:AGC 客户端此前把 `agentLlm.<agent>.maxRetries` 按运行档位重新收进固定区间——`autonomous-game-build` 档位(自主构建的游戏开发 Agent 及其继承档位的专业子 Agent)被抬到 12~16,`standard` 档位(含立项策划入口的策划 Agent 与普通 Agent 对话)被压到最多 3;瞬态分类里的上游 400 还在同一预算上再收窄到 2 次。现场把 `maxRetries` 设成 5 时,游戏开发与策划两条链路都不按设置执行。
- 决策:删除这三处区间限制,`maxRetries` 严格等于允许的物理重试次数(显式 `0` 仍表示不重试)。运行档位只保留「上游 400 是否算瞬态错误」的判定(autonomous 档位才重试 400),不再改写次数:`AGENT_RUNTIME_PROVIDER_TRANSIENT_RETRY_LIMIT`、`AGENT_RUNTIME_AUTONOMOUS_PROVIDER_TRANSIENT_RETRY_FLOOR`、`AGENT_RUNTIME_AUTONOMOUS_PROVIDER_TRANSIENT_RETRY_LIMIT`、`AGENT_RUNTIME_AUTONOMOUS_PROVIDER_UPSTREAM_400_RETRY_LIMIT` 四个常量与 `game_creator_agent_runtime_provider_transient_max_retries_at` 一并删除,换成返回 `{max_retries, retry_upstream_400}` 的 `game_creator_agent_runtime_provider_transient_retry_policy_at`。
- 边界:只改重试次数的来源。瞬态错误分类(`timeout / connectivity / transport / 408 / 429 / 5xx / empty-response / deserialize / stream-unavailable`,以及 autonomous 档位下的 400)、`retryBackoffMs` 指数退避(仍封顶 30s)、durable retry sidecar / lifecycle / `-transient-N` slot 身份、cancel / steer / goal 门禁、耗尽后的 reconciliation 口径与「泥点不足不重试」都不变;前端设置面板与 `agentLlm.<agent>.maxRetries` 契约不变。
- 验证方式:`provider_transient_retry_` 7 项中重写后的档位用例与 upstream-400 用例通过(断言 `maxRetries` 直取设置值、400 与其它瞬态共用同一预算),`provider_retry_` 其余 26/28 通过;该组 2 项(`provider_transient_retry_transport_failure_closes_then_stable_retry_succeeds`、`provider_transient_retry_backoff_is_exponential_and_capped_at_thirty_seconds`)与 `provider_retry_waiting_final_reply_*` 2 项在本机改动前后同为失败(`stash` 基线复跑确认,现象是等待自动重试唤醒超时)。本机串行全量套件另有既有环境失败(`tempfile::tempdir()` 归属校验、缺少 npm 构建产物、Windows 启动失败 MessageBox 阻塞 `startup_log_slot_fail_without_path...`);抽查其中 5 项在 `stash` 基线上同样失败,与本次改动无关。仓库 `cargo fmt --check`、`npm run check:encoding`、`git diff --check` 通过。
- 关联文档:[AI游戏创作智能体App实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、[踩坑记录](pitfalls.md)。
## 2026-09-17 AGC 抠图提交使用远端画布项目身份
- 背景:AGC 已通过本地项目 ID 建立并持久化本地项目到主站远端画布项目的绑定,但 `agc_remove_background` 提交请求仍把本地 `manifest.project_id` 放入 `projectId`;`assetFolderId` 已使用远端素材目录 ID。主站因此按项目不存在或不属于当前账号返回 404,主站抠图和 BgFilter 本身均正常。
- 决策:抠图请求及工具回执统一使用 `prepare_external_canvas_generation_context` 返回的远端 `context.project_id`;本地 manifest 项目 ID 只用于绑定键和本地状态,不得作为主站业务请求的 `projectId`。
- 验证:客户端定向 Rust 测试、格式、编码和 diff 检查通过;未修改主站路由或 BgFilter。
## 2026-09-17 图集切分模式改为显式声明
## 2026-09-17 DirectProject 首屏历史锚点只认订阅回执的 lastCompletedItemId
@@ -32,6 +46,22 @@
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`(上限与文案的唯一口径)、`agent/direct_tool_bridge.rs`(按 kind 判定与未登记源资源提示)、`agent/direct_tools_mcp.rs`(schema 与校验)、`resources/agc-skills/agc-client-projection/**` 与清单指纹(version `2026-08-26.18`)。**未改** `/api/external/v1` 契约与 OpenAPI、SpacetimeDB schema、前端 TS 侧 `resourceEditPromptMaxLength` 数字、客户端 UI 行为。
- 验证方式:新增 `tool_prompt_limits_agree_with_the_client_authority`(四个 kind 的 schema 上限、MCP 校验与客户端权威口径同数字,超限文案带真实上限)、`bridge_resource_prompt_limits_follow_the_client_authority`(工具桥侧同类门禁,含图片编辑的 32000 边界)、`edit_image_tool_reaches_the_platform_image_edit_route` 与 `background_music_tool_reaches_the_platform_audio_route`(MCP 工具层 → 真实工具桥 → 假平台,断言 `/api/editor/images/edits` 与 `/api/editor/audios/background-music/generations` 的路径、Bearer、Idempotency-Key、正文与派生资源落盘,图片编辑正文不得回填 assetKind)、`background_music_prompt_over_the_limit_is_rejected_before_any_bridge_call`(超限在桥请求之前失败)、`unregistered_source_reports_the_registration_follow_up_tools`;`agent::direct_tools_mcp` 22 passed、`agent::skill_pack` 4 passed、`agent::direct_tool_bridge` 17 passed(7 条本机既有失败见下)、`npm run agc:skill-pack:check` 与 `skill-pack:test` 通过。本机 `tempfile::tempdir()` 归属校验失败导致的既有用例(`project::resource_editor` 45 条、`agent::direct_tool_bridge` 7 条)在本轮改动前后**同为失败**(stash 基线复跑确认),与本次无关。
- 关联文档:[AI游戏创作智能体App实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、[踩坑记录](pitfalls.md)。
## 2026-09-17 资源画布支持引擎资源只读预览
- 背景:Cocos Creator 工程里已有的引擎资源(模型、动画、预制体、材质、图集、压缩纹理…)此前在发现层就止步:`.glb` / `.prefab` / `.anim` / `.texture` 等扩展名既不可登记,也不进资源画布,工程导入后画布上只看得到位图、音频与脚本。
- 决策(范围):本轮只做**只读预览**。引擎资源可以被发现、登记进 manifest、进入资源画布并按类型出预览;不承接编辑、派生、生成与回写,也不解码引擎私有容器(`.texture` / `.cubemap` / `.rt` / `.skel` / `.dbbin` / `.psd` / `.exr` / `.pcm` 只出类型卡)。
- 决策(契约):**不新增 manifest 契约字段、不新增 canonical kind、不新增画布分类轴**。引擎资源复用既有 kind(模型/场景/预制体/地形 → `scene`,动画 → `character-animation`,材质/特效 → `code`,图集与容器 → `document`,图像容器 → `image`,裸 PCM → `audio`),避免 `deny_unknown_fields` 让旧客户端读不出整份 manifest;引擎语义由**类型角标**(模型 / 动画 / 材质 / 图集 / 纹理…)表达,不复用「图片 / 文档」。
- 决策(卡面与读取):新增三个卡面分支 —— `model`(`.glb` / `.gltf` / `.fbx`,由单例 WebGL 渲染器出缩略图,整页只保留一个 WebGL 上下文)、`structured`(Cocos 序列化资源的结构摘要,非 UTF-8 变体降级成类型卡而不是报错)、`binary`(不发起任何读取,不占预览读取槽)。图像容器(`.tga` / `.tif` / `.tiff` / `.hdr`)先在原生侧转码成 PNG,再走既有图片预览链路。
- 边界:`.meta` 等引擎导入侧车文件仍然只可发现、不可登记;发现层新增 `model` / `binary` 两个**发现类别**(不是 manifest kind)。多文件 glTF(外部 `.bin` / 贴图)与超限模型降级成类型卡;预览管线既有语义(可见性门禁、3 槽并发、LRU 预算、取消与重试口径)不变。
- 决策(发现过滤):引擎工程的 `library/` / `temp/` / `profiles/` / `local/` 不再进发现结果,判定收窄为「工程根直接子目录 + 当前目录确实是 Cocos Creator 工程(`package.json.creator.version` + `assets/`)」。**不放进全局跳过表**:这些名字在别的工程里可能是真实源码目录。过滤落在唯一一份目录遍历(`list_local_project_files_at`)上,因此 Agent 发现、前端资源树与提示词里的未登记清单同步生效;项目快照 / 版本指纹 / 检查点的语义本轮不动。
- 决策(模型上限与缓存键):模型预览字节上限从 16 MiB 放宽到 32 MiB,与通用媒体预览取同一上限(base64 载荷约 43 MiB);超过上限仍是类型卡,不做半渲染。缩略图缓存键改用**稳定身份**(资源身份 + 路径 + 字节数)而不是 blob URL:预览缓存淘汰后重读同一模型不会重新解析 + 重新渲染。要再往上放宽,必须先把预览载荷换成 Tauri 原始字节通道。
- 决策(模型放大预览):模型卡可以在工具条打开「3D 预览」独立浮层,浮层内是**交互式视角**(OrbitControls:左键旋转 / 右键或中键平移 / 滚轮缩放 / 复位视角),与三维建模软件同一套操作习惯。加载 / 取景 / 释放三条口径抽到共用模块 `resourceModelScene`,缩略图与浮层不许各写一套;画布上的卡片仍然是静态缩略图并继续共用**唯一**一个 WebGL 上下文,只有打开浮层时才新建交互式上下文,关闭即 dispose。浮层仍是只读预览:不写 manifest、不参与编辑与派生。
- 验证:`cargo check`、`cargo fmt --check` 通过;`cargo test … cocos` 9 条通过(发现分类 / 登记 / 提示词投影 / 插件门禁);`cargo test … resource_inspect::tests` 7 条通过(含结构化预览与二进制降级、TGA→PNG 转码、模型签名判定);`cargo test … agent_asset_import_tests` 9 条通过;生成目录过滤用例通过(引擎工程过滤、非引擎工程不过滤);`tests/resourceCocosPreviewContract.test.tsx` 7 条通过(含模型卡渲染不可用时的降级、稳定缓存键);`npx vitest run apps/ai-game-creator-shell/tests/resource apps/ai-game-creator-shell/tests/project` 52 文件 / 543 用例通过;`appSurface.test.ts` 450 通过 / 20 跳过;app `tsc --noEmit`、`npm run check:encoding`、`git diff --check`、`npm run check:doc-index` 通过。
- 真机验收(2026-09-17 补):在真实客户端内打开一个含模型 / 动画 / 序列化资源 / TGA / 引擎容器的 Cocos 夹具工程,模型卡出三维缩略图、序列化资源出结构摘要、TGA 出转码后的真实图片、引擎容器出类型卡;同现场 `list_local_project_files` 对根级 `library/` / `temp/` / `profiles/` 返回 0 条、`assets/library/` 正常列出。证据见里程碑文档「证据要求」。
- 未验证:本机其余仍用 `tempfile::tempdir()` 的既有 Rust 用例继续被 `Windows 安全对象不属于当前用户` 阻断(与本决策无关;根因与手工夹具相同,已记入 `pitfalls.md`)。
- 关联文档:`docs/project-memory/plans/【里程碑】资源画布支持引擎资源预览-2026-09-17.md`。
## 2026-09-16 抠图模式与背景色契约
- External v1 抠图和 AGC `agc_remove_background` 支持 `complex`(语义分割识别前景)与 `flat`(纯色背景抠图);明确纯色背景优先 flat,模式缺省仍为 complex,主站前端保持现有行为。
@@ -8216,8 +8246,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
## 2026-08-24 AGC Direct 抠图语义工具
- 决策:将 External v1 `/api/external/v1/editor/images/background-removals` 通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;External v1 负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
- 决策:将抠图能力通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;普通登录态使用账号鉴权的 `/api/editor/images/background-removals`,ExternalDeveloper 模式使用 External v1 `/api/external/v1/editor/images/background-removals`。客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
- 约束:异步结果与恢复语义以本文「2026-09-17 AGC 抠图接入本地资源编辑恢复闭环」决策为准。不允许模型自行构造源 URL 或在不确定提交后更换请求身份;两种路由都接收客户端稳定幂等身份,External v1 继续负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复
@@ -8870,3 +8900,38 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 边界:Deploy 阶段在远端 dev / release agent 执行,不受该上限约束。调整只动这两处:`systemctl set-property / revert jenkins.service`、`docker update --cpus=<n> gitea-runner` 加同步 compose(备份 `/opt/gitea-stack/compose.yml.bak-<时间戳>`)。
- 验证:限速后 `Genarrative-Full-Build-And-Deploy` #289 / #290 SUCCESS;采样期 Jenkins 峰值 10.2~10.5 核、限流不足 2s(可忽略),runner 峰值 12.07 核且持续出现 throttling,整机回落到 2.6%~19.8%。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。
## 2026-09-17 AGC 抠图接入本地资源编辑恢复闭环
- 背景:`agc_remove_background` 原先只提交 `/api/editor/images/background-removals` 并返回 `queued`,没有轮询远端任务、下载完成媒体或写入本地 manifest;BgFilter 已成功处理但 Agent 因此永远只能看到受理回执。
- 决策:抠图作为 `LocalProjectResourceEditKind::BackgroundRemoval` 接入现有资源编辑账本,模式和背景色写入 operation 身份;提交后复用同一套轮询、结果下载、staging、manifest 提交和恢复逻辑。已有账本优先恢复,禁止在未知结果时换 operation/idempotency 重发。
- 边界:主站异步队列、BgFilter 和 SpacetimeDB schema 不变;Agent 只获得本地完成资源和安全身份投影,不接触内部 worker 或凭据。
- 恢复:已受理任务中断后按原 operation 续查;提交结果不确定时保留账本并人工对账。升级前无账本的 queued 回执不自动迁移或重发,已有远端成果通过正式素材导入恢复。
- 展示:恢复面板按账本显示抠图模式,平面背景模式同时显示已记录的自动背景色或颜色值,缺失字段不推断默认值,帮助区分同名待处理任务。
## 2026-09-17 Jenkins 公网入口 jenkins.genarrative.world 复用 router 反向隧道口径
- 背景:Jenkins controller 实际与 Gitea 同机运行在 `genarrative-station`(`jenkins.service`,`--httpPort=8080 --prefix=/jenkins`,`JENKINS_HOME=/var/lib/jenkins`),此前只有内网入口 `http://192.168.35.82:8080/jenkins/`;`router.genarrative.world` 已有「dev Nginx → dev loopback → station 反向隧道」的成熟口径。
- 决策:沿用 router 口径,不新增网关组件。`genarrative-station` 的 `gitea-reverse-tunnel.service` 增加 `-R 127.0.0.1:18085:127.0.0.1:8080`(dev loopback `18085` → station Jenkins `127.0.0.1:8080`);dev 新增 `/etc/nginx/conf.d/jenkins.genarrative.world.conf`:`80` 只做 ACME webroot 与 `301`,`443` 用 Certbot 证书反代 `http://127.0.0.1:18085` 并保留 `Upgrade` / `X-Forwarded-*`;证书按 router 口径用 `certbot certonly --webroot -w /var/www/html -d jenkins.genarrative.world --renew-hook 'systemctl reload nginx'` 申请。
- 路径口径:Jenkins 固定 `--prefix=/jenkins`,域名根路径 `302` 到 `https://jenkins.genarrative.world/jenkins/login`,`/jenkins` 补斜杠,其余未带前缀路径 `302` 到 `/jenkins$request_uri`;证书不复制到 Pingora 私有目录,公网 `80/443` 仍由 dev Nginx 监听。
- 边界:本次只暴露 HTTP/HTTPS UI,`slaveAgentPort` 保持 `-1`(agent 继续由 Jenkins 用 SSH launcher 连 dev / release),不改 Jenkins `jenkinsUrl` 与鉴权策略;Jenkins 登录页因此进入公网可达面,访问控制继续依赖 Jenkins 自身账号体系。
- 验证:dev `nginx -t` 与 `systemctl reload nginx` 通过;`curl -sI https://jenkins.genarrative.world/` 返回 `302 /jenkins/login`、`/jenkins/login` 返回 `200`、登录页静态资源 `200`、`http://` 入口 `301`;Let's Encrypt 证书 `CN=jenkins.genarrative.world` 到期 `2026-12-16`;公网探测 `82.157.175.59` 仍只开放 `80/443/22`。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。
## 2026-09-17 预览部署控制面公网入口 build.genarrative.world
- 背景:多人内网预览控制面(`preview-deployer-server` + `shared/Genarrative-Preview-Deployer` Job)此前只在内网 `http://192.168.35.82/build/` 提供,2026-08-15 决策明确「不配置公网域名」;本次要求给它加公网入口。
- 决策:沿用 router / Jenkins 同一口径新增 `build.genarrative.world` 作为控制面公网入口,并把 `preview.genarrative.world` 作为 `*.preview.genarrative.world` 规划的父域名先落一张落地页(实例本身仍只在内网)。station `gitea-reverse-tunnel.service` 增加 `-R 127.0.0.1:18086:127.0.0.1:8410`;dev 新增 `/etc/nginx/conf.d/build.genarrative.world.conf`(`80` ACME+`301`,`443` 把 `/build/`、`/api/preview-deployer/` 反代到 `127.0.0.1:18086`,`/` 跳 `/build/`,公网侧 `proxy_cookie_flags ~ secure`)与 `/etc/nginx/conf.d/preview.genarrative.world.conf`(单域名证书 + 落地页)。
- 白名单:控制面按 `Host` 精确匹配、对非 GET 的 `/api/*` 精确匹配 `Origin`,因此同步改为 `GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_HOSTS=192.168.35.82,build.genarrative.world`、`GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_ORIGINS=http://192.168.35.82,https://build.genarrative.world`;`GENARRATIVE_PREVIEW_DEPLOYER_SECURE_COOKIE` 保持 `false`(内网 HTTP 入口继续可用),公网 cookie 的 `Secure` 由 dev nginx 强制。重启 `genarrative-preview-deployer.service` 会清空内存会话,内网用户需重新输入口令。
- 边界:本次只暴露控制面(触发/查看构建、卸载),预览实例不暴露;`preview.genarrative.world` 没有通配记录,实例地址仍是内网 `http://192.168.35.82:84xx`,控制面页面展示的 `webUrl` 也仍是内网地址。要变成公网实例地址,需要 `*.preview.genarrative.world` 通配证书(只能 DNS-01)、station 侧按 Host 分发和页面 URL 口径改造。
- 验证:`nginx -t` 与 reload 通过;`https://build.genarrative.world/` `302 → /build/`、`/build/` `200`、SPA 资源 `200`、`/api/preview-deployer/session` 返回 `{"authenticated":false}`;错误或缺失 `Origin` 的 POST `403`、错误口令 `401`、字段名不符 `422`;两个新域名证书到期 `2026-12-16`;内网 `Host: 192.168.35.82` 仍 `200`、未知 Host `403`;jenkins / dev / git 入口回归正常。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)、[Jenkins容器预览部署控制面技术方案](../../technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md)。
## 2026-09-17 预览控制面增加公网预览地址口径(代码已实现,待随控制面发布)
- 背景:控制面页面此前只展示内网地址 `http://192.168.35.82:<webPort>`;公网通配域名 `*.preview.genarrative.world` 已解析到 dev,需要页面能显示对应的公网入口。
- 决策:公网地址由控制面自己派生,不接受 Jenkins 产物或状态文件提供的任意地址。`preview-deployer-server` 新增可选配置 `GENARRATIVE_PREVIEW_DEPLOYER_WEB_DOMAIN`(如 `preview.genarrative.world`,只接受小写字母、数字、短横线和点号,不带协议与端口),并在公开 DTO 新增 `webPublicUrl`:仅当记录已有内网 `webUrl` 时取 `https://<instanceId>.<WEB_DOMAIN>`(实例 ID 仍由分支派生,形如 `preview-<16位hex>`),卸载时与 `webUrl` / `webPort` 一起清空;状态文件里的旧值在加载时被重新派生覆盖。
- 前端:`apps/preview-deployer-web` 在存在公网地址时把「打开公网预览」作为主入口,内网地址降级为次级链接;未配置时行为与之前一致。
- 上线依赖(本次未完成):`*.preview.genarrative.world` 通配证书(Let's Encrypt 通配只能走 DNS-01,域名在 DNSPod,certbot 无官方插件,需要 DNSPod API Token 配合 acme.sh)、station 侧按 Host 分发到 `84xx` 端口、dev 通配 vhost 与隧道;控制面本体需在 station 用 `scripts/deploy/preview-deployer-install.sh` 重建发布。
- 验证:`cargo test -p preview-deployer-server`(13 项)、`apps/preview-deployer-web` vitest(13 项,含新增公网地址用例)、`npx tsc --noEmit`、`npm run preview-deployer:web:build`(`PREVIEW_DEPLOYER_WEB_BASE=/build/`)、`npm run check:preview-deployer`、`npm run check:encoding`、`git diff --check` 全部通过。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)、[Jenkins容器预览部署控制面技术方案](../../technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md)。
@@ -1,5 +1,13 @@
# 踩坑与排障记录
## AGC 空快照测试必须等待请求完成
`waitFor(() => expect(activeTurns).toEqual([]))` 在 Hook 初始状态就能成功,不能证明首次异步读取已经完成。引用稳定性回归应显式控制 Promise 完成,并同时检查首次空响应与禁用后的引用;快照签名初值必须与初始空数组一致。窗口同步测试应验证未变化状态不重复发布,不能依赖一次多余的空态更新。
## AGC Windows 开发态首次页面加载缓慢
Vite 默认监听应用根下的 Rust `src-tauri/target`,构建产物较多时会创建大量 Windows 文件监听器。AGC 配置通过 `server.watch.ignored: ['**/src-tauri/target/**']` 排除此目录,不关闭业务源码、CSS、共享组件监听或 HMR。排查时区分后端就绪、Vite 扫描和原生窗口首绘;监听目录回归不能代替实机首绘测量,验证入口见本地开发运维文档。
## 2026-09-17 AGC 输入盒的「推理档」弹层被祖先裁切:要放开裁切而不是挪弹层
- **现象**:窄窗口下(视口 ≤1000px 时右侧对话面板只有 280px 宽)点开输入盒右下角的「推理档」,弹层是个**空盒子**:档位文字(默认 / 低 / 中 / 高 / 最高)整片看不见,只剩一个方框。
@@ -8,6 +16,16 @@
- **易错点**:① 把弹层改成 `left: 0` 或往右挪也能让它可见,但那是改变展开方向,弹层会跑到触发钮右边(用户明确否决);② 只放开最外层聊天列不够——surface 与 conversation 各自都会裁,三层必须同时放开;③ 只按宽度比大小会误判:280px 面板里控制排本身也超出(发送钮右侧溢出 22px,被窗口右缘吃掉),那不是本条的原因,别顺手去改控制排布局。
- **验证**:`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts` 的 `keeps the landscape workbench edge-to-edge with internal chat scrolling` 钉住三条 override 声明在场(删掉任一条即红)。真机几何用 playwright-cli 打开一份只含真实 `styles.css` 与真实 composer DOM 的最小复现页实测(视口 1000×700、面板 280px):弹层 rect 修复前后都是 `[-42, 108]`(位置未动),`elementFromPoint` 的命中区间从修复前的 `[2, 108]` 变成整块;档位文字在截图中完整可见。
- **关联**:`apps/ai-game-creator-shell/src/styles.css`(`面板纵向布局(2026-07 Codex 风格改造)` 区块之后)、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`。
## 2026-09-17 资源卡「内容跟着边框动」与「模型缩略图被拉伸」是两条不同的几何陷阱
- **内容跟着状态边框位移**:卡片底态是 `border: 0`,悬停 / 选中才加 1px 边框;卡片是 `box-sizing: border-box`,而卡面(`.game-resource-card-visual`)与角标都是 `position: absolute; inset: 0`(包含块 = **padding box**)⇒ 状态一切换,内容盒四边各被吃掉 1px,卡面与角标整体位移并缩小 2px。修法:底态写成 `border: 1px solid transparent;`,状态只点亮 `border-color`;契约用例改成断言「资源卡规则里不得出现非 1px 的 `border` / `border-width`」。
- **模型缩略图看起来被拉伸 / 被裁**:三个原因叠在一起 —— ① 缩略图渲染器的 `PerspectiveCamera` 是单例复用件,`aspect` 默认 `1` 且从没更新,方形投影被塞进宽扁缓冲;② 卡面里 `width/height: 100%` 的图片挂在 `place-items: center` 的网格里,网格项高度会退化成"按内容定高"(百分比高度解析成 `auto`),图片按自然比例长过卡片、被 `overflow: hidden` 裁掉,看起来就像被拉伸;③ 栏目画布用 `transform: scale(var(--resource-section-zoom))` 放大(真机 1.5 倍),而 `ResizeObserver` / `offsetWidth` 只看**布局盒**,缩放变化既不触发 observer,按布局尺寸 1:1 渲染的图也会被放大到发虚。修法:渲染前设 `camera.aspect`;图片改 `position: absolute; inset: 0` + `object-fit: contain`;渲染尺寸取 `offsetWidth/offsetHeight × 2` 超采样(上限 1024),并把实际渲染尺寸暴露到 `data-model-render-size` 方便排障。
## 2026-09-17 从提权会话创建的目录会被 AGC 的 Windows owner 校验直接拒绝
- **现象**:在 Codex 会话里手工创建的工程目录(例如 `C:\Users\<user>\Documents\Codex\...\cocos-preview-fixture`),用客户端打开时报 `Windows 安全对象不属于当前用户:<path>`;Rust 侧同样用 `tempfile::tempdir()` 建夹具的用例也成片失败在同一句上。
- **原因**:这个 shell 以管理员身份运行,`New-Item` / `tempfile` 新建目录的 owner 是 `BUILTIN\Administrators`,而 AGC 的校验要求 owner 等于当前用户 SID(`KDLETTERS\<user>`)。`Get-Acl <path> | Select Owner` 与 `whoami` 一比就能定性;同一台机器上由客户端自己创建的目录 owner 正确,所以「客户端自己建的项目能用、手建的不能用」。
- **处理**:手工夹具先 `icacls <path> /setowner "<DOMAIN>\<user>" /T`;Rust 用例改用工程自带的 `crate::tests::canonical_test_tempdir(prefix)`(它会 canonicalize 并重置目录 owner),不要直接用 `tempfile::tempdir()`。判「用例失败与本改动无关」时,先确认失败信息是不是这一条。
## JSON 卡片显示与 UI 编辑能力必须同源
@@ -17,6 +35,8 @@ JSON 的文本读取分支不等于卡面应该展示原始 State 摘要。卡
工作台向窗口标题栏发布运行项目时,若 effect 依赖普通函数派生的回调,发布 Context 会重新渲染工作台,进而再次发布并清理,形成更新深度循环。转发入口须稳定,并在提交阶段更新实际处理器引用;发布数据变化与卸载清理分开。回归测试必须组合真实窗口 Provider 和工作台消费者,只有独立画布测试无法覆盖这条反馈链;回归时用有界发布次数阻止测试失控。画布快速操作时暴露的更新深度错误,也须检查外层状态同步,不能直接归因于滚轮频率。
活动回合快照的初始签名须与初始空数组一致,首次异步返回空数组不能额外换引用。停用、重新启用或切换读取器时应使旧请求失效,避免晚到结果覆盖新快照;测试需控制 Promise 完成时机,不能用“初始数组已为空”当作请求已结束。无原生读取器时窗口只发布一次空状态。
## 2026-09-17 工具 schema 声明的上限与真实校验不一致,会表现成「agent 调不动这个功能」
- **现象**:用户反馈「客户端没法由 agent 调用图片快速编辑功能以及背景音乐生成功能」。查工具目录时两个工具都在(`agc_edit_image`、`agc_create_or_derive_resource`),图片快速编辑在真实项目日志里还有成功记录;但 agent 侧写一句正常长度的背景音乐描述就失败,而客户端 UI 用同一个提示词却只是被截断加提示。
@@ -16,6 +16,7 @@
## 开发中
- 画布卡片类型与信息角标共用 `CanvasCardCornerActions`;菜单收纳共用 `OverflowActions`,宿主决定展示数量和资源命令。AGC 选中菜单前 5 项直显,Web 默认不折叠;浮层 portal 继续接入现有画布关闭与滚轮归属判据。
- 修改范围保持聚焦;优先扩展现有系统、页面、组件、DTO 和脚本,不新建平行入口或业务真相。
- UI 开发优先复用现有公共组件;跨页面或跨端重复的视觉/交互模式应沉淀到 `packages/shared`,由现有页面迁移使用,禁止在业务页复制同类 UI。共享组件只承载通用表现与交互,不下沉领域规则、后端副作用或正式业务状态。
- AGC 当前 Agent 与策划 Agent 的消息层级共用 `packages/shared` 的 `AgentMessageContent`:正文使用 `body`,思考、中间输出与工具调用使用 `process`;宿主不按 Agent 类型重新定义过程字号和颜色,错误状态保留语义色。