Merge remote-tracking branch 'origin/codex/wooden-fish-template'

This commit is contained in:
kdletters
2026-05-22 08:09:58 +08:00
617 changed files with 31612 additions and 237 deletions

View File

@@ -5,6 +5,7 @@
## 记录格式
```md
## 问题标题
- 现象:看到什么错误或异常行为
@@ -1289,3 +1290,51 @@
- 处理:`platformEntryTypes.ts` 必须注册 `jump-hop-workspace/generating/result/runtime/gallery-detail``appPageRoutes.ts` 必须补 `/creation/jump-hop/workspace``/creation/jump-hop/generating``/creation/jump-hop/result``/gallery/jump-hop/detail``/runtime/jump-hop``PlatformEntryFlowShellImpl.tsx` 必须持有 JumpHop session/work/run/gallery/runtimeReturnStage/generationState/error/busy并提供 `mapJumpHopWorkToPublicWorkDetail``RpgEntryHomeView.tsx` 的公开卡片类型描述要给 JumpHop 单独返回 `跳一跳`
- 验证:`npm run typecheck`,并跑 `npm test -- src/routing/appPageRoutes.test.ts` 覆盖 JumpHop 阶段路径。
- 关联:`src/components/platform-entry/platformEntryTypes.ts``src/routing/appPageRoutes.ts``src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/RpgEntryHomeView.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## image2 dry-run 带参考图时不要直接打印 data URL
- 现象:使用 VectorEngine `gpt-image-2-all` 生成带参考图的概念图时,如果 dry-run 直接打印完整请求体,参考图会被转成超长 `data:image/png;base64,...`,终端日志会被数百万字符淹没。
- 原因:生成请求支持 `image` 数组传入 data URL 参考图dry-run 如果复用 live 请求体输出,就会把参考图内容完整打印。
- 处理dry-run 输出摘要,只保留 `imageReferenceCount`、尺寸、模型和 prompt不输出完整 base64。live 请求仍按实际需要传 `image` 数组。
- 验证:执行 `node scripts/generate-edutainment-tv-map-concepts.mjs --dry-run`,输出应只显示 `imageReferenceCount: 1`,不出现完整 base64。
- 关联:`scripts/generate-edutainment-tv-map-concepts.mjs``docs/design/【前端体验】寓教于乐电视端乐园地图入口概念图-2026-05-18.md`
## 生成图资产不能只拼 generated legacy path
- 现象:结果页或运行态拿到 `/generated-*-assets/.../image.png` 后图片不显示;前端 `ResolvedAssetImage` 会先调用 `/api/assets/read-url?legacyPublicPath=...`,但换签后的 OSS URL 仍指向不存在对象。
- 原因:后端只写了看起来像生成图的 legacy path没有真正调用 image2、上传 OSS、登记 `asset_object` 并绑定实体。`/api/assets/read-url` 只负责签名读取,不会凭空生成或补写对象。
- 处理:玩法生成链路必须在 `api-server` 完成外部副作用:调用 VectorEngine `gpt-image-2-all`,用 `GeneratedImageAssetAdapter` 准备 `PutObject`,上传 OSS 私有对象,调用 `confirm_asset_object``bind_asset_object_to_entity`,再把返回的 `legacyPublicPath` 写入玩法 profile。
- 验证:`cargo check -p api-server --manifest-path server-rs/Cargo.toml`;契约测试应断言前端 JSON 自带的 `hitObjectAsset` 会被忽略spacetime-client 定向测试应断言缺少服务端注入的真实 `hitObjectAsset` 时不能编译;浏览器 Network 中 generated 图片应先换签,签名 URL 指向已存在对象。
- 关联:`server-rs/crates/api-server/src/wooden_fish.rs``server-rs/crates/spacetime-client/src/wooden_fish.rs``src/components/ResolvedAssetImage.tsx``src/services/assetReadUrlService.ts`
## `dev:spacetime` 启动后 3101 又断开先查 publish 是否被 spacetime.json 干扰
- 现象:浏览器报 `Failed to initiate WebSocket connection`,目标为 `ws://127.0.0.1:3101/v1/database/<db>/subscribe`,端口检查发现 `3101` 没有长期监听;手动运行 `npm run dev:spacetime` 可看到 standalone 短暂启动后退出,发布阶段报 `No database target matches '<db>'`
- 原因SpacetimeDB CLI 会读取仓库根目录 `spacetime.json`。如果本地发布命令没有显式 `--no-config`CLI 可能按配置文件里的 target 解析数据库,覆盖脚本已传入的 `.env.local` 数据库名和 `--server`,导致 publish 失败;`dev.mjs` 捕获错误后会清理刚启动的 standalone于是浏览器看到 3101 被拒绝连接。
- 处理:`scripts/dev.mjs` 的本地 publish 固定追加 `--no-config`只使用脚本解析出的数据库名、module path 和实际 SpacetimeDB server。排查时前台运行 `npm run dev:spacetime -- --no-interactive`,若看到该错误,先确认脚本是否仍带 `--no-config`,再查 `.env.local` / `spacetime.local.json` 的数据库名。
- 验证:`npm run test -- scripts/dev.test.ts` 覆盖 publish 参数包含 `--no-config``npm run dev:spacetime -- --no-interactive``http://127.0.0.1:3101/v1/ping` 应保持 200。
- 关联:`scripts/dev.mjs``scripts/dev.test.ts``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## 本地 api-server 启动订阅 401 先查 Web identity token 注入
- 现象:`npm run dev` 启动到 api-server 恢复认证快照时,日志出现 `Failed to initiate WebSocket connection ... /v1/database/<db>/subscribe?compression=Brotli: HTTP error: 401 Unauthorized`
- 原因SpacetimeDB SDK 订阅需要 Web API identity token本地 `.env.local` 常把 `GENARRATIVE_SPACETIME_TOKEN` 留空,只靠 CLI 登录态 publish 成功并不能让 api-server 的 WebSocket subscribe 获得权限。
- 处理:`scripts/dev.mjs` 在 SpacetimeDB 就绪后调用 `/v1/identity` 创建当前进程专用 Web API identity token并只注入本次 `api-server` 环境;不要把临时 token 写进 `.env.local` 或日志。若仍报 401先确认是否使用了项目脚本启动、日志是否出现 `已创建本地 Web identity`,以及 `GENARRATIVE_SPACETIME_SERVER_URL` / 数据库名是否指向本次启动的实例。
- 验证:`npm run test -- scripts/dev.test.ts`;重新运行 `npm run dev` 后 api-server 启动日志不再出现上述 subscribe 401`/healthz` 返回 200。
- 关联:`scripts/dev.mjs``scripts/dev.test.ts``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## 创作作品架消失先查入口配置 procedure 与本地库权限
- 现象:寓教于乐或创作中心下草稿 / 已发布作品突然整块消失,`GET /api/creation-entry/config` 返回 `502`details 中为 `No such procedure`
- 原因:本地 `.env.local``spacetime.local.json` 指向的 SpacetimeDB 库没有发布当前 `spacetime-module`,或当前 CLI 身份无权发布该库;例如旧 `xushi-p4wfr` 库缺 `get_creation_entry_config` 时,前端拿不到入口配置就不会渲染作品架。
- 处理:优先切换到拥有目标库权限的 SpacetimeDB 身份后重新运行 `npm run dev` 完成发布;若只是本地验证,可用 gitignored 的 `spacetime.local.json` 指向可发布的本地库。debug 构建的 `api-server` 对入口配置缺 procedure 会使用后端默认入口配置兜底,避免作品架因本地库漂移整块空白。
- 验证:`curl.exe -i http://127.0.0.1:8082/api/creation-entry/config` 返回 `200` 且包含 `baby-object-match`;前端草稿页作品架重新渲染。
- 关联:`server-rs/crates/api-server/src/state.rs``server-rs/crates/api-server/src/creation_entry_config.rs``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## 存档选择入口不要只藏在“玩过”弹窗里
- 现象:用户有 RPG / 拼图运行态存档,但平台底部 `草稿` Tab 只展示作品架,个人中心只有点击 `玩过` 后才可能看到“可继续”,导致看起来没有存档选择入口。
- 原因:`/api/profile/save-archives` 已在入口 bootstrap 加载,但前端只把 `saveEntries` 注入 `ProfilePlayedWorksModal`;没有独立的存档入口。
- 处理:个人中心 `常用功能` 必须保留 `存档` 快捷入口,点击后打开独立存档选择弹窗并复用 `SaveArchiveCard`;恢复仍走 `/api/profile/save-archives/{worldKey}`,拼图存档继续走拼图 resume 分支RPG 走 `handleContinueGame(snapshot)`
- 验证:`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "profile page exposes save archive picker"`
- 关联:`src/components/rpg-entry/RpgEntryHomeView.tsx``src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/useRpgEntryBootstrap.ts``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`