Merge remote-tracking branch 'origin/master' into fix/wrong-report
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m25s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 53s
Project CI / Backend tests (pull_request) Failing after 21s
Project CI / Frontend tests (pull_request) Successful in 2m25s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m45s
Project CI / Native shell tests (pull_request) Successful in 5m48s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 10m34s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 11m18s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m25s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 53s
Project CI / Backend tests (pull_request) Failing after 21s
Project CI / Frontend tests (pull_request) Successful in 2m25s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m45s
Project CI / Native shell tests (pull_request) Successful in 5m48s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 10m34s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 11m18s
# Conflicts: # docs/project-memory/shared-memory/decision-log.md # docs/project-memory/shared-memory/pitfalls.md
This commit is contained in:
@@ -12,6 +12,27 @@
|
||||
- **Rust 侧不得把结构化错误降级成字符串**:`refresh_session_inner` 的非权威失败直接返回 `Err(ClientAuthError)`,视图不带 `errorMessage`;一旦折成 `String`,前端就只能拿文案判断,变体信息永久丢失。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/auth_session.rs`、`apps/ai-game-creator-shell/src/services/{clientAuth.ts,errorReporting.ts,platformSession.ts}`、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`。
|
||||
|
||||
## 2026-10-01 Rust 分片编译失败只剩汇总错误
|
||||
|
||||
- **原因**:`--message-format=json` 把编译诊断写到 stdout;只读取 `compiler-artifact` 的运行器会丢弃 `compiler-message`,CI 只能看到「due to 1 previous error」。
|
||||
- **处理**:使用 `json-render-diagnostics` 保留 JSON artifact,同时让 Cargo 将诊断渲染到 stderr;运行器继承 stderr。既有分片 fixture 同时覆盖编译失败详情、测试 panic 和成功摘要,见开发运维文档的 Rust lane 口径。
|
||||
- **排查边界**:函数签名变更与另一分支新增测试可能在无文本冲突的合并后产生参数不匹配;合并后必须检查测试目标,不能只验证普通二进制。
|
||||
|
||||
## 2026-10-01 线上 Nginx 手工维护漂移:`/profile` 刷新 404、发行包请求体仍限 64m
|
||||
|
||||
- **现象**:登录后刷新 `https://www.genarrative.world/profile` 直接 404,返回 Nginx 默认 404 页(162 字节);应用内点击进入正常。`curl` 复现:`/`、`/project`、`/creation`、`/components`、`/design-system`、`/games*`、`/editor/canvas` 全部 200 且正文与 `/` 同一份 SPA 外壳,只有 `/profile`(含 `/PROFILE`、`/profile/`)404。
|
||||
- **原因**:刷新是真实 HTTP 请求,命中线上 `/etc/nginx/conf.d/genarrative.conf` 的 SPA allowlist;线上白名单是手工维护的(留档 `genarrative.conf.bak-sparoutes-20260923T153751Z`),`profile` 从未加入,该深链落回 `location /` 的 `try_files $uri $uri/ =404`。仓库三份模板当时都写了 `profile`,而 `npm run check:nginx-spa-routes` 只校验仓库模板,查不出线上漂移。同一份线上文件还把 `client_max_body_size` 停在 `64m`(模板 210m,会让 200 MiB 发行包上传 413),并保有 4 处模板没有的 host-only 块(画廊读取限流 location、`/finance-forecast/`、`/medical-science/`、`/home/` 官网首页入口),所以直接拿模板整文件覆盖会删掉这些线上能力。
|
||||
- **处理**:线上只收敛 SPA allowlist(12 条,含 `/profile`)并补 `error_page 404 /404.html`;host-only 块收进 `deploy/nginx/snippets/genarrative-host-extras.conf`,由主模板 include、由 `Genarrative-Server-Provision` 安装。退役路由不进白名单,也不需要显式 404 配置,落 `location /` 即得品牌 404(非 HTML 客户端保持纯 404)。
|
||||
- **判据/取证**:改前 `grep -c profile /etc/nginx/conf.d/genarrative.conf` 为 0;改后 `curl -s -o /dev/null -w '%{http_code}' https://www.genarrative.world/profile` 为 200(正文 sha 与 `/` 一致),`sudo nginx -T | grep -c 'editor/canvas|profile|project'` 为 1,`match3d` 计数为 0。
|
||||
- **长期口径**:任何 SPA/路由白名单改动都要同时核对**生效配置**(`sudo nginx -T`)与仓库模板;线上主站 vhost 只由模板 + `genarrative-host-extras.conf` 生成,不再手工追加路由。snippet 内的 location 不在 Pingora 路由矩阵覆盖范围,Pingora 接公网 443 前要单独确认这些 host-only 路径。
|
||||
|
||||
## 2026-10-01 AGC 账户总量门槛会阻断单项查询与导入
|
||||
|
||||
- 账户素材库接口当前返回全量快照;客户端不能以全库条数代替单次查询、返回或导入的资源边界。原先在解析阶段拒绝超过 500 项,会让 `limit=1` 和指定 ID 导入一起失效,且检查发生在下载和 JSON 解析之后,不能保护这两步开销。
|
||||
- 账户查询允许偏移超过 500,仍按单页最多 100 项返回安全元数据。修改时同步 MCP schema/校验、Direct 工具桥、Runtime 执行层及 `agent_native_tools.rs` 下发的 strict 原生函数 schema;仅直接调用 observation 会绕过模型侧参数约束,无法证明后续页可达,必须验证实际生成的函数参数 schema。共享分页函数的项目文件调用方仍保留自己的范围,画布资源数量边界独立维护。
|
||||
- 实际平台会话导入还需避免在身份租约内再次调用会话读取/校验:租约持有非重入互斥锁,提交 helper 重复校验会等待自身释放。下载后校验并取得租约,持有至本地提交完成即可;不能用无平台会话的提交夹具替代完整导入链路验证。
|
||||
- 后端有界分页与按 ID 读取由 #574 跟进,不作为 #549 客户端恢复可用的前置条件。当前合同与验证入口见 [AGC 实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md) 的“账户 / 项目画布 / 本地素材导入”。
|
||||
|
||||
## 2026-09-30 构建期 staging 撞上不装 npm 依赖的 Linux 门禁:AGC 壳 Rust lane 全红
|
||||
|
||||
- **现象**:`Project CI` 的 AGC 壳 Rust 三条 lane(`npm run check:native-shells:agc-rust-shard-*`)在 `fb130d184` 之后全部失败,日志只有 `error: failed to run custom build command for genarrative-ai-game-creator-shell` 与 `thread 'main' panicked at build.rs:65:28: Claude Agent SDK 缺失;请先执行 npm ci`(run 3083 / job 17521 实测,1 分钟即失败)。
|
||||
@@ -36,6 +57,12 @@
|
||||
- **判据/取证**:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell direct_thread_delta`——新增 `direct_thread_delta_sanitization_preserves_line_breaks` 钉住「逐段脱敏 == 整段脱敏」,去掉 `split_inclusive` 即红;真实文本的回归用渲染侧夹具复核(修复前 table/li/h2 全 0,修复后与整段脱敏一致:1 个 table / 3 个 th / 4 个 h2)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs`、`.../agent/generation/prompt_context.rs`、`.../agent/thread_manager/wire.rs`、`apps/ai-game-creator-shell/src/view/project-development/chat/conversation/directThreadChat.ts`、`#384`。
|
||||
|
||||
## 2026-10-01 文件列举被无关临时锁删除打断
|
||||
|
||||
- `read_dir` 返回名字后,文件可能在 `symlink_metadata` 前正常消失;`.agent/project.lock` 的正常释放就能触发这类竞态。不要先全项目扫描再按 Agent 的 `path` 和可见性过滤。
|
||||
- 项目内列举在元数据读取前裁剪范围和受保护路径,只进入目标子树及必要祖先;共享文件树保留自己的可见性策略。枚举后消失的文件/目录仅跳过 `NotFound`,其它 IO 错误仍可诊断,进入排队目录前复核链接/重解析点。
|
||||
- 并发回归用通道协调真实写锁的释放与元数据读取,不靠高频循环碰撞;分页按本次观察到的可见文件排序,不保证跨请求快照。完整合同见 AGC 实施计划“项目文件列举的范围与并发边界”。
|
||||
|
||||
## 2026-09-29 Game Agent 读工具被项目相对路径规则拦住
|
||||
|
||||
- 项目外读取不能只依赖末段 `O_NOFOLLOW`:父目录符号链接可隐藏 `.ssh` 等受保护名字。文件读取和目录列表在访问前逐段检查原始路径,拒绝符号链接与 Windows 重解析点;目录扫描对子目录再次检查。系统临时目录若含平台别名(例如 macOS `/var`),普通读取测试使用临时目录的 canonical 路径,不能通过 canonicalize 待读路径来抹掉待检测链接。
|
||||
@@ -6194,3 +6221,41 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **同时**:主站顶栏(`platform-desktop-topbar`)在窄屏必须保持单行——加 `flex-wrap` 后新增一个动作入口就会让品牌折到第二行;窄屏降级靠 `< 640px` 图标化下载入口、`< 480px` 只留品牌 IP 标识。
|
||||
- **验证**:真机尺寸下用 `document.elementFromPoint(按钮中心)` 断言命中的是按钮自身而不是底部菜单(`overlay.parentElement === document.body`);顶栏断点矩阵(320–768)断言单行且无横向溢出。
|
||||
- **关联**:`src/components/common/SquareImageCropModal.tsx`、`src/components/platform-entry/PlatformProfileModalShell.tsx`、`src/components/platform-entry/PlatformEntryActiveFlowShell.tsx`、`src/index.css`。
|
||||
|
||||
## 2026-10-01 cc/Anthropic 必须走平台网关,客户端不得直连 Router
|
||||
|
||||
- **现象**:目录里把 `claude-opus-5-5` 标成 `agentMode=cc` 后,选它发消息必然失败:先是 Claude CLI 自己打印 `Not logged in · Please run <path>`,后来卡满 `requestTimeoutMs=180000`。
|
||||
- **根因**:`claude_code_cli.rs` 用配置里残留的 `llm.baseUrl`(`https://router.genarrative.world/v1`)当 `ANTHROPIC_BASE_URL`,再把平台会话 token 塞成 `ANTHROPIC_AUTH_TOKEN`。平台凭据边界是「客户端只出示平台 access token,账号的 Router key 由 api-server 解析、绝不下发」(见 `api-server/src/llm/mod.rs`)。实测 `POST https://router.genarrative.world/v1/messages` 用平台 token 返回 `401 Invalid token (new_api_error)`。
|
||||
- **现行口径**:baseUrl 不带路由与版本段,统一由协议自己拼 `v1/<op>`(服务端 `router_protocol_url` 会先把历史凭据末尾的 `/v1` 归一化掉)。平台侧 Anthropic 是独立路由 `/api/llm/anthropic/{*path}`,客户端 `ANTHROPIC_BASE_URL` 设成 `{apiBaseUrl}/api/llm/anthropic`;OpenAI 侧是 `/api/llm/v1/responses` 与 `/api/llm/v1/chat/completions`,旧的无 `v1` 路径保留为已发布客户端的兼容别名。
|
||||
- **注意**:Claude Agent SDK 固定请求 `{ANTHROPIC_BASE_URL}/v1/messages?beta=true`(外加一次 `HEAD /api/hello` 探测),网关不要自己再补 `v1`,用通配段承接客户端协议路径。
|
||||
- **后台协议选项**:`AgcAgentMode` 增加 `anthropic`(显式 Anthropic 协议),`cc` 保留为同一执行器的历史别名;新增目录项应直接写 `anthropic`。
|
||||
|
||||
## 2026-10-01 AGC 壳 Rust 单测片内 `--test-threads=1` 不等于没有并发:宿主后台回合会跨用例发通知
|
||||
|
||||
- **现象**:`agent::thread_manager::tests::active_turn_changes_publish_one_notification_per_real_change` 在 CI 偶发红,`进度内容变化必须通知一次` 拿到 6 而非 5(也见过入队断言就多 1);本地要跑 3–5 次才复现。这条用例靠一个进程级计数器数「运行中的项目」通知。
|
||||
- **原因**:`--test-threads=1` 只串行**测试线程**。上一个用例里 `TurnReservation::drop` → `kick_queue_dispatch` 会用 `tauri::async_runtime::spawn` 起整轮,这轮在 tokio 工作线程上继续跑(项目路径不存在 → 落盘失败 → `complete_turn_if_reserved` → `emit_direct_active_turns_changed`),恰好落在下一个用例的取样窗口里。用工作线程 ID + backtrace 才抓到现场。
|
||||
- **处理(现行口径)**:通知计数器改成线程作用域,只统计本测试线程发出的通知,断言语义(放行 1 / 内容变化 1 / 重复进度 0 / 收口 1)不变。「进程级全局量 + 片内串行 ⇒ 安全」这个假设在 AGC 壳单测里不成立,要么按线程隔离,要么别用全局量当断言依据。
|
||||
- **同时**:分片器的失败复核原先只认 `tests::` / `process_session::tests::` 前缀,而 bin 目标用例名是 `agent::…::tests::…`,于是 3143 条里 2218 条永远进不了复核,偶发假红直接判红;现在按本片名单精确匹配,并且复核也失败时保留首次现场。
|
||||
- **验证**:本机用 CI 那一片(`--shards=4 --shard-index=1` 的第 3 块)连跑:修复前 5 次红 3 次,修复后 18 次全绿;`node --test apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs` 3 项通过。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/entrypoints.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/thread_manager/dispatch.rs`、`apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs`。
|
||||
|
||||
## 2026-10-01 AGC 壳 Rust 单测里等 mock provider 请求的 2 秒预算,在 CI 分片并发下偏窄
|
||||
|
||||
- **现象**:`tests::sessions::background_agent_runtime_can_write_memory_and_project_files` 在 CI `agc-rust-shard-4` 偶发红,`sessions.rs:405` 报 `second llm request: Timeout`——首个请求已经通过,只有第二次 provider 往返超时;失败复核同样红,整条用例 3.6 秒就结束。
|
||||
- **原因**:该断言只等了 `Duration::from_secs(2)`。一轮 mock 往返要等 Agent Runtime 后台线程跑到发请求那一步,CI 上 4 个 rust shard 与前端任务同机并发时单轮耗时会被拖到数秒;本机空载实测该请求 0.99–1.29 秒,同机负载下曾到 4.87 秒 / 5.18 秒。链路本身是好的,缺的是预算——这是分片并发放大出来的抖动,不是产品缺陷(同一次失败切回基线 commit 照样复现)。
|
||||
- **处理(现行口径)**:`src/tests/mod.rs` 新增 `MOCK_PROVIDER_REQUEST_TIMEOUT`(30 秒)作为「等一条 mock provider 请求到达」的唯一预算,`wait_for_captured_mock_request` 与 `sessions.rs` 里 7 处 `recv_timeout` 统一引用它;负向断言(确认「不会有下一个请求」的 200ms 窗口)保持不动。请求到达即返回,成功路径零成本,真断链仍会失败,只是失败得更晚。
|
||||
- **验证**:`cargo test --bin genarrative-ai-game-creator-shell tests::sessions:: -- --test-threads=1` 44 passed;`node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4` 787 tests ok in 88.7s(正是原先失败的那一片)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/tests/sessions.rs`、`apps/ai-game-creator-shell/src-tauri/src/tests/mod.rs`。
|
||||
|
||||
## 2026-10-01 AGC 官方 Claude 模型缺少账号令牌会被误报为通道断开
|
||||
|
||||
- **现象**:模型目录把回合路由到 `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`;首页仍会阻止自动命名、建项和首次生成。
|
||||
- **根因**:`homeWebPreflight.ts` 原先对 `preflight_web_game_creation` 的所有异常统一抛出 `HOME_WEB_PREFLIGHT_FAILURE`,调用方 `useHomeProjectCreation.ts` 的两层 `catch` 又再次覆盖原始错误。于是宿主真实预检码、Tauri IPC 断连、命令未注册和 WebView `Failed to fetch` 都显示成 Node/npm/浏览器故障。
|
||||
- **处理**:保留预检失败关闭语义,但提取 Rust 返回的安全错误码;对无错误码的 Tauri 调用故障显示独立 IPC 提示,并仅对这类瞬态调用失败重试一次。首页调用方必须透传该安全提示,不能重新覆盖为通用文案。
|
||||
- **验证**:`apps/ai-game-creator-shell/tests/homeWebPreflight.test.tsx` 覆盖 ready、具体宿主错误码、IPC 失败、无错误码 blocked 和 planning 绕过;同一 Windows 安装包 CLI 实测 Node `v22.22.2`、npm `10.9.7`、Edge desktop/mobile PNG 均通过。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/features/app-shell/homeWebPreflight.ts`、`apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts`、`apps/ai-game-creator-shell/tests/homeWebPreflight.test.tsx`、`apps/ai-game-creator-shell/src-tauri/src/environment_check/web_creation.rs`。
|
||||
|
||||
Reference in New Issue
Block a user