图集切片上限对齐平台契约:客户端结果门 64 → 256 #442

Merged
suzmii merged 2 commits from fix/spritesheet-slice-limit-256 into master 2026-09-21 10:27:39 +08:00
Member

背景(现场)

项目 gameagent-6e53c9e8,2026-09-21 07:54「AI 生成图标素材」失败:

生成素材失败:platform-generation-result-unknown: 异步生成完成结果无法绑定到 operationId:
External Editor 旧同步结果的图集切片超过 64 个

排查(本机日志与项目运行时账本):

  • .agent/runtime/asset-generation-tasks/tasks.jsontaskId=c70a4198-…kind=icon-spritesheet,created 07:54:04 → failed 07:55:43(跑了 99 秒,平台确实生成并切完了图),assetId: null —— 结果没落 manifest,这次付费产物被丢弃
  • .agent/runtime/canvas-generation-requests/manual-canvas-asset-generate/slot-560175669f….jsonsliceMode: "connected-components"sliceCount: null(自动切分),即切几片由平台决定;客户端并没有要求一个 >64 的数量。

根因:同一条链路两个上限

环节 上限
平台切分(server-rs/crates/api-server/src/editor_project_icon.rs EDITOR_ICON_SPRITESHEET_MAX_SLICES 256
Agent 工具 schema sliceCountagent_native_tools.rs 1..256
工具参数校验(direct_tool_bridge.rs 1..256
持久化产物批次(spacetime-module/src/editor_project_storage.rs 1..256
公开契约(docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md 「最多 256 个输出」
客户端结果绑定门(两处) 64

两处门由 602723ea0(2026-08-03)引入,之后平台与工具 schema 统一到 256,这两个门没跟着改。自动切分落进 65~256 这个窗口时,客户端比平台更严 → 平台合法产出被整条判失败,用户只看到一句定位不到原因的「无法绑定到 operationId」。

改动

  • 新增 PLATFORM_ART_SPRITESHEET_MAX_SLICES = 256(放在 canvas_generation.rs 现有 PLATFORM_ART_SPRITESHEET_* 预算常量旁),注释点名三处同值权威:平台切分常量、工具 schema sliceCount、公开契约。
  • 判据与拒绝文案各只留一份:platform_art_spritesheet_slice_count_exceeds_limit / platform_art_spritesheet_slice_limit_error(数字由常量插值,不再手写「64」)。
  • 两个入口改用它:external_generation_state.rs 的旧同步结果转换、canvas_generation.rs 的切片准备;文案从 64 更新为 256。

验证(本机)

  • 新增 canvas_generation_tests::spritesheet_slice_limit_matches_platform_and_tool_contract:上限值 = 256、边界判据(0/64/256 通过、257 拒绝)、拒绝文案的数字来自常量。ok
  • 新增 external_generation_state_tests::legacy_result_accepts_slice_counts_up_to_platform_limit_and_rejects_beyond:64 / 65 / 256 片的旧同步结果必须能持久化且切片一条不少,257 片必须按同一句文案拒绝。ok
  • 变异验证:把常量改回 64,上面那条回归用例立刻变红(已实测并还原),证明用例真的钉住了这次的现场。
  • 定向回归:cargo test -- spritesheet(22 passed)、cargo test -- external_generation_state_tests::(10 passed);cargo fmt --check 干净;npm run check:encodinggit diff --check 干净。

未覆盖 / 说明

  • 真实客户端复验未做:需要重新生成一次图标素材(connected-components 自动切分)确认 65~256 片的产出能正常落盘。
  • 256 片时的累计下载 / 解码像素预算未实测:平台自己的总像素上限是 2048×2048,客户端预算是 4096²,按「切片是整图互不重叠子矩形」推算不会先撞预算;真撞了也只会给出明确错误,不会写出损坏数据。
  • 未动平台切分、OpenAPI、数据库与前端。
  • 顺带发现两条不在本 PR 范围的线索,已单独记录备查:① 09-18 那次是 sliceCount=4 而平台识别出 94 个素材,按契约「自动拆分超限以 sliceWarning 非阻断降级且不产生切片」返回 0 切片(属既有设计,但前端是否展示了该 warning 值得复看);② 应用日志反复报 rawKind=art-spritesheet 不是 canonical 值(canonical 是 icon-spritesheet),manifest 里已有 7 个 unknown 资产,来源需另查。
## 背景(现场) 项目 `gameagent-6e53c9e8`,2026-09-21 07:54「AI 生成图标素材」失败: ``` 生成素材失败:platform-generation-result-unknown: 异步生成完成结果无法绑定到 operationId: External Editor 旧同步结果的图集切片超过 64 个 ``` 排查(本机日志与项目运行时账本): - `.agent/runtime/asset-generation-tasks/tasks.json`:`taskId=c70a4198-…`、`kind=icon-spritesheet`,created 07:54:04 → failed 07:55:43(**跑了 99 秒,平台确实生成并切完了图**),`assetId: null` —— 结果没落 manifest,**这次付费产物被丢弃**。 - `.agent/runtime/canvas-generation-requests/manual-canvas-asset-generate/slot-560175669f….json`:`sliceMode: "connected-components"`、**`sliceCount: null`(自动切分)**,即切几片由平台决定;客户端并没有要求一个 >64 的数量。 ## 根因:同一条链路两个上限 | 环节 | 上限 | | --- | --- | | 平台切分(`server-rs/crates/api-server/src/editor_project_icon.rs` `EDITOR_ICON_SPRITESHEET_MAX_SLICES`) | **256** | | Agent 工具 schema `sliceCount`(`agent_native_tools.rs`) | **1..256** | | 工具参数校验(`direct_tool_bridge.rs`) | **1..256** | | 持久化产物批次(`spacetime-module/src/editor_project_storage.rs`) | **1..256** | | 公开契约(`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`) | 「最多 **256** 个输出」 | | **客户端结果绑定门(两处)** | **64** ❌ | 两处门由 `602723ea0`(2026-08-03)引入,之后平台与工具 schema 统一到 256,**这两个门没跟着改**。自动切分落进 65~256 这个窗口时,客户端比平台更严 → 平台合法产出被整条判失败,用户只看到一句定位不到原因的「无法绑定到 operationId」。 ## 改动 - 新增 `PLATFORM_ART_SPRITESHEET_MAX_SLICES = 256`(放在 `canvas_generation.rs` 现有 `PLATFORM_ART_SPRITESHEET_*` 预算常量旁),注释点名三处同值权威:平台切分常量、工具 schema `sliceCount`、公开契约。 - 判据与拒绝文案各只留一份:`platform_art_spritesheet_slice_count_exceeds_limit` / `platform_art_spritesheet_slice_limit_error`(数字由常量插值,不再手写「64」)。 - 两个入口改用它:`external_generation_state.rs` 的旧同步结果转换、`canvas_generation.rs` 的切片准备;文案从 64 更新为 256。 ## 验证(本机) - 新增 `canvas_generation_tests::spritesheet_slice_limit_matches_platform_and_tool_contract`:上限值 = 256、边界判据(0/64/256 通过、257 拒绝)、拒绝文案的数字来自常量。**ok** - 新增 `external_generation_state_tests::legacy_result_accepts_slice_counts_up_to_platform_limit_and_rejects_beyond`:64 / 65 / 256 片的旧同步结果必须能持久化且切片一条不少,257 片必须按同一句文案拒绝。**ok** - **变异验证**:把常量改回 64,上面那条回归用例立刻变红(已实测并还原),证明用例真的钉住了这次的现场。 - 定向回归:`cargo test -- spritesheet`(22 passed)、`cargo test -- external_generation_state_tests::`(10 passed);`cargo fmt --check` 干净;`npm run check:encoding`、`git diff --check` 干净。 ## 未覆盖 / 说明 - **真实客户端复验未做**:需要重新生成一次图标素材(`connected-components` 自动切分)确认 65~256 片的产出能正常落盘。 - 256 片时的累计下载 / 解码像素预算未实测:平台自己的总像素上限是 2048×2048,客户端预算是 4096²,按「切片是整图互不重叠子矩形」推算不会先撞预算;真撞了也只会给出明确错误,不会写出损坏数据。 - 未动平台切分、OpenAPI、数据库与前端。 - 顺带发现两条**不在本 PR 范围**的线索,已单独记录备查:① 09-18 那次是 `sliceCount=4` 而平台识别出 94 个素材,按契约「自动拆分超限以 `sliceWarning` 非阻断降级且不产生切片」返回 0 切片(属既有设计,但前端是否展示了该 warning 值得复看);② 应用日志反复报 `rawKind=art-spritesheet` 不是 canonical 值(canonical 是 `icon-spritesheet`),manifest 里已有 7 个 `unknown` 资产,来源需另查。
suzmii added 1 commit 2026-09-21 08:32:12 +08:00
图集切片上限对齐平台契约:客户端结果门 64 → 256
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m55s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m56s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 7m42s
Project CI / Backend tests (pull_request) Successful in 5m19s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 8m39s
Project CI / Native shell tests (pull_request) Successful in 7m24s
Project CI / Repository checks (pull_request) Successful in 3m54s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m32s
Project CI / Frontend tests (pull_request) Successful in 4m54s
1997dfeb80
- 现场(gameagent-6e53c9e8)「AI 生成图标素材」失败:平台已生成并切完图,客户端在绑定结果这一步把整条结果判失败(assetId 为空、付费产物被丢)。
- 根因:同一条链路存在两个上限——平台切分 / Agent 工具 schema sliceCount / 持久化产物批次都是 256,客户端两处结果绑定门仍是 64(external_generation_state.rs、canvas_generation.rs)。
- 修复:两处门统一到 PLATFORM_ART_SPRITESHEET_MAX_SLICES = 256,判据与拒绝文案各只留一份(数字由常量插值),注释点名三处同值权威。
- 用例:新增上限值/边界/文案用例与「64/65/256 片必须能落盘、257 片必须拒」的回归用例;两条都做过变异验证(常量改回 64 即红)。
- 文档:decision-log 与 pitfalls 各补一条(客户端门不得比平台产品上限更严;上限引同一常量)。
suzmii added 1 commit 2026-09-21 10:15:48 +08:00
Merge remote-tracking branch 'origin/master' into fix/spritesheet-slice-limit-256
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m58s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m50s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m50s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m57s
Project CI / Backend tests (pull_request) Successful in 8m54s
Project CI / Native shell tests (pull_request) Successful in 10m16s
Project CI / Frontend tests (pull_request) Successful in 8m14s
Project CI / Repository checks (pull_request) Successful in 7m19s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m56s
56dcb06fb8
# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
suzmii merged commit 959d367ed4 into master 2026-09-21 10:27:39 +08:00
suzmii deleted branch fix/spritesheet-slice-limit-256 2026-09-21 10:27:39 +08:00
Sign in to join this conversation.