作品管理与Phaser4客户端发布 #565
Reference in New Issue
Block a user
Delete Branch "feat/game-works-management"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
这个 PR 做了什么
一、定位:作品管理与 Phaser4 客户端发布的第二轮
328ac3184(09-20)游戏分发实现;cc958f367(09-23)后台游戏管理;b60aea4e7(10-01)游戏评价 #561。二、净差规模
现算口径(
git diff origin/master...HEAD --shortstat/--name-status):口径随 head 更新:现 head
e593236f9,对比基准 origin/mastera2f250364。历史锚点:2f6df57ce时为 127 files / +12028 / -1461 / 新增 27 / 删除 0;aa4d17895时为 130 files / +12235 / -1470 / 新增 27;本轮再追加2e7409274(5 files / +27 / -22)与e593236f9(3 files / +91 / -8),即上表现值。三、不可替代的能力(master 上没有)
apps/ai-game-creator-shell/src-tauri/与server-rs/crates/api-server/src/modules/game_distribution.rs。VERSION_NUMBER_CONFLICT(GAME_DISTRIBUTION_VERSION_NUMBER_CONFLICT)。GET / PATCH / DELETE /api/game-distribution/my-games/{game_id}。GET /admin/api/game-distribution/games。POST /admin/api/game-distribution/versions/{version_id}/preview-session+GET /api/game-distribution/admin-previews/{preview_token}[/{*asset_path}]。server-rs/crates/api-server/src/modules/game_distribution_release_storage_bootstrap.js。packages/shared新增GameDetailDisplay/GameDetailShowcase组件:作者侧与后台复用同一套作品详情展示(packages/shared/src/components/GameDetailDisplay/)。四、路由变化
api-server/src/app.rs未变(0 行改动),新路由全部在模块内挂载。my-games/{game_id}与admin-previews家族)。五、SpacetimeDB 变更
GameDistributionGame.deleted_at、CreateVersionInput.version_number、GameSnapshot.local_project_id、AdminGameSnapshot.deleted_at_micros、AdminGameListResult.next_cursor、ReviewListInput.{keyword, owner_user_id, status, cursor}。update_game_distribution_game_metadata_and_return、delete_game_distribution_game_and_return。六、本轮质量修复
apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs:1025(append_agent_game_iteration_version_at改为返回是否登记成功)与apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs:4181-4193(登记后emit_game_creator_manifest_invalidated)。resourceProjectionModel.ts的orderIndex与compareResourceOrder。projectVersion写链路。apps/ai-game-creator-shell/src/styles.css。AdminGameDistributionReviewPage.test.tsx:256)。a1b80ffde+33552d2ed已含游戏评价页排布实现,本分支对应改动删除,以 master 为准(提交aa4d17895,+1/-18)。七、验证
2f6df57ce)= 8/8 全绿;run 3335(headaa4d17895)= 8/8 全绿。e593236f9,本 PR 现 head)截至写入时状态为queued,结论待 CI(已注册,尚未产出结果)。AdminGameDistributionReviewPage.test.tsx7 passed、AdminGameReviewsPage.test.tsx8 passed;前端整体 246 passed / 0 failed(run 3335 口径)。八、同日追加修复(发布版本文案 / 作者名)
head
aa4d17895→e593236f9追加两笔:2e7409274发布版本文案去诱导(5 files / +27 / -22)、e593236f9公开作品详情页作者名(3 files / +91 / -8)。8.1 发布版本文案:为什么要去掉「发布标签 v{N}」
事实依据(为何原文案会误导):
resolveGameCreationAppProjectVersion(manifest)返回versions.length(packages/shared/src/contracts/gameCreationApp.ts:993-999);面板把它作为只读派生值取用(apps/ai-game-creator-shell/src/components/game-distribution/GameDistributionPublishPanel.tsx:282),提交时原样写入versionNumber(同文件:775)。即:发布固定取最新内部版本,与用户在画布上选中哪张卡无关。WorkspaceLauncher.tsx:199的activeVersionId→resourceCanvasVersionBindingModel.ts:51-82,其中isResourceUsedByCurrentVersion要求resource.manifestAssetId !== null;而版本卡在投影里manifestAssetId恒为null(resourceProjectionModel.ts的project-version分支)——版本卡根本没有选中态,画布上无法「选中要发布的那一版」。export_local_project_package_at(apps/ai-game-creator-shell/src-tauri/src/project/export.rs:25)打的是项目当前构建,与任何版本号无关。改法:
game-resource-card-version-latest-tag(apps/ai-game-creator-shell/src/view/project-development/index.tsx:1301;样式apps/ai-game-creator-shell/src/styles.css:8176)。GameDistributionPublishPanel.tsx:292-303)。8.2 公开作品详情页作者名:为什么后端只会给「创作者」
事实依据:
server-rs/crates/api-server/src/modules/game_distribution.rs:2976为game.author_name.as_deref().unwrap_or("创作者")(后台行同族退化为"未知作者"),而创建接口固定写空(同文件:1460为author_name: None)。因此每款游戏的公开详情页都显示「创作者」,不可能是真实昵称。GET /api/auth/public-users/by-id/{id}(前端封装src/services/authService.ts:465-471,与陶泥号by-code同族,无需登录态)。改法:
src/components/game-distribution/useGameAuthorName.ts:useGameAuthorDisplayName—— 回包名是占位词(「创作者」/「未知作者」)或空时,按author.id(=owner_user_id)查询真实displayName;查询中返回null(避免先闪占位词),查不到返回空串、不编造名字;回包名本来就是真实昵称时不再发请求(后端补上后此 hook 自然退化为普通渲染)。src/components/game-distribution/GameDetailPage.tsx:300-304:详情页以解析后的名字装配displayGame(author: { ...game.author, name: authorName ?? '' }),替代直接使用公开投影的author.name。packages/shared/src/components/GameDetailDisplay/index.tsx:214、:239-243:空名不渲染「由 X 制作」那一行;头像首字改用Array.from(authorName)[0](避免 emoji 代理对被截断);作者行补title悬浮显示完整名字。8.3 已知待办(未做 / 待决策)
versionNumber+expectedPublicationRevision的 CAS 语义);(c) 先定义「发布某个版本」到底打什么包(当前打包与版本无关)。未做。server-rs/crates/spacetime-module/src/game_distribution.rs:3286-3293既有口径(按owner_user_id联user_account.display_name,空则回退快照),在公开投影侧解析,可免掉前端这一跳;需重发 spacetime 模块 + api-server。未做。src/components/game-distribution/GameGalleryPage.tsx:114直接渲染game.author.name,同样只会得到「创作者」;每卡一次查询,需先评估 QPS 再定前端补查或后端解析。未做。原始描述(保留)
关联 Issue: #470
状态
WIP。先建立持续可见的实现入口,后续按客户端上传、Web 作者管理、后台作品管理和真实栈验收逐步补齐。
当前范围
不变式
验收
真实 Phaser4 工程完成构建→归一化 ZIP→分片上传→送审;中断/重启恢复不重复创建游戏或版本;作者不能越权读取私有作品;后台可筛选分页并执行审核/下架/恢复;补齐定向测试、类型检查、schema、编码、文档索引、diff 和真实本地栈/客户端 smoke。
详细实施计划:docs/project-memory/plans/【实施计划】游戏作品管理与客户端发布收口-2026-09-30.md
合并前状态
aad8eb2ab;已合并origin/master 13a15fed5(base 为最新,分支不含落后提交)aad8eb2ab,state=success)。本次在本地跑过的门禁结论:check:nginx-spa-routes通过(OK, 14 SPA routes, 1 prefix routes, 3 Nginx templates)、check-pingora-route-parity通过(OK, 25 routes)、check:encoding通过(5250 files)、check:doc-index通过(240 份 Markdown)、git diff --check干净、定向vitest通过(2 files / 49 passed)、eslint --max-warnings 0通过、根typecheck通过Frontend tests的PlatformEntryActiveFlowShell.test.tsx:748是时序敏感 flaky —— 同一文件 blob 在绿头上相同、历史 16+ 次全绿、重跑即过,与本次改动无关check:git-hooks的 EBUSY、以及环境性的resources/plugins档位差异,都不影响 CImanifest.versions.length派生且面板只读(真机实测value=4、readOnly,并并列显示「线上最近提交 v6」)scrollTopMax不变)payment_admin.rs的ORDER BY)、MCP 工具表多播 2 个经 MCP 调用必 401 的工具- api-server 新增 GET /api/game-distribution/my-games/{gameId},作者可读自己名下审核中/被驳回/已下架/已撤回的作品 - 抽取 owner_game_entry_payload 统一作者条目形状,公开投影继续不携带版本私有状态 - 版本私有状态补 entryUrl 与 packageFileCount,供客户端作品详情展示版本历史与公开链接 - 主规范路由表登记新路由,并修正 /my-games 的错名 - 新增里程碑实施计划【实施计划】游戏作品管理与客户端发布收口-2026-09-30本轮实现已完成,仍保持 WIP:
自动验证:主站/后台/AGC typecheck;Web/后台 104 个定向测试;AGC 发布 27 个定向测试;api-server 游戏分发 33 个测试、spacetime-module 262 个过滤测试、shared-contracts 4 个测试;Rust fmt、schema、generated bindings、DTO parity、encoding、doc-index、diff check 均通过。真实 Web 未登录作者入口与后台登录入口已用 Chromium 冒烟;真实 AGC GUI 发布、生产开闸和 OSS 清理策略仍未验证。
补充修正:客户端发布现已对无 npm build 工作区的单文件项目主动失败,只支持 Phaser 4 + Vite;对应 Rust 门禁与导出测试已通过。
更正测试统计:spacetime-module 本次命令匹配到 0 个测试(262 个被过滤),不是 262 个通过;api-server 游戏分发 33 passed,shared-contracts 4 passed,AGC Phaser 门禁 5 + 4 passed。
真实验证边界不变:Web/后台已验证未登录入口与视觉渲染;作者登录后作品操作、后台登录后管理列表、真实 AGC GUI 发布、生产开闸和 OSS 清理仍待具备 warm token/真实客户端与生产条件后验证。
已同步最新 master 到功能分支并推送:\n- 合并提交:e471d4505(包含主线 DirectProject/Codex 相关更新);\n- 修复主线将 game_package_upload 拆到 runtime 后的发布进度接线冲突;\n- 合并后 Web/后台 104 个定向测试、AGC 发布 27 个定向测试、主站类型检查、AGC cargo check、Rust fmt、编码/文档/diff 门禁通过。
已按需求移除 AGC 发布入口的前端灰度阻断。DirectProject 工作台现在直接传递发布回调;仍保留 Tauri 宿主、项目原型完成、封面/资料校验、服务端鉴权与审核校验。
同时删除 read_game_publish_availability 原生命令、配置白名单与对应测试夹具。验证:入口/发布定向测试 29 passed,AGC typecheck、check-config、ESLint、Prettier、Rust fmt 和 encoding/diff 门禁通过。提交:618c69e76。
- 改动文件:apps/ai-game-creator-shell/tests/gameDistributionPublishPanel.test.tsx(仅此一个文件) - 写法替换:把 `Promise.withResolvers<unknown>()` 换成本地手写 deferred(`let settlePending` + `new Promise((resolve) => { settlePending = resolve })`),resolve 仍由外部持有、promise 仍交回测试,与原 onResolve/onReject 语义一致(原 reject 未被使用) - 为什么不改 tsconfig:`Promise.withResolvers` 需要 lib ES2024,而 apps/ai-game-creator-shell/tsconfig.json 是 target/lib ES2022;为单个测试把 lib 提到 ES2024 会改整个 AGC 客户端的编译目标与运行时假设,影响面远大于此处需求,故只在测试内手写等价 deferred - 验证:npm run agc:typecheck 五步全 0(check:tests:types → tsc -p tsconfig.json → skill-pack:check → bundled-resources:check → check-config.mjs)- export.rs:npm_export_tests 的 2 条用例(npm_package_contains_dist_project_root_assets_and_publish_readme、本批新增的 built_project_publish_payload_includes_uploaded_assets_and_prefers_dist_assets)不再用 std::env::var("HOME") 拼临时根,改用仓库既有约定的 crate::tests::canonical_test_tempdir - 动机:Windows 只设 USERPROFILE、不设 HOME 时,std::env::var("HOME").unwrap() 会以 NotPresent panic;改后与本批另 5 条 publish_export_tests 的临时根口径一致 - 未动断言:两条用例的断言与语义保持不变,只替换临时根来源(canonical_test_tempdir 同样返回 tempfile::TempDir,root.path() 用法不变) - 验证(同一命令 cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell -- --test-threads=1 project::export::):HOME 未设置(env -u HOME)→ exit 0,test result: ok. 12 passed; 0 failed;HOME=C:/Users/dongy → exit 0,test result: ok. 12 passed; 0 failed - 顺带核对:project/verification.rs 仍有 1 处同名 HOME 写法,属其它模块范围,本次不动CI run 3295 结论(head
0bf84e288)一句话:CI run 3295 = 6 passed / 2 failed;两处失败均为 master 既有缺陷,与本 PR 无因果关系;本 PR 此前唯一的真实门禁红(AGC shell lane 的旧导出断言)已修复并转绿。
本轮失败项与出处
Backend tests(run 3295 / job 19355)cargo test --locked --workspace --exclude spacetime-module --no-fail-fast --manifest-path server-rs/Cargo.tomlcrates/api-server/src/external_mcp/semantic/tests.rs:33→assertion \left == right` failed/left: 32/right: 30`4b529a895「新增支付服务接入与订单收银台」(PR #597) 在docs/openapi/genarrative-external-v1.openapi.json追加了/api/external/v1/payment/orders与/api/external/v1/payment/orders/{orderId}两条外部操作,未标x-mcp-excluded,因此经external_mcp.rs的自动派生进入 MCP 工具表;而semantic/tests.rs的期望值未同步(MCP_OPERATIONS.len() == 30)。master 自身 run 3268 / job 19139 逐字相同。 紧随的names.len() == 45(实际 47)亦已过期,同源。Repository checks(run 3295 / job 19358)npm run check:repository-ci[check:nginx-spa-routes] FAILED - deploy/nginx/genarrative.conf 缺少 SPA 路由: /pay, /profile/payment(共 15 条诊断,涉及deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、deploy/container/nginx.conf)/pay、/profile/payment加进src/routing/activeAppPageRoutes.ts,但未同步 nginx 模板与 Pingora 网关 SPA 清单。master 自身 run 3268 / job 19142 相同。check:pingora-route-parity因 npm 链在该步中断而未执行;同一缺口同样存在于server-rs/crates/pingora-gateway/src/main.rs的MAIN_SPA_PATHS(12 条 vs 应为 14 条)。deploy/**、src/routing/**、server-rs/crates/api-server/src/external_mcp/**零差异,可在 master 上独立复现。本 PR 本轮修掉的门禁红
AI game creator shell Rust lane 2/2(run 3295 / job 19353)→ success。原用例project::export::npm_export_tests::npm_package_contains_only_dist_and_publish_readme的期望(仅 3 条目、不含项目根assets/**)与生产行为冲突;其对应的真实回归用例已随一次 master 合并被删除。现已改名为npm_package_contains_dist_project_root_assets_and_publish_readme并按契约文档口径扩断言,另补回 6 条发布前工程栈/导出用例。本轮主要内容
manifest.versions条数派生、面板只读;拆除projectVersion写链路与该手改命令。pitfalls.md4 条新条目)。真机核验(非测试替代)
真实 AGC 客户端(WebView2 + CDP)实测发布面板:「项目版本」输入框
value=4、readOnly=true、aria-readonly=true,与get_local_game_manifest的versions.length=4一致;面板并列显示「线上最近提交 v6 · 审核中」;面板内可编辑版本号输入框数量为 0;磁盘清单中遗留的projectVersion键已被抹除。本机环境项(不经由 CI,避免误读)
Windows 本机
check:git-hooks因临时目录EBUSY失败;AGC rust 分片 runner 会被一个 Windows 阻塞模态用例挂死(Linux CI 不受影响,本地改用等价分块跑)。两者均不经由 CI;本机 cargo 类门禁已在 featureless 档位下实跑通过。补充:CI run 3297(head
c4b3d2204)Backend tests←crates/api-server/src/external_mcp/semantic/tests.rs:33期望 30 实际 32;Repository checks←check:nginx-spa-routes缺/pay、/profile/payment;本批引入 = 0。check:pingora-route-parity仍因 npm 链在前一步中断而未执行(无报错原文,推定同类)。c4b3d2204补回 AGC 客户端发布/导出路径的 10 条机器级用例(project::export::本机 19 passed / 0 failed),CI 用例数1884 → 1894,两个 AGC Rust lane 均 success。base is ancestor of head满足(origin/master = ccd271572,盯梢期间未推进)。- 改动规则:apps/ai-game-creator-shell/src/styles.css 的 `.game-distribution-publish-panel` 追加 `scrollbar-width: none` 与其 `::-webkit-scrollbar { display: none; }` - 为什么用 scrollbar-width: none:该抽屉自身就是唯一滚动区(用户口径:这条抽屉里的滚动条太丑);实测隐藏后引擎不再为滚动条预留槽位,内容宽度恒定、无宽度跳变 - 另一条 webkit 规则的必要性:`scrollbar-width` 只覆盖标准路径(Firefox / 新引擎),Chromium / WebView2(AGC 真机)仍需 `::-webkit-scrollbar` 才能隐藏,否则真机上滚动条依旧可见 - 真机实测结论(CDP,WebView2):滚动条布局占位 15px → 0,`clientWidth` 503 → 518,`scrollTopMax` 231 不变,滚轮与键盘仍可滚到底 - 验证命令:`npm run check:encoding`、`git diff --check`- 根因:版本追加只写盘、不发「清单已变」事件,而前端被当作「回合结束刷新」的那次调用实际发生在入队时(useDirectProjectChatController.ts:588-592 紧跟 startTurn 的 finally,await 的只是 enqueue_direct_codex_turn),于是 agent 追加版本后无人重读清单,新卡必须先重开项目才出现(旁证:resource-layouts/dependency.json 里 74 分钟缺该版本,直到重开项目才补齐) - 修复:版本真正追加成功时才 emit_game_creator_manifest_invalidated(root, "direct-codex.version")(与素材工具桥同口径),订阅侧立刻重读清单 - 版本卡显示顺序:orderIndex(内部版本序数)+ compareResourceOrder,修正初始版本被排到最后(4 版读作 2/3/4/1 → 现为 1..N 单调);不带 orderIndex 的资源排序与坐标逐值不变 - 最新版本卡角标:`最新 · 发布标签 v{N}`(只给 versions 最后一条,N = versions.length = 发布面板取的标签) - 发布面板文案:「本次发布标签 = 最新内部版本 v{N};agent 再改进一次后,下一次发布将是 v{N+1}」,并保留「平台线上号是平台侧事实、与本标签互不推导」 - 验证:vitest apps/ai-game-creator-shell/tests 1935 passed / 0 failed;cargo check(带编辑器 feature)通过;encoding / typecheck 通过;真机未验(环境重新拉起前无法核验,待补)- AdminGameReviewsPage.tsx:删除我们新增的重复 tableClassName(master 已在同一组件上有一份,保留 master 那处),保证同名 JSX 属性只剩一处 - AdminGameReviewsPage.tsx:操作列 td 去掉 className="admin-game-reviews-actions-cell"(该 class 的唯一样式来源已随本提交删除,留着就是死代码) - styles/admin.css:整段删除我们尾部新增的平行实现(.admin-game-reviews-table th:last-child 与 .admin-game-reviews-actions-cell .admin-action-row 的 width/nowrap/flex-end) - 保留 master 的评价列表排布体系:.admin-game-reviews-table .admin-status、td:last-child .admin-action-row{flex-wrap:nowrap;justify-content:flex-start} 与 >button{flex-shrink:0} - 自证:tableClassName="admin-game-reviews-table" 命中 1 处;admin-game-reviews-actions-cell 命中 0 处- resourceProjectionModel.ts:publicationTag 注释改为「值是内部版本序数(versions.length),也就是发布时会提交的那个标签」,并注明卡面只渲染中性的「最新」角标,不再写「发布标签 v{N}」 - GameDistributionPublishPanel.tsx:「项目版本」说明注释写明发布提交的标签是 versions.length(最新内部版本序数),不读任何「选中版本」;画布上的选中/「当前版本」只影响当前版本素材与运行播放哪一版 - index.tsx / styles.css:版本卡角标由「最新 · 发布标签 v{N}」改为中性的「最新」,并换用卡面既有的中性 pill 样式 - gameDistributionPublishPanel.test.tsx:同步旧的角标/文案断言- useGameAuthorName.ts(新增):useGameAuthorDisplayName —— 回包名是占位词(「创作者」/「未知作者」)或空时,按 author.id 调 GET /api/auth/public-users/by-id/{id} 取 displayName;查询中返回 null(避免先闪占位词),查不到返回空串、不编造名字 - GameDetailPage.tsx:详情页用解析后的作者名装配展示对象(displayGame),替代直接用公开投影里的 author.name - packages/shared/src/components/GameDetailDisplay/index.tsx:作者名为空时不渲染「由 X 制作」那一行;头像首字改用 Array.from(name)[0](避免 emoji 代理对被截断);作者行补 title 悬浮显示完整名字- GameDistributionPublishPanel.tsx:删除「项目版本」下的说明段(projectVersionHint 派生逻辑与其 useMemo、以及渲染它的 hint span),面板不再写「发布将使用最新内部版本 v{N}…」「线上最近提交 vN 是平台侧事实…」 - GameDistributionPublishPanel.tsx:删除简介/分类的三态提示文案(「正在根据创作内容生成简介和分类…」/「AI 已根据创作内容生成…」/「已根据创作内容预填…」)及支撑它们的 metadataSuggestionPending、metadataSuggestionReady 两个 state 与请求完成后的置位,保留静默预填行为 - apps/ai-game-creator-shell/src/styles.css:删除随上述提示一起作废的样式(-7 行) - tests/gameDistributionPublishPanel.test.tsx:同步删除对已废弃提示文案的断言(-15 行)合并意见(阻塞项评审)
结论:可合 —— 未发现阻塞项。
git diff origin/master...HEAD5a651f3ea65514328f一、范围与结论
deleted_at,spacetime-module/src/game_distribution.rs的delete_game_distribution_game_tx/ensure_game_distribution_game_not_deleted),版本行与冻结资料不可变。⚠️ 后台「删除评价」是硬删(moderation的tx.db.game_distribution_review().review_id().delete(...)),但经git grep origin/master确认该硬删在 master 已存在(module:691、admin 路由:243),本 PR 的 module diff 对该处零命中 → 存量行为,非本 PR 引入;审计表game_distribution_review_moderation_log不含score/comment,误删不可恢复。/admin/api/game-distribution/*全部require_admin_auth(:277-281、:378-384);admin-previews的 token 是Uuid::new_v4(state.rs:771-793)、TTL 600s(:2495)、读时收敛过期、拒绝带 Cookie 的请求(:2562-2570)、仅放行pending_review/rejected(:2581-2584);公开面只走get_public_game_distribution_game,serve_release_asset先查 public 再校验 published;作者写路径用claims.user_id并与模块owner_user_id逐条比对。GameDistributionGame.deleted_at的#[default(None::<Timestamp>)]等),migration.rs同步,module_bindings已重生成;/api/external/v1无变更 → OpenAPI 无需同步。createGameDistributionPublishKey/readGamePublishAvailability/createSuspendIdempotencyKey/GameShowcase(Thumb)/applyVersionMediaDefaults/useGameReviews参数变化)逐个反查无残留调用;与 mastera1b80ffde/33552d2ed的重复实现已去干净(tableClassName只在 sharedAdminListPanel定义一次;admin-game-reviews-actions-cell全库 0 命中)。TODO/FIXME/HACK= 0;loadVersionRecoveryActions已接线(MyGamesPage:200-211);抽出的CATEGORIES/ORIENTATIONS/applyVersionMediaDefaults已落到新文件并被两页共用。2e7409274/5a651f3ea仅文案与死样式/死状态;e593236f9的useGameAuthorDisplayName仅GameDetailPage:300一处使用(详情页单请求),排除占位词「创作者」「未知作者」,GameDetailDisplay:214/237空名不渲染作者行;AdminFormat.normalizeAdminTimestamp只归一^\d+\.\d{1,9}Z$。二、重要但不阻塞(5 条,含证据与最小修法)
spacetime-module/src/game_distribution.rs:936GameDistributionCreateVersionInput.version_number插在中间(非末尾)→ 若 module 与 api-server 非严格同批上线,procedure 入参按位 BSATN 解析会错位(显式失败,非静默脏数据);最小修法:挪到末尾并重生成绑定。api-server/src/modules/game_distribution.rs:1206-1226(load_owner_game_versions,MAX_LIST_LIMIT=48见:87)→ 单作者作品 > 48 时/my-games/{id}版本列表为空(有降级、无错数据);放量前补按game_id精确列版本的 procedure。src/services/gameDistributionClient.ts(loadVersionRecoveryActions)→ 作者中心首屏最多约 48 个并发版本回读(已收敛为「每作品仅 latestVersion 且需作者动作」);量级变大时加并发上限或批量接口。server-rs/crates/api-server/src/modules/game_distribution_release_storage_bootstrap.js+game_distribution.rs:107-112→ 发行/试玩 HTML 前无条件用内存实现覆盖window.localStorage/sessionStorage:opaque sandbox 下是必要修复,但若发行侧存在真实 origin会把原生持久化顶掉(托管游戏本地存档不跨刷新保留);建议探测式安装或在描述里写明「托管游戏不做持久化」。docs/project-memory/plans/【实施计划】游戏分发阶段A领域合同-2026-09-19.md:116仍写「未配置game-distribution:publish时默认开放」,与state.rs:1236-1252及本 PR 注释相反 → 仅文档陈旧,建议同步。三、遗留待办(非阻塞,已在描述「八、3」标注)
owner_user_id补查/api/auth/public-users/by-id/{id});GameGalleryPage.tsx:114同根因;四、评审边界
不确定项:
.delete(按存量处理);