Merge remote-tracking branch 'origin/master' into feat/agc-runtime-picking-engines
Project CI / AI game creator shell Rust crates (pull_request) Successful in 6m10s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m28s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m15s
Project CI / Backend tests (pull_request) Successful in 8m43s
Project CI / Frontend tests (pull_request) Successful in 2m47s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m27s
Project CI / Native shell tests (pull_request) Successful in 8m14s
Project CI / Repository checks (pull_request) Successful in 6m38s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 6m10s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m28s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m15s
Project CI / Backend tests (pull_request) Successful in 8m43s
Project CI / Frontend tests (pull_request) Successful in 2m47s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m27s
Project CI / Native shell tests (pull_request) Successful in 8m14s
Project CI / Repository checks (pull_request) Successful in 6m38s
# Conflicts: # docs/project-memory/shared-memory/pitfalls.md
This commit is contained in:
@@ -14,6 +14,42 @@
|
||||
- **原因**:Phaser 4 默认用 XHR 装载图片,地址只在当前会话内有效,不携带仓库 / manifest 里的路径信息。
|
||||
- **结论(现行口径)**:Phaser 4 项目的素材身份必须靠玩法显式标注(`setData`)发布,不要试图从 blob 地址反推文件名;没有标注就退化为不带素材的区域引用。与主规范「运行画面素材点选(2026-10-04)」的边界一致。
|
||||
|
||||
## 2026-10-03 AGC 随包 plugins 的 feature 档位必须与消费方一致,且门禁会因 build.rs 未重跑而假通过
|
||||
|
||||
- **现象**:Windows 本机 `npm run check:generated-bindings`(`npm run lint` 链内,`scripts/check-repository-ci.sh` 的 Repository checks 也走它)在 `build.rs:167:29` panic:`插件随包资源校验失败:随包插件存在未声明文件:.../src-tauri/resources/plugins/agc-godot-editor/native/gdextension/bin/win-x64/agc_godot_editor.dll(目标 x86_64-pc-windows-msvc 与当前 feature 组合不允许;请先执行随包资源准备步骤)`;树上换成 `agc-unity-editor/dotnet/publish/win-x64/Agc.Unity.Attach.exe` 时报同一类错。反向还有更隐蔽的形态:门禁 2 秒就 exit 0 说「通过」,但 tree 上其实带着编辑器产物。
|
||||
- **原因**:`apps/ai-game-creator-shell/src-tauri/resources/plugins/` 是 gitignored 但被 dev / 发布 / 门禁多流程共用的目录,它的**档位**(staging 里放了哪些编辑器产物)必须与本次 cargo 调用实际生效的 feature 组合一致。`apps/ai-game-creator-shell/src-tauri/Cargo.toml` 的 `[features] default =` 是空的,而 `scripts/check-generated-bindings.mjs` 对 AGC 用的是裸 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml export_bindings`(**不带任何 feature**);`build.rs` 按「当前 TARGET + 已启用 `CARGO_FEATURE_*`」构造 plugins 允许集合,于是为 windows 编辑器 feature 准备的 `origin: prepared` / `libraryStaging` 产物全成了「未声明文件」。
|
||||
- **关键坑(假通过)**:`resources/**` **不是**构建脚本声明的输入(设计如此,避免每次资源变化都重编),所以缓存命中的 `cargo test` **根本不会重跑 `build.rs`**,校验被整个跳过。实测:树上带着未声明的 unity 产物时,门禁仍以 2.33 秒 exit 0「通过」;`touch apps/ai-game-creator-shell/src-tauri/build.rs` 强制重跑后才暴露。
|
||||
- **处理(现行口径)**:校验 / 无 feature 的消费方先复位到 featureless:`npm run agc:bundled-resources:prepare -- --features=`。要带编辑器能力的本地 AGC:`npm run agc:bundled-resources:prepare -- --target x86_64-pc-windows-msvc --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute`(`cocos-editor-bridge.dll` 的 native payload 另需 `cocos-editor-injection`)。要求门禁**真校验**过,先 `touch apps/ai-game-creator-shell/src-tauri/build.rs` 再跑门禁。
|
||||
- **判据/取证**:复位后强制重跑那次输出 `生成绑定校验通过:shared-contracts(1 个文件)` / `生成绑定校验通过:ai-game-creator-shell(104 个文件)` / `生成绑定校验通过:合计 105 个文件与 Rust 声明一致`(exit 0)。featureless 判据:`resources/plugins` 只有各插件 `plugin.json` + `src/`(cocos 另有 `panels/`),不存在 `Agc.Unity.Attach.exe` / `agc_godot_editor.dll` / `cocos-editor-bridge.dll`。
|
||||
- **边界**:这是同一工作树里多人共享的档位——featureless 是无 feature 构建与并发 cargo 运行的前提,带编辑器产物会让它们失败,反之亦然。`prepare-bundled-resources.mjs` 的原子替换要 rename `resources/plugins`,撞上外部目录句柄会 `EPERM` 并把 staging 留在 `resources/plugins-staging-<pid>-<hex>`(`.gitignore:61` 已声明该模式),确认无并发进程后重试即可。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/build.rs`(`validate_staged_plugins` / `validate_prepared_payloads`)、`src-tauri/build_support/package-layout.json`、`apps/ai-game-creator-shell/scripts/{prepare-bundled-resources.mjs,cargo-features.mjs}`、`scripts/check-generated-bindings.mjs`;另见本文件「随包资源的写入方按产物来源分界」「AGC 随包资源的布局只能改声明文件」「构建期 staging 撞上不装 npm 依赖的 Linux 门禁」三条。
|
||||
|
||||
## 2026-10-03 工作树位于 `.worktrees/` 下时前端 Vite 被自己的忽略规则整棵排除:改代码不热更
|
||||
|
||||
- **现象**:在 `.worktrees/<name>/` 里的工作树改主站或后台前端源码,浏览器看不到任何更新;重启 Vite 也不生效(本轮有人重启 3 次才发现不是缓存问题)。
|
||||
- **原因**:根 `vite.config.ts`(`ignoredWatchGlobs`,约 461-468 行)与 `apps/admin-web/vite.config.ts`(约 19-24 行)的忽略列表都含 `'**/.worktrees/**'`。工作树本身就在 `.worktrees/` 目录下,该 glob 会匹配工作树内的**每一个文件**,等于把整个项目排除出 watch——不报错、不提示,只是永远不触发 HMR / 重建。
|
||||
- **影响面**:主站 web 与 admin-web 都中招(两者共用这条规则);`npm run dev:web` 走 `scripts/vite-cli.mjs`,它只是转发到 Vite 自带 bin、用的是同一份根配置,所以经 CLI 入口启动也一样。`apps/ai-game-creator-shell/vite.config.ts` 只忽略 `'**/src-tauri/target/**'`,**不受影响**。
|
||||
- **处理方向(本轮未改配置)**:忽略规则必须只排除**其它** worktree 而放行当前 root——例如按真实仓库根计算,形如只忽略 `<repoRoot>/.worktrees/*/` 且显式排除当前 root;或当项目 root 自身已落在 `.worktrees/` 内时不注入该条。不要保留无条件的 `'**/.worktrees/**'`。
|
||||
- **验证方向**:修改 `src/**` 一句话后,主站与 admin-web 各自应打印 HMR 更新(修改后立即生效),而不是只在重启后才生效;必要时打印生效的 watch 忽略集合确认不再覆盖当前 root。
|
||||
- **关联**:`vite.config.ts`、`apps/admin-web/vite.config.ts`、`apps/ai-game-creator-shell/vite.config.ts`、`scripts/vite-cli.mjs`。
|
||||
|
||||
## 2026-10-03 同一工作树并发拉起多份 dev 栈:`.app/dev-stack.json` 互相覆盖,启动兜底清扫会反杀健康栈
|
||||
|
||||
- **现象**:在同一工作树里再开一个 `npm run dev` 之后,AGC 侧报「后端归属校验失败」,或前端代理连到别的端口(「后端端口记错」);更严重的是新会话启动后,`8082` / `8083` 上原本健康的后端被清掉,旧会话随即报连接失败。
|
||||
- **原因**:`.app/dev-stack.json` 是**全工作树单文件**(`scripts/dev.mjs` 的 `resolveDevStackStatePath()` → `<repoRoot>/.app/dev-stack.json`,`scripts/dev-all.mjs` 与若干 e2e 脚本也读它),每个 `DevRunner` 都整份覆写快照,端口、SpacetimeDB data-dir 与 instance id 只保留最后写入者,于是两份并发栈互相覆盖实例信息。同时 `dev.mjs` 在启动/退出时会按身份兜底清扫 `stopWindowsWorktreeBackendProcesses`(`api-server.exe` 绝对路径 + SpacetimeDB `--data-dir`),这是**按工作树**而不是按会话匹配的:其它会话留下的半死栈一旦重启,就会把当前健康栈一并收走。
|
||||
- **处理(现行口径)**:同一工作树保持**单栈**;确需并发时用显式端口参数(`--api-port` / `--web-port` / `--admin-web-port` / `--spacetime-port` 等)错开,并接受状态文件只有一个「最后写入者」。清理残留必须按**端口 → PID → 命令行**确认归属,再杀该 PID 的整棵进程树;不要 `taskkill /IM node.exe`(会误伤其它会话与 IDE 的 Node 进程)。
|
||||
- **排查顺序**:先比对 `.app/dev-stack.json` 的 status / 端口与实际监听(`Get-NetTCPConnection -State Listen -LocalPort ...`)是否一致,再用 `Get-CimInstance Win32_Process` 按本工作树 `server-rs\target\debug\api-server.exe` 路径与 SpacetimeDB `--data-dir` 核对归属;不要因为 `/healthz` 返回 200 就认定后端属于当前会话。
|
||||
- **关联**:`scripts/dev.mjs`(`resolveDevStackStatePath` / `stopWindowsWorktreeBackendProcesses`)、`scripts/dev-windows-process.mjs`、`scripts/dev-all.mjs`、`scripts/check-game-distribution-ratings-e2e.mjs`;另见本文件「`npm run agc` 的 Ctrl+C 不能只依赖 shell 包装层与端口健康检查」与 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 2026-10-03 Windows 上会弹阻塞模态的启动失败用例挂死本机分片 runner
|
||||
|
||||
- **现象**:Windows 本机 `npm run ai-game-creator-shell:check:rust:shell -- --shard-index=3/4` 长时间无进展(可到 2400s 超时);单独跑那条用例同样超时——进程还在、CPU 不再增长、也没有子进程,形态很像「测试死锁」或「分片器坏了」。
|
||||
- **原因**:`apps/ai-game-creator-shell/src-tauri/src/main.rs:2042` 的 `startup_log_slot_fail_without_path_still_reports_instead_of_going_silent` 调 `StartupLogSlot::fail()`(约 1854 行),而 `fail()` 会走 `show_startup_error_dialog()`;Windows 实现(约 1740 行)用的是 `MessageBoxW(..., MB_OK | MB_ICONERROR | MB_SETFOREGROUND)`,是**阻塞模态**,没有人点「确定」就永不返回。`STARTUP_ERROR_DIALOG_SHOWN`(约 1534 行)只在同一个进程内保证「只弹一次」,对测试用例没有任何豁免。分片 runner 用 `--exact <名单> --test-threads=1` 串行执行,一条挂死就整片挂死。
|
||||
- **影响面**:Linux CI 走非 Windows 分支(约 1778 行)只写 stderr,**不受影响**;这是本机专属现象,不要据此判定 Rust 代码或分片规则有问题。
|
||||
- **处理(本机绕过)**:改用等价分块跑,而不是整片上阵——同一个测试二进制、同一 `--exact <名单>` 与 `--test-threads=1` argv、同一 TMPDIR 隔离,把这条阻塞用例排除或单独限定。
|
||||
- **判据/取证**:单独执行 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell -- --exact startup_log_slot_fail_without_path_still_reports_instead_of_going_silent --test-threads=1` 本机同样挂住;对照非 Windows 分支只产生 stderr 文案。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/main.rs`、`apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs`;另见本文件「AGC 壳 Rust 套件按「一片一 job」拆分,且分片必须自校验覆盖」条中此前记为「本机跑到约 20 分钟后长时间无进展」的同一现象。
|
||||
|
||||
## 2026-10-03 AGC 画布「引用」死按钮:window 事件的消费者挂在一个只在另一条链路赋值的 ref 上
|
||||
|
||||
- **现象**(Issue 602):AGC 资源画布选中一张已登记素材,选中工具条点「引用」(图标 `@`、可见文案与 `title` 都是「引用」)没有任何反应——聊天输入框里不出现 `@素材名` 芯片,也没有任何提示。普通项目(`directProjectMode`)必现,立项策划项目(`planningStartMode`)复现不出来;把素材卡拖到对话栏的批量引用同样没反应。
|
||||
@@ -55,16 +91,19 @@
|
||||
- **原因**:Windows 的 `npm.cmd` 是批处理入口;Node `child_process.spawn('npm.cmd', args, { shell: false })` 会直接返回 `EINVAL`,还未执行根 `npm run dev`。
|
||||
- **处理**:`scripts/dev-all.mjs` 在 Windows 使用 `shell: true`、`windowsHide: true` 启动 npm 子进程;POSIX 仍使用独立进程组,退出时按进程组收束。
|
||||
- **验证**:Windows 实测根开发栈已启动并完成端口漂移(Web `3001`、API `8084`、worker `8085`、SpacetimeDB `3104`、后台 `3105`),之后 AGC 因当前工作区缺少 `@anthropic-ai/claude-agent-sdk` 退出;dev:all 已收束根栈进程。
|
||||
|
||||
## 2026-10-02 AGC 页面在自绘标题栏外壳里自己算 `100vh`:底部被裁而且没得滚
|
||||
|
||||
- **现象**:帮助页(使用指南 / 联系客服 / 更新日志)在矮窗口里底部卡片看不到,把窗口拉高才出现;外壳 `.launcher-main { overflow: hidden }` 之下没有任何可滚动祖先,页面既滚不动也裁得干净。首页在通知横幅出现时用 `h-[calc(100vh-32px)]`,同样把窗口高度当成了舞台高度。
|
||||
- **原因**:AGC 桌面外壳是自绘标题栏(`--window-chrome-height`;窗口 100vh=800 时舞台只有 750),页面根节点写 `100vh` / `100dvh` / `calc(100vh - Npx)` 就比真实舞台高一整个标题栏,差额被外壳裁掉;横幅是 `.launcher-main` 里的真实行,再写 `-32px` 等于重复扣一次。帮助页还没有内层滚动容器,连「内容超高就在内部滚动」这条兜底也不存在。
|
||||
- **处理(现行口径)**:页面高度只由外壳分配——`apps/ai-game-creator-shell/src/styles.css` 里 `.launcher-main:has(<页面钩子>)` 是纵向 flex 列(`height: 100dvh`,窗口外壳命中 `height: 100%` 时贴合真实舞台),`.launcher-main > <页面根节点>` 统一 `flex: 1 1 auto; height: auto; min-height: 0`,帮助页这类没有内层滚动容器的再加 `overflow-y: auto`。页面根节点一律不再写 `100vh` / `100dvh` / `calc(100vh - Npx)`;有横幅就靠 flex 自动少一份,不要手算偏移。
|
||||
- **验证**:真机判据是 Vite + Chromium 量页面根节点是否正好等于 `.window-chrome__content` 的高度(1440x800 / 1440x560 / 390x844 / 390x560,带与不带横幅),帮助页应可滚动到底。
|
||||
|
||||
## 2026-10-02 固定试玩误判祖先的指针穿透样式
|
||||
|
||||
- `pointer-events:none` 不会强制禁用整棵子树;后代显式 `auto` 可以恢复命中。控件探针只检查目标的计算样式,继承未覆盖的 `none` 仍拒绝;可见性、遮挡、disabled 与 inert 保留各自检查。
|
||||
- 修复和回归必须经过生产输入入口及可信事件驱动的状态变化,不能用程序化点击证明真实可玩。双视口 generic 回归与真实触摸验收需要区分,详见 AGC 实施计划“固定试玩控件的指针命中边界”。
|
||||
|
||||
## 2026-10-01 Rust 分片编译失败只剩汇总错误
|
||||
|
||||
- **原因**:`--message-format=json` 把编译诊断写到 stdout;只读取 `compiler-artifact` 的运行器会丢弃 `compiler-message`,CI 只能看到「due to 1 previous error」。
|
||||
@@ -6241,6 +6280,7 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **现象**:模型目录把回合路由到 `cc`,本地 `game-creator.config.json` 的 `llm.apiKey` 为空时,Claude Agent SDK 返回失败终态;界面只显示“执行通道未能建立或已断开”。
|
||||
- **根因**:Claude sidecar 只从 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 或本地 `llm.apiKey` 读取认证,没有复用已登录的 AGC 平台会话;同时失败终态解析丢弃了上游错误摘要。
|
||||
- **处理**:官方模型且未启用自定义目录时,将当前平台会话令牌仅注入 sidecar 子进程环境;保留最多 512 字符的 Claude 终态错误摘要,继续由统一诊断层脱敏,避免凭据落盘。
|
||||
|
||||
## 2026-10-01 AGC 首页把 Web 预检错误与 Tauri IPC 错误合并,造成无法诊断的生成阻拦
|
||||
|
||||
- **现象**:用户在首页点击「做游戏」后看到「Web 游戏环境预检未通过,请检查 Node/npm 或浏览器」,但同一安装包的 `--environment-check` 可能已经返回 `status=ready`;首页仍会阻止自动命名、建项和首次生成。
|
||||
@@ -6344,11 +6384,10 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **验证**:`cargo test --locked -p api-server --bin api-server app::tests::http_tracing`(默认并发与 `--test-threads=1` 各连跑 20 次)、`cargo test -p platform-llm observability_tests`;更接近 CI 并发的是整段 `app::tests::`(91 用例同进程)与 `--skip bgfilter_worker --skip wallet_refund_outbox` 的全量 bin(1133 用例)连跑。
|
||||
- **关联**:`server-rs/crates/api-server/src/app.rs`、`server-rs/crates/platform-llm/src/observability_tests.rs`。
|
||||
|
||||
|
||||
## 2026-10-04 AGC 通知计数与 graceful terminate 的断言偶发都来自"跨线程 / 跨用例串台"
|
||||
|
||||
- **现象**:`agent::thread_manager::tests::active_turn_changes_publish_one_notification_per_real_change` 偶发 `left: 8 / right: 7`(进度内容变化必须通知一次);`process_session::tests::process_session_graceful_terminate_keeps_wrapper_alive_for_target_cleanup` 偶发 `left: "exited" / right: "terminated"`;两者都在 `AI game creator shell Rust lane 2/2` 分片里红。
|
||||
- **原因 1(通知计数串台)**:测试计数器 `DIRECT_ACTIVE_TURNS_EVENT_TEST_COUNT` 在 *2026-10-01 已按线程作用域隔离*(`thread_local! Cell`),但 2026-10-02 退役 `runtime_driver` 把这段接缝搬进 `agent/direct_events.rs` 时**降级回进程级 `static AtomicU64`**。`--test-threads=1` 只串行测试线程,宿主 `tauri::async_runtime` 的后台回合仍在自己的工作线程上广播「运行中的项目」变了,于是断言取到别的回合的广播。
|
||||
- **原因 1(通知计数串台)**:测试计数器 `DIRECT_ACTIVE_TURNS_EVENT_TEST_COUNT` 在 _2026-10-01 已按线程作用域隔离_(`thread_local! Cell`),但 2026-10-02 退役 `runtime_driver` 把这段接缝搬进 `agent/direct_events.rs` 时**降级回进程级 `static AtomicU64`**。`--test-threads=1` 只串行测试线程,宿主 `tauri::async_runtime` 的后台回合仍在自己的工作线程上广播「运行中的项目」变了,于是断言取到别的回合的广播。
|
||||
- **原因 2(terminate 竞速)**:测试命令里 leader 打印 READY 后立刻 `exit 0`,同组后代仍存活,trampoline 从 leader 被回收那一刻开始 `PROCESS_SESSION_TARGET_TERMINATE_GRACE_MS=800ms` 宽限;客户端只要在 leader 退出后 >800ms 才发出 terminate(CI 高负载下要跨 durable record 写盘、registry 注册、线程 spawn),会话已按 `exited` 收口,terminate 只能读到既成事实——不是产品缺陷,是测试赌了客户端调度。
|
||||
- **处理(现行口径)**:①测试专用的通知计数必须留在测试线程作用域(`thread_local! Cell`),不要用进程级 Atomic;②graceful terminate 用例的 leader 打印 READY 后要用 `wait` 等后台子进程,让 terminate 必然落在会话仍 running 时(断言、trap、`sleep 0.4`、marker 名字都不改)。
|
||||
- **验证**:①修复前把计数器临时改回 Atomic 时同一并行口径 42/50 红;修复后并行 50 次 0 红、`--test-threads=1` 200 次 0 红、CI 现场等价块(145 用例)3 次 0 红;②该用例是 `#[cfg(target_os = "linux")]`,Windows 本机跑不到,用真实 Linux 内核(WSL Alpine)验证命令形状:leader 活到 TERM、同组后代完成 400ms 延迟清理(marker=done,real 0.41s)、清理后组内零残留;CI 侧仍应跑 `node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4` 复核。
|
||||
|
||||
Reference in New Issue
Block a user