合并 master 6c9566058:保留 v2 账本/命名任务与 master 侧改动
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m17s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m44s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m26s
Project CI / Frontend tests (pull_request) Successful in 2m58s
Project CI / Backend tests (pull_request) Successful in 7m24s
Project CI / Repository checks (pull_request) Successful in 6m23s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m47s
Project CI / Native shell tests (pull_request) Successful in 9m34s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m17s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m44s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m26s
Project CI / Frontend tests (pull_request) Successful in 2m58s
Project CI / Backend tests (pull_request) Successful in 7m24s
Project CI / Repository checks (pull_request) Successful in 6m23s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m47s
Project CI / Native shell tests (pull_request) Successful in 9m34s
- 冲突仅在 docs/project-memory/shared-memory/decision-log.md 与 pitfalls.md(双方都在顶部追加条目):两边条目全部保留——本分支的 Issue 599 条目(AI 项目命名解耦 / 生成任务账本 taskType + live 集合)在前,master 侧新增条目(发布版本标签派生、随包 plugins feature 档位、.worktrees 下 Vite 忽略规则、并发 dev 栈、Windows 阻塞模态用例)原样保留,逐条核对无丢失。 - 其余文件(desktop.rs、check-config.mjs、view/project-development/index.tsx、src-tauri 多处、admin-web、game distribution 等)均为自动合并,无冲突。 - 合并后自证:v2 账本常量/命令注册/命名任务命令名/条件改名空 expected 校验/entrySettled 顺序/命名行与事件刷新/三个测试文件的关键断言全部在位(脚本逐条核对 ALL_OK)。
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# 【实施计划】AGC 发布版本以工程内部版本为准
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】AGC发布版本以工程内部版本为准-2026-10-03.md` |
|
||||
| Status | proposed |
|
||||
| Owner | Genarrative Agent |
|
||||
| 关联 | PR #565(`feat/game-works-management`);取代 `【里程碑/实施计划】AGC已发布游戏版本更新-2026-10-02` 中「用户可编辑发行版本标签」的口径 |
|
||||
|
||||
## 修改边界(不动的东西)
|
||||
|
||||
- 不改 `server-rs/crates/{spacetime-module,module-game-distribution,api-server,spacetime-client}`:服务端 `versionNumber` 语义与 `publicationRevision` CAS 保持不变。
|
||||
- 不改 `apps/admin-web/**`、不改 `src-tauri/src/project/export.rs`(两者正被其它 worker 修改)。
|
||||
- 不删除本地字段 `projectVersion`(Rust DTO 带 `deny_unknown_fields`:`server-rs/crates/shared-contracts/src/game_creation_app.rs:1121`)。
|
||||
|
||||
## 修改顺序
|
||||
|
||||
1. 文档:主规范改写 → 本里程碑 → 本实现计划 → `decision-log` 条目 → 两份 2026-10-02 计划文档的取代标注。
|
||||
2. 契约:TS 解析器改为内部版本派生。
|
||||
3. Rust:拆掉两条 `project_version` 写链路 + 删除手改命令。
|
||||
4. 渲染层:面板改只读派生、删失焦写回与 `onManifestUpdated`。
|
||||
5. service:删保存函数、改默认值与注释。
|
||||
6. 测试改写 + 门禁全跑。
|
||||
|
||||
## 逐条改动(文件 → 位置 → 改什么 → 验收)
|
||||
|
||||
| # | 文件 | 位置 | 改什么 | 验收 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | `packages/shared/src/contracts/gameCreationApp.ts` | `:1001-1013` | `resolveGameCreationAppProjectVersion` 去掉 `binding` 形参,返回 `versions.length > 0 ? versions.length : GAME_CREATION_APP_FIRST_PROJECT_VERSION`;注释改为「唯一来源 = 工程内部版本」 | `npm run agc:typecheck`;面板/服务用例 |
|
||||
| 2 | 同上 | `:1034-1037` | `projectVersion?: number \| null` 保留,注释改「遗留位:旧清单兼容读,不再读写」 | 静态检查 |
|
||||
| 3 | `server-rs/crates/shared-contracts/src/game_creation_app.rs` | `:1107-1118`、`:1090-1096` | 删 `resolve_game_creation_app_project_version` 与 `GAME_CREATION_APP_FIRST_PROJECT_VERSION` / `is_game_creation_app_project_version`;对应单测改为「字段被忽略」 | `cargo test -p shared-contracts` |
|
||||
| 4 | 同上 | `:1146-1150` | `project_version` 字段保留,注释改「遗留:仅为旧清单可解析,不再读写」 | 同上 |
|
||||
| 5 | `apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs` | `:24-27` | 去掉解析器 import | `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml game_distribution_publish` |
|
||||
| 6 | 同上 | `:536-541`(`write_publication_binding`) | 删除 `manifest.project_version = Some(resolve(...))`,改为置 `None`;保留 `manifest.publication = Some(binding.clone())` | 同上 + `jq` 自检 |
|
||||
| 7 | 同上 | `:1531-1535`(发布成功回写) | 删除 `manifest.project_version = Some(version.version_number)`,改为置 `None`;保留 publication 回写 | 同上 |
|
||||
| 8 | 同上 | `:944-965` | 删除 `update_game_distribution_project_version` 命令 | 编译 + `rg` 零命中 |
|
||||
| 9 | `apps/ai-game-creator-shell/src-tauri/src/desktop.rs` | `:614` | 从 invoke handler 清单删除该命令 | 编译 |
|
||||
| 10 | `apps/ai-game-creator-shell/scripts/check-config.mjs` | `:138` | 从命令注册表删除 `'update_game_distribution_project_version'` | `npm run agc:typecheck` |
|
||||
| 11 | `apps/ai-game-creator-shell/src/services/gameDistributionPublish.ts` | `:148-167` | 删除 `saveGameDistributionProjectVersion` | typecheck + 用例 |
|
||||
| 12 | 同上 | `:329-337`、`:362-363` | 注释改为「唯一来源 = 工程内部版本」;默认值 `args.versionNumber ?? resolveGameCreationAppProjectVersion(args.manifest)` | 同上 |
|
||||
| 13 | `apps/ai-game-creator-shell/src/components/game-distribution/GameDistributionPublishPanel.tsx` | `:129-136`、`:283-288`、`:331-332`、`:397-403`、`:683-708`、`:731-734`、`:804` | 删 `parseProjectVersion`、`projectVersion`/`savedProjectVersion` state、`projectVersionSaveRequestRef`、`handleProjectVersionCommit` 与三处 setState;新增派生值 `useMemo(() => resolveGameCreationAppProjectVersion(manifest), [manifest.versions])`;提交改为 `versionNumber: projectVersion` | 面板用例 |
|
||||
| 14 | 同上 | `:947-1000` | 「项目版本」输入框改 `readOnly`(保留 `aria-label="项目版本"`),删 `onChange`/`onBlur`/`disabled`;提示语改为「来自 AGC 内部版本(版本 N)…」 | 面板用例 |
|
||||
| 15 | 同上 | `:242-243`、`:700` | 删除 `onManifestUpdated` prop 与唯一调用点 | typecheck |
|
||||
| 16 | `apps/ai-game-creator-shell/src/App.tsx` | `:1816`、`:1951` | 删除两处 `onManifestUpdated={setManifest}` | typecheck |
|
||||
| 17 | `apps/ai-game-creator-shell/tests/gameDistributionPublishPanel.test.tsx` | `:735-761`、`:778-823`、`:825-868` | 前两条改为「标签 = 内部版本数」;后两条(失焦写回 / 非法回退)整块删除;fixture `MANIFEST` 补非空 `versions` | vitest |
|
||||
| 18 | `apps/ai-game-creator-shell/tests/gameDistributionPublish.test.ts` | `:319-420` | 删 `saveGameDistributionProjectVersion` 用例块;默认值断言改为「无 `versions` → 1;N 条 → N」 | vitest |
|
||||
| 19 | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md` | `:97`、`:99`、`:106` + 新增两条 | 见主规范新条款 | `npm run check:doc-index` |
|
||||
| 20 | `docs/project-memory/plans/【实施计划】AGC已发布游戏版本更新-2026-10-02.md` | `:11`、`:23` | 行内追加「被 2026-10-03 里程碑取代」标注 | 阅读 |
|
||||
| 21 | `docs/project-memory/plans/【里程碑】AGC已发布游戏版本更新-2026-10-02.md` | `:27-30` | 同上,逐条标注被取代/收窄 | 阅读 |
|
||||
| 22 | `docs/project-memory/shared-memory/decision-log.md` | 最新条目区 | 追加本决策条目 | `npm run check:doc-index` |
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
npx vitest run apps/ai-game-creator-shell/tests/gameDistributionPublish.test.ts \
|
||||
apps/ai-game-creator-shell/tests/gameDistributionPublishPanel.test.tsx \
|
||||
apps/ai-game-creator-shell/tests/gameDistributionPublishLive.test.ts
|
||||
npm run agc:typecheck
|
||||
cd server-rs && cargo test -p shared-contracts
|
||||
cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml game_distribution_publish
|
||||
npm run check:doc-index
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
# 静态断言:命令与字段来源都已拆干净
|
||||
rg -n "update_game_distribution_project_version" . # 期望 0 命中
|
||||
rg -n "projectVersion" apps/ai-game-creator-shell/src packages/shared/src # 期望仅剩遗留声明/注释
|
||||
```
|
||||
|
||||
## 数据迁移
|
||||
|
||||
- 无迁移脚本。读路径立即忽略 `projectVersion`;改动点 6/7 会在下一次「平台回读绑定 / 发布成功」时把它置空,`skip_serializing_if` 使该键随下次落盘消失。
|
||||
- 平台侧历史版本行不做任何改写(不可变留痕)。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 回滚点:只动 AGC 客户端 + 本地契约 + 文档;回滚 = 还原提交,服务端数据无副作用。
|
||||
- 旧客户端降级:清单里 `projectVersion` 被置空后,旧版 AGC 会回落到绑定 `latestVersionNumber`(旧行为重现),仅影响本机,需写 release note。
|
||||
- 派生标签会因「截尾删除版本」而回退(删素材连带删版本),平台接受该回退,但会产生「同一标签先后指向不同包」——这属平台现行契约允许的形态(用户手动重复提交同版本同样会产生)。
|
||||
|
||||
## 不做(本里程碑之外)
|
||||
|
||||
1. 「线上最近提交 vN」加「(平台)」字样(纯文案)。
|
||||
2. 平台历史版本号对齐(**明确不做**:不可变历史行无改写入口,会破坏审核留痕)。
|
||||
3. 后台/作者侧的「同标签多实例」可辨识列(落在 `apps/admin-web/**`,正被其它 worker 修改)。
|
||||
4. 「截尾删除致标签回退」的产品兜底(引入单调计数器 = 新字段 = SpacetimeDB schema 变更,须单独评审)。
|
||||
5. `edit-<operationId>` 形状的资源编辑版本是否计入内部版本序数(当前计入,与资源卡口径一致)。
|
||||
6. 旧客户端降级提示写 release note。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 【实施计划】AGC 已发布游戏版本更新
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】AGC已发布游戏版本更新-2026-10-02.md` |
|
||||
| Status | proposed |
|
||||
| Owner | Genarrative Agent |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- `packages/shared/src/contracts/gameCreationApp.ts`:本地唯一 `projectVersion` 和 `publication` 绑定,不混入内部编辑迭代 `versions[]`。**(2026-10-03 取代:发布标签改为由工程内部 `versions[]` 派生、只读;`projectVersion` 降级为不再读写的遗留兼容位,见 `【里程碑】AGC发布版本以工程内部版本为准-2026-10-03.md`。)**
|
||||
- `packages/shared/src/contracts/gameDistribution.ts` 与 Rust shared-contracts:创建版本请求支持正整数 `versionNumber`,允许重复标签提交。
|
||||
- `apps/ai-game-creator-shell`:绑定回读、首次发布/更新 UI、项目版本输入、原 gameId 新提交、结果回写。
|
||||
- `api-server`、`spacetime-client`、`spacetime-module`:创建版本事务只校验正整数;同 gameId/同 versionNumber 生成新 versionId,publicationRevision 继续 CAS。
|
||||
- 现有创建版本 procedure 输入/生成绑定需要同步,但不修改持久表字段。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 契约先补唯一 `projectVersion` 和本地 publication,明确 versionId/版本标签/内部 projectRevision 的边界。
|
||||
2. 创建版本事务不再要求大于 max;未传时保留旧客户端自动递增,传入时校验正整数,并按同版本最新有效提交处理 pending 替代。
|
||||
3. AGC 发布绑定保存 gameId、最新 versionId、状态和 revision;按账号和 API origin 隔离绑定。
|
||||
4. AGC 打开项目回读已有作品;旧项目通过 localProjectId 恢复关联。
|
||||
5. 发布面板编辑 projectVersion,区分首次发布/更新,允许重复提交和回退,不再存在独立 targetVersion。**(2026-10-03 取代:面板不再编辑版本标签,标签由工程内部 `versions[]` 派生且只读;仍区分首次发布/更新。)**
|
||||
6. Rust 发布 facade 在更新模式跳过 create game,使用原 gameId 创建新 version;幂等账本把目标 projectVersion、资料和提交意图纳入发布意图。
|
||||
7. 验证重复提交同版本、回退版本、响应丢失、切换账号、publicationRevision 冲突和旧公开版本继续可玩。
|
||||
|
||||
## 数据库影响
|
||||
|
||||
- 不新增表。
|
||||
- 不修改 `version_number` 字段类型或重排字段。
|
||||
- 修改创建版本 procedure 输入 DTO 与生成绑定;按当前 SpacetimeDB 门禁验证。
|
||||
- 老客户端不传版本号仍自动递增,已有版本和历史数据不需要回填。
|
||||
|
||||
## 验证
|
||||
|
||||
- 领域/数据库创建版本测试:默认递增、指定更高版本、重复/较小/零/负数/非整数/越界输入、并发相同目标号。
|
||||
- AGC 发布面板测试:首发、更新、默认目标版本、用户改号和恢复失败。
|
||||
- AGC Rust 发布测试:跳过 create game、原 gameId、新 version、结果持久化、幂等意图区分。
|
||||
- `npm run typecheck`、定向 Rust/Vitest、schema/生成绑定检查、编码与 diff。
|
||||
- 真机发布:首次发布 → 重开项目 → 修改游戏 → 指定更高版本 → 更新送审 → 审核后原公开链接运行新包。
|
||||
@@ -0,0 +1,52 @@
|
||||
# 【实施计划】后台游戏审核详情与待审版本试玩
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】后台游戏审核详情与待审版本试玩-2026-10-02.md` |
|
||||
| Status | in-progress |
|
||||
| Owner | Genarrative Agent |
|
||||
|
||||
## 修改边界
|
||||
|
||||
### 允许修改
|
||||
|
||||
- `server-rs/crates/api-server`:管理员版本详情/预览会话/待审版本资源读取,以及现有发行资源公共逻辑的安全复用。
|
||||
- `server-rs/crates/shared-contracts`、`apps/admin-web/src/api`:审核详情、预览会话和资料字段 DTO。
|
||||
- `apps/admin-web/src/pages/AdminGameDistributionReviewPage.tsx` 及其测试:详情工作台、试玩入口、审核动作刷新。
|
||||
- 现有 admin 权限映射、发行 sandbox/CSP/storage 兼容逻辑的必要扩展。
|
||||
- 当前玩法主规范、里程碑证据和实施计划。
|
||||
|
||||
### 明确不修改
|
||||
|
||||
- `spacetime-module` 持久表结构、`migration.rs`、SpacetimeDB schema 和现有生成绑定,除非调研发现当前字段不足并重新获得迁移确认。
|
||||
- `/api/external/v1`、作者发布页面、公开游戏详情和用户评价数据模型。
|
||||
- 审核员分派、批量审核、精选推荐和后台游戏资料编辑。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 固化现有详情响应的字段映射与权限边界,先补 admin DTO 和契约测试。
|
||||
2. 定义版本绑定的短期预览会话与错误语义,复用发行 ZIP 路径白名单、大小限制、HTML 注入、CSP 和响应头。
|
||||
3. 实现管理员待审版本预览资源读取,确认公开发行路径不能读取未公开版本。
|
||||
4. 将审核列表升级为详情工作台:资料分组、发布者、版本摘要、封面/截图、试玩状态和审核动作。
|
||||
5. 补齐正常、拒绝、过期、越权、Cookie、缺文件、资源 404、CAS 冲突和幂等重放测试。
|
||||
6. 运行真实本地审核 smoke,确认打开的是待审 `versionId`,不是当前公开版本;完成后回写主规范并删除临时计划。
|
||||
|
||||
## 验证命令
|
||||
|
||||
1. `npm run typecheck`
|
||||
2. `npm exec -- vitest run apps/admin-web/src/pages/AdminGameDistributionReviewPage.test.tsx apps/admin-web/src/api/adminApiClient.test.ts --root .`
|
||||
3. `cargo test -p api-server game_distribution --manifest-path server-rs/Cargo.toml`
|
||||
4. `cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check`
|
||||
5. `npm run check:encoding`
|
||||
6. `npm run check:doc-index`
|
||||
7. `git diff --check`
|
||||
8. 本地 `npm run dev` + 管理员登录 + 待审版本详情/试玩 smoke。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- **预览越权**:Token 必须绑定 `versionId` 和过期时间;错误实现会把私有待审包变成可枚举资源。回滚点是关闭预览会话路由,不影响公开发行网关。
|
||||
- **版本串播**:预览 URL 不能复用 `game.currentVersion.entryUrl`;详情和资源读取都必须携带并校验待审版本。回滚点是隐藏试玩按钮,保留资料审核。
|
||||
- **sandbox 放宽**:不得添加 `allow-same-origin`;如 storage 兼容层回归,回滚新增注入逻辑而不放宽 sandbox。
|
||||
- **资料漂移**:详情必须展示版本冻结快照;若快照缺失,只显示明确缺失状态,不静默用作者当前资料替代。
|
||||
- **审核并发**:继续使用 `publicationRevision` 和幂等键;CAS 冲突只刷新,不自动重放决定。
|
||||
- **API 兼容**:新字段可选,旧后台响应不能因缺少冻结资料而崩溃;预览接口失败不能影响公开游戏游玩。
|
||||
@@ -0,0 +1,81 @@
|
||||
# 【实施计划】游戏作品管理与客户端发布收口-2026-09-30
|
||||
|
||||
关联 Issue:`#470`「做一下创作者作品管理,从工具到平台的一键导出和发布」。
|
||||
分支:`feat/game-works-management`。
|
||||
|
||||
## 目标
|
||||
|
||||
在 10.7「陶泥儿游戏平台」上线前,把「创作者作品管理(用户侧 / 后台)+ 客户端打包上传(只保证 Phaser 4)」这条链路补齐到可用:作品能进来、能看得见状态、能管住。
|
||||
|
||||
- 发布管道(AGC 构建打包 → 8 MiB 分片上传 → 送审 → 后台审核 → 公开可玩 → 发行网关)已实现,并有真实栈证据(见[实施计划【游戏分发阶段A领域合同】](【实施计划】游戏分发阶段A领域合同-2026-09-19.md))。
|
||||
- 本轮已补齐:客户端 Phaser 4 + Vite 工程栈门禁、发布阶段/上传百分比、发布根幂等账本;Web 作者私有详情、状态/版本筛选、版本历史、资料编辑、软删除和作者操作;后台作品搜索、作者/状态筛选、游标分页与删除状态展示。
|
||||
- 生产发布开关仍默认关闭;真实客户端 GUI 发布、生产首个作品和发布包清理策略仍需运行时取证。
|
||||
|
||||
## 已落地(本分支)
|
||||
|
||||
| 项 | 落点 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| 作者读自己名下单个游戏详情 `GET /api/game-distribution/my-games/{gameId}` | `api-server/src/modules/game_distribution.rs`(`get_owner_game`、`load_owner_game_versions`、`owner_game_entry_payload`) | `cargo test -p api-server game_distribution::tests` → 26 passed;`owner_game_entry_payload_carries_private_versions_that_public_payload_omits` 断言作者条目带版本私有状态、公开投影不带 |
|
||||
| 版本私有状态补 `entryUrl` / `packageFileCount` | 同上 `private_version_payload` | 同上测试断言 |
|
||||
| 主规范路由表登记新路由并修正 `/my/games` 错名 | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md` 路由表 | — |
|
||||
|
||||
设计要点:不新增 SpacetimeDB 表与 procedure。详情路由的 game 走 `get_game_distribution_game`(owner 作用域,精确命中),版本沿用作者自有列表 procedure 的聚合结果。
|
||||
|
||||
## 计划与落地状态
|
||||
|
||||
### 波 1:闭环可用
|
||||
|
||||
- [ ] AGC 真机把「构建 → 打包 → 分片上传 → 送审」跑通一次。
|
||||
- [x] 发布进度接线:前端监听既有 `game-package-upload-progress`,渲染真实阶段与百分比。
|
||||
- [x] 幂等键落盘:按账号、origin、本地项目和包摘要持久化根键,重试复用同一 key。
|
||||
- [ ] 运营开闸 + 生产发布第一个作品,走完审核 → 公开 → 游客可玩。
|
||||
|
||||
### 波 2:管理完整
|
||||
|
||||
- [x] 作品资料编辑:展示资料在 `game` 级可编辑、立即生效并留审计;包仍随版本冻结。
|
||||
- [x] 作品软删除:`game_distribution_game` 末尾追加 `deleted_at`,公开投影与列表过滤软删行。
|
||||
- [x] 网页侧对齐:`/games/mine` 补删除、资料编辑、版本历史、作者查看未公开作品。
|
||||
- [x] 后台作品列表:游标分页 + 搜索 + 按作者/状态筛选。
|
||||
|
||||
### 波 3:运营治理
|
||||
|
||||
11. 审核历史展示(复用版本已有的 `reviewed_by_user_id` / `submitted_at` / `reviewed_at` / `published_at`,不新增表)。
|
||||
12. 批量审核操作。
|
||||
13. 精选 / 推荐位(与游玩线的推荐排序定边界)。
|
||||
14. 举报处理(先确认能否复用现役反馈管道)。
|
||||
|
||||
## 尚未完成
|
||||
|
||||
- AGC 真机 GUI 端到端发布取证。
|
||||
- 生产环境发布第一个作品并开闸。
|
||||
- 非 Phaser(Godot / Cocos / Unity)发布的主动拦截:这些工程没有 npm 构建工作区,仍只给泛化错误文案(单 HTML 项目同理)。
|
||||
- 发布包清理策略的上线验收。
|
||||
|
||||
## 不变式
|
||||
|
||||
- 新增字段一律追加到 Rust 表结构体末尾并带明确默认值;不改名、不重排、不改类型。改 schema 后同步 `migration.rs`、表目录与生成绑定,并跑 `npm run check:spacetime-schema`。
|
||||
- 公开投影不得携带作者私有字段(包摘要、驳回理由、文档本地路径)。作者视角走 owner 作用域路由。
|
||||
- 复用现役管道与组件,不新造上传、审核、发行通道。
|
||||
|
||||
## 波 1 · AGC 客户端发布切片(2026-10-01 落地;自动门禁已通过,真实 GUI/生产待验收)
|
||||
|
||||
| 项 | 落点 | 行为 |
|
||||
| --- | --- | --- |
|
||||
| 发布进度接线 | `src-tauri/src/game_package_upload.rs`、`game_distribution_publish.rs`、`commands.rs`、`src/services/gamePackageUploadProgress.ts`、`src/components/game-distribution/GamePublishProgressDialog.tsx`、`GamePublishPhaseSteps.tsx`、`GameDistributionPublishPanel.tsx`、`App.tsx` | 既有 `game-package-upload-progress` 事件扩展出 `phase`(`prepare` / `upload` / `verify` / `submit`)与 `message`,上传阶段仍带 `receivedBytes` / `totalBytes`;`/complete`(重算摘要 + 展开清单)之前由上传器回调切到「校验」。渲染层在全屏进度弹窗显示「构建」步骤,在发布面板显示「构建 → 准备 → 上传 → 校验 → 送审」步骤条与上传百分比。 |
|
||||
| 根幂等键落盘 | 新增 `src-tauri/src/game_publish_attempt.rs`(应用数据目录 `game-publish-attempts.json`,原子写 + 进程内锁) | 键按「账号 + origin + 本地项目 + 归一化包摘要」解析:同一份包重发、响应丢失、进程重启与分片续传复用同一根键(服务端因此只认一次尝试);送审成功后清除记录,下一次发布会新增版本。渲染层不再铸键,`publishLocalProjectGame` 只在显式传入时才带 `idempotencyKey`。账本只存账号标识、origin、本地项目标识、包摘要与根键,不含 token 与本地绝对路径。 |
|
||||
| 发布前工程栈校验 | `src-tauri/src/project/export.rs`(`ensure_publish_project_stack`,导出与发布两条入口都调用) | 存在 npm 构建工作区时要求 Phaser 4 + Vite:缺 `phaser` 依赖、声明的主版本不是 4(含 `package-lock.json` 回退判定)、以及既没有 `vite` 依赖也没有 `vite.config.*` 都给出可操作中文错误(指明缺什么、当前声明、怎么修)。没有 npm 工作区的老单文件项目保持原有发布能力。 |
|
||||
|
||||
## 已知限制
|
||||
|
||||
- 作者自有游戏列表与新增的作者详情共享同一个单次条数上限(48):单作者作品数超过上限时,详情页拿不到版本记录。放量前需要补一条按 `gameId` 精确列版本的 procedure。
|
||||
- 客户端不暴露 `localProjectId`:本地项目与线上作品的对应关系由客户端本地账本维护,服务端只在发布时用它复用 `gameId`。
|
||||
- 发布尝试账本按「本地项目 + 包摘要」判同一尝试:改了工程或重新导出了不同内容的包就是新尝试(新建版本),旧版本的半成品包仍需走网页作者侧的恢复/重置入口。
|
||||
- 发布阶段条按「本次订阅窗口」收敛(事件的 `versionId` 在命令返回前对渲染层不可知);同一窗口同一时刻只有一次发布在跑。
|
||||
- 工程栈校验按首版「只支持 Phaser 4」执行:npm 工程(含 Three.js 等三维工程)未声明 `phaser` 会被拦下并提示安装 Phaser 4。若产品决定放行其它 npm 技术栈,需要在这里改成「phaser 声明存在时必须是 4,其余技术栈按白名单放行」。
|
||||
|
||||
## 尚未完成
|
||||
|
||||
- AGC 真机 GUI 端到端发布取证。
|
||||
- 生产环境发布第一个作品并开闸。
|
||||
- 非 Phaser(Godot / Cocos / Unity)发布的主动拦截:这些工程没有 npm 构建工作区,仍只给泛化错误文案(单 HTML 项目同理)。
|
||||
- 发布包清理策略的上线验收。
|
||||
@@ -49,6 +49,8 @@ AGC 发布面板完成三项收敛:
|
||||
## 6. 本次缺陷修复记录
|
||||
|
||||
- 2026-09-23:AGC 生成游戏封面请求补充 `generationInputs.source = "ai-game-creator-client"`。队列 worker 依据该来源选择 `GameCreatorResourceEditor` 结果契约;未标记来源时会按 `Standard` 紧凑化并省略 `result`,导致生成完成后无法回传 `assetObjectId`。对应前端定向测试已锁定请求字段与平台素材 ID 回填链路。
|
||||
- 2026-10-02:生成结果中的 `imageSrc/objectKey` 是私有素材引用,不再直接交给 WebView `<img>`;AGC Rust 发布 facade 经鉴权 `read-url` 有界下载为 `data:` 图片预览,预览失败不阻断已登记素材发布。
|
||||
|
||||
## 7. 本轮核对(2026-09-28)
|
||||
|
||||
六项交付结果都在合并后的工作树里,并有本轮实测:
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 【里程碑】AGC 发布版本以工程内部版本为准
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | proposed |
|
||||
| Date | 2026-10-03 |
|
||||
| Parent Spec | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`(「身份、状态、审核与更新」章) |
|
||||
|
||||
## 目标
|
||||
|
||||
让 AGC 发布面板的「项目版本」只有一个权威来源:AGC 工程内部版本记录 `manifest.versions[]`(即资源总览「项目版本」栏目里的版本卡)。标签等于当前存活的内部版本条数(最新卡的「版本 N」),只读、不可手填、不被平台 `version_number` 回填或覆盖,与 `publicationRevision` 无关。
|
||||
|
||||
## 边界
|
||||
|
||||
- 不改服务端能力:`publicationRevision` 继续严格 CAS;平台 `versionNumber` 继续接受正整数、重复与回退(网页端与历史客户端依赖该兼容口径)。
|
||||
- 不新增或修改 SpacetimeDB 表、字段与 procedure;`game_distribution_version.version_number` 不动。
|
||||
- 不重写平台历史版本行;不改公开地址、审核状态机与发行网关。
|
||||
- 不做版本历史页、版本对比、一键回滚 UI。
|
||||
- 不删除本地清单字段 `projectVersion`(Rust DTO 带 `deny_unknown_fields`,删除会让存量清单解析失败),它降级为不再读写的兼容位。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 发布面板显示的「项目版本」等于当前 `manifest.versions` 的条数(`versions` 为空时为 1),与平台 `version_number`、`publicationRevision` 均无关。
|
||||
- [ ] 面板不提供任何修改该值的入口;提交时 `versionNumber` 逐字等于该派生值。
|
||||
- [ ] 打开已有线上作品(线上最近提交 v6、内部版本 4 条)时,面板显示 v4,且仍并列显示「线上最近提交 v6 · 审核中」。
|
||||
- [ ] AGC 任何路径都不再写入 `projectVersion`(含发布成功回写、绑定回读回填、面板失焦写回)。
|
||||
- [ ] 存量清单里已有的 `projectVersion` 值不影响展示与提交。
|
||||
- [ ] 内部版本被截尾删除(长度 4 → 3)后,面板显示 v3 且能正常提交,不需要任何本地冲突处理。
|
||||
|
||||
## 依赖
|
||||
|
||||
- 无外部依赖;与「AGC 已发布游戏版本更新(2026-10-02)」是**取代**关系(见该文档内的取代标注)。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 【里程碑】AGC 已发布游戏版本更新
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | proposed |
|
||||
| Date | 2026-10-02 |
|
||||
| Parent Spec | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md#AGC 游戏分发与在线游玩合同` |
|
||||
|
||||
## 目标
|
||||
|
||||
让 AGC 区分首次发布与已有作品更新:已有作品继续使用原 `gameId`;用户维护唯一项目发行版本标签,同一标签可以重复提交或回退,具体提交由不可变 `versionId` 标识。
|
||||
- 同一 `gameId` 的新版本上传、校验、送审和结果回写。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- AGC 完整版本历史页、版本对比和一键回滚 UI;回退版本标签的发布路径属于本里程碑。
|
||||
- 原地替换已发布 `versionId`;已发布提交保持不可变。
|
||||
- 新增 SpacetimeDB 持久表或修改 `game_distribution_version.version_number` 类型。
|
||||
- 改变公开 URL、审核状态机、发行网关或后台审核权限。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 首次发布完成后项目清单保存 `gameId`、`versionId`、版本号、状态和 `publicationRevision`。
|
||||
- [ ] 重新打开同一项目显示“更新游戏”,不再显示“创建平台游戏”。
|
||||
- [ ] 项目清单只有一个用户发行版本字段,发布面板编辑它,不存在独立 targetVersion。**(2026-10-03 取代:发布标签改由 AGC 工程内部 `versions[]` 派生且只读,面板不再编辑;见 `【里程碑】AGC发布版本以工程内部版本为准-2026-10-03.md`。)**
|
||||
- [ ] 同一 `gameId`、同一 `versionNumber` 可以重复提交,生成新的 `versionId`,不覆盖旧公开实例。
|
||||
- [ ] 项目版本可以低于线上最新版本,服务端允许回退标签,但仍校验为正整数。**(2026-10-03 收窄:平台侧继续成立;AGC 侧不再由用户手填回退,派生标签只在内部版本被截尾删除时变小。)**
|
||||
- [ ] 同版本旧 pending 提交被取消或被最新提交替代,审核队列不产生重复有效任务。
|
||||
- [ ] `publicationRevision` 仍用于并发 CAS,与用户版本号规则独立。
|
||||
- [ ] 更新使用同一 `gameId`,只增加一个新 `versionId`,公开地址保持不变。
|
||||
- [ ] 旧项目没有发布绑定时能按作者和 `localProjectId` 恢复;恢复失败时明确回到首次发布。
|
||||
- [ ] 上传响应丢失、重复提交、版本号冲突和切换账号不会创建重复游戏或错误接管作品。
|
||||
@@ -0,0 +1,51 @@
|
||||
# 【里程碑】后台游戏审核详情与待审版本试玩
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | in-progress |
|
||||
| Date | 2026-10-02 |
|
||||
| Parent Spec | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏审核详情与待审版本试玩合同` |
|
||||
|
||||
## 目标
|
||||
|
||||
把后台游戏版本审核从“列表操作”补成可复核的审核详情工作台:审核员能确认发布者和冻结游戏资料,能试玩当前待审版本,并能在同一详情上下达审核决定。
|
||||
|
||||
## 范围
|
||||
|
||||
- 待审版本详情回读。
|
||||
- 发布者、游戏资料、版本包摘要和冻结资料展示。
|
||||
- 单个待审版本的短期隔离试玩会话。
|
||||
- 试玩资源、Cookie、sandbox、CSP 和版本绑定边界。
|
||||
- 版本级审核动作(通过、拒绝)与现有 CAS/幂等规则对齐;游戏级安全下架入口留在「游戏管理」页。
|
||||
- admin-web、api-server、shared-contracts 和相关测试的契约同步。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- 新增游戏审核、发布者、试玩会话或游戏资料持久化表。
|
||||
- 审核员分派、SLA、批量审核和精选推荐。
|
||||
- 后台修改游戏资料、重新构建或重新上传发行包。
|
||||
- 公开发行接口、作者发布链路和 `/api/external/v1`。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- 现有管理员版本详情接口继续返回游戏投影和版本冻结资料。
|
||||
- 现有 SpacetimeDB `game_distribution_game` 与 `game_distribution_version` 字段满足详情展示。
|
||||
- 现有发行 ZIP 校验、静态资源白名单和 HTML storage 兼容层可以抽取复用。
|
||||
- 现有游戏审核权限映射作为第一版预览权限,不新增权限角色。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 审核详情显示发布者、完整游戏资料、版本摘要和审核状态。
|
||||
- [ ] 详情使用待审版本冻结资料,不被后续 game 行资料修改污染。
|
||||
- [ ] 审核员可以打开当前 `versionId` 试玩,且 JS/CSS/图片/音频等同包资源可加载。
|
||||
- [ ] 预览 Token 绑定版本、短期有效、过期和越权读取均失败。
|
||||
- [ ] 预览不读取平台 Cookie,不授予 `allow-same-origin`,保留发行 sandbox/CSP/storage 约束。
|
||||
- [ ] 审核页的通过/拒绝继续执行理由、CAS、幂等和刷新规则;行内不出现游戏级安全下架。
|
||||
- [ ] 无 SpacetimeDB schema、migration、公开契约破坏性变化。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:admin-web 详情/动作测试、api-server 预览会话和权限测试、DTO/类型检查、编码和 diff 检查。
|
||||
- 运行时:本地管理员打开待审版本,确认资料、封面/截图和待审包试玩;确认旧公开版本不会被误播。
|
||||
- 边界:过期 Token、错误 versionId、Cookie、越权管理员、缺入口、资源 404、CAS 冲突和幂等重放。
|
||||
@@ -8,6 +8,19 @@
|
||||
- 边界:改名是簿记写入,与既有 `rename_local_game_project` 同口径**不推进项目 revision**(推 revision 会让运行时验证凭证无故漂移);返回的 `revision` 是当时盘上的值。后台改名结果写「当前项目上下文」与「最近项目行重检」必须等建项主体收尾(`entrySettled`):AI 比进项目更快时直接写上下文会被随后的 `enterProjectDevelopment` 用兜底名覆盖,最近项目行也要等进项目登记过才会被重检;写入前再过壳的生命周期守卫(`mounted` + 代次),关窗/卸载后只保留已落盘的改名,不写 UI 投影。账本是**展示旁路**:`enqueue` / `update` 失败只写诊断日志,绝不影响建项、命名与首轮创作;「做方案」与手动选目录建项不发起自动命名。本次未改共享契约(`packages/shared/**`)、server-rs 与任何 SpacetimeDB schema/HTTP 路由。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/{commands.rs,desktop.rs,asset_generation_tasks.rs,asset_generation_tasks/runtime.rs}`、`apps/ai-game-creator-shell/src/{app/types.ts,features/app-shell/{useHomeProjectCreation.ts,WorkspaceLauncher.tsx},features/resource-canvas/{resourceCanvasAssetGenerationTaskModel.ts,ResourceCanvasAssetGenerationTasksPanelView.tsx},view/project-development/index.tsx}`、`apps/ai-game-creator-shell/tests/{homeProjectNamingAsync.test.tsx,projectNamingGenerationTaskRow.test.tsx,appSurface/home.suite.ts}`。
|
||||
- 验证:`npx vitest run apps/ai-game-creator-shell/tests/homeProjectNamingAsync.test.tsx`(12 passed,含「命名请求永不返回仍进工作区」「AI 结果先于进项目落定仍不被兜底名覆盖」「手动改名不被覆盖且任务按已完成+已跳过收口」「非法/空响应 → 任务 failed 且保留兜底名」「做方案不发起命名」「切到别的工作区不被劫持」「卸载/pagehide 后不写上下文」,并断言入队→命名中→终态的账本推进序列);`projectNamingGenerationTaskRow.test.tsx`(3 passed:固定展示名/不渲染缩略图提示词定位、终态才显示结论、失败徽章与原因 + 在途计数);AGC 全量 `npm run test -- apps/ai-game-creator-shell/tests`(1925 passed / 17 skipped);`cargo test --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute asset_generation_task`(22 passed,含 `naming_task_stays_in_flight_across_ledger_reads_until_terminal` 与 `legacy_v1_ledger_reads_back_with_the_default_asset_generation_task_type`)与 `conditional_project_rename_tests`(6 passed);`npm run agc:typecheck`(含 `check:tests:types`)、`cargo fmt --check`、`npm run check:encoding`、`git diff --check`。
|
||||
## 2026-10-03 AGC 发布版本标签改为由工程内部版本派生,取代「用户可编辑标签」口径
|
||||
|
||||
- 背景:用户实机验收指出发布面板「项目版本」显示 v6,而 AGC 工程内部只有 4 条正式版本记录(资源总览「项目版本」栏目 4 张卡,顶栏「智能体修订」下拉同样只有这 4 条)。核实:面板值来自本地清单 `manifest.projectVersion` 这个可编辑标量,它被三条链路反复钉到**平台** `game_distribution_version.version_number` 上——发布成功回写(`apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs:1531-1535`)、打开面板回读绑定回填(`:536-541`)、用户手改(`:944-965`);而 `manifest.versions` 从头到尾不参与该值。`publicationRevision` 只做 CAS,与任何版本号都无推导关系(`module-game-distribution/src/domain.rs:27-40` 的版本号解析只比 `max_existing` 与 `requested`)。
|
||||
- 决策:AGC 发布面板的「项目版本」改为**由 AGC 工程内部版本记录 `versions[]` 派生**(标签 = 当前存活内部版本条数,即最新版本卡的「版本 N」,空数组取 1),**只读**,且不得被平台 `version_number` 回填或覆盖、不得读取 `publicationRevision`。平台侧 `versionNumber` 语义不变(正整数、允许重复与回退;`None` 仍自动 `max+1` 以兼容网页端与历史客户端)。本地字段 `projectVersion` 降级为遗留兼容位:保留可解析、不再读写(Rust DTO 带 `deny_unknown_fields`,删字段会让存量清单解析失败)。
|
||||
- 取代(逐条):
|
||||
1. `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md:97,99`(原「项目清单保存唯一用户发行版本 `projectVersion`」「AGC 发布面板直接编辑项目清单的 `projectVersion`」)→ 已就地改写为「派生只读 + 遗留兼容位」。
|
||||
2. 同文件原 `:105`(「允许用户修改项目版本标签、重复提交同版本和回退到旧版本标签」)→ 已就地改写为「用户不再编辑版本标签;回退与重复仍由平台侧接受」。
|
||||
3. `docs/project-memory/plans/【实施计划】AGC已发布游戏版本更新-2026-10-02.md:11,23`(「本地唯一 `projectVersion`…不混入内部编辑迭代 `versions[]`」「发布面板编辑 `projectVersion`」)→ 行内追加取代标注。
|
||||
4. `docs/project-memory/plans/【里程碑】AGC已发布游戏版本更新-2026-10-02.md:27-30` 的验收项「项目清单只有一个用户发行版本字段,发布面板编辑它」被取代;「项目版本可以低于线上最新版本」在平台侧继续成立,AGC 侧口径改为「派生标签可能因截尾删除而变小」。
|
||||
- 边界:不改 SpacetimeDB 表/字段/procedure,不改 `publicationRevision` CAS,不改公开地址与审核状态机,不重写平台历史版本行,不做版本历史/回滚 UI。AGC 工程内部版本 `versions` 只能追加或按显式放行删除一个后缀(删素材连带删版本),因此派生标签不保证单调;平台必须继续接受回退标签。
|
||||
- 影响范围:`packages/shared/src/contracts/gameCreationApp.ts`、`server-rs/crates/shared-contracts/src/game_creation_app.rs`、`apps/ai-game-creator-shell/src-tauri/src/{game_distribution_publish.rs,desktop.rs}`、`apps/ai-game-creator-shell/src/{services/gameDistributionPublish.ts,components/game-distribution/GameDistributionPublishPanel.tsx,App.tsx}`、`apps/ai-game-creator-shell/scripts/check-config.mjs`、`apps/ai-game-creator-shell/tests/**`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/project-memory/plans/`。
|
||||
- 验证方式:`npx vitest run …gameDistributionPublish*.test.*`、`npm run agc:typecheck`(含 `scripts/check-config.mjs`)、`cargo test -p shared-contracts`、`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml game_distribution_publish`、`npm run check:doc-index`、`npm run check:encoding`、`git diff --check`;真机:内部版本 4 条、线上最近提交 v6 的项目打开面板显示 v4 且只读,提交 `versionNumber: 4`。
|
||||
- 关联文档:[平台入口与玩法链路](../../【玩法创作】平台入口与玩法链路-2026-05-15.md)、[里程碑](【里程碑】AGC发布版本以工程内部版本为准-2026-10-03.md)。
|
||||
## 2026-10-04 游戏游玩次数修订:关停不强制 flush、flush 失败丢弃剩余分片、客户端 IP 只信 X-Real-IP
|
||||
|
||||
- 变更:ADR `docs/adr/【ADR】游戏游玩次数计数-2026-10-03.md` 修订——原「正常 SIGTERM/滚动重启必须在 `finalize_shutdown` 内 force flush」作废;崩溃、被杀、正常关停都允许丢最后一个未落库窗口,`api-server` 不再注册关停 flush。
|
||||
@@ -27,6 +40,18 @@
|
||||
- 权威文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md` 的 `game_distribution_game` 节,以及 `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md` 的「游玩计数(已实现)」节。
|
||||
- 验证:`cargo check -p api-server` 与 `cargo test -p api-server game_play_counter`(9 passed)通过;前端定向 vitest(点击上报断言 + clientId 稳定性)与 `eslint --max-warnings 0` 通过;`npm run check:server-rs-ddd`、`npm run check:generated-bindings`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check` 通过。
|
||||
|
||||
## 2026-10-04 AGC 工作台顶栏一行到底:三档降级 + 播放并入运行页签 + 运行画面刷新
|
||||
|
||||
- 背景:用户现场截图指出四个问题(改动前的形态与口径见 `docs/technical/assets/agc-toolbar-layout-after-20261004/README.md`):① 顶栏放不下时两侧容器各自 `flex-wrap: wrap`,第二行只剩「播放」与版本入口,两行控件分裂;② 版本入口不贴右缘,被前面按钮的文本宽度顶开;③「播放」与「运行」两个入口说的是同一件事;④ 运行画面里的游戏不是 vite dev 的实时刷新,改完代码只能切到资源管理再切回来才能重载页面(飞书讨论里提的「单独的刷新」)。
|
||||
- 决策(顶栏排版):工具条改**一行到底**(`flex-wrap: nowrap` + `overflow: hidden`),空间不足不再换行,而是按固定顺序降级、档位写在工具条的 `data-layout` 上:`full` → `compact-version`(版本入口只留 `版本 N`)→ `collapsed-actions`(`打开项目目录 / 资源面板 / 整理画布` 收进「更多」下拉)。顺序与 1px 判定余量是纯函数(`workbenchToolbarModel.ts`,单独用例钉顺序);档位由 `useWorkbenchToolbarLayout` 实测写入——`ResizeObserver` 管可用宽度、`MutationObserver` 管**内容**变化(切运行页、出现「恢复草稿」都不改顶栏宽度,只看尺寸会停在旧档位上),每次量宽都把候选档位真的写到 DOM 再读 `scrollWidth`。档位不参与 React 状态:写的是工具条自己的属性,React 不声明就不会覆盖;也没用容器查询(阈值随模式与按钮出现与否变化,写死必然抖)。
|
||||
- 决策(两个「吞掉溢出」的坑,都是实测踩出来的):`overflow: hidden` 的 flex 子项能缩到 0 或靠省略号吸收溢出,档位判定就永远量不到真实溢出——所以版本入口默认 `min-width: max-content`(只在 `collapsed-actions` 档放开为 `0`,那一档已退无可退,省略号才是兜底)、「依赖 / 类型」分段补 `flex: 0 0 auto`(此前窄宽度下被压成 0 宽)。`max-width: 1000px` / `max-width: 760px` 两处把工具条改成 `flex-direction: column` 的媒体查询删除(那是「第二行」的另一个来源,降级已由档位负责),`≤1000px` 里给动作区的 `justify-content: flex-end` 一并删除(溢出会甩到左边,`scrollWidth` 看不见)。最后一档确实放不下时(视口远小于 1280 合同宽度)才改右对齐:宁可裁左边,也不把钉在最右的版本入口裁没。
|
||||
- 决策(版本入口与两端分组):工具条这一行**两端留给体量最大的两枚分组控件**——左端「资源管理 / 运行」、右端「依赖 / 类型」(用户口径:「最大的这两个放两边」),中间依次是动作按钮与版本入口;版本入口 `margin-left: auto` 推到动作区右侧,且紧邻排序分段左侧(用户口径:「版本应该在依赖 / 类型左边」),不再是最右那一枚。显示 `版本 N(原因 · 时间)`,其中 `版本 N` 复用资源画布版本卡的编号口径(`manifest.versions` 落盘顺序 + 1,`formatIterationVersionTitle`),括号里那截是独立一层(`formatIterationVersionDetail`)——窄档位收掉的是这一层而不是整枚入口;可访问名、菜单项与排障文案一律保留完整标识。DOM 顺序由 `tests/resourceVersionSwitch.test.tsx`(版本入口在排序分段之前)与 `tests/resourceCanvasGenerationTasksSidebarDismiss.test.tsx`(排序分段是动作行最后一个子元素)双向钉住。
|
||||
- 决策(播放并入运行 + 刷新入口):删掉独立的「播放」按钮,「运行」页签前加 ▶ 图标,点页签=`showRunView()` + `onPlay?.()`(与旧播放按钮逐字等价,含「再点一次=重跑」);不可运行时页签不置灰、点了既不切视图也不发播放请求,只出既有提示。运行画面右下角新增「刷新运行画面」(全屏那一枚左侧):`onPlay` 命中活体预览只切视图、不重启服务,真正重载页面靠换 `iframe` 的元素身份(`LocalGamePreviewFrame` 新增 `reloadNonce`)——运行页在另一个端口上,跨域 iframe 里 `contentWindow.location.reload()` 会被浏览器挡掉。
|
||||
- 决策(进入项目自动载入):站在运行视图上却没有画面可看时自动补发一次 `onPlay`——典型现场是从别的项目切过来(工作台不重挂,`mode` 是工作台自己的 state,上一条项目的运行视图原样留下而画面已经没了),用户只会看到「客户端运行画面尚未载入」,像坏了一样。只在**没有画面且可运行**时发;`showRunView` 先记账再自己发播放(`autoRunPreviewProjectRef`)所以点页签不会被重复触发;同一个项目只自动补一次,失败不打转,手动重跑仍走页签或画面上的刷新按钮。
|
||||
- 边界:不改后端、契约与 SpacetimeDB;`runAvailable` / `showRunView` 的门槛语义不变,自动切运行的两条路径(会话内已确认的预览、播放请求)不走 `showRunView`,不会多发播放请求。窄于合同宽度只保证不崩,不做移动端布局。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/{styles.css,view/project-development/{index.tsx,workbenchToolbarModel.ts,useWorkbenchToolbarLayout.ts,WorkbenchMoreActionsMenu.tsx},features/resource-canvas/{GameRunVersionPicker.tsx,resourceCanvasVersionBindingModel.ts},features/project-workspace/LocalGamePreviewFrame.tsx}`;用例 `tests/{workbenchToolbarLayout,runPreviewRefresh,runAutoLoadOnEnter}.test.ts(x)`(新增)、`tests/appSurface/project-development.suite.ts`、`tests/{gameRunToolbarActionsStyle,resourceCanvasVersionBindingModel}.test.ts`;文档 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(S17 改写 + 新增 S17a)、`docs/README.md`、新目录 `docs/technical/assets/agc-toolbar-layout-after-20261004/`。
|
||||
- 验证:`npx vitest run apps/ai-game-creator-shell/tests`(200 passed / 1 skipped 文件,1929 passed / 17 skipped 用例,末次全量);定向 8 个文件 253 passed;`npm run typecheck`(在 `apps/ai-game-creator-shell`,含 `check:tests:types`——只跑 `tsc -p tsconfig.json` 覆盖不到 `tests/`)、eslint `--max-warnings 0`、`prettier --check`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check` 全绿。真机几何用一次性 Vite 夹具在真实 Chromium 里逐档实测(视口 1412 / 1240 / 1100 / 1024 / 960 / 860 / 800 / 760 / 700 / 640 / 560 / 480 / 400):始终单行、排序分段贴右缘(`工具栏右缘 - padding - 排序分段右缘 = 0`)、版本入口始终在排序分段左侧、1240/1100 走 `compact-version`、1024/800/700/640/560/480 走 `collapsed-actions`,「更多」下拉三条动作可点且点「资源面板」真的开面板、点「刷新运行画面」`iframe` 换新节点而 `src` 不变;截图见 `docs/technical/assets/agc-toolbar-layout-after-20261004/`。
|
||||
|
||||
## 2026-10-03 AGC 画布引用统一走「活跃聊天输入区」注册表(Issue 602)
|
||||
|
||||
- 背景:画布的「引用」按钮与「拖拽批量引用」只派发 window 事件,消费者只有 `App.tsx` 一处,而它插的是绑在 `PlanningChatView` 上的 `chatComposerRef`;2026-09-22 DirectProject 拆分后普通项目走 `directProjectMode` 提前 return,渲染不到策划面 → ref 恒为 `null`,可选链静默吞掉点击(画布上是死按钮)。同一批合并冲突还丢了 `RESOURCE_REFERENCE_INSERT_MANY_EVENT` 的监听,批量引用连消费者都没有。
|
||||
|
||||
@@ -19,6 +19,41 @@
|
||||
- **判据/取证**:`npx vitest run apps/ai-game-creator-shell/tests/homeProjectNamingAsync.test.tsx` 的「keeps the AI name when the naming result lands before the project is entered」(去掉等待即复现兜底名覆盖);appSurface 的「syncs the workbench title and recent project list with the AI name after creation」。
|
||||
- **边界**:条件改名的判据必须由宿主校验(项目 ID + 当前名称仍是兜底名),前端只转述 `expectedProjectId` / `expectedName`;用户已手动改名时宿主返回 `renamed: false`,回填整体跳过、绝不覆盖用户输入。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts`、`apps/ai-game-creator-shell/src-tauri/src/commands.rs`。
|
||||
## 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 上
|
||||
|
||||
|
||||
Reference in New Issue
Block a user