This commit is contained in:
2026-04-28 19:36:39 +08:00
parent a9febe7678
commit f0471a4f8d
206 changed files with 18456 additions and 10133 deletions

View File

@@ -10,6 +10,7 @@
- [技术方案](./technical/README.md):动画、服务端、外部产品形态拆解。
- [规划与优先级](./planning/README.md):当前阶段的迭代排序与落地优先级。
- [参考目录](./reference/README.md):脚本/Function 速查入口。
重点补充RPG 创作与运行时脚本职责地图见 [RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md](./reference/RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md)。
- [PRD](./prd):产品需求与阶段计划;新增 RPG 开场动画方案见 [AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md](./prd/AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md)。
## 推荐阅读顺序

View File

@@ -17,6 +17,10 @@
- [CUSTOM_WORLD_CREATOR_TOOL_AUDIT_2026-04-08.md](./CUSTOM_WORLD_CREATOR_TOOL_AUDIT_2026-04-08.md):自定义世界创作工具当前问题、体验断层和优化优先级审计。
- [AGENT_TO_DRAFT_TO_WORLD_PIPELINE_AUDIT_2026-04-20.md](./AGENT_TO_DRAFT_TO_WORLD_PIPELINE_AUDIT_2026-04-20.md)Agent 聊天、草稿生成、作品库存储与进入世界之间的断点、多 pipeline、冗余与未实装项审计。
- [CHARACTER_ASSET_PROMPT_CHAIN_AUDIT_2026-04-20.md](./CHARACTER_ASSET_PROMPT_CHAIN_AUDIT_2026-04-20.md):角色资产默认描述文本、正式图像/动作 prompt、共享模板与保留接口的分层与冗余审计。
- [RPG_RUNTIME_DIRECT_DRAFT_PROFILE_AUDIT_2026-04-25.md](./RPG_RUNTIME_DIRECT_DRAFT_PROFILE_AUDIT_2026-04-25.md)RPG 运行时进入世界时改为直读 Agent session 草稿 profile 的链路检查。
- [RPG_WORLD_DRAFT_EDIT_AUTOSAVE_OVERRIDE_AUDIT_2026-04-28.md](./RPG_WORLD_DRAFT_EDIT_AUTOSAVE_OVERRIDE_AUDIT_2026-04-28.md)RPG 世界草稿结果页编辑后被旧设定覆盖的前端本地态、session 真相源与自动保存链路审计。
- [engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md](./engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md)RPG 前端脚本中仍应迁到 `server-rs` / SpacetimeDB 的开局、快照、story engine、战斗、NPC/背包规则与创作残留后门审计。
- [engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md](./engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md)RPG 前端脚本后端迁移完成度复核,标明已完成项、已收口的结果页保存 normalize以及仍需收尾的 `camp_travel_home_scene` 前端专用旅行分支。
- [engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md):对 `2026-04-19` 工程清理审计的当前仓库复核,区分已完成项、仍存边界问题和新的热点迁移。
- [engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md):未引用垃圾、旧入口残留、前后端双份真相与后端迁移项的专项审计。

View File

@@ -0,0 +1,233 @@
# RPG 世界草稿编辑后被旧设定覆盖问题审计 2026-04-28
## 1. 问题摘要
当前 RPG 世界草稿结果页存在一条明显的“本地编辑态与 Agent session 真相源分叉”问题链:
1. 用户在结果页编辑世界设定时,前端只更新本地 `generatedCustomWorldProfile`
2. Agent 草稿会话里的 `draftProfile / resultPreview.preview` 并不会随着这次编辑同步更新。
3. 自动保存触发时,系统又会优先重新拉取 session 最新快照,并以 session 返回的 profile 作为最终保存内容。
4. 如果 session 里仍是编辑前旧设定,那么前端刚刚改过的内容就会被旧快照覆盖回来。
所以,这个问题表面看起来像“自动保存覆盖了我刚保存的内容”,本质上是:
- 结果页编辑写到了前端内存态;
- 自动保存落库前又改为优先相信 session 真相;
- 但 session 真相本身没有承接这次编辑。
## 2. 当前主链证据
### 2.1 结果页编辑只改前端本地 profile
`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx:2637-2640`
- `RpgCreationResultView``onProfileChange` 只调用:
- `sessionController.setGeneratedCustomWorldProfile(normalizeAgentBackedProfile(profile))`
- 这里没有触发任何 `update_draft_card``sync_result_profile` 或其它后端写回动作。
这意味着结果页里的“保存修改”目前只是更新前端内存中的 `generatedCustomWorldProfile`
### 2.2 结果页展示数据优先来自 session 预览
`src/services/rpg-creation/rpgCreationPreviewAdapter.ts:27-33`
- `buildCustomWorldProfileFromAgentSession()` 当前优先读取:
- `session.resultPreview.preview`
- 若没有,再回退到:
- `session.draftProfile.legacyResultProfile`
也就是说Agent 结果页最终重新打开、重新同步或重新计算时,主数据源仍是 session 快照,而不是用户刚改过的前端本地对象。
### 2.3 自动保存前会先刷新 session并优先保存 session 返回的最新结果
`src/components/rpg-entry/useRpgCreationResultAutosave.ts:181-236`
- `syncAgentDraftResultProfile()` 在 session 与前端 profile 签名不一致时,不再把前端 profile 回写 session。
- 它只会执行:
- `syncAgentSessionSnapshot(activeAgentSessionId)`
- 然后把拉回来的 session profile 重新塞回:
- `setGeneratedCustomWorldProfile(latestProfile)`
`src/components/rpg-entry/useRpgCreationResultAutosave.ts:326-340`
- 自动保存延迟触发后,如果当前是 Agent 草稿结果页:
1. 先调用 `syncAgentDraftResultProfile(profileToSave)`
2. 再把 `syncedResult.profile ?? profileToSave` 作为最终要入库的 profile
也就是:
- 自动保存不是直接保存用户刚改的前端 profile
- 它会先“向 session 对齐”;
- 对齐后优先保存 session 返回的新 profile。
如果 session 里还是旧设定,那么保存和界面都会一起回滚到旧设定。
## 3. 当前测试口径也在强化这条行为
### 3.1 单测明确要求“不触发 sync_result_profile只保存 session 最新草稿”
`src/components/rpg-entry/useRpgEntryAgentDraftRestore.test.tsx:195-276`
这条测试验证的是:
1. 前端当前持有的是 `oldProfile`
2. `syncAgentSessionSnapshot()` 返回的是 `latestSession`
3. 自动保存最终必须保存 `latestSession.draftProfile`
4. 并且明确断言不应触发 `sync_result_profile`
这说明现有实现不是偶然漏掉了 session 写回,而是被当前测试和实现共同锁定为:
- “自动保存只刷新 session不回写前端结果页编辑态”
### 3.2 交互测试也要求“结果页自动保存优先保存 session 最新快照”
`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx:2867-3006`
这条测试进一步验证:
1. 结果页最终保存的对象应来自 `syncedSession.resultPreview.preview`
2. 不应触发 `sync_result_profile`
所以现在的覆盖行为不是单点 bug而是当前结果页保存架构的直接产物。
## 4. 系统层面的根因拆解
### 4.1 双真相源并存
当前至少同时存在两份“看起来都像真相”的数据:
1. 前端结果页本地 `generatedCustomWorldProfile`
2. Agent session 内的 `draftProfile / resultPreview.preview`
结果页编辑写前者,自动保存与恢复又优先读后者,所以只要两边没有同事务同步,就一定会出现覆盖风险。
### 4.2 编辑动作没有接入正式后端写回动作
仓库契约和 Rust 模块实际上已经存在两条正式动作:
- `update_draft_card`
- `sync_result_profile`
证据:
- `packages/shared/src/contracts/rpgAgentActions.ts`
- `server-rs/crates/spacetime-module/src/custom_world/mod.rs`
其中:
- `update_draft_card` 适合基于卡片/section 的结构化修改写回 `draftProfile`
- `sync_result_profile` 适合把结果页完整 profile 回写 session 并重建 preview
但当前结果页 `onProfileChange` 没有接入这两条正式链路,导致“编辑成功”只停留在本地内存态。
### 4.3 自动保存策略与编辑链路不一致
自动保存的当前设计目标是正确的:
- 不再轻易把旧前端快照反向污染 session
- 优先保存 session 最新快照,避免把陈旧 preview 写进作品库
但前提应该是:
- 结果页编辑本身已经先进入 session 真相源
现在前提不成立,于是自动保存的“防旧快照污染”策略,反过来变成了“用 session 旧值覆盖用户刚编辑的新值”。
### 4.4 结果页定位混乱
当前结果页同时承担了两种角色:
1. 预览/发布页
2. 可深度编辑的草稿编辑器
但现有数据链路更偏向第 1 种:
- 数据来自 session preview
- 自动保存优先对齐 session preview
而 UI 行为却提供了第 2 种能力:
- 用户可以直接改世界、角色、场景等内容
两种定位没有统一,最终表现就是“能编辑,但编辑不能稳定进入主真相源”。
## 5. 用户视角下的实际故障表现
从用户体感看,当前问题会表现为以下几种:
1. 刚改完字段,短暂显示新内容,随后自动回弹为旧内容。
2. 页面提示“已自动保存”,但重新进入草稿页后仍然是旧设定。
3. 某些字段改了能停留一会儿,触发自动保存或 session 刷新后又被覆盖。
4. 用户会误以为“保存按钮坏了”或“自动保存有缓存问题”,但真实原因是保存目标和展示真相源不一致。
## 6. 影响范围
这个问题不只影响“世界简介文本”,而是整个结果页编辑体系:
1. 世界基础信息编辑
2. 角色编辑
3. 场景编辑
4. 封面/派生内容依赖当前 profile 的场景
5. 结果页返回、恢复、自动打开、发布前预览一致性
只要编辑发生在 `generatedCustomWorldProfile`,但没有进入 session 真相源,就都有同类风险。
## 7. 建议修复方向
### 7.1 第一优先级:统一结果页编辑的唯一真相源
推荐二选一,不要继续混用:
1. 结果页只做预览,不允许直接编辑复杂设定
2. 结果页继续允许编辑,但每次保存必须先落到 session 真相源,再更新本地显示
按当前产品形态,更合理的是第 2 条。
### 7.2 如果保留结果页编辑,建议采用的正式链路
1. 世界/角色/场景编辑保存时,优先调用 Rust/SpacetimeDB 的正式动作写回 session。
2. 能用 `update_draft_card` 的地方尽量走结构化 section 更新。
3. 对无法被 card section 覆盖的完整 profile 级编辑,再评估是否保留 `sync_result_profile`,或新增更细粒度 reducer。
4. session 写回完成后,再重新读取 session 并刷新结果页。
5. 自动保存只负责“把已经写进 session 的最新真相同步到作品库”,不要再承担“猜测该保存前端还是 session”的职责。
### 7.3 自动保存策略需要降级为“作品库存档层”不要兼任“session 真相协调层”
当前自动保存同时承担:
1. 防止旧前端快照污染 session
2. 选择最终保存哪份 profile
3. 刷新结果页显示
职责太重,也导致覆盖问题难以定位。
建议改为:
1. 编辑保存阶段:负责写 session
2. 自动保存阶段:只负责把 session 已确认真相落作品库
3. 结果页渲染阶段:只读 session 最新快照
### 7.4 需要补的测试口径
当前测试主要在保护“不要再触发 `sync_result_profile` 污染 session”。
后续修复后,至少要补以下测试:
1. 用户编辑世界基础字段后session `draftProfile / resultPreview` 会同步更新。
2. 自动保存不会把 session 旧值覆盖到刚编辑的新值上。
3. 刷新页面或重新进入结果页后,看到的是编辑后的新设定。
4. 进入世界、发布世界、继续扩展时,消费的是同一份最新 session 真相。
## 8. 结论
这次问题的核心结论是:
1. 你的判断“像是自动保存问题”是对的,但更准确地说,是“自动保存对齐 session 时,覆盖了未写回 session 的本地编辑态”。
2. 当前结果页编辑没有接上正式的 session 写回链路,这是第一根断点。
3. 当前自动保存被设计成优先信任 session 最新快照,这是第二根断点。
4. 两个断点叠加后,就形成了“编辑后又自动变回原设定”的现象。
如果后续要真正修掉这个问题,重点不该是单独调 debounce 时间或加一个“防抖保存中”提示,而是:
- 把结果页编辑动作重新接回 Rust/SpacetimeDB 的 session 真相源;
- 让自动保存只负责作品库存档,不再替代编辑写回链路做裁决。

View File

@@ -10,21 +10,25 @@
这一版是第六批落地记录,聚焦删除无入口 `questDirector`、旧观察文案 helper、一次性硬编码同步脚本并补齐后端运行时 function catalog 契约覆盖。
3. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md)
这一版是第五批落地记录,聚焦旧命名 re-export、空路由骨架、旧发布服务、前端 prompt 镜像与无入口编辑器壳层的物理删除。
4. [FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md](./FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md)
4. [RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md](./RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md)
这一版专项扫描 `src/` 下 RPG 开头脚本明确运行时开局、快照、story engine、战斗后处理、NPC/背包规则和创作链残留后门中应迁到 `server-rs` 的逻辑。
5. [RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md](./RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md)
这一版复核 RPG 前端脚本后端迁移完成度确认开局、快照、存档、NPC、背包/锻造、结果页保存前 normalize 与角色资产 prompt 主链已收口,同时标出 `camp_travel_home_scene` 前端专用旅行分支仍未完全迁完。
6. [FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md](./FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md)
这一版是本轮前端越界逻辑专项审计,专门汇总当前仍应继续迁到 `server-rs` 的运行时、鉴权、生成编排与本地真相残留。
5. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md)
7. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md)
这一版是第四批落地记录,聚焦未接入业务的数据生成产物、测试专用 stub 与对应配置残留出清。
6. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md)
8. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md)
这一版是第三批落地记录,聚焦鉴权真相收口,先移除前端保存自动登录用户名/密码的本地真相,并明确运行时快照前置写入为什么当前还不能硬砍。
7. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md)
9. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md)
这一版是第二批落地记录,聚焦旧主流程壳层、旧 bootstrap 和旧 inventory / forge / equipment flow Hook 的正式出清。
8. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md)
10. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md)
这一版是第一批落地记录聚焦高置信度小型孤岛、prompt 壳子、stub 和无入口 modal 的首轮清理。
9. [CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md](./CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md)
11. [CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md](./CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md)
这一版是面向当前仓库状态的优化点盘点,适合直接拿来排优先级和拆执行批次。
10. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
12. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
这一版是对 `2026-04-19` 基线的当前仓库复核,明确哪些问题已经处理、哪些表述需要纠正、热点又迁移到了哪里。
11. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
13. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
这一版保留原始问题快照和执行回填,适合回看“为什么会有这轮清理与边界收口”。
## 融合结论

View File

@@ -0,0 +1,650 @@
# RPG 前端脚本后端迁移审计2026-04-28
## 0. 审计目标
这份文档只回答一个问题:
**当前 `src/` 下 RPG 开头或 RPG 目录内的前端脚本中,哪些逻辑已经越过“前端只做表现”的边界,应该继续迁移到 `server-rs` / SpacetimeDB 后端。**
本轮只做审计与迁移拆分,不改业务代码。
## 1. 审计范围
本轮扫描范围:
1. `src/RpgRuntimeApp.tsx`
2. `src/components/rpg-*/*.ts`
3. `src/components/rpg-*/*.tsx`
4. `src/hooks/rpg-*/*.ts`
5. `src/services/rpg-*/*.ts`
6. `src/services/rpgRuntimeChatTypes.ts`
不把测试文件作为迁移对象,但会参考测试暴露的当前行为。
本轮依据:
1. `docs/reference/RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md`
2. `docs/audits/engineering/FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md`
3. `docs/technical/RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md`
4. `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
5. `spacetimedb-concepts` / `spacetimedb-rust` / `spacetimedb-typescript` skill 约束
## 2. 判断标准
### 2.1 应迁后端
只要逻辑满足下面任一项,就不应继续以浏览器前端作为正式真相:
1. 生成或解释 `GameState`、任务、背包、装备、NPC 状态、战斗状态、剧情记忆。
2. 决定开局初始状态、场景落点、遭遇、初始物品、初始装备。
3. 决定 action 是否合法、价格、数量、奖励、掉落、复活、场景推进。
4. 组装正式 AI prompt / story context / fallback generation。
5. 持久化完整运行时快照,或让前端上传整份 `GameState` 作为后端写入依据。
6. 对服务端返回的快照做业务补丁,补齐战斗阵型、场景跳转、任务推进等正式状态。
7. 解释 Agent session / draft / result preview 哪份才是创作真相。
### 2.2 可留前端
下面这些可以继续留在前端:
1. 面板开关、tab、modal、loading、error、按钮禁用展示。
2. 动画播放、镜头、过场、打字机效果、临时视觉态。
3. API client、请求封装、SSE 文本消费。
4. 纯展示 view model 适配,但不能改变正式业务含义。
5. 用户正在编辑的表单草稿,但保存、校验、合并、发布门禁以后端为准。
## 3. 结论先行
当前 RPG 前端脚本中仍有 9 类逻辑应该迁移到后端:
1. `P0` 运行时开局 `GameState` 装配。
2. `P0` runtime story 网关中的“客户端带快照解析”和快照补丁。
3. `P0` 前端自动保存整份运行时快照。
4. `P0` 前端 story engine / chapter / world mutation / prompt context 编排。
5. `P0` 战斗胜负后处理、死亡复活、战斗后章节推进。
6. `P1` NPC 交易、送礼、价格、数量与库存校验。
7. `P1` 背包、装备、锻造可用性与配方视图。
8. `P1` RPG 创作 profile 生成的非浏览器 legacy AI 回退。
9. `P1` RPG 创作结果页自动保存、session/result preview 真相优先级与 legacy preview fallback。
一句话判断:
**前端已经大面积开始调用 server-rs但仍保留了“后端兼容不完整时由前端补真相”的模式这部分需要继续收口否则 SpacetimeDB 表和 reducer 永远无法成为唯一运行时真相源。**
## 4. 高优先级迁移项
## 4.1 `P0` 运行时开局 `GameState` 装配仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-session/useRpgSessionBootstrap.ts`
关键逻辑:
1. `PLAYER_BASE_MAX_HP = 180`
2. `mergeStarterInventoryItems(...)`
3. `normalizeExplicitStarterCategory(...)`
4. `inferExplicitStarterSlot(...)`
5. `buildExplicitCustomWorldRoleStarterState(...)`
6. `createInitialGameState()`
7. `handleCustomWorldSelect(...)`
8. `handleCharacterSelect(...)`
对应代码表现:
1.`58` 行到第 `151` 行:前端把角色初始物品编译成 `InventoryItem`,推断装备槽,并直接生成 `runtimeMetadata`
2.`185` 行到第 `235` 行:前端创建完整初始 `GameState`
3.`517` 行到第 `563`前端选择世界时重置运行时上下文、progression、story memory、战斗态。
4.`572` 行到第 `710` 行:前端选角时决定开局场景、开局 NPC、初始 NPC state、初始装备、血蓝、货币、背包、任务、队伍、战斗字段。
### 为什么应迁
这些不是表现层逻辑,而是“新开局正式状态”的创建权。只要开局状态仍由浏览器本地装配,后端就只能被动接收一份客户端快照,无法成为 runtime session 的唯一真相。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story.rs`
2. `server-rs/crates/api-server/src/story_sessions.rs`
3. `server-rs/crates/module-runtime/src/lib.rs`
4. `server-rs/crates/module-story/src/lib.rs`
5. `server-rs/crates/module-inventory/src/lib.rs`
6. `server-rs/crates/module-progression/src/lib.rs`
7. `server-rs/crates/module-runtime-story-compat/src/game_state.rs`
SpacetimeDB 方向:
1. 新增或扩展 `begin_rpg_runtime_session` reducer。
2. 由 reducer 基于 `ctx.sender()`、作品 profile id、角色 id 创建 runtime session。
3. 在表中写入初始场景、角色状态、背包、装备、NPC 状态、任务、progression。
4. 前端只调用 reducer / API 并订阅或读取后端返回的 session view model。
注意:外部 AI / 网络调用不能放进 reducer如果开局需要 LLM应由 `api-server` / `platform-llm` 完成生成,再把确定结果写入 SpacetimeDB。
## 4.2 `P0` runtime story 网关仍要求客户端携带快照解析
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts`
关键逻辑:
1.`112` 行到第 `123` 行:`buildRuntimeSnapshotRequest(...)` 把本地 `gameState``currentStory` 一并提交给后端。
2.`125` 行到第 `213` 行:`bridgeServerSceneTravelSnapshot(...)` 在前端补齐服务端旅行快照。
3.`216` 行到第 `317` 行:`bridgeServerNpcBattleSnapshot(...)` 在前端补齐 NPC 战斗阵型。
4.`323` 行到第 `341` 行:拉取 option catalog 时仍基于客户端快照。
5.`387` 行到第 `430` 行:正式动作结算后继续由前端对服务端快照做桥接修补。
### 为什么应迁
这类桥接逻辑意味着服务端返回结果不是完整业务真相,前端还在做二次裁决:
1. 推断下一场景。
2. 补齐场景 preset。
3. 补齐遭遇预览。
4. 补齐任务推进。
5. 补齐 NPC 战斗阵型。
6. 改写 `GameState` 中的战斗、场景、任务字段。
这些都应该由后端 runtime action resolver 在同一个事务边界内完成。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story/compat/game_state.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat/npc_actions.rs`
3. `server-rs/crates/api-server/src/runtime_story/compat/equipment_actions.rs`
4. `server-rs/crates/api-server/src/runtime_story/compat/quest_actions.rs`
5. `server-rs/crates/module-runtime-story-compat/src/options.rs`
6. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
7. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
迁移后前端应该只提交:
1. `sessionId`
2. `functionId`
3. `runtimePayload`
4. 当前 UI 所需的弱表现参数
后端应该基于已存 session state 解析动作,不再要求前端上传完整 `GameState`
### 本轮落地口径2026-04-28
本轮先收口 `runtime story` 网关这条 P0 主链:
1. 前端 `rpgRuntimeStoryGateway.ts` 不再构造或上传 `snapshot.gameState / currentStory`
2. 前端 `rpgRuntimeStoryClient.ts` 的状态读取统一走 `GET /api/runtime/story/state/:sessionId`,动作结算统一走 `POST /api/runtime/story/actions/resolve` 且请求体只包含 `sessionId / clientVersion / action`
3. `idle_travel_next_scene` 的下一场景、场景 preset、遭遇清理、战斗清理与旅行计数由 `server-rs/crates/api-server/src/runtime_story/compat.rs` 在持久化快照上完成。
4. `npc_fight / npc_spar` 的战斗阵型、`sceneHostileNpcs`、战前 encounter 归档与战斗字段由 `server-rs/crates/api-server/src/runtime_story/compat/npc_actions.rs` 在后端完成。
5. 前端只消费后端返回的 hydrated snapshot 和 presentation不再保留 `bridgeServer*Snapshot` 业务补丁。
### 本轮落地验收2026-04-28
1. `rpgRuntimeStoryGateway.ts` 已删除 `buildRuntimeSnapshotRequest``bridgeServerSceneTravelSnapshot``bridgeServerNpcBattleSnapshot` 以及 NPC 战斗/旅行快照补丁依赖option catalog 和 action resolve 都只提交 `sessionId / clientVersion / action / payload`
2. `rpgRuntimeStoryClient.ts` 已移除前端 `RuntimeStorySnapshotRequest` 入参;状态读取固定为 `GET /api/runtime/story/state/:sessionId`,动作结算请求体不再带 `snapshot` 字段。
3. `server-rs` 已覆盖 `idle_travel_next_scene``npc_fight``npc_spar` 的后端快照补齐测试,并将 route 级测试改为从服务端持久化 session 读取,不再通过旧 save 端点上传整份前端快照铺垫。
4. 已执行验收:`cargo check -p api-server --message-format short``cargo test -p api-server runtime_story_ -- --nocapture``npm run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts`
5. 搜索确认:`src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts``src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 不再包含 `/state/resolve``buildRuntimeSnapshotRequest``bridgeServer*``params.snapshot` 或动作请求上传 `snapshot.gameState/currentStory` 的路径。
## 4.3 `P0` 自动保存仍由前端上传整份运行时快照
### 代码证据
主要文件:
1. `src/hooks/rpg-session/useRpgSessionPersistence.ts`
2. `src/services/rpg-runtime/rpgSnapshotClient.ts`
关键逻辑:
1. `useRpgSessionPersistence.ts``12` 行到第 `23` 行:前端判断哪些运行态可以入正式存档。
2.`90` 行到第 `150` 行:前端把 `gameState / bottomTab / currentStory` 作为 payload 写入远端。
3.`216` 行到第 `234` 行:前端监听本地 `gameState` 变化并自动保存。
4.`236` 行到第 `263` 行:手动存档也上传整份本地快照。
5. `rpgSnapshotClient.ts``31` 行到第 `48` 行:`putRpgSaveSnapshot(...)` 直接发送 `SavedGameSnapshotInput`
### 为什么应迁
运行时存档不是普通表单保存它是玩家进度、任务、背包、战斗、NPC、故事状态的正式真相。当前模式仍是“前端本地状态变化 -> 上传整份快照 -> 后端持久化”,后端缺少对状态版本、动作来源、事务一致性的最终解释权。
### 后端落点
建议改成:
1. 后端 action resolver 每次动作结算时事务性写入 session state。
2. 自动保存只变成后端已有 session state 的归档或 checkpoint。
3. 前端最多提交 `sessionId``saveSlot``bottomTab` 等非业务 UI 状态。
4. `runtime_save.rs` 负责快照归档,不再信任浏览器传入的整份 `GameState`
涉及表结构时同步检查并更新 `migration.rs`
## 4.4 `P0` story engine / chapter / world mutation 仍在前端编排
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/progressionActions.ts`
2. `src/hooks/rpg-runtime-story/storyContextBuilder.ts`
3. `src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.ts`
关键逻辑:
1. `progressionActions.ts``193` 行到第 `293` 行:前端生成章节任务并写入 `quests`
2.`295` 行到第 `395` 行:前端收集 story signals、推进 thread、同伴反应、章节、旅程 beat、阵营/世界 mutation、setpiece。
3.`644` 行到第 `782` 行:前端提交生成后状态、追加 story history、执行 AI 后再做 recovery。
4. `storyContextBuilder.ts``1` 行到第 `51` 行:前端引入大量 story engine director / graph / memory / prompt context 组件。
5. `useRpgRuntimeStoryController.ts``80` 行到第 `103` 行:前端把 `generateInitialStory / generateNextStep` 注入本地 story request runtime。
### 为什么应迁
这些逻辑不是 UI而是叙事引擎状态机
1. 章节任务发放。
2. 线索和 thread 更新。
3. 同伴关系变化。
4. 营地事件与 setpiece 触发。
5. 世界 mutation。
6. 生成上下文组织。
7. fallback / recovery。
这些都应作为后端 story session 的 reducer / domain service而不是 React hook 的本地副作用。
### 后端落点
建议收口到:
1. `server-rs/crates/api-server/src/runtime_story/compat/ai.rs`
2. `server-rs/crates/api-server/src/prompt/rpg/runtime_chat.rs`
3. `server-rs/crates/module-story/src/lib.rs`
4. `server-rs/crates/module-progression/src/lib.rs`
5. `server-rs/crates/module-runtime-story-compat/src/core.rs`
6. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
前端保留:
1. `currentStory` 展示。
2. loading / error。
3. SSE 增量文本。
4. 视觉过场。
## 4.5 `P0` 战斗后处理、死亡复活、章节推进仍在前端补真相
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts`
2. `src/hooks/rpg-runtime-story/postBattleFlow.ts`
关键逻辑:
1. `storyChoiceRuntime.ts``1` 行到第 `6` 行仍引用掉落、背包合并等本地规则。
2.`304` 行到第 `390` 行:服务端动作返回后,前端仍判断死亡、等待复活、构造复活状态、构造战斗胜利后状态与 story。
3. `postBattleFlow.ts``86` 行到第 `105` 行:前端构造战斗胜利后的 `GameState`
4.`107` 行到第 `158` 行:前端推进 scene act 并构造战斗后 story / deferred options。
5.`161` 行到第 `205` 行:前端决定复活回第一场景、恢复血蓝、清战斗态、重建首幕 encounter preview。
### 为什么应迁
死亡、胜利、切磋完成、复活、掉落、战斗后章节推进都属于正式玩法结果。前端可以播死亡动画和过场,但不能决定:
1. 玩家是否死亡。
2. 玩家在哪里复活。
3. 复活后血蓝。
4. 战斗是否结束。
5. 任务或章节是否推进。
6. deferred options 里有哪些正式可选动作。
### 后端落点
建议收口到:
1. `server-rs/crates/module-combat/src/lib.rs`
2. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
3. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
4. `server-rs/crates/api-server/src/story_battles.rs`
5. `server-rs/crates/api-server/src/runtime_story/compat/presentation.rs`
前端保留:
1. 根据后端返回的 `presentation.battle` 播动画。
2. 根据后端返回的 `nextStory` 展示文本和选项。
3. 根据后端返回的 `GameState` 或 view model 渲染血条、角色、敌人。
## 5. 中优先级迁移项
## 5.1 `P1` NPC 交易、送礼的价格和库存校验仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/npcInteraction.ts`
关键逻辑:
1.`186` 行到第 `218` 行:前端读取 NPC / 玩家物品,计算交易单价、最大数量、数量 clamp。
2.`629` 行到第 `683` 行:前端按本地价格和库存判断买入 / 卖出是否允许,再提交后端。
3.`685` 行到第 `702` 行:前端检查礼物是否存在,并构造送礼 action。
### 当前判断
这条链已经比早期更好:正式交易和送礼会调用 `resolveRpgRuntimeChoice(...)` 走后端。但前端仍在承担“能不能买、能不能卖、价格是多少、数量是否合法”的第一层裁决。
### 迁移建议
后端应该:
1. 计算价格。
2. 校验库存和货币。
3. 校验 NPC 亲和度影响。
4. 原子更新玩家库存、NPC 库存、货币和 NPC state。
5. 返回可展示的交易 view model 与错误原因。
前端可以保留:
1. 当前选择的 itemId / quantity。
2. 后端返回价格的展示。
3. modal 开关和数量输入。
### 本次落地设计2026-04-28
为避免“前端只是少算了一处价格,但仍在提交前裁决库存”的半迁移,本次按下面边界收口:
1. `server-rs` 在 runtime story compat 状态桥中编译 `gameState.runtimeNpcInteraction` 派生 view包含当前 NPC、玩家货币、购买列表、出售列表、赠礼列表、服务端单价、服务端库存上限、赠礼好感增益与不可选原因。
2. `resolve_npc_trade_action(...)``resolve_npc_gift_action(...)` 仍是唯一正式结算入口,提交后重新校验 mode、itemId、quantity、NPC 库存、玩家背包数量与玩家货币;前端展示的 view 只作为 UI 提示,不作为可信输入。
3. 前端 `npcInteraction.ts` 不再读取本地 NPC / 玩家物品计算单价、总价、最大数量,也不再因为本地库存或货币不足提前阻断提交;确认时只提交 `{ mode, itemId, quantity }`,后端返回错误即展示错误。
4. 前端 `NpcModals.tsx` 只渲染 `runtimeNpcInteraction` 中的价格、库存和赠礼增益;按钮禁用仅保留“未选择条目 / 服务端 view 标记不可选 / 当前操作正在提交”的表现态,不再用浏览器本地价格和货币重新裁决。
5. 验收测试必须覆盖后端购买成功、NPC 库存不足拒绝、玩家货币不足拒绝、出售成功、玩家背包数量不足拒绝、赠礼成功、礼物不存在拒绝;前端测试覆盖确认交易/赠礼在本地库存或货币不满足时仍提交后端,由后端错误接管。
## 5.2 `P1` 背包、装备、锻造可用性与配方视图仍在前端
### 代码证据
主要文件:
1. `src/hooks/rpg-runtime-story/inventoryActions.ts`
关键逻辑:
1.`44` 行到第 `52` 行:前端基于 `playerInventory / playerCurrency / worldType` 计算 forge recipe views。
2.`97` 行到第 `180` 行:前端先查找物品 / 装备 / 配方,再提交后端 reducer 风格 action。
### 当前判断
正式动作已经走后端,这是正确方向。但 `forge recipe view` 和本地物品存在性判断仍会让前端表现出“它知道业务规则”的形态。
### 迁移建议
后端应该提供:
1. 当前玩家背包 view。
2. 当前装备 view。
3. 当前可锻造配方 view。
4. 每个动作的 disabled / reason。
前端只渲染后端 view并把用户选择提交给后端。
## 5.3 `P1` RPG 创作 profile 生成仍保留非浏览器 legacy AI 回退
### 迁移回填2026-04-28
已完成本项迁移:
1. `src/services/rpg-creation/rpgCreationGenerationClient.ts` 删除 `LegacyAiModule``loadLegacyAiModule``typeof window === 'undefined'` 分支。
2. `src/services/rpg-creation/rpgCreationGenerationClient.node.test.ts` 锁定 node 环境也只 mock `requestJson`,不再触发前端 legacy AI 模块。
3. `src/services/rpg-creation/index.ts` 删除 `generateLegacyCustomWorldProfile` 旧命名导出。
4. `server-rs/crates/api-server/src/app.rs` 挂载 `POST /api/runtime/custom-world/profile`
5. `server-rs/crates/api-server/src/custom_world.rs` 复用 `generate_custom_world_foundation_draft(...)` 生成 profile并补齐前端结果页所需稳定字段。
6. 迁移说明见 `docs/technical/RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md`
### 迁移前历史证据
主要文件:
1. `src/services/rpg-creation/rpgCreationGenerationClient.ts`
关键逻辑:
1.`9` 行到第 `19` 行:前端 client 仍定义 `LegacyAiModule = typeof import('../ai')` 并动态加载旧 AI 模块。
2.`32` 行到第 `35` 行:非浏览器环境直接调用 `aiClient.generateCustomWorldProfile(...)`
3.`43` 行到第 `51` 行:浏览器环境才请求 `/api/runtime/custom-world/profile`
### 为什么应迁
这与 `RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md` 的目标冲突。即使该分支主要服务测试或 SSR它仍保留了“前端可持有 RPG 生成逻辑和 prompt”的技术后门。
### 迁移建议
1. 删除 `import('../ai')` 回退。
2. 非浏览器环境也应通过 `server-rs` client / test mock 调用后端 contract。
3. 若测试需要离线能力,应 mock `requestJson`,不要恢复前端生成链。
后端落点:
1. `server-rs/crates/api-server/src/custom_world.rs`
2. `server-rs/crates/api-server/src/custom_world_foundation_draft.rs`
3. `server-rs/crates/api-server/src/prompt/rpg/foundation_draft.rs`
4. `server-rs/crates/platform-llm/`
## 5.4 `P1` 创作结果页保存与 Agent session 真相优先级仍在前端编排
### 代码证据
主要文件:
1. `src/components/rpg-entry/useRpgCreationResultAutosave.ts`
2. `src/components/rpg-entry/useRpgEntryLibraryDetail.ts`
3. `src/services/rpg-creation/rpgCreationPreviewAdapter.ts`
关键逻辑:
1. `useRpgCreationResultAutosave.ts` 负责 profile signature、自动保存请求去重、`upsertRpgWorldProfile(...)`、Agent session refresh。
2. `useRpgEntryLibraryDetail.ts` 负责判断 draft work 应打开 Agent workspace、生成过程页还是结果页。
3. `rpgCreationPreviewAdapter.ts``resultPreview` 缺失时回退读取 `draftProfile.legacyResultProfile`
### 当前判断
这部分大多是创作 UI 编排,不能简单全迁。但下面三件事属于后端真相:
1. 保存前 profile normalize。
2. Agent session / result preview / legacyResultProfile 的优先级。
3. 发布门禁与草稿是否可进入结果页。
### 迁移建议
后端应该提供一个稳定的 `creation_result_view``work_detail_view`
1. 已标准化 profile。
2. 当前 session / work 状态。
3. 是否可发布。
4. 应打开的前端 stage。
5. 缺失或失败时的恢复指令。
前端只根据 view model 切页面,不再自行解释 session 阶段。
## 5.5 `P1` 角色资产工坊默认 prompt 与缓存合并规则仍在前端
### 本轮落地状态2026-04-28
已完成默认 prompt 与缓存合并规则迁移:
1. 新增 `server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs`,作为角色资产工坊默认 prompt、legacy prompt 过滤、逐动作 prompt 缓存合并的后端主源。
2. 新增 `POST /api/runtime/custom-world/asset-studio/role/{character_id}/workflow`,由前端提交当前正在编辑的角色快照,后端返回合并后的 workflow view。
3. 新增 `PUT /api/runtime/custom-world/asset-studio/role/{character_id}/workflow`,复用 OSS JSON 缓存保存,并补齐 `animationPromptTextByKey` 持久化。
4. `RpgCreationRoleAssetStudioModalImpl.tsx` 不再调用 `buildDefaultRolePromptBundle`,也不再包含 legacy prompt 判断和缓存合并函数。
5. 删除 `src/prompts/customWorldRolePromptDefaults.ts` 与旧兼容 re-export避免 `src/` 继续持有角色资产默认 prompt 主源。
### 代码证据
主要文件:
1. `src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModalImpl.tsx`
2. `src/components/rpg-creation-asset-studio/useRoleVisualCandidateWorkflow.ts`
3. `src/components/rpg-creation-asset-studio/useRoleAnimationWorkflow.ts`
关键逻辑:
1. `RpgCreationRoleAssetStudioModalImpl.tsx``54` 行到第 `85` 行:前端合并默认动画 prompt、缓存 prompt、legacy prompt。
2.`564` 行到第 `591` 行:前端调用 `buildDefaultRolePromptBundle(baseRole)` 生成默认视觉 / 动作 prompt。
3.`807` 行到第 `830` 行:前端保存工作流缓存,包括 prompt、草稿、asset id、animation map。
4. `useRoleVisualCandidateWorkflow.ts``useRoleAnimationWorkflow.ts` 把 prompt 文本直接作为生成 payload。
### 当前判断
用户正在编辑的 prompt 草稿可以留在前端表单里,但默认 prompt 生成、legacy prompt 判断、缓存合并、生成参数默认值不应继续散落在 UI modal 内。
### 迁移建议
后端应该提供:
1. `GET /api/runtime/custom-world/asset-studio/role/:id/workflow`
2. `PUT /api/runtime/custom-world/asset-studio/role/:id/workflow`
3. `POST /api/runtime/custom-world/assets/role-visual-candidates`
4. `POST /api/runtime/custom-world/assets/role-animation`
并在 `server-rs/crates/api-server/src/prompt/rpg/` 或资产 prompt 模块中统一默认 prompt 生成。
前端只保留:
1. prompt 文本框。
2. 参考图上传 UI。
3. 候选图 / 动画预览。
4. 用户点击生成、应用、保存的交互入口。
## 6. 可保留在前端的 RPG 脚本
以下脚本当前主要承担表现层或 request client 职责,可以暂时保留:
1. `src/components/rpg-runtime-shell/*`
2. `src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`
3. `src/components/rpg-runtime-panels/RpgAdventurePanelOverlays.tsx`
4. `src/components/rpg-entry/RpgEntryHomeView.tsx`
5. `src/components/rpg-entry/RpgEntryWorldDetailView.tsx`
6. `src/services/rpg-runtime/rpgRuntimeRequest.ts`
7. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
8. `src/services/rpg-runtime/rpgSnapshotClient.ts`
9. `src/services/rpg-creation/rpgCreationAgentClient.ts`
10. `src/services/rpg-creation/rpgCreationAssetClient.ts`
11. `src/services/rpg-creation/rpgCreationLibraryClient.ts`
12. `src/services/rpg-creation/rpgCreationRuntimeClient.ts`
但要注意:
1. client 文件可以保留请求封装,但不应继续加入 fallback 生成逻辑。
2. UI 文件可以根据后端 view model 展示 disabled / reason但不能本地重算业务可用性。
3. snapshot client 可以保留读取 / 删除 / 归档入口,但不应让前端上传整份 `GameState` 作为正式真相。
## 7. 推荐迁移顺序
### 第一阶段:收运行时真相
优先迁移:
1. `useRpgSessionBootstrap.ts`
2. `rpgRuntimeStoryGateway.ts`
3. `useRpgSessionPersistence.ts`
目标:
1. 后端创建 session。
2. 后端保存 session state。
3. runtime action 不再依赖客户端完整快照。
4. 前端不再补战斗 / 旅行快照。
### 第二阶段:收 story engine 与战斗后处理
优先迁移:
1. `progressionActions.ts`
2. `storyContextBuilder.ts`
3. `storyChoiceRuntime.ts`
4. `postBattleFlow.ts`
目标:
1. 后端统一处理 story history、thread、chapter、companion reaction、world mutation。
2. 后端统一处理死亡、复活、胜利、切磋完成、战斗后选项。
3. 前端只播放 presentation。
### 第三阶段:收 NPC / 背包 / 锻造可用性
优先迁移:
1. `npcInteraction.ts`
2. `inventoryActions.ts`
目标:
1. 后端提供交易 / 礼物 / 背包 / 锻造 view model。
2. 前端不再重算价格、数量、配方、动作合法性。
### 第四阶段:收创作链残留后门
优先迁移:
1. `rpgCreationGenerationClient.ts`
2. `useRpgCreationResultAutosave.ts`
3. `useRpgEntryLibraryDetail.ts`
4. `rpgCreationPreviewAdapter.ts`
5. `RpgCreationRoleAssetStudioModalImpl.tsx`
目标:
1. 移除 `import('../ai')`
2. 后端输出稳定 result/work view。
3. 后端统一角色资产工坊默认 prompt 和缓存合并规则。
## 8. 后端实现注意事项
### 4.3 落地记录:自动保存 checkpoint 化2026-04-28
本轮已完成 `自动保存仍由前端上传整份运行时快照` 的迁移:
1. `src/hooks/rpg-session/useRpgSessionPersistence.ts` 自动保存与手动保存不再构造 `gameState / currentStory` 上传体,只提交 `sessionId / bottomTab` checkpoint 元数据。
2. `src/services/rpg-runtime/rpgSnapshotClient.ts``PUT /api/runtime/save/snapshot` 请求体改为 `RuntimeSaveCheckpointInput`,前端保存链路只请求后端刷新 checkpoint。
3. `server-rs/crates/api-server/src/runtime_save.rs` 改为读取已存在的服务端 `runtime_snapshot`,校验 `runtimeSessionId` 一致后刷新 `savedAt / bottomTab / runtimeStats.playTimeMs / lastPlayTickAt`,不再信任浏览器传入完整运行态。
4. 旧式 `gameState / currentStory` 上传体会被 `PutRuntimeSaveCheckpointRequest``deny_unknown_fields` 拒绝无服务端快照、session 不一致、preview/test 快照都会返回冲突。
5. 本轮不涉及 SpacetimeDB 表结构变更,因此 `server-rs/crates/spacetime-module/src/migration.rs` 无需调整。
对应测试:
1. `src/services/rpg-runtime/rpgSnapshotClient.test.ts` 覆盖保存请求体不含 `gameState / currentStory`
2. `src/hooks/runtimeAuthGuards.test.tsx` 覆盖自动保存只提交 checkpoint。
3. `server-rs/crates/api-server/src/runtime_save.rs` 覆盖旧式整快照上传拒绝、缺少服务端快照冲突、session 不一致冲突、成功 checkpoint 沿用服务端快照真相。
按 SpacetimeDB 约束,后续落地时要遵守:
1. reducer 不返回数据,前端通过订阅 / 查询读取 view model。
2. reducer 使用 `ctx.sender()` 做鉴权,不信任前端传入身份。
3. reducer 必须确定性,不能访问网络、文件、外部随机。
4. LLM、OSS、图片生成等外部 I/O 放在 `api-server` / `platform-*` crate 中,再把确定结果写回 SpacetimeDB。
5. 前端调用 reducer 使用生成绑定和对象参数,不编辑生成代码。
6. 涉及表结构修改时同步更新 `migration.rs`
7. 修改后端代码后统一执行 `npm run api-server:maincloud`,并跑对应自动测试。
## 9. 最小验收标准
后续迁移完成后,前端 RPG 脚本应满足:
1. 搜索 `src/hooks/rpg-*` 不再出现创建完整 `GameState` 的逻辑。
2. runtime action 请求不再携带完整 `gameState`
3. 前端不再出现 `bridgeServer*Snapshot` 这类业务补丁函数。
4. 前端不再 `putSnapshot({ gameState, currentStory })`
5. 前端不再动态 `import('../ai')`
6. NPC 交易、送礼、背包、锻造的价格、合法性、结果都以后端 view model 为准。
7. 战斗死亡、复活、胜利后状态都以后端返回为准。
8. 创作结果页打开哪个 stage 由后端 work/session view 指示。
## 10. 一句话结论
当前 RPG 前端脚本最需要迁移的不是 UI 组件,而是仍残留在 hooks / gateway / client 里的:
**开局造状态、运行时带快照解析、前端补服务端快照、自动保存整份 GameState、story engine 编排、战斗后处理、NPC/背包/锻造规则裁决,以及 RPG 创作生成和结果预览的 legacy 后门。**

View File

@@ -0,0 +1,169 @@
# RPG 前端脚本后端迁移完成度核验2026-04-28
## 1. 核验结论
本次按 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 中列出的应迁后端项逐项检查当前代码。
结论:**应迁移项尚未全部迁移完成。**
当前状态:
1. `已完成`9 项。
2. `部分完成`1 项。
3. `未发现完全未启动`0 项。
本轮重新核查的变化:
1. 上次残留的 `RPG 创作结果页` 保存前 profile normalize 已完成后端化。
2. 新发现 `camp_travel_home_scene` 已登记为服务端 runtime function id但正式点击仍会被前端专用旅行分支提前拦截并本地拼装场景迁移状态。
## 2. 核验口径
### 2.1 判定为已完成
满足以下条件才记为已完成:
1. 前端不再构造正式业务状态。
2. 前端不再上传完整 `GameState` 作为后端写入依据。
3. 前端不再用本地规则裁决价格、库存、掉落、复活、章节推进、结果页真相优先级。
4. 前端只保留请求封装、UI 展示、表单草稿、loading/error、动画表现和按钮禁用展示。
### 2.2 判定为部分完成
满足以下任一情况记为部分完成:
1. 主链已经迁到 `server-rs`,但旧前端分支仍可被正式入口调用。
2. 后端已经提供 view/action但前端仍保留影响业务真相的 normalize、fallback 或 AI context 编排。
3. 后端只覆盖部分状态机,前端仍负责另一部分正式状态推进。
## 3. 逐项结果
| 原审计项 | 当前状态 | 核验结论 |
| --- | --- | --- |
| `P0` 运行时开局 `GameState` 装配 | 已完成 | 正式开局状态由 `server-rs/crates/api-server/src/runtime_story/compat/bootstrap.rs` 创建并持久化;前端 `useRpgSessionBootstrap.ts` 只保留选择页占位态和 `beginRpgRuntimeStorySession(...)` 调用。 |
| `P0` runtime story 网关客户端快照解析/补丁 | 已完成 | `rpgRuntimeStoryGateway.ts` 不再有 `buildRuntimeSnapshotRequest` / `bridgeServer*Snapshot``rpgRuntimeStoryClient.ts` 读取 `/state/:sessionId`,动作提交 `/actions/resolve`,不再上传完整 `snapshot.gameState/currentStory`。 |
| `P0` 自动保存整份运行时快照 | 已完成 | `useRpgSessionPersistence.ts` / `rpgSnapshotClient.ts` 保存链路只提交 `sessionId/bottomTab` checkpoint`runtime_save.rs` 从服务端已有快照刷新 checkpoint并测试拒绝旧式完整快照上传。 |
| `P0` story engine / chapter / world mutation / prompt context 编排 | 部分完成 | 后端已有 `project_story_engine_after_action(...)``build_runtime_story_prompt_context(...)``/story/initial``/story/continue``sessionId` 时只从服务端 snapshot 投影 world / character / history / prompt context`camp_travel_home_scene` 已是服务端 function id前端仍先走本地 `runCampTravelHomeChoice(...)` 拼装场景迁移、encounter preview 和 runtimeStats。 |
| `P0` 战斗胜负后处理、死亡复活、战斗后章节推进 | 已完成 | `battle_* / inventory_use` 正式点击统一走 `runServerRuntimeChoiceAction(...)` 与后端 `/actions/resolve``storyChoiceContinuation.ts` 对战斗 / 逃脱 / 物品动作加硬保护,不再裁决掉落、任务推进、死亡复活或战后 story`postBattleFlow.ts` 正式状态构造函数已删除。 |
| `P1` NPC 交易/送礼价格数量库存校验 | 已完成 | 前端 `npcInteraction.ts` 已改为消费 `runtimeNpcInteraction` view 并只提交 `{ mode, itemId, quantity }`;后端 `npc_actions.rs` / `npc_support.rs` 负责价格、库存、货币、赠礼好感和原子更新。 |
| `P1` 背包/装备/锻造可用性与配方视图 | 已完成 | 前端 `inventoryActions.ts` 读取 `loadRpgRuntimeInventoryView(...)`,根据后端 action/view 提交;后端 `view_model.rs` / `forge.rs` 生成背包、装备槽、配方、`canCraft/enabled/reason`。 |
| `P1` RPG 创作 profile 生成 legacy AI 回退 | 已完成 | `rpgCreationGenerationClient.ts` 只调用 `/api/runtime/custom-world/profile`,不再动态 `import('../ai')` 或导出 `generateLegacyCustomWorldProfile`。 |
| `P1` 创作结果页保存与 Agent session/result preview 真相优先级 | 已完成 | 后端已提供 `GET /api/runtime/custom-world/agent/sessions/:sessionId/result-view`,统一 `targetStage/profileSource/canAutosaveLibrary/canSyncResultProfile`,前端不再直接读取 `legacyResultProfile`;保存前 canonicalize 已迁到 `server-rs``normalizeRpgEntryAgentBackedProfile(...)` 现在只透传兼容旧导入。 |
| `P1` 角色资产工坊默认 prompt 与缓存合并规则 | 已完成 | 默认 prompt、legacy prompt 过滤、逐动作缓存合并已在 `server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs`;前端 modal 只调用 workflow API、保存用户草稿和发起生成/发布。 |
## 4. 仍未完成的具体收尾点
### 4.1 story engine / prompt context 主链已完成,但 `camp_travel_home_scene` 仍残留前端正式分支
当前后端已经处理动作结算后的确定性 story projector
1. `server-rs/crates/module-runtime-story-compat/src/story_engine.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs`
本轮已补齐 prompt context 后端 projector
1. `server-rs/crates/module-runtime-story-compat/src/prompt_context.rs`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs`
3. `server-rs/crates/api-server/src/runtime_chat.rs`
4. `server-rs/crates/api-server/src/runtime_chat_plain.rs`
完成状态:
1. 前端不再决定 `conversationSituation``conversationPressure`、NPC 对话上下文、party relationship notes、scene pressure 文本等正式 prompt context。
2. 前端 story initial / continue 在有 `runtimeSessionId` 时只提交 `sessionId / clientVersion / choice / lastFunctionId / requestOptions` 等轻量字段。
3. 后端从已持久化 runtime snapshot 投影 `worldType / playerCharacter / sceneHostileNpcs / storyHistory / context`,旧 payload 字段只保留兼容。
4. 角色私聊、NPC 对话、NPC 单轮聊天、NPC 招募对话已支持 `sessionId`,有 session 时上下文同样以后端 snapshot 为准。
5. 前端奖励领取、NPC 聊天闭合与旧 `deferredRuntimeState` 兼容分支不再写入 `storyEngineMemory`章节、scene act、thread、world mutation 等正式叙事记忆只以后端快照为准。
本轮验收:
1. `cargo test -p module-runtime-story-compat prompt_context --manifest-path server-rs\Cargo.toml` 覆盖后端 prompt context projector 对场景、NPC 披露阶段、对话压力和关系态度的投影。
2. `cargo test -p shared-contracts runtime_story_ai_request --manifest-path server-rs\Cargo.toml` 覆盖 story AI 请求可只携带 `sessionId` 的共享契约。
3. `cargo test -p api-server runtime_story_initial_uses_server_snapshot_prompt_context_when_session_id_present --manifest-path server-rs\Cargo.toml` 覆盖 `/story/initial` 在 session 模式下以后端快照覆盖浏览器传入的 world / character / context。
4. `npm run test -- src/services/ai.test.ts src/hooks/rpg-runtime-story/storyRequestCoordinator.test.ts src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.test.tsx` 覆盖前端 story / chat 请求在 session 模式下只发送轻量 payload。
5. `npm run test -- src/hooks/rpg-runtime-story/sessionActions.test.ts src/hooks/rpg-runtime-story/choiceActions.test.ts src/hooks/rpg-runtime-story/npcEncounterActions.test.ts` 覆盖前端旧 UI 分支不再回写后端拥有的 `storyEngineMemory`
重新核查新增残留:
1. `packages/shared/src/contracts/rpgRuntimeStoryAction.ts` 已把 `camp_travel_home_scene` 纳入 `TASK5_RUNTIME_FUNCTION_IDS` / `SERVER_RUNTIME_FUNCTION_IDS`
2. `server-rs/crates/api-server/src/runtime_story/compat.rs` 已有 `camp_travel_home_scene` resolver 分支,但当前只清理 encounter并未承接前端旧分支里的目标场景、encounter preview 与完整离营故事提交。
3. `src/hooks/rpg-runtime-story/choiceActions.ts` 仍在 `isRpgRuntimeServerFunctionId(...)` 之前判断 `isCampTravelHomeOption(...)`,并调用 `runCampTravelHomeChoice(...)`
4. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts``runCampTravelHomeChoice(...)` 会在浏览器中决定目标场景、清理战斗/遭遇、递增 `scenesTraveled`、构造 encounter preview并通过 `commitGeneratedStateWithEncounterEntry(...)` 写入后续故事。
影响:
1. 这条链不是纯动画表现,而是正式场景迁移、运行时统计、遭遇预览和后续故事提交。
2. 它已经具备服务端 function id 身份,却没有统一走 `/api/runtime/story/actions/resolve`,因此仍不满足“前端只提交 action后端返回 hydrated snapshot”的边界。
### 4.2 本地战斗 continuation 已收口到后端
当前后端已经有战斗终局收口:
1. `server-rs/crates/module-runtime-story-compat/src/post_battle.rs`
2. `server-rs/crates/module-runtime-story-compat/src/battle.rs`
3. `server-rs/crates/api-server/src/runtime_story/compat.rs`
本轮已完成前端旧正式分支收口:
1. `src/hooks/rpg-runtime-story/choiceActions.ts` 不再保留 `shouldResolveCombatChoiceLocally(...)`,所有服务端 function id 都统一进入 `runServerRuntimeChoiceAction(...)`
2. `src/hooks/rpg-runtime-story/storyChoiceContinuation.ts``battle_* / inventory_use` 以及被分类为 `battle / escape` 的动作加硬保护,误入时只报错回退,不会写掉落、任务、复活或战后 story。
3. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts` 删除本地敌对 NPC 掉落 reward helper不再调用 `rollHostileNpcLoot(...)` / `addInventoryItems(...)` 构造正式战斗奖励。
4. `src/hooks/rpg-runtime-story/postBattleFlow.ts` 与对应测试删除,前端不再保留 `buildPostBattleVictoryState(...)``buildPostBattleVictoryStory(...)``buildRevivedFirstSceneState(...)``buildDeathStory(...)` 作为正式状态构造入口。
已消除风险:
1. `battle_* / inventory_use` 不再因 `inBattle``currentBattleNpcId`、可见 story option 等状态回落到本地结算。
2. 本地 continuation 不再调用敌对 NPC 掉落、背包合并、敌对 NPC 任务推进。
3. 前端不再构造死亡复活状态、胜利后 story、deferred options 和章节推进。
本轮验收:
1. `choiceActions.test.ts` 覆盖 `battle_use_skill``battle_attack_basic` stale option、`inventory_use` 均只调用后端 resolver不触发 `buildResolvedChoiceState(...)` / `playResolvedChoice(...)`
2. `storyChoiceRuntime.test.ts` 保留服务端 battle presentation 验收,确认胜利 / 失败最终采用服务端 hydrated snapshot。
3. 搜索确认 `src/hooks/rpg-runtime-story` 不再包含 `shouldResolveCombatChoiceLocally``buildPostBattleVictory*``buildRevivedFirstSceneState``buildDeathStory``buildHostileNpcBattleReward`
### 4.3 创作结果页保存前 normalize 已完成后端化
后端已经负责 Agent result-view
1. `server-rs/crates/api-server/src/custom_world.rs`
2. `packages/shared/src/contracts/rpgCreationResultView.ts`
3. `src/services/rpg-creation/rpgCreationAgentClient.ts`
重新核查结果:
1. `src/components/rpg-entry/rpgEntryShared.ts`
2. `src/components/rpg-entry/useRpgCreationResultAutosave.ts`
3. `server-rs/crates/api-server/src/custom_world.rs`
当前完成状态:
1. `normalizeRpgEntryAgentBackedProfile(...)` 现在直接返回原始 `profile`,注释明确保存前 canonicalize 已迁到 `server-rs`
2. `stringifyRpgEntryAgentBackedProfile(...)` 现在只做 `JSON.stringify(profile)`,不再触发前端 normalize。
3. `put_custom_world_library_profile(...)` 写入作品库前调用 `canonicalize_custom_world_library_profile_payload(payload.profile)`
4. `serialize_sync_result_profile_action_payload(...)` 会在 Agent `sync_result_profile` action payload 中对 `profile` 执行 `canonicalize_custom_world_profile_before_save(...)`
5. 后端测试 `sync_result_profile_payload_is_canonicalized_on_server``custom_world_library_profile_payload_is_canonicalized_on_server` 已覆盖保存前 canonicalize。
本项不再计为未迁移残留。
## 5. 已完成项的保留边界
以下前端残留可以保留,不视为未迁移:
1. `useRpgSessionBootstrap.ts``createSelectionGameState()`:只服务选择页占位,正式开局不使用它造运行时真相。
2. `NpcModals.tsx` 的数量 stepper、价格展示和按钮禁用只消费后端 view不重新裁决价格或库存。
3. `inventoryActions.ts``submitInventoryAction(...)`:只读取后端 action 的 `enabled/reason`,不本地计算规则。
4. 角色资产工坊 modal 中的 prompt 输入框与缓存保存:这是用户正在编辑的 UI 草稿,默认 prompt 和合并规则已由后端 workflow 输出。
5. `playServerBattlePresentation(...)`:只播放临时动画态,最终 `GameState/currentStory` 仍以服务端 snapshot 为准。
## 6. 下一步建议
推荐按风险顺序继续:
1.`camp_travel_home_scene` 点击链统一改为 `runServerRuntimeChoiceAction(...)` / `/api/runtime/story/actions/resolve`
2. 扩展 `server-rs/crates/api-server/src/runtime_story/compat.rs` 中的 `camp_travel_home_scene` resolver让目标场景、encounter preview、`scenesTraveled`、故事提交和快照持久化全部由后端完成。
3. 补齐前端测试,锁定 `camp_travel_home_scene` 不再调用 `runCampTravelHomeChoice(...)`;补齐后端 route 级测试,覆盖离营后 hydrated snapshot 字段。
## 7. 一句话结论
**当前迁移已经完成了开局、快照、存档、story engine / prompt context 主链、NPC、背包/锻造、战斗后处理、profile 生成、创作结果页 normalize 和角色资产 prompt 主链;但 `camp_travel_home_scene` 仍由前端专用分支拼装正式场景迁移状态,所以不能判定“应迁移项已全部迁移完成”。**

View File

@@ -0,0 +1,33 @@
# 平台入口隐藏大鱼吃小鱼创作入口设计
日期:`2026-04-28`
## 1. 变更背景
平台当前“选择创作类型”弹层同时暴露 RPG、大鱼吃小鱼、拼图三类入口。
本轮需求只要求在平台里隐藏“大鱼吃小鱼”的创作入口,不要求删除已有玩法实现、运行时路由、作品数据或后台能力。
## 2. 落地边界
- 只调整平台入口层展示,不修改大鱼吃小鱼已有前后端链路。
- 不删除 `big-fish` 相关路由、服务、作品详情、运行时与数据结构。
- 隐藏策略应收敛到统一配置层,避免首页、弹层、后续复用入口出现显示状态漂移。
## 3. 实现方案
1.`src/components/platform-entry/platformEntryCreationTypes.ts` 的创作类型元数据中增加 `hidden` 字段。
2.`big-fish` 类型标记为 `hidden: true`
3. 平台创作类型弹层渲染前统一过滤 `hidden` 项。
这样可以保证:
- 平台用户看不到“大鱼吃小鱼”创作入口。
- 若后续重新开放,只需改回配置,不必再拆 UI 逻辑。
- 不影响既有直达路由、历史作品数据和开发中的玩法链路。
## 4. 验收点
- 平台“选择创作类型”弹层不再显示“大鱼吃小鱼”卡片。
- RPG、拼图、“敬请期待”类卡片顺序与交互保持稳定。
- 代码层不引入对 Big Fish 运行时或结果页的额外耦合修改。

View File

@@ -11,6 +11,7 @@
- [CUSTOM_WORLD_SELF_OWNED_SETTING_LAYER_OPTIMIZATION_2026-04-08.md](./CUSTOM_WORLD_SELF_OWNED_SETTING_LAYER_OPTIMIZATION_2026-04-08.md):把模板依赖逐步迁成自定义世界自有设定层,并保证不破坏当前生成流程的优化方案。
- [MOBILE_CREATION_NEW_WORK_COMPACT_LAYOUT_2026-04-24.md](./MOBILE_CREATION_NEW_WORK_COMPACT_LAYOUT_2026-04-24.md):移动端创作页新建作品模块最多占用首屏约 1/3 高度的紧凑布局设计。
- [PLATFORM_CATEGORY_AND_CREATE_TAB_DESIGN_2026-04-24.md](./PLATFORM_CATEGORY_AND_CREATE_TAB_DESIGN_2026-04-24.md):平台入口新增分类 Tab、登录态导航裁剪与创作 Tab 视觉强化设计。
- [PLATFORM_BIG_FISH_ENTRY_HIDE_2026-04-28.md](./PLATFORM_BIG_FISH_ENTRY_HIDE_2026-04-28.md):平台入口暂时隐藏大鱼吃小鱼创作卡片,但保留现有玩法链路。
- [UNIFIED_MODAL_WINDOW_DESIGN_2026-04-25.md](./UNIFIED_MODAL_WINDOW_DESIGN_2026-04-25.md):统一平台风与 RPG 像素风模态窗口外壳、交互边界和迁移顺序。
- [AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md](./AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md):运行时物品生成系统重设计。
- [LEVEL_PROGRESS_AND_CHAPTER_NPC_AUTO_SCALING_DESIGN_2026-04-20.md](./LEVEL_PROGRESS_AND_CHAPTER_NPC_AUTO_SCALING_DESIGN_2026-04-20.md):等级成长、章节经验节奏与 NPC 自动定级设计。

View File

@@ -0,0 +1,80 @@
# 大鱼吃小鱼草稿生成链路修复 2026-04-28
## 背景
大鱼吃小鱼玩法的结果页已经具备等级卡、主图工坊、动作工坊和背景工坊,但当前 `big_fish_compile_draft` 只是把锚点交给 `module-big-fish``compile_default_draft(...)` 做静态模板拼装。
这会导致两个直接问题:
1. 草稿编译虽然能成功进入结果页,但每一级实体只会拿到非常概括的模板文本,无法真正产出“实体名称、文字描述、形象描述、待机动作描述、移动动作描述”这一组首稿。
2. 主图和动作工坊默认提示词没有绑定到一份足够细的草稿真相源,动作面板只能看到合并后的 `motionPromptSeed`,会表现成“草稿生成一带而过,所有内容都没有正常生成”。
## 本次修复口径
### 1. 每级等级蓝图必须补齐的文本字段
大鱼吃小鱼每一级 `level blueprint` 在保留原有字段的同时,新增并持久化下面这些文本真相:
1. `textDescription`
- 当前等级实体的正文文字描述。
- 用于结果页等级卡和后续重生成时的人类可读设定底稿。
2. `visualDescription`
- 当前等级实体的形象描述。
- 主图工坊默认输入内容直接取这份字段。
3. `idleMotionDescription`
- 当前等级待机动作描述。
- `idle_float` 动作工坊默认输入内容直接取这份字段。
4. `moveMotionDescription`
- 当前等级移动动作描述。
- `move_swim` 动作工坊默认输入内容直接取这份字段。
### 2. 默认提示词流转规则
草稿生成、结果页工坊和正式资产生成统一按下面口径流转:
1. 草稿编译阶段先产出上述结构化文本字段。
2. 主图工坊默认文案:
- 优先显示 `visualDescription`
- `visualPromptSeed` 作为主图正式生图提示词的冻结快照,可由 `visualDescription` 组合生成
3. 动作工坊默认文案:
- `idle_float` 优先显示 `idleMotionDescription`
- `move_swim` 优先显示 `moveMotionDescription`
- `motionPromptSeed` 继续保留为动作方向总提示词摘要,但具体动作正式生图必须显式拼入动作位对应描述
4. 草稿阶段生成的正式主图、动作图和后续重生成,都只能读取同一份 `draft.levels[*]` 真相,前端不得本地拼接新的设定文案。
### 3. 编译策略
`big_fish_compile_draft` 需要升级为:
1. `api-server` 先调用 LLM 做结构化草稿编译。
2. 若 LLM 成功,则把完整 `draft_json` 写回 SpacetimeDB。
3. 若 LLM 不可用、返回非法 JSON 或字段缺失,则退回 `compile_default_draft(...)` 的 deterministic fallback。
这样可以同时保证:
1. 正常环境下草稿不再只是模板壳。
2. 模型偶发失败时不会打断结果页主链。
3. SpacetimeDB reducer 不承担外部网络调用,仍然符合后端边界。
## 落地范围
本次修复涉及:
1. `server-rs/crates/module-big-fish`
2. `server-rs/crates/spacetime-module`
3. `server-rs/crates/spacetime-client`
4. `server-rs/crates/shared-contracts`
5. `server-rs/crates/api-server`
6. `packages/shared/src/contracts/bigFish.ts`
7. `src/components/big-fish-result/BigFishResultView.tsx`
## 验收口径
修复后需要满足下面这些观察结果:
1. 点击“生成草稿”后,`draft.levels[*]` 不再只有空泛模板,而是每级都带名称、文字描述、形象描述、待机动作描述、移动动作描述。
2. 打开主图工坊时,默认文本来自当前等级的 `visualDescription`
3. 打开待机动作工坊时,默认文本来自当前等级的 `idleMotionDescription`
4. 打开移动动作工坊时,默认文本来自当前等级的 `moveMotionDescription`
5. 资产槽位 `promptSnapshot` 与对应动作位 / 主图位的默认提示词一致。
6. LLM 不可用时仍然能生成一版可用 fallback 草稿,而不是直接报错或写入空草稿。

View File

@@ -0,0 +1,27 @@
# 拼图本地运行态通关排行榜误请求修复记录
## 问题现象
拼图关卡完成后,右下角会弹出错误提示,内容表现为拼图 `run` 不存在。
## 根因
当前拼图玩法仍有一条前端本地兜底链路:
1. 进入拼图测试或公开作品体验时,前端先创建 `local-puzzle-run-*` 形式的本地运行态。
2. 这类 `run` 只存在于前端内存,不存在后端持久化记录。
3. 通关副作用里却统一调用了后端 `submitPuzzleLeaderboard(runId, payload)`
4. 后端拿到本地 `runId` 后无法找到真实记录于是返回“run 不存在”,最终在运行时右下角暴露成错误提示。
## 修复口径
本次不改后端接口,也不把本地兜底 run 强行持久化到后端,而是先把边界收口到前端:
1. 显式识别 `local-puzzle-run-*` 这类本地 run。
2. 本地 run 通关后不再请求后端排行榜接口。
3. 直接在前端本地生成只包含当前玩家成绩的排行榜数据,保证结算弹窗仍可展示成绩。
4. 真实后端 run 仍继续走正式排行榜提交流程,不影响后续 Rust / SpacetimeDB 版本的统一收口。
## 经验结论
只要某条玩法链路还保留“本地 run / 本地快照”兜底,就不能在通关、副作用、排行榜、下一关等后置动作里默认把它当成后端真 run 使用。必须先做运行态来源分流,再决定是否调用依赖真实 runId 的接口。

View File

@@ -32,3 +32,5 @@
- [AGENT_EMPTY_SESSION_DRAFT_VISIBILITY_2026-04-26.md](./AGENT_EMPTY_SESSION_DRAFT_VISIBILITY_2026-04-26.md):记录 Agent 空会话不应进入作品草稿列表的后端判定规则。
- [BIG_FISH_PUBLISH_FEEDBACK_FIX_2026-04-26.md](./BIG_FISH_PUBLISH_FEEDBACK_FIX_2026-04-26.md):记录大鱼吃小鱼发布成功后结果页反馈与作品列表刷新的修复口径。
- [BIG_FISH_WORKS_JSON_COMPAT_FIX_2026-04-28.md](./BIG_FISH_WORKS_JSON_COMPAT_FIX_2026-04-28.md):记录大鱼作品列表 `items_json` 字段升级后的向后兼容修复口径,避免旧 JSON 直接打崩 works 接口。
- [BIG_FISH_DRAFT_GENERATION_CHAIN_FIX_2026-04-28.md](./BIG_FISH_DRAFT_GENERATION_CHAIN_FIX_2026-04-28.md):记录大鱼吃小鱼草稿生成从结构化内容产出到主图/动作默认提示词回填的修复口径。
- [PUZZLE_LOCAL_RUN_LEADERBOARD_FIX_2026-04-28.md](./PUZZLE_LOCAL_RUN_LEADERBOARD_FIX_2026-04-28.md):记录拼图本地 run 通关后误请求后端排行榜、导致“run 不存在”报错的边界修复口径。

View File

@@ -4,11 +4,13 @@
- [BUSINESS_PROMPT_INVENTORY_2026-04-19.md](./BUSINESS_PROMPT_INVENTORY_2026-04-19.md):业务中现存提示词的总清单,覆盖后端主链、前端遗留、自定义世界、角色形象生成、场景背景生成与工具链 prompt。
- [FUNCTION_SCRIPT_CATALOG_2026-04-04.md](./FUNCTION_SCRIPT_CATALOG_2026-04-04.md)Function 独立脚本目录与分类速查。
- [RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md](./RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md)RPG 创作功能脚本与 RPG 运行时脚本的职责地图覆盖前端入口、编排、表现、client、`server-rs` 与 SpacetimeDB 侧落点。
- [TASK_GENERATION_TRACE_2026-04-08.md](./TASK_GENERATION_TRACE_2026-04-08.md):任务描述、达成条件与奖励生成链路梳理。
- [CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md](./CUSTOM_WORLD_TEMPLATE_DEPENDENCY_INVENTORY_2026-04-08.md):自定义世界当前仍依赖哪些模板世界设定的清单。
## 使用建议
- 需要快速定位 Function 脚本,而不是阅读长篇方案时,优先看这里。
- 需要快速判断“RPG 创作链和 RPG 运行时链分别该改哪些脚本”时,优先看 RPG 脚本职责地图。
- 需要判断“武侠 / 仙侠模板层”哪些还能删、哪些不能删时,优先看自定义世界模板依赖清单。
- 如果要评估 Function 分层是否合理,再配合 `docs/audits/FUNCTION_DESIGN_AUDIT_2026-04-03.md` 一起看。

View File

@@ -0,0 +1,873 @@
# RPG 创作功能与运行时脚本职责地图2026-04-28
## 1. 文档目的
这份文档只做一件事:
**把当前仓库里 RPG 创作功能相关脚本,以及 RPG 运行时游戏相关脚本,按真实职责整理成一份可检索的地图。**
这里的“脚本职责”强调的是:
1. 哪个脚本是入口。
2. 哪个脚本负责状态编排。
3. 哪个脚本只负责表现层。
4. 哪个脚本负责请求后端。
5. 哪个脚本在 `server-rs` 中承接正式业务真相。
6. 哪些脚本仍处于兼容桥接层,不应继续扩张。
---
## 2. 当前口径
### 2.1 唯一后端口径
按当前工程基线,正式后端以 `server-rs` 为准:
1. HTTP / SSE 门面:`server-rs/crates/api-server/src/`
2. 共享契约:`server-rs/crates/shared-contracts/src/`
3. SpacetimeDB 模块与领域表/过程:`server-rs/crates/module-*/src/`
4. 客户端绑定与调用封装:`server-rs/crates/spacetime-client/src/`
### 2.2 当前前端口径
前端已基本按 RPG 域拆成三条主链:
```text
平台入口与创作入口
-> RPG 创作链
-> RPG 运行时链
```
但当前仍存在一些兼容桥接脚本,例如:
1. `src/components/rpg-entry/RpgEntryFlowShell.tsx`
2. `src/services/rpg-runtime/rpgRuntimeChatClient.ts`
3. `server-rs/crates/api-server/src/runtime_story.rs`
这些文件的职责主要是**兼容旧入口**,不是长期承载复杂逻辑的主战场。
---
## 3. 全局总图
```text
平台入口层
-> 创作入口 / 运行时入口分流
RPG 创作链:
平台入口
-> 创作工作台 / 共创会话
-> 结果页
-> 实体编辑器 / 角色资产工坊
-> 创作域 client
-> server-rs custom_world / custom_world_ai / prompt/rpg
-> SpacetimeDB custom-world 相关表与过程
RPG 运行时链:
平台入口 / 世界详情 / 继续游戏
-> session bootstrap / persistence
-> runtime shell / panel router / adventure panel
-> runtime story hooks / gateway / client
-> functionCatalog / prompt/rpg
-> server-rs runtime_story / runtime_chat / runtime_save / story_battles / story_sessions
-> shared-contracts + module-runtime + module-story + module-runtime-story-compat
```
---
## 4. RPG 创作功能脚本职责
## 4.1 创作入口与平台分流
### `src/components/platform-entry/PlatformEntryFlowShell.tsx`
职责:
1. 平台入口通用壳层。
2. 统一分流 RPG、Big Fish、Puzzle 等不同玩法入口。
3. 作为多玩法并列入口的稳定门面。
说明:
1. 这是当前真正的平台入口壳层。
2. RPG 创作和 RPG 运行时入口都先经过这里。
### `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
职责:
1. 平台首页、详情页、创作类型选择、RPG 创作恢复、结果页进入等总编排。
2. 协调 RPG 创作 Agent、Big Fish、Puzzle 的进入和返回。
3. 连接平台级导航、作品库、公开广场、详情页和创作工作流。
说明:
1. 这是当前“平台级大编排器”。
2. 它不是纯 RPG 文件,但当前 RPG 创作入口真实在这里收口。
### `src/components/custom-world-home/CustomWorldCreationHub.tsx`
职责:
1. 展示 RPG 创作工作台。
2. 负责草稿、已发布作品、跨玩法作品卡片的列表层表现。
3. 负责“创建新作品”“打开草稿”“进入已发布作品”“体验作品”“删除作品”的入口分发。
说明:
1. 这是 RPG 创作库的主表现层。
2. 它不负责正式生成逻辑,只负责把用户动作抛给上层控制器。
---
## 4.2 RPG 共创会话链
### `src/components/custom-world-agent/CustomWorldAgentWorkspace.tsx`
职责:
1. 把 RPG 世界共创会话映射成通用 `CreationAgentWorkspace` 可消费的视图模型。
2. 组织世界承诺、玩家幻想、主题边界、核心冲突等锚点展示。
3. 提供“发送消息”“快捷动作”“生成底稿”等交互入口。
说明:
1. 这是 RPG 创作 Agent 会话的前端展示壳层。
2. 它负责把领域数据翻译成工作台视图,不负责真正的网络请求。
### `src/services/rpg-creation/rpgCreationAgentClient.ts`
职责:
1. 创建 RPG 创作会话。
2. 读取会话快照。
3. 发送消息、流式发送消息。
4. 执行创作动作。
5. 读取操作进度和草稿卡详情。
说明:
1. 这是 RPG 创作 Agent 的主 client 入口。
2. 前端所有“会话级创作行为”最终都应该走这里,而不是继续回流到通用 `aiService.ts`
### `server-rs/crates/api-server/src/custom_world.rs`
职责:
1. 承接 RPG 世界库、世界详情、作品发布、取消发布、作品删除等 HTTP 接口。
2. 承接创作 Agent session、message、action、operation、card detail 等接口。
3. 协调底稿写回、资产生成、结果预览、发布门禁等后端业务。
说明:
1. 这是当前 RPG 创作后端最大入口文件之一。
2. 前端创作会话和作品库的大部分正式请求都在这里收口。
### `server-rs/crates/api-server/src/custom_world_agent_turn.rs`
职责:
1. 承接 RPG 共创聊天单轮执行。
2. 负责把用户消息、会话状态、锚点内容和输出结构组织成一轮 Agent turn。
3. 负责回写消息、操作、阶段进度和结果。
### `server-rs/crates/api-server/src/custom_world_foundation_draft.rs`
职责:
1. 负责 RPG 世界底稿生成链。
2. 组织底稿结构、草稿写回、阶段进度推进和失败恢复。
### `server-rs/crates/api-server/src/custom_world_ai.rs`
职责:
1. 提供 RPG 创作过程中的场景图、场景 NPC、角色、地标等 AI 衍生生成接口。
2. 把前端“补实体”“补图”“补角色”动作收口到后端。
---
## 4.3 RPG 创作结果页与编辑链
### `src/components/rpg-creation-result/RpgCreationResultView.tsx`
职责:
1. RPG 创作结果页 façade。
2. 只桥接到真实实现,不承载复杂逻辑。
说明:
1. 这是稳定入口。
2. 真正逻辑在 `RpgCreationResultViewImpl.tsx` 及其配套 hooks 内。
### `src/components/rpg-creation-result/useRpgCreationResultActions.ts`
职责:
1. 管理结果页上的实体新增、删除、局部重生成、最近新增实体高亮等交互。
2. 调用 `rpgCreationAssetClient` 生成 playable / story / landmark。
3. 协调结果页与编辑器、资产工坊之间的动作状态。
说明:
1. 这是结果页最核心的动作编排脚本之一。
2. “结果页做什么”主要在这里定义,不在纯展示组件里定义。
### `src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx`
职责:
1. RPG 实体编辑器 modal 的稳定入口。
2. 把复杂表单实现桥接到 `RpgCreationEntityEditorModalImpl.tsx`
说明:
1. 这是编辑器 façade。
2. 后续编辑器拆 section 时应继续改 impl不要把复杂逻辑塞回 façade。
### `src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.tsx`
职责:
1. 角色资产工坊 modal 的稳定入口。
2. 把角色形象、动作、候选图、动画等细节桥接到真实实现。
### `src/components/rpg-creation-result/RpgCreationAssetDebugPanel.tsx`
职责:
1. 用于查看创作资产链路状态、调试结果页资产问题。
2. 服务于结果页调试与排查,而不是普通玩家主流程。
---
## 4.4 RPG 创作域 client 分层
### `src/services/rpg-creation/rpgCreationGenerationClient.ts`
职责:
1. 负责“生成 RPG 世界底稿 / profile”。
2. 浏览器、SSR、Vitest node 环境统一请求 `/api/runtime/custom-world/profile`
3. 不再动态导入 `src/services/ai.ts`,测试离线能力只能 mock API client。
说明:
1. 这是“世界底稿生成”的入口 client。
2. profile 生成 prompt 与 LLM 编排已经收口到 `server-rs`,前端只保留请求 contract。
### `src/services/rpg-creation/rpgCreationAssetClient.ts`
职责:
1. 负责场景图、角色、地标、场景 NPC、封面图等资产请求。
2. 负责历史资产列表读取。
3. 给结果页和资产工坊提供统一资产接口。
### `src/services/rpg-creation/rpgCreationLibraryClient.ts`
职责:
1. 负责世界库、作品广场、作品详情、保存、删除、发布、下架。
2. 承接 RPG 创作结果进入库与进入广场的正式请求。
### `src/services/rpg-creation/rpgCreationPreviewAdapter.ts`
职责:
1. 把 Agent session / result preview 转成结果页可消费预览模型。
2. 是“生成链结果”和“结果页展示模型”之间的适配层。
### `src/services/rpg-creation/rpgCreationWorkClient.ts`
职责:
1. 负责作品工作台列表和会话删除等工作流外围请求。
---
## 4.5 RPG 创作提示词与内容编排脚本
### 前端提示词目录 `src/prompts/`
与 RPG 创作直接相关的脚本:
1. `src/prompts/customWorldPrompts.ts`
负责自定义世界生成相关提示词。
2. `src/prompts/customWorldEntityActionPrompts.ts`
负责实体生成与实体动作提示词。
说明:
1. 角色资产工坊默认 prompt 与缓存合并规则已经迁入 Rust前端不再保留 `customWorldRolePromptDefaults.ts` 主源。
### Rust 提示词目录 `server-rs/crates/api-server/src/prompt/rpg/`
与 RPG 创作直接相关的脚本:
1. `foundation_draft.rs`
负责 RPG 世界底稿生成提示词。
2. `agent_chat.rs`
负责 RPG 共创聊天提示词。
3. `role_asset_studio.rs`
负责角色资产工坊默认 prompt、legacy prompt 过滤与缓存合并 workflow view。
说明:
1. RPG 创作 prompt 已在 Rust 侧按 `rpg/` 子目录收口。
2. 创作语义的正式后端提示词应该优先在这里改,而不是散改到路由或 service 中。
---
## 4.6 RPG 创作在 SpacetimeDB / 契约层的职责
### `server-rs/crates/shared-contracts/src/runtime.rs`
职责:
1. 提供 RPG 创作请求/响应需要复用的共享运行时契约。
### `server-rs/crates/module-custom-world/src/lib.rs`
职责:
1. 承接自定义世界作品、会话、发布、草稿等正式模块能力。
2. 作为 RPG 创作在 SpacetimeDB 的主要领域模块之一。
### `server-rs/crates/spacetime-client/src/custom_world.rs`
职责:
1.`api-server` 提供自定义世界相关过程调用和数据读取封装。
2. 把 API 层和 SpacetimeDB 模块调用隔开。
---
## 5. RPG 运行时游戏脚本职责
## 5.1 运行时入口与 Session 初始化
### `src/components/rpg-entry/RpgEntryFlowShell.tsx`
职责:
1. 兼容旧 RPG 入口导入路径。
2. 真实实现已桥接到 `PlatformEntryFlowShell`
说明:
1. 这是兼容入口,不是当前主逻辑实现点。
2. 如果要改多玩法入口或 RPG 入口主链,应优先看 `platform-entry/`
### `src/hooks/rpg-session/useRpgSessionBootstrap.ts`
职责:
1. 负责 RPG 新开局初始化。
2. 负责世界选择、角色确认、初始场景、初始遭遇、初始库存、初始装备、初始 progression 的装配。
3. 负责把自定义世界 profile 编译成可运行 `GameState`
说明:
1. 这是“开始游戏”最关键的前端 session 装配脚本。
2. 进入运行态前的本地初始态主要在这里成型。
### `src/hooks/rpg-session/useRpgSessionPersistence.ts`
职责:
1. 负责远端快照读取。
2. 负责自动存档。
3. 负责继续游戏恢复。
4. 负责在恢复快照后刷新 runtime story 状态。
说明:
1. 这是运行时持久化主入口。
2. 继续游戏、保存退出、自动存档主要都在这里编排。
### `src/hooks/rpg-session/useRpgRuntimeSession.ts`
职责:
1. 作为 RPG 主运行态装配器。
2. 组合 bootstrap、persistence、combat、npc interaction、runtime story、背景音乐等能力。
3. 最终输出 `RpgRuntimeShell` 所需完整 props。
说明:
1. 这是前端运行时主装配入口。
2. 查“为什么运行态拿到这些状态和事件回调”时,应优先看这里。
---
## 5.2 运行时 UI 壳层与面板层
### `src/components/rpg-runtime-shell/RpgRuntimeShell.tsx`
职责:
1. 承接 RPG 运行态总外壳。
2. 装配画布舞台、阶段路由、overlay host、运行时级 UI chrome。
3. 保持平台壳层和 RPG 壳层之间的显示切换。
### `src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx`
职责:
1. 在平台入口态、选角态、冒险态之间路由。
2. 把 session / story / entry 的装配结果分发给对应页面。
### `src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx`
职责:
1. 在冒险、角色、背包等主面板间切换。
2. 管运行态主 tab 的表现层分发。
### `src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`
职责:
1. 作为 RPG 冒险主面板。
2. 负责展示剧情文本、选项列表、任务状态、战斗状态、资源状态、存档入口等主玩法信息。
3. 负责把运行态 story 选项真正落成可点击 UI。
说明:
1. 这是当前运行时前台最核心的展示脚本之一。
2. 它负责表现和 UI 交互,不负责正式状态真相裁决。
---
## 5.3 运行时剧情主链
### `src/hooks/rpg-runtime-story/useRpgRuntimeStory.ts`
职责:
1. 作为 RPG runtime story 顶层装配入口。
2. 组合角色聊天流、story controller、story flow。
3. 向运行态主链输出 `currentStory``displayedOptions`、战斗奖励 UI、NPC UI、任务 UI 等完整剧情交互能力。
说明:
1. 前端剧情主链的总入口是它。
2. 如果要理解运行时故事层如何被装配,先看这里。
### `src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.ts`
职责:
1. 管理当前故事、加载态、错误态和故事请求控制。
2. 是前端 story 状态层的核心控制器。
### `src/hooks/rpg-runtime-story/useRpgRuntimeStoryFlow.ts`
职责:
1. 负责真正的剧情流编排。
2. 负责选项刷新、选项点击、NPC 交互、地图旅行、战斗奖励 UI、任务 UI、目标 UI 的组合。
### `src/hooks/rpg-runtime-story/useRpgRuntimeInteractionFlow.ts`
职责:
1. 负责把不同类型的运行时交互分发到对应流程。
2. 是运行时“动作入口分发层”。
### `src/hooks/rpg-runtime-story/useRpgRuntimeNpcInteraction.ts`
职责:
1. 负责 NPC 聊天、送礼、招募、委托查看等 NPC 交互链。
2. 组织 NPC 相关 UI 状态和动作入口。
### `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts`
职责:
1. 负责前端 runtime story 与后端 runtime story 的网关衔接。
2. 负责加载 option catalog、恢复 runtime story、提交 choice。
3. 负责把服务端快照桥接回前端可消费状态。
说明:
1. 这是前端到后端 runtime story 主链的关键边界文件。
2. 正式动作解析应该尽量经过这里,而不是在面板里直接组请求。
### `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
职责:
1. 请求 `/api/runtime/story` 的 state 和 action 接口。
2. 把服务端 `RuntimeStoryOptionView` 适配成前端 `StoryOption`
3. 负责 sessionId、clientVersion、snapshot 请求结构和响应快照反序列化。
说明:
1. 这是运行时故事 HTTP client 的主入口。
2. “服务端动作解析”和“服务端状态读取”最终都要走这里。
### `src/services/rpg-runtime/rpgRuntimeChatClient.ts`
职责:
1. 提供角色私聊、NPC 单轮聊天、招募对话等聊天能力。
2. 当前仍桥接旧 `aiService`
说明:
1. 这是运行时聊天 client 的兼容收口层。
2. 当前属于过渡桥接脚本,后续不应继续扩大旧 `aiService` 的依赖面。
### `src/services/rpg-runtime/rpgSnapshotClient.ts`
职责:
1. 提供运行时快照读取、写入、删除能力。
2. 给 session persistence 层提供正式持久化接口。
---
## 5.4 运行时选项函数与本地规则脚本
### `src/data/functionCatalog/`
职责:
1. 维护运行时 function 的独立定义脚本。
2.`state / npc / treasure / flow / panel` 分类收口。
3. 统一管理 functionId、动作标题、说明、部分 helper 和运行时定义。
关键目录:
1. `src/data/functionCatalog/flow/`
负责剧情流程控制型 function。
2. `src/data/functionCatalog/npc/`
负责 NPC 交互型 function。
3. `src/data/functionCatalog/runtimeIndex.ts`
负责运行时 function 的统一索引;该文件只依赖各分目录入口,不反向依赖 `functionCatalog/index.ts`,避免模块初始化循环。
### `src/data/stateFunctions.ts`
职责:
1. 聚合基础状态 function。
2. 对运行时 option 做优先级、排序和过滤支撑。
### `docs/reference/FUNCTION_SCRIPT_CATALOG_2026-04-04.md`
职责:
1. 作为 function 脚本目录的专项速查文档。
2. 如果只想查某个运行时 function 的脚本落点,优先看它。
---
## 5.5 运行时提示词脚本
### 前端 `src/prompts/rpg/`
关键脚本:
1. `runtimeStoryPrompts.ts`
负责 RPG 运行时剧情和运行时叙事提示词。
2. `characterChatPrompts.ts`
负责角色私聊提示词。
### Rust `server-rs/crates/api-server/src/prompt/rpg/`
关键脚本:
1. `runtime_chat.rs`
负责运行时剧情、NPC 对话、运行时聊天相关提示词。
说明:
1. 运行时 prompt 的正式后端组织应优先看 Rust 侧 `prompt/rpg/`
2. 前端 prompt 更多承担适配和兼容角色。
---
## 5.6 `server-rs` 中的 RPG 运行时后端职责
### `server-rs/crates/api-server/src/runtime_story.rs`
职责:
1. 当前作为 RPG runtime story 的后端门面模块。
2. 对外导出 `compat` 中的状态读取、动作解析、初始剧情和继续剧情能力。
说明:
1. 当前文件本身很薄。
2. 真正逻辑在 `runtime_story/compat/` 里。
### `server-rs/crates/api-server/src/runtime_story/compat/`
职责:
1. 承接当前 RPG runtime story 的兼容实现。
2. 负责 AI、装备动作、任务动作、NPC 动作、表现层 view model 和测试支撑。
关键脚本:
1. `ai.rs`
负责运行时叙事 AI 相关兼容逻辑。
2. `npc_actions.rs`
负责 NPC 动作解析。
3. `quest_actions.rs`
负责任务动作解析。
4. `equipment_actions.rs`
负责装备和面板动作解析。
5. `presentation.rs`
负责运行时表现层 view model 编译。
6. `game_state.rs`
负责兼容态下的状态组织。
### `server-rs/crates/api-server/src/runtime_chat.rs`
职责:
1. 提供运行时 NPC 单轮聊天 SSE 接口。
2. 负责构建 NPC 对话 prompt。
3. 负责 deterministic fallback 回复、建议选项和 function suggestion 回退。
### `server-rs/crates/api-server/src/runtime_save.rs`
职责:
1. 提供当前快照读取、写入、删除接口。
2. 提供存档归档列表和恢复接口。
3. 负责区分正式快照与 preview / test 的临时快照写入语义。
### `server-rs/crates/api-server/src/runtime_inventory.rs`
职责:
1. 承接运行时背包状态读取。
### `server-rs/crates/api-server/src/runtime_profile.rs`
职责:
1. 承接运行时相关玩家资料、统计、充值中心、钱包流水等外围接口。
### `server-rs/crates/api-server/src/story_battles.rs`
职责:
1. 承接故事战斗状态创建、NPC 战斗创建、战斗结算与战斗状态查询。
### `server-rs/crates/api-server/src/story_sessions.rs`
职责:
1. 承接 story session 的 begin / continue / state 查询。
2. 是运行时故事会话层的重要接口之一。
### `server-rs/crates/api-server/src/app.rs`
职责:
1. 统一挂接 `api-server` 的 Axum 路由树。
2. 把 runtime story、runtime chat、runtime save、story battle、story session 等接口注册到 HTTP 层。
说明:
1. 它是路由总装配文件,不是 RPG 运行时业务细节实现文件。
---
## 5.7 SpacetimeDB 模块与共享契约层职责
### `server-rs/crates/shared-contracts/src/runtime_story.rs`
职责:
1. 提供 RPG 运行时故事域共享契约。
### `server-rs/crates/shared-contracts/src/runtime.rs`
职责:
1. 提供运行时快照、创作、资料等通用契约。
### `server-rs/crates/module-runtime/src/lib.rs`
职责:
1. 提供运行时快照、保存相关模块基础能力。
### `server-rs/crates/module-story/src/lib.rs`
职责:
1. 提供故事会话、故事状态相关模块能力。
### `server-rs/crates/module-combat/src/lib.rs`
职责:
1. 提供战斗状态相关模块能力。
### `server-rs/crates/module-quest/src/lib.rs`
职责:
1. 提供任务状态、任务推进相关模块能力。
### `server-rs/crates/module-inventory/src/lib.rs`
职责:
1. 提供背包和物品库存相关模块能力。
### `server-rs/crates/module-runtime-story-compat/src/`
职责:
1. 承接当前 runtime story compat 领域逻辑。
2. 把战斗、锻造、选项、NPC 支撑、view model 等兼容逻辑收在独立 crate 中。
关键脚本:
1. `battle.rs`
2. `forge.rs`
3. `forge_actions.rs`
4. `game_state.rs`
5. `npc_support.rs`
6. `options.rs`
7. `view_model.rs`
说明:
1. 这是当前 RPG 运行时兼容逻辑的重要承载层。
2. 如果要继续把前端旧本地规则向 Rust 收口,这里是关键迁移落点之一。
---
## 6. 支撑 RPG 创作与运行时的工具脚本职责
这些脚本不直接参与玩法,但直接支撑开发、发布、绑定和检查:
### `scripts/api-server-maincloud.mjs`
职责:
1. 启动当前 Rust API server 主链路开发入口。
2. 按仓库约束,后端联调应优先通过它启动。
### `scripts/generate-spacetime-bindings.mjs`
职责:
1. 生成 SpacetimeDB 绑定。
2. 支撑前后端契约同步。
### `scripts/check-encoding.mjs`
职责:
1. 检查仓库中文文件编码是否被写坏。
2. 修改中文文档或中文注释后应优先运行。
### `scripts/validate-content.ts`
职责:
1. 做内容结构校验。
### `scripts/validate-overrides.ts`
职责:
1. 做覆盖项和配置项校验。
### `scripts/smoke-content.ts`
职责:
1. 做内容层 smoke 验证。
---
## 7. 当前最值得优先记住的入口脚本
如果只想快速建立脑图,建议优先记住下面这些文件:
### RPG 创作链
1. `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
2. `src/components/custom-world-agent/CustomWorldAgentWorkspace.tsx`
3. `src/components/rpg-creation-result/useRpgCreationResultActions.ts`
4. `src/services/rpg-creation/rpgCreationAgentClient.ts`
5. `src/services/rpg-creation/rpgCreationAssetClient.ts`
6. `src/services/rpg-creation/rpgCreationLibraryClient.ts`
7. `server-rs/crates/api-server/src/custom_world.rs`
8. `server-rs/crates/api-server/src/custom_world_ai.rs`
### RPG 运行时链
1. `src/hooks/rpg-session/useRpgRuntimeSession.ts`
2. `src/hooks/rpg-session/useRpgSessionBootstrap.ts`
3. `src/hooks/rpg-session/useRpgSessionPersistence.ts`
4. `src/components/rpg-runtime-shell/RpgRuntimeShell.tsx`
5. `src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`
6. `src/hooks/rpg-runtime-story/useRpgRuntimeStory.ts`
7. `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts`
8. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
9. `server-rs/crates/api-server/src/runtime_story.rs`
10. `server-rs/crates/api-server/src/runtime_chat.rs`
11. `server-rs/crates/api-server/src/runtime_save.rs`
---
## 8. 当前兼容层与后续阅读建议
### 8.1 兼容层识别
下面这些脚本当前更多是兼容门面:
1. `src/components/rpg-entry/RpgEntryFlowShell.tsx`
2. `src/services/rpg-runtime/rpgRuntimeChatClient.ts`
3. `server-rs/crates/api-server/src/runtime_story.rs`
阅读这些文件时要注意:
1. 如果文件很薄,往往真实逻辑在它桥接到的 impl / compat / platform-entry 目录里。
2. 不要把复杂新逻辑继续堆回这些 façade。
### 8.2 推荐阅读顺序
如果要继续开发 RPG 创作:
1. 先看 `PlatformEntryFlowShellImpl.tsx`
2. 再看 `CustomWorldAgentWorkspace.tsx`
3. 再看 `rpgCreationAgentClient.ts`
4. 再看 `custom_world.rs`
如果要继续开发 RPG 运行时:
1. 先看 `useRpgRuntimeSession.ts`
2. 再看 `useRpgRuntimeStory.ts`
3. 再看 `rpgRuntimeStoryGateway.ts`
4. 再看 `rpgRuntimeStoryClient.ts`
5. 最后看 `server-rs/crates/api-server/src/runtime_story/compat/`
---
## 9. 结论
当前仓库里的 RPG 主链已经基本形成两套脚本地图:
1. **RPG 创作链**:以平台入口分流、共创会话、结果页编辑、资产工坊、创作域 client、`server-rs` 自定义世界接口为主。
2. **RPG 运行时链**:以 session bootstrap、session persistence、runtime shell、runtime story hook、runtime story client、`server-rs` runtime story / runtime chat / runtime save 为主。
如果后续继续做职责收口,优先方向应该是:
1. 继续减少 façade 承载业务。
2. 继续把前端兼容桥接逻辑向 `server-rs` 和 SpacetimeDB 正式域收口。
3. 继续让“创作链”和“运行时链”各自维持清晰入口,而不是重新回到通用大文件。

View File

@@ -0,0 +1,115 @@
# RPG 脚本中文注释补充进度2026-04-28
## 1. 文档目的
这份文档用于记录当前仓库里 RPG 相关脚本的中文注释补充进度,避免后续“挨个补充”时重复扫描、重复改同一批文件,或者遗漏运行时主链上的关键脚本。
当前原则:
1. 先补职责最核心、状态流最复杂、后续最常被继续修改的脚本。
2. 每一批都尽量按完整链路补,不只补单点文件。
3. 注释以解释“为什么这样编排”“这一层负责什么边界”为主,不堆砌逐行翻译式废话。
---
## 2. 本轮已补充的脚本
### 2.1 RPG 运行时 session 主链
1. `src/hooks/rpg-session/useRpgRuntimeSession.ts`
2. `src/hooks/rpg-session/useRpgSessionBootstrap.ts`
3. `src/hooks/rpg-session/useRpgSessionPersistence.ts`
本轮重点:
1. 说明 session 装配器如何组合 bootstrap、story、combat、persistence。
2. 说明自定义世界开局场景、首遇 NPC、初始装备与初始物品的装配原因。
3. 说明自动存档、继续游戏、手动保存退出的状态边界。
### 2.2 RPG 运行时 story 主链
1. `src/hooks/rpg-runtime-story/useRpgRuntimeStory.ts`
2. `src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.ts`
3. `src/hooks/rpg-runtime-story/useRpgRuntimeStoryFlow.ts`
4. `src/hooks/rpg-runtime-story/useRpgRuntimeInteractionFlow.ts`
5. `src/hooks/rpg-runtime-story/useRpgRuntimeStoryState.ts`
6. `src/hooks/rpg-runtime-story/storyInteractionCoordinator.ts`
7. `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts`
本轮重点:
1. 说明 controller、flow、interaction、state 四层的职责切分。
2. 说明 NPC 遭遇自动进入交互态、NPC 战斗快照桥接、地图旅行桥接的原因。
3. 说明 story reset、story hydration、服务端动作结算的编排边界。
### 2.3 RPG 运行时 service / client 主链
1. `src/services/rpg-runtime/rpgRuntimeRequest.ts`
2. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
3. `src/services/rpg-runtime/rpgSnapshotClient.ts`
本轮重点:
1. 说明 runtime 请求的统一重试策略。
2. 说明服务端 `RuntimeStoryOptionView` 到前端 `StoryOption` 的适配原因。
3. 说明远端快照读取、写入后为什么要先 rehydrate。
---
## 3. 建议的后续补充顺序
为了保持“按链路读得通”,下一轮建议继续按下面顺序推进:
### 3.1 运行时 story 子模块
1. `src/hooks/rpg-runtime-story/storyChoiceCoordinator.ts`
2. `src/hooks/rpg-runtime-story/storyChoiceRuntime.ts`
3. `src/hooks/rpg-runtime-story/storyRequestCoordinator.ts`
4. `src/hooks/rpg-runtime-story/storyRequestRuntime.ts`
5. `src/hooks/rpg-runtime-story/storyGenerationState.ts`
6. `src/hooks/rpg-runtime-story/storyEncounterState.ts`
7. `src/hooks/rpg-runtime-story/storyPresentation.ts`
8. `src/hooks/rpg-runtime-story/sessionActions.ts`
9. `src/hooks/rpg-runtime-story/progressionActions.ts`
10. `src/hooks/rpg-runtime-story/npcInteraction.ts`
11. `src/hooks/rpg-runtime-story/inventoryActions.ts`
12. `src/hooks/rpg-runtime-story/goalFlow.ts`
原因:
1. 这些文件已经紧贴本轮完成的主编排层。
2. 它们包含大量“局部规则 + 状态迁移 + UI 结果”的细节,最需要注释解释。
### 3.2 运行时 UI 与入口层
1. `src/components/rpg-runtime-shell/RpgRuntimeShell.tsx`
2. `src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx`
3. `src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx`
4. `src/components/rpg-runtime-panels/RpgAdventurePanel.tsx`
5. `src/hooks/rpg-session/useRpgSessionBootstrap.ts` 周边引用组件
6. `src/components/rpg-entry/` 目录里的 RPG 运行时入口桥接脚本
原因:
1. 主链编排层补完后,再补表现层会更容易写出真正有用的注释。
2. 入口层里有兼容 façade需要明确标出“不要继续堆复杂逻辑”的边界。
### 3.3 RPG 创作链
1. `src/services/rpg-creation/` 目录主 client
2. `src/components/rpg-creation-result/` 主动作脚本
3. `src/components/rpg-creation-editor/` 主编辑链
4. `src/components/rpg-creation-asset-studio/` 角色资产工坊链
原因:
1. 创作链文件也很多,但当前运行时主链更核心、更常改。
2. 等运行时链注释连续后,再切创作链更不容易打断理解。
---
## 4. 本轮备注
1. 本轮以局部补丁方式补注释,没有整文件重写。
2. 本轮没有改业务逻辑,只补中文注释和进度文档。
3. 后续每补完一批,建议同步更新本文件,保持可追踪。

View File

@@ -192,6 +192,7 @@ npm run dev:rust
1. 资源 URL 不再是 `/generated-big-fish/...`
2. 而是 `/generated-big-fish-assets/...`
3. 结果页状态显示为 `已生成`,而不是 `占位已生成`
4. `Lv.x 主图``idle_float / move_swim` 正式图若下载结果为 PNG后端会在写 OSS 前复用 RPG 角色主图透明背景 alpha 后处理;`生成背景` 不走该处理
### 7.2 Custom World 场景图

View File

@@ -0,0 +1,57 @@
# 大鱼吃小鱼草稿进度与会话超时兜底修复 2026-04-28
## 背景
大鱼吃小鱼在 `2026-04-28` 完成草稿结构化升级后,结果页草稿已经不再是单纯模板壳,而是会生成等级文本、形象描述、动作描述与运行参数。
但当前链路仍暴露出两个直接体验问题:
1. 前端草稿进度页仍把大鱼吃小鱼展示成单个 `compile` 步骤,用户会感觉“整个生成过程只有一步,而且一直卡在第一步”。
2. 前端在打开大鱼草稿或结果页时,会通过 `GET /api/runtime/big-fish/agent/sessions/:sessionId` 拉取完整会话;当 Maincloud 上游偶发抖动时Rust `spacetime-client` 统一 10 秒超时会直接映射成 `502`,用户会看到反复报错。
## 修复口径
### 1. 草稿进度页改为多阶段感知
大鱼吃小鱼的 `big_fish_compile_draft` 仍然保持为一次后端 compile action不拆成多个新的后端接口。
但前端进度读模型不再把它渲染成单步,而是拆成下面三段:
1. `整理玩法骨架`
- 收拢玩法承诺、成长阶梯与风险节奏。
2. `编译等级蓝图`
- 生成每级角色描述、形象描述与动作描述。
3. `校准场地与参数`
- 整理背景蓝图与运行参数,准备结果页。
这样做的边界是:
1. 不把动作正式出图重新塞回 compile action。
2. 只增强生成中的阶段反馈,不改动现有结果页资产工坊分工。
3. 进度阶段属于前端展示语义,不要求后端额外维护细粒度 procedure 状态。
### 2. 会话读取增加短重试与超时语义收口
大鱼会话读取现在补充两层守卫:
1. `api-server` 在读取大鱼 session 时,对 `SpacetimeClientError::Timeout``SpacetimeClientError::ConnectDropped` 做一次短重试。
2. 若最终仍然超时,则错误状态码从泛化 `502` 收口为更准确的 `504 Gateway Timeout`
这样可以覆盖两类常见情况:
1. Maincloud 连接偶发抖动,第一次 procedure 超时但第二次马上恢复。
2. 用户打开草稿页时碰到短暂断链,不再被立即判定成稳定的坏网关故障。
## 落地范围
1. `src/services/miniGameDraftGenerationProgress.ts`
2. `src/services/miniGameDraftGenerationProgress.test.ts`
3. `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
4. `server-rs/crates/api-server/src/big_fish.rs`
## 验收口径
1. 用户点击大鱼吃小鱼“生成草稿”后,进度页至少能看到三个结构化阶段,而不是单个 compile 步骤。
2. 这三个阶段都只描述草稿编译,不出现“生成动作素材”之类结果页资产动作。
3. `GET /api/runtime/big-fish/agent/sessions/:sessionId` 遇到短暂 SpacetimeDB 抖动时,会先做一次短重试。
4. 如果最终仍超时,接口返回语义应体现为超时,而不是继续统一落成泛化 `502`

View File

@@ -0,0 +1,82 @@
# 大鱼吃小鱼角色主图透明背景后处理对齐说明 2026-04-28
## 背景
当前大鱼吃小鱼的等级主图与动作关键帧 prompt 已经明确要求“按 RPG 角色资产口径生成透明背景”,但正式图片链实际仍主要依赖供应商出图结果本身。
这会带来一个问题:
1. prompt 约束只能提高透明背景命中率,不能保证每次都没有残留底色。
2. RPG 角色主图链已经在 Rust 后端落了一层 PNG alpha 后处理,大鱼链没有对齐,导致两条“角色主图口径”资产在最终成品一致性上仍有差异。
## 本次目标
把“大鱼吃小鱼生成的角色主图后处理流程”对齐到 RPG 角色主图链:
1. 等级主图正式图生成后,若下载结果为 PNG则复用 RPG 现有透明背景 alpha 后处理。
2. `idle_float` / `move_swim` 动作关键帧静态图同样复用这套处理。
3. 场地背景图不走这套处理,避免误把 9:16 场景背景做成透明底。
## 落地方案
### 1. 复用 RPG 透明背景后处理能力
`server-rs/crates/api-server/src/character_visual_assets.rs`
冻结现有 `try_apply_background_alpha_to_png(...)``pub(crate)` 复用入口,继续由 RPG 主图链维护这套“绿底/白底/软边缘”透明背景清理逻辑。
### 2. Big Fish 正式图链按资产类型决定是否启用后处理
`server-rs/crates/api-server/src/big_fish.rs`
`BigFishFormalAssetContext` 中新增:
1. `apply_transparent_background_post_process`
映射规则如下:
1. `level_main_image``true`
2. `level_motion``true`
3. `stage_background``false`
### 3. 下载完成后、写 OSS 前执行统一处理
`download_big_fish_remote_image(...)` 新增布尔开关参数。
当满足以下条件时执行后处理:
1. 当前资产槽位需要透明背景后处理
2. 上游下载结果 `mime_type == image/png`
执行顺序冻结为:
1. DashScope 出图
2. 下载远端 PNG
3. 复用 RPG `try_apply_background_alpha_to_png(...)`
4. 再写入 Big Fish 正式 OSS 对象
5. 确认 `asset_object`
6. 绑定到 Big Fish 槽位
## 为什么这样做
1. 这次需求说的是“生成后处理流程和 RPG 角色主图一致”,因此不能只继续加强 prompt必须把后处理链对齐。
2. 直接复用 RPG 已有实现,比在 Big Fish 再复制一份抠图算法更稳,也更符合仓库“默认复用现有系统”的约束。
3. 背景图是环境资产,不属于“角色主图口径”,如果也启用透明背景后处理,会造成错误裁底风险。
## 验收口径
1. 在 Big Fish 结果页点击 `生成并应用正式图 -> Lv.x 主图` 后,若 DashScope 返回 PNG正式落库前会执行和 RPG 主图相同的透明背景 alpha 处理。
2. 在 Big Fish 动作工坊点击 `生成并应用正式图` 后,`idle_float` / `move_swim` 的静态关键帧图同样执行该处理。
3. `生成背景` 仍保持完整场景图,不走透明背景后处理。
4. 编码检查通过Rust `api-server` 定向编译通过。
## 影响范围
1. `server-rs/crates/api-server/src/character_visual_assets.rs`
2. `server-rs/crates/api-server/src/big_fish.rs`
## 风险与边界
1. 当前后处理只在下载结果本身是 PNG 时生效;若供应商返回 JPEG/WebP则仍按原始格式入库。
2. 本次不新增新的 Big Fish 专属抠图算法,不改变 DashScope prompt 和 OSS 绑定协议。
3. 本次不修改 SpacetimeDB schema也不涉及 `migration.rs` 变更。

View File

@@ -0,0 +1,126 @@
# 大鱼吃小鱼提示词脚本拆分 2026-04-28
## 背景
大鱼吃小鱼当前在 `server-rs/crates/api-server/src/big_fish.rs``server-rs/crates/api-server/src/big_fish_agent_turn.rs` 中同时承载了三类不同职责的提示词:
1. Agent 聊天阶段的草稿生成提示词。
2. 结果页主图 / 生图提示词。
3. 结果页动作关键帧提示词。
这会带来两个直接问题:
1. 聊天共创脚本和正式资产脚本混在路由业务文件中,后续继续调词时很容易顺手改到状态编排逻辑。
2. 大鱼吃小鱼已经明确要求“草稿编译”和“结果页资产工坊”分离,如果提示词仍散落在业务实现里,后续很容易再次把动作资产逻辑误塞回 compile action。
## 本轮目标
把下面三类提示词显式拆到独立 prompt 脚本中:
1. 草稿生成提示词。
2. 生图提示词。
3. 动作提示词。
并保持以下边界不变:
1. 不改变 Big Fish 的会话表、草稿表、资产表结构。
2. 不改变 compile action 只编译草稿、不串行生成资产的现有口径。
3. 不改写当前中文提示词语义,只做脚本落位和调用收口。
## 落位方案
新增文件:
```text
server-rs/crates/api-server/src/prompt/big_fish.rs
```
该文件统一收口:
1. `BIG_FISH_AGENT_SYSTEM_PROMPT`
2. `build_big_fish_agent_prompt(...)`
3. `build_big_fish_level_main_image_prompt(...)`
4. `build_big_fish_level_motion_prompt(...)`
5. `build_big_fish_stage_background_prompt(...)`
6. `BIG_FISH_DEFAULT_NEGATIVE_PROMPT`
7. `BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT`
同时把 `prompt/mod.rs` 补齐为正式导出入口,和现有:
1. `puzzle_image.rs`
2. `character_visual.rs`
3. `character_animation.rs`
4. `scene_background.rs`
保持同一层级。
## 调用边界
### 1. 草稿生成
`server-rs/crates/api-server/src/big_fish_agent_turn.rs`
改为只负责:
1. 调用公共 LLM turn 执行器。
2. 解析 `replyText / progressPercent / nextAnchorPack`
3. 组装 finalize input。
不再内联维护大段 system prompt / output contract / chat prompt 拼接逻辑。
### 2. 生图与动作
`server-rs/crates/api-server/src/big_fish.rs`
改为只负责:
1. 读取当前 session 与 draft。
2. 根据 `asset_kind` 构造正式资产上下文。
3. 调用 DashScope 出图。
4. 下载、后处理、持久化并写入资产绑定。
主图、动作关键帧、背景图的正式提示词脚本都从 `crate::prompt::big_fish` 引入,不再内联在路由业务脚本中。
## 为什么三类脚本要继续分开
### 草稿生成提示词
它的职责是把玩法灵感收束成:
1. 玩法承诺
2. 生态视觉主题
3. 成长阶梯
4. 风险节奏
它面向的是 LLM 的结构化共创,不面向图片模型。
### 生图提示词
它的职责是把已经落库的等级蓝图翻译成“单体鱼形主图”的正式图片提示词。
它面向的是透明背景主体资产,需要强调:
1. 单体主体
2. 透明背景
3. 中心构图
4. 不出现 UI / 场景 / 多主体
### 动作提示词
它的职责是把等级蓝图和动作槽位翻译成“静态关键帧预览图”的正式图片提示词。
它和主图区别在于:
1. 需要显式带入 `motion_key`
2. 需要区分 `idle_float / move_swim`
3. 需要强调动作方向和关键帧姿态
因此不能继续复用同一段文本拼接后靠 if 分支临时改句子。
## 本轮验收
1. 大鱼吃小鱼草稿生成提示词已从 `big_fish_agent_turn.rs` 抽离。
2. 大鱼吃小鱼主图、动作、背景提示词已从 `big_fish.rs` 抽离。
3. 路由业务文件只保留编排、鉴权、调用与错误映射职责。
4. 新增 prompt 文件具备最小测试覆盖。
5. `npm run check:encoding` 通过,确保新增中文文档与 Rust 注释未被写坏。

View File

@@ -0,0 +1,63 @@
# 创作页公开广场与 Agent 恢复指针兜底修复 2026-04-28
## 1. 问题现象
浏览器控制台同时出现两类请求错误:
1. `GET /api/runtime/custom-world/agent/sessions/:sessionId` 返回 `404`
2. `GET /api/runtime/big-fish/gallery` 返回 `400`
第一类错误发生在平台页尝试恢复 RPG / Custom World Agent 旧工作区时。旧 URL 或旧 sessionStorage 指针里可能只有 `customWorldSessionId`,没有本机保存的 `ownerUserId`,登录后前端仍会直接读取受保护 session后端按 `owner_user_id + session_id` 查不到后返回 `404`
第二类错误发生在首页读取大鱼吃小鱼公开广场时。公开广场是平台首屏的可选展示数据,即使 SpacetimeDB procedure 暂未就绪、连接短暂断开或旧环境缺少对应 procedure也不应该阻断平台主界面。
## 2. 落地原则
1. URL 中的 `customWorldSessionId` 只用于深链恢复,不作为鉴权凭据。
2. 受保护 Agent session 恢复必须能确认本机 `ownerUserId` 与当前登录用户一致。
3. 未登录状态仍保留登录弹窗流程,不提前丢弃深链;登录完成后若仍无法确认归属,再清空恢复指针。
4. Big Fish 公开广场只展示 `published` 作品;读取失败时允许空态降级,不把错误写成 UI 主错误。
5. 私有作品列表、会话详情、发布、删除仍保持严格错误,不复用公开广场的软降级策略。
## 3. 本次修改
### 3.1 RPG Agent 恢复指针
`src/services/customWorldAgentUiState.ts` 读取 URL query 时,会尝试从 sessionStorage 中匹配同一个 `activeSessionId``ownerUserId`
如果 URL 指针和本机存储匹配:
1. 返回 `activeSessionId`
2. 同时带回本机 `ownerUserId`
如果 URL 指针没有对应本机归属:
1. 只返回 session 指针。
2. 登录后 `useRpgCreationSessionController` 会清空指针。
3. 不调用 `getRpgCreationSession()`,避免向后端发起必然 404 的失效恢复请求。
### 3.2 Big Fish 公开广场
前端 `listBigFishGallery()``400 / 404` 返回 `{ items: [] }`,让平台首页可以继续渲染空广场。
Rust `api-server``list_big_fish_gallery()` 对以下 SpacetimeDB 读取问题做服务端空态降级:
1. `SpacetimeClientError::Runtime(_)`
2. `SpacetimeClientError::ConnectDropped`
3. 明确指向 `list_big_fish_works` procedure 缺失或不可用的 procedure 错误
服务端会保留 `warn` 日志,便于部署环境继续排查 schema / publish 状态。`Timeout` 不降级,仍按网关超时暴露,避免长时间卡死被误认为正常空广场。
## 4. 验收标准
1. 已登录用户打开只带旧 `customWorldSessionId`、但本机没有匹配 `ownerUserId` 的页面时,不再请求 `GET /api/runtime/custom-world/agent/sessions/:sessionId`
2. 未登录用户打开带 `customWorldSessionId` 的深链时,仍先打开登录弹窗。
3. Big Fish 公开广场返回 `400 / 404` 时,前端展示空列表,不把“读取大鱼吃小鱼广场失败”写入主错误态。
4. 服务端遇到 Big Fish gallery 可降级 SpacetimeDB 错误时返回成功 envelope`items` 为空,并记录 warn 日志。
## 5. 回归范围
1. `src/services/customWorldAgentUiState.test.ts`
2. `src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`
3. `src/services/big-fish-gallery/bigFishGalleryClient.test.ts`
4. `cargo test -p api-server big_fish_gallery`

View File

@@ -2,14 +2,27 @@
## 背景
RPG 在点击生成草稿后会离开聊天工作区,进入独立的生成进度页,并在该页展示生成链路的阶段、锚点与最终草稿内容。拼图与大鱼吃小鱼此前点击“生成结果页”后直接跳到结果页,正式图片、动作与背景仍分散在结果页工坊里逐个生成,导致用户无法看到“正在一次性准备完整草稿”的过程
RPG 在点击生成草稿后会离开聊天工作区,进入独立的生成进度页,并在该页展示生成链路的阶段、锚点与最终草稿内容。拼图与大鱼吃小鱼此前点击“生成结果页”后直接跳到结果页,缺少一个明确的“草稿编译中”过渡态
但在 `2026-04-28` 的大鱼吃小鱼链路修正后,产品口径进一步收紧为:
1. 拼图仍然保留“生成草稿时一并补齐结果页主资产”的收口方式。
2. 大鱼吃小鱼的“生成草稿”只负责把玩法锚点编译成结果页草稿。
3. 大鱼吃小鱼的主图、动作、背景都留在结果页工坊按需生成,不再塞进草稿编译动作里串行执行。
这样做的原因是:
1. 大鱼吃小鱼草稿阶段的核心目标是先稳定产出等级蓝图、背景蓝图和运行参数,而不是在这一刻把整套资产都做完。
2. 动作素材生成耗时最长,把它塞进草稿 action 会让用户长时间停留在首步等待态,形成“卡在第一步”的体感。
3. 草稿阶段不需要配置动作,动作应当属于结果页资产精修阶段。
## 落地边界
- 前端只负责展示生成进度与触发已有后端动作,不新增 server-node 或 PostgreSQL 链路。
- 后端继续沿用 `server-rs` + SpacetimeDB 的会话、草稿与资产写入能力;“一次性生成所有需要的东西”必须由 `server-rs` 的 compile action 承担,前端只发起一次 action 并展示进度页
- 拼图生成草稿链路必须包含:结果页草稿、候选图生成、正式图确认。
- 大鱼吃小鱼生成草稿链路必须包含:玩法草稿、关卡主图、动作素材、场地背景
- 后端继续沿用 `server-rs` + `SpacetimeDB` 的会话、草稿与资产写入能力。
- 拼图生成草稿链路包含:结果页草稿、候选图生成、正式图确认。
- 大鱼吃小鱼生成草稿链路包含:玩法草稿、等级蓝图、背景蓝图与运行参数编译
- 大鱼吃小鱼的主图、动作、背景都在结果页工坊单独触发,不再属于草稿编译阶段。
- 生成过程中展示的“角色描述、角色图片、动作”等,统一映射为锚点、草稿蓝图与资产步骤,不把规则说明类文本写成默认 UI 文案。
## 交互设计
@@ -17,8 +30,9 @@ RPG 在点击生成草稿后会离开聊天工作区,进入独立的生成进
1. 用户在拼图或大鱼吃小鱼 Agent 工作区点击生成按钮。
2. 页面立即切换到独立生成进度页,同时只向 `server-rs` 发起一次 compile action返回按钮在生成中禁用避免中途回退造成状态漂移。
3. 进度页左侧展示阶段进度、步骤卡片与错误信息;右侧展示当前锚点与已成形的草稿结构。
4. 全量生成成功后自动进入对应结果页,结果页直接展示已生成的资产
5. 生成失败时停留在进度页,用户可返回工作区补充设定,或点击重试重新执行完整草稿链路
4. 生成成功后自动进入对应结果页。
5. 拼图结果页直接展示已生成的正式图;大鱼结果页则展示刚编译完成的玩法草稿,后续资产由结果页工坊继续生成
6. 生成失败时停留在进度页,用户可返回工作区补充设定,或点击重试重新执行完整草稿链路。
## 阶段映射
@@ -32,11 +46,14 @@ RPG 在点击生成草稿后会离开聊天工作区,进入独立的生成进
### 大鱼吃小鱼
- `big_fish_compile_draft`:在 `server-rs` 内生成玩法草稿、关卡角色描述、背景蓝图与运行参数。
- `big_fish_compile_draft`:同一次后端 action 内按每个关卡生成主角色/鱼群图片。
- `big_fish_compile_draft`:同一次后端 action 内按每个关卡生成 `idle_float``move_swim` 动作素材。
- `big_fish_compile_draft`:同一次后端 action 内生成玩法场地背景。
- `ready`:进入大鱼吃小鱼结果页。
补充冻结:
- 大鱼吃小鱼草稿阶段不展示“生成动作素材”步骤。
- `big_fish_generate_level_main_image``big_fish_generate_level_motion``big_fish_generate_stage_background` 继续保留为结果页中的独立资产动作。
- 如果后续需要扩展大鱼草稿生成进度,也只能扩展“草稿结构编译”相关阶段,不能再把动作生成塞回 compile action。
## 前端流程收口
- 拼图与大鱼吃小鱼共用 `usePlatformCreationAgentFlowController` 管理会话、流式回复、忙碌态、错误态和草稿恢复,页面层不再重复手写两套 submit/action 流程。
@@ -48,8 +65,10 @@ RPG 在点击生成草稿后会离开聊天工作区,进入独立的生成进
## 验收点
- 拼图和大鱼吃小鱼点击生成草稿后不再直接停留在聊天工作区等待。
- 生成中可看到独立进度页,且进度步骤随 action 完成逐步推进
- 拼图结果页打开时已有正式图;大鱼结果页打开时主图、动作和背景资产均已写入 `assetSlots`
- 前端点击生成草稿时不串行调用多个资产 action多阶段业务编排收敛在 `server-rs`
- 生成中可看到独立进度页。
- 拼图结果页打开时已有正式图。
- 大鱼结果页打开时至少已有完整玩法草稿,不要求主图、动作和背景资产在草稿阶段写入 `assetSlots`
- 大鱼吃小鱼草稿生成进度中不再出现“生成动作素材”步骤。
- 前端点击生成草稿时不串行调用多个大鱼资产 action大鱼资产生成留在结果页独立触发。
- 返回 Agent 工作区后,聊天区不出现“拼图结果页草稿已生成。”“本级主图已正式生成,可在结果页继续预览。”这类生成进度页状态消息。
- 不新增 server-node 依赖,不复活 legacy public 静态资产路径。

View File

@@ -4,6 +4,13 @@
## 文档列表
- [RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md](./RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md):冻结 RPG 提示词禁止存在前端的边界,明确前端只保留 API client角色私聊/NPC 对话/剧情续写等 prompt 统一收口到 `server-rs`
- [RPG_CREATION_RESULT_VIEW_BACKEND_TRUTH_MIGRATION_2026-04-28.md](./RPG_CREATION_RESULT_VIEW_BACKEND_TRUTH_MIGRATION_2026-04-28.md):冻结 RPG 创作结果页保存、Agent session/result preview 真相优先级和结果页入口裁决迁移到后端 result-view 的落地边界。
- [RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md](./RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md):记录 RPG 创作 profile 生成移除非浏览器 legacy AI 回退,统一通过 `server-rs``/api/runtime/custom-world/profile` 生成世界底稿。
- [CREATION_PUBLIC_GALLERY_AND_AGENT_RESTORE_GUARD_FIX_2026-04-28.md](./CREATION_PUBLIC_GALLERY_AND_AGENT_RESTORE_GUARD_FIX_2026-04-28.md):记录 RPG Agent 旧 URL 恢复指针必须有本机用户归属才读取受保护 session以及 Big Fish 公开广场读取失败按空广场降级的修复口径。
- [BIG_FISH_DRAFT_PROGRESS_AND_SESSION_TIMEOUT_GUARD_FIX_2026-04-28.md](./BIG_FISH_DRAFT_PROGRESS_AND_SESSION_TIMEOUT_GUARD_FIX_2026-04-28.md):记录大鱼吃小鱼草稿进度页从单步 compile 改为多阶段感知展示,以及大鱼会话读取在 Maincloud 抖动时增加短重试与超时语义收口的修复口径。
- [BIG_FISH_PROMPT_MODULE_EXTRACTION_2026-04-28.md](./BIG_FISH_PROMPT_MODULE_EXTRACTION_2026-04-28.md):记录大鱼吃小鱼草稿生成、生图、动作三类提示词从业务脚本中抽离到独立 `prompt/big_fish.rs` 模块的边界与职责划分。
- [BIG_FISH_MAIN_IMAGE_TRANSPARENT_BACKGROUND_ALIGNMENT_2026-04-28.md](./BIG_FISH_MAIN_IMAGE_TRANSPARENT_BACKGROUND_ALIGNMENT_2026-04-28.md):记录大鱼吃小鱼等级主图与动作关键帧正式图在 Rust 后端复用 RPG 角色主图透明背景 alpha 后处理的对齐口径,并明确场地背景不走该处理。
- [PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md](./PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md):记录拼图结果页名称与标签编辑自动保存、发布门槛统一到 `3~6` 标签,以及前端发布校验不再被旧 session blocker 卡死的修复口径。
- [SPACETIMEDB_START_SH_EARLY_EXIT_DIAGNOSTICS_2026-04-27.md](./SPACETIMEDB_START_SH_EARLY_EXIT_DIAGNOSTICS_2026-04-27.md):记录发布包 `start.sh` 只输出“SpacetimeDB 进程在就绪前退出”时的诊断补强,启动失败或超时时自动回显 `logs/spacetimedb.log``server ping`、端口监听和 root-dir 相关进程。
- [RPG_AND_AGENT_CHAT_TRUE_SSE_STREAMING_2026-04-26.md](./RPG_AND_AGENT_CHAT_TRUE_SSE_STREAMING_2026-04-26.md):记录 RPG 运行时 NPC 聊天、RPG/自定义世界 Agent 与大鱼 Agent 从“拼完整 SSE 字符串后一次性返回”改为 `mpsc + Sse<Event>` 真流式输出的后端落地口径。

View File

@@ -0,0 +1,51 @@
# RPG 创作 profile 生成后端迁移2026-04-28
## 1. 背景
`docs/audits/engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 的 5.3 指出,`src/services/rpg-creation/rpgCreationGenerationClient.ts` 在非浏览器环境仍会动态 `import('../ai')`,让 RPG 创作 profile 生成继续保留前端 legacy AI 后门。
这与当前边界冲突:
1. 前端只负责表现和 API client。
2. RPG 创作 prompt 与 LLM 编排只能在 `server-rs/crates/api-server/src/prompt/rpg/``api-server` 侧出现。
3. 外部 LLM 调用不能进入 SpacetimeDB reducer必须由 Axum / `platform-llm` 完成后再把确定结果交给后续持久化链。
## 2. 本轮落地
### 2.1 前端
`src/services/rpg-creation/rpgCreationGenerationClient.ts` 现在不再判断 `typeof window`,也不再动态导入 `src/services/ai.ts`
无论浏览器、SSR 还是 Vitest node 环境,`generateRpgWorldProfile(...)` 都只调用:
```text
POST /api/runtime/custom-world/profile
```
测试如需离线运行,应 mock `requestJson`,不能恢复本地 AI 生成链。
### 2.2 后端
`server-rs/crates/api-server/src/app.rs` 新增:
```text
POST /api/runtime/custom-world/profile
```
handler 落在 `server-rs/crates/api-server/src/custom_world.rs`
1. 校验 `settingText`
2. 要求 Bearer 鉴权。
3. 要求 `platform-llm` 可用。
4. 复用 `generate_custom_world_foundation_draft(...)` 生成 profile 草稿。
5. 补齐结果页需要的 `id / settingText / templateWorldType / compatibilityTemplateWorldType / items / generationMode / generationStatus / creatorIntent`
6. 直接返回 `CustomWorldProfile` JSON保持前端旧 client contract 不变。
本轮不新增 SpacetimeDB 表,不修改 `migration.rs`
## 3. 验收
1. `src/services/rpg-creation/**` 不再出现 `import('../ai')``LegacyAiModule``loadLegacyAiModule`
2. `src/services/rpg-creation/index.ts` 不再导出 `generateLegacyCustomWorldProfile`
3. node 环境测试确认 profile 生成只走 `requestJson` mock。
4. Rust `api-server` 测试确认 `/api/runtime/custom-world/profile` 未登录返回 `401`

View File

@@ -0,0 +1,82 @@
# RPG 创作结果页后端真相视图迁移方案2026-04-28
## 1. 本次落地边界
本次只迁移 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 中 5.4 对应链路:
1. 创作结果页自动保存前的 profile normalize 与 session 同步顺序。
2. Agent session / result preview / legacyResultProfile 的真相优先级。
3. 作品草稿点击后应进入 Agent workspace、生成过程页还是结果页的裁决。
不在本轮处理运行时 GameState、战斗、NPC、背包和锻造规则。
## 2. 后端读模型
新增稳定读模型:
```text
GET /api/runtime/custom-world/agent/sessions/:sessionId/result-view
```
响应字段:
1. `session`:最新 Agent session snapshot。
2. `profile`:服务端按优先级选出的结果页 profile。
3. `profileSource``result_preview` / `draft_profile` / `none`
4. `targetStage`:前端应打开的 stage。
5. `generationViewSource` / `resultViewSource`:前端视图来源。
6. `canAutosaveLibrary`:作品库自动保存是否可执行。
7. `canSyncResultProfile`:结果页编辑是否允许回写 session。
8. `recoveryAction`:缺失或失败时的恢复指令。
## 3. 真相优先级
服务端统一执行以下优先级,前端不再自己解释:
1. 首选 `session.resultPreview.preview`
2. 若没有 result preview`draftProfile` 是已可打开结果页的完整 profile则使用 `draftProfile`
3. `draftProfile.legacyResultProfile` 只作为后端兼容恢复来源,不再由前端直接读取。
4. 没有可用 profile 时,服务端返回 `targetStage` 指示前端回生成过程页或 Agent workspace。
## 4. 保存链路
结果页编辑仍允许前端持有临时表单态,但保存必须按顺序:
1. 前端调用 `sync_result_profile` action把编辑后的 profile 写回 Agent session。
2. 前端读取 `result-view`,以服务端返回的 `profile` 刷新界面。
3. 自动保存作品库只保存 `result-view.profile`,不再自己决定 session/profile 优先级。
4. Agent 结果页保存成功后,作品库响应只刷新列表、详情与自动保存签名;当前编辑界面仍以 `result-view.profile` 为准,避免兼容响应缺少角色、地标等完整字段时覆盖正在编辑的结果页。
### 4.1 保存前 profile canonicalize
`creatorIntent -> settingText` 的保存前归一必须在后端执行:
1. `sync_result_profile` action 入站时,后端基于 `payload.profile.creatorIntent` 生成 canonical `settingText` 后再写入 Agent session 与 `resultPreview`
2. `PUT /api/runtime/custom-world-library/:profileId` 入站时,后端对 `payload.profile` 执行同一规则后再抽取 metadata 与写入作品库。
3. 前端结果页、作品详情页、平台壳层只能保存用户当前编辑草稿,不再调用 `normalizeRpgEntryAgentBackedProfile(...)` 改写正式字段。
4. 前端自动保存去重签名使用草稿 JSON 本身;保存成功后以服务端返回的 canonical entry/result-view 刷新界面。
该规则的唯一语义是:当 `creatorIntent` 含有有效锚点时,按“世界一句话 / 玩家开局 / 主题气质 / 核心冲突 / 关键关系 / 标志元素”的固定顺序生成 foundation text并覆盖保存入库或 session 的 `settingText`。没有有效锚点时不改写用户草稿。
## 5. 前端职责
前端只保留:
1. 页面切换。
2. loading / error / autosave 状态。
3. 用户正在编辑的临时 profile。
4. 调用后端 action 和 result-view。
前端禁止继续:
1. 直接读取 `draftProfile.legacyResultProfile`
2. 自行判断草稿应打开 Agent workspace、生成过程页还是结果页。
3. 自动保存前只刷新 session 后用 session 旧快照覆盖本地编辑。
## 6. 验收
1. `rpgCreationPreviewAdapter` 不再读取 `legacyResultProfile`
2. `useRpgCreationResultAutosave` 对 Agent 草稿结果页会先执行 `sync_result_profile`,再读取后端 result-view。
3. `useRpgEntryLibraryDetail` 根据 result-view 的 `targetStage` 切页。
4. 测试覆盖编辑后不会被旧 session 覆盖、无 result preview 时由后端决定恢复入口。
5. 测试覆盖 `sync_result_profile` 与作品库 upsert 入站时由后端 canonicalize `settingText`,前端 autosave 不再保存前 normalize。

View File

@@ -0,0 +1,125 @@
# RPG 选项函数与提示词编辑面整理方案2026-04-28
## 背景
当前 RPG 运行时已经把不少选项 function 的定义拆到了 `src/data/functionCatalog/`,但仍存在两个影响编辑效率的问题:
1. `src/data/stateFunctions.ts` 里还残留一批按 `functionId` 分支的运行时文案、优先级与细节逻辑,导致“定义在独立文件,行为还混在总文件里”。
2. RPG 运行时提示词虽然已经有独立模块,但前端 `src/prompts/` 与 Rust `server-rs/crates/api-server/src/prompt/` 里仍然缺少按 `rpg` 维度统一收口的子目录,编辑提示词时仍要在多个平铺文件里来回找。
用户目标是:
1. RPG 中不同选项 function 拆成独立函数,并且能在同一个脚本中看到所有选项 function 的代码入口。
2. RPG 中运行时提示词都整理进 `prompt` 文件夹,并把 RPG prompt 脚本整理到更适合专注编辑提示词的结构中。
## 本次落地边界
1. 只整理 RPG 相关的前端运行时 function 与 prompt 结构。
2. Rust 侧只整理 `server-rs` 的 prompt 模块结构,不兼容 `server-node`
3. 不改玩法语义,不重写大段中文提示词正文;优先移动文件、补兼容导出、增加聚合入口。
4. 不在 UI 里增加说明文案。
## 目标结构
### 前端 RPG prompt
整理为:
```text
src/prompts/
├─ customWorldEntityActionPrompts.ts
├─ customWorldPrompts.ts
└─ rpg/
├─ index.ts
├─ runtimeStoryPrompts.ts
└─ characterChatPrompts.ts
```
说明:
1. `runtimeStoryPrompts.ts` 承载原 `storyPromptBuilders.ts` 的 RPG 运行时剧情导演、NPC 对话导演、招募对话等提示词。
2. `characterChatPrompts.ts` 承载原角色面板私聊提示词。
3. 旧入口 `src/services/prompt.ts``src/services/characterChatPrompt.ts` 保留兼容转发,避免一次性改调用方。
4. 角色资产工坊默认 prompt 与缓存合并规则不再放在前端 prompt 目录,统一迁到 `server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs`
### Rust 侧 RPG prompt
整理为:
```text
server-rs/crates/api-server/src/prompt/
├─ big_fish.rs
├─ character_animation.rs
├─ character_visual.rs
├─ puzzle_image.rs
├─ scene_background.rs
├─ mod.rs
└─ rpg/
├─ mod.rs
├─ agent_chat.rs
├─ foundation_draft.rs
├─ role_asset_studio.rs
└─ runtime_chat.rs
```
说明:
1. `prompt/rpg/agent_chat.rs` 承载 RPG 共创聊天提示词。
2. `prompt/rpg/foundation_draft.rs` 承载 RPG 草稿生成提示词。
3. `prompt/rpg/role_asset_studio.rs` 承载角色资产工坊默认 prompt、legacy prompt 过滤与缓存合并 workflow view。
4. `prompt/rpg/runtime_chat.rs` 承载 RPG 运行时剧情、NPC 对话、战斗结果叙事等提示词。
5. 顶层 `prompt/mod.rs` 继续向外导出 RPG 子模块,保证原调用点只做最小修改。
### RPG function 总览
新增一个面向编辑者的聚合入口,用来同时暴露:
1. 所有 RPG function 文档项。
2. 所有状态类 function source。
3. 每个状态类 function 的运行时行为处理器入口。
目标是让后续查看时可以先打开一个总览文件,再跳到对应 function 文件,而不是先从 `stateFunctions.ts` 的大 `switch` 里反查。
## 代码落地策略
### 1. function 运行时逻辑继续拆分
`src/data/stateFunctions.ts` 中这些按 `functionId` 写死的逻辑继续拆出:
1. 建议 actionText 生成。
2. detailText 生成。
3. function priority 计算。
4. 必要的运行时 definition 微调。
每个状态类 function 文件在保留 `definition + documentation + promptDescription` 的基础上,追加该 function 的运行时处理器。
### 2. 总览脚本
新增聚合入口文件,统一导出:
1. 各域 function 文档。
2. 状态类 function runtime source。
3. 便于编辑时查找的数组/映射。
这样“同一个脚本看到所有选项 function 的代码”具体落地为:
1. 先看总览脚本知道有哪些 function。
2. 每个 function 仍在独立文件维护,避免再次回到一个巨型文件。
3. 总览脚本只能依赖 `state / npc / treasure / flow / panel` 等分目录入口,不能从 `src/data/functionCatalog/index.ts` 反向导入聚合常量,避免浏览器 ESM 初始化时出现 `Cannot access before initialization`
## 验证
1. `npm run check:encoding`
2. `npm run test -- src/services/prompt.test.ts src/hooks/rpg-runtime-story/storyResponseOptions.test.ts`
3. `npm run typecheck`
4. 如涉及 Rust prompt 模块编译错误,再补 `cargo check -p api-server`
## 后续编辑约定
1. 想改 RPG 运行时提示词时,优先进入:
- 前端:`src/prompts/rpg/`
- Rust`server-rs/crates/api-server/src/prompt/rpg/`
2. 想改 RPG 选项 function 时,优先进入:
- 总览:`src/data/functionCatalog/index.ts`
- 状态类分项:`src/data/functionCatalog/state/*.ts`
3. 后续不要再把 RPG prompt 正文重新塞回 `services`、路由或运行时编排文件。

View File

@@ -0,0 +1,99 @@
# RPG 背包 / 装备 / 锻造视图后端迁移落地方案2026-04-28
## 0. 本次目标
依据 `docs/audits/engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md``5.2 P1` 项,本次只收口一类职责:
**背包、装备、锻造面板的可用性、禁用原因、配方视图由 `server-rs` 计算,前端只渲染后端 view并提交用户选择。**
本次不顺手迁移以下 P0 链路:
1. runtime action 仍携带完整 `GameState` 快照。
2. 战斗胜负后处理与旅行桥接仍在既有阶段迁移。
3. `inventory_slot` 表真相与 compat `GameState` 快照仍按现状共存。
## 1. 后端落点
本阶段采用最小后端落点:
1. `server-rs/crates/shared-contracts/src/runtime_story.rs`
- 扩展 `RuntimeStoryViewModel`,新增 `inventory` 字段。
- 定义背包物品、装备槽、物品动作、锻造配方、配方材料需求的 view contract。
2. `server-rs/crates/module-runtime-story-compat/src/forge.rs`
- 公开确定性的配方定义与需求统计能力。
- 配方定义补齐前端已有 `forgeSystem.ts` 的三条 forge 配方。
3. `server-rs/crates/module-runtime-story-compat/src/view_model.rs`
-`GameState` 快照编译 `inventory` view model。
- 输出每个动作的 `enabled / reason`,由后端统一说明为什么不可用。
4. `server-rs/crates/api-server/src/runtime_story/compat/*`
- 原 action resolver 复用同一套 forge 定义和可用性判断。
## 2. 前端落点
1. `packages/shared/src/contracts/rpgRuntimeStoryState.ts`
- 与 Rust contract 对齐新增 `inventory` view 类型。
2. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts`
- 提供 `loadRpgRuntimeInventoryView`,通过 runtime story session state 获取后端 view。
- 当前 runtime story 主链已完成不上传完整 `GameState` 的迁移,因此该读取入口只按 `runtimeSessionId` 请求后端持久化状态。
3. `src/hooks/rpg-runtime-story/inventoryActions.ts`
- 删除 `getForgeRecipeViews(...)` 本地配方计算。
- 不再通过本地 `playerInventory.find(...)` / `playerEquipment[...]` 作为正式动作门禁。
- 使用后端 view 的 `actions``forgeRecipes` 判断按钮可用性和 action 文案。
4. `src/components/InventoryPanel.tsx`
- 继续只展示传入的 view。
- 支持展示后端 `disabledReason`,不再自行解释配方规则。
- 背包列表优先使用后端 `backpackItems`,货币文案优先使用后端 `currencyText`
## 3. 可用性规则
后端 `inventory` view 应至少输出:
1. `backpackItems`
- 背包里的物品快照。
- `actions.use / equip / dismantle / reforge`
2. `equipmentSlots`
- `weapon / armor / relic` 三槽。
- 每槽当前装备与 `actions.unequip`
3. `forgeRecipes`
- 配方 id、名称、类型、说明、产物、货币花费。
- 每项需求的 `owned / quantity`
- `canCraft``disabledReason`
4. `currencyText`
- 由后端按 `worldType` 格式化。
禁用规则:
1. 缺少玩家角色时,所有正式动作不可用。
2. 战斗中,装备 / 卸装 / 锻造 / 拆解 / 重铸不可用;可用物品仍可由战斗动作链处理。
3. 不可使用的物品返回 `use.enabled=false`
4. 非装备物品返回 `equip.enabled=false`
5. 无 buildProfile 且非装备的物品不可拆解。
6. 非装备或材料不足 / 货币不足的物品不可重铸。
7. 材料或货币不足的配方不可制作,并返回原因。
## 4. 验收
1. 前端 `inventoryActions.ts` 不再引用 `getForgeRecipeViews`
2. 前端配方按钮使用后端 `forgeRecipes`
3. 后端 `RuntimeStoryViewModel` JSON 中存在 `inventory`
4. Rust contract / compat view model 有单元测试覆盖:
- 配方 `canCraft` 与需求数量。
- 装备、卸装、拆解、重铸禁用原因。
5. TypeScript client 测试覆盖后端 inventory view 获取与保留。
6. 修改后执行:
- Rust 相关测试。
- TypeScript 相关测试。
- `npm run api-server:maincloud`
## 5. 本次实现结果
1. `server-rs` 已在 `RuntimeStoryViewModel.inventory` 输出背包、装备槽、锻造配方、动作 payload 与禁用原因。
2. `module-runtime-story-compat` 的锻造配方定义已补齐 `forgeSystem.ts` 中仍留在前端的合成 / 锻造配方,并对 `mana / 法力` 标签做后端匹配兼容。
3. `src/hooks/rpg-runtime-story/inventoryActions.ts` 已改为:
- 读取 `loadRpgRuntimeInventoryView(...)`
- 用户动作只使用后端 action view 的 `functionId / actionText / payload`
- 缺失或禁用时展示后端 `reason / disabledReason`
4. `src/components/InventoryPanel.tsx` 已改为:
- 背包格子优先渲染后端 `backpackItems`
- 工坊列表渲染后端 `forgeRecipes`
- 禁用配方展示后端原因。

View File

@@ -0,0 +1,102 @@
# RPG 提示词前端禁存与 server-rs 收口方案2026-04-28
## 背景
当前 RPG 运行时虽然已经大面积切到 `server-rs``/api/runtime/**`,但前端仍残留以下错误边界:
1. `src/services/ai.ts` 仍保留 RPG 剧情、角色私聊、NPC 对话、招募对话的本地 prompt 生成与浏览器侧 LLM fallback。
2. `src/prompts/rpg/``src/prompts/storyPromptBuilders.ts``src/prompts/characterChatPrompts.ts` 仍存放 RPG 提示词正文。
3. `src/services/aiService.ts` 在非浏览器环境下仍会回退到 `./ai`,等价于保留“前端可持有 RPG prompt”的技术后门。
这与仓库约束“前端只负责表现,逻辑、数据放后端”直接冲突,也会让提示词编辑入口继续分裂。
## 本次强约束
1. RPG 提示词禁止存在前端工程。
2. RPG 提示词唯一允许存在于 `server-rs/crates/api-server/src/prompt/rpg/`
3. 前端只允许保留:
- 运行时请求 contract
- API client
- UI 展示与交互状态
4.`src/services/ai.ts` 不再承担 RPG 剧情/聊天 prompt 生成职责。
## 收口目标
### 后端唯一 prompt 目录
RPG 运行时提示词统一收口到:
```text
server-rs/crates/api-server/src/prompt/rpg/
├─ mod.rs
├─ agent_chat.rs
├─ foundation_draft.rs
└─ runtime_chat.rs
```
其中:
1. `agent_chat.rs` 负责创作态 RPG Agent prompt。
2. `foundation_draft.rs` 负责 RPG 草稿生成 prompt。
3. `runtime_chat.rs` 负责运行时剧情、角色私聊、NPC 聊天、招募对话等 prompt。
### 前端职责缩减
前端保留:
1. `src/services/aiService.ts`
- 只负责请求 `/api/runtime/**`
- 不再回退到本地 RPG prompt 构造
2. `src/services/rpg-runtime/*`
- 只负责按运行时域转发 client
3. `src/hooks/rpg-runtime-story/*`
- 只消费 API 回包并驱动 UI
前端移除:
1. `src/prompts/rpg/*`
2. `src/prompts/storyPromptBuilders.ts`
3. `src/prompts/characterChatPrompts.ts`
4. `src/services/prompt.ts`
5. `src/services/characterChatPrompt.ts`
6. `src/services/ai.ts` 中全部 RPG prompt / RPG 本地 LLM fallback 逻辑
## 运行时接口对齐
为彻底去掉前端 prompt`server-rs` 必须承接以下接口:
1. `POST /api/runtime/story/initial`
2. `POST /api/runtime/story/continue`
3. `POST /api/runtime/chat/character/suggestions`
4. `POST /api/runtime/chat/character/summary`
5. `POST /api/runtime/chat/character/reply/stream`
6. `POST /api/runtime/chat/npc/dialogue/stream`
7. `POST /api/runtime/chat/npc/turn/stream`
8. `POST /api/runtime/chat/npc/recruit/stream`
其中:
1. 非流式接口统一返回 `{ text: string }`
2. 流式接口统一返回可被前端直接消费的纯文本 SSE 增量
3. NPC turn 仍保留当前带 `suggestions / functionSuggestions / chatDirective` 的专用 SSE 结构
## 前端代码落地要求
1. `aiService.ts` 在 RPG 相关方法中禁止再动态 `import('./ai')`
2. 若在非浏览器环境误调用 RPG 运行时能力,应直接报错,明确提示必须走 `api-server`
3. 角色私聊目标状态类型等纯类型定义可以留在前端,但必须与 prompt 文件彻底解耦
## 验证
1. `npm run check:encoding`
2. `npm run test -- src/services/ai.test.ts src/hooks/rpg-runtime-story/storyResponseOptions.test.ts`
3. `cargo check -p api-server`
4. `npm run api-server:maincloud`
## 后续编辑约定
之后如果要改 RPG 提示词:
1. 只进入 `server-rs/crates/api-server/src/prompt/rpg/`
2. 不允许在 `src/` 下新增任何 RPG prompt、system prompt、prompt builder
3. 前端若出现“为了临时 fallback 先放一个 prompt”的需求视为越界必须改为补后端接口

View File

@@ -0,0 +1,103 @@
# RPG 角色资产工坊默认 Prompt 与缓存合并后端迁移2026-04-28
## 背景
`docs/audits/engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 已明确指出:角色资产工坊中用户正在编辑的 prompt 草稿可以留在前端表单,但默认 prompt 生成、legacy prompt 判断、缓存合并和工作流初始态不应继续散落在 `RpgCreationRoleAssetStudioModalImpl.tsx`
本次迁移只处理这一项边界,不扩大到角色生图、动作生成的模型参数默认值重构。
## 落地边界
1. 后端新增角色资产工坊 workflow view负责输出
- 从角色字段挑选出的默认视觉 / 动作 / 场景 prompt 种子。
- 过滤 legacy 旧生成 prompt 后的视觉 prompt。
- 按动作 key 合并后的动作 prompt map。
- 从缓存回填的候选图、选中候选、选中动作、形象资产和动作 map。
2. 前端只把当前正在编辑的角色快照传给后端解析 workflow view。
3. 前端保存缓存时只保存用户当前表单草稿和资产结果,不再计算合并规则。
4. 现有 OSS JSON 缓存继续复用,不新增 SpacetimeDB 表结构,因此本轮不修改 `migration.rs`
## 接口设计
新增解析接口:
```text
POST /api/runtime/custom-world/asset-studio/role/{character_id}/workflow
```
请求体:
```json
{
"cacheScopeId": "world-id",
"role": {
"id": "role-id",
"name": "角色名",
"title": "头衔",
"role": "世界身份",
"visualDescription": "角色视觉描述",
"actionDescription": "角色动作描述",
"sceneVisualDescription": "场景描述",
"description": "通用描述",
"backstory": "背景",
"combatStyle": "战斗风格"
}
}
```
响应体:
```json
{
"ok": true,
"cache": {},
"workflow": {
"defaultPromptBundle": {
"visualPromptText": "",
"animationPromptText": "",
"scenePromptText": ""
},
"visualPromptText": "",
"animationPromptTextByKey": {
"run": "",
"attack": "",
"idle": "",
"die": ""
}
}
}
```
新增保存接口:
```text
PUT /api/runtime/custom-world/asset-studio/role/{character_id}/workflow
```
它复用原 `POST /api/assets/character-workflow-cache` 的 OSS JSON 缓存保存逻辑,并补齐 `animationPromptTextByKey` 持久化。
## 合并规则主源
后端主源:
```text
server-rs/crates/api-server/src/prompt/rpg/role_asset_studio.rs
```
规则保持现有语义:
1. `visualPromptText` 默认优先取 `visualDescription`,其次 `description`,长度上限 220。
2. `animationPromptText` 默认优先取 `actionDescription`,其次 `combatStyle`,长度上限 180。
3. `scenePromptText` 默认优先取 `sceneVisualDescription`,其次 `backstory`,长度上限 220。
4. 角色存在新的 `visualDescription` 时,不使用缓存视觉 prompt 覆盖默认值。
5. 角色存在新的 `actionDescription` 时,所有动作 prompt 使用新的默认动作 prompt。
6. 角色没有新的动作描述时,逐动作优先使用 `animationPromptTextByKey`,再回退旧 `animationPromptText`,最后回退默认动作 prompt。
7. 命中历史生成模板标记的视觉 / 动作 prompt 不再作为可继承缓存。
## 验收
1. `src/` 不再引用 `buildDefaultRolePromptBundle`
2. `RpgCreationRoleAssetStudioModalImpl.tsx` 不再包含 legacy prompt 判断与缓存合并函数。
3. `CharacterWorkflowCachePayload` 能读写 `animationPromptTextByKey`
4. Rust 单测覆盖默认 prompt、legacy 过滤、逐动作缓存合并。
5. 前端 client 单测覆盖 workflow 解析接口和 PUT 保存接口。

View File

@@ -0,0 +1,70 @@
# RPG 运行时开局 GameState 后端迁移落地2026-04-28
## 目标
本次只收口 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md``4.1 P0 运行时开局 GameState 装配仍在前端`
## 边界
前端保留:
1. 选择世界、选择角色、切换 tab、地图弹层等 UI 状态。
2. 世界选择后的“尚未选角”中间态,用于展示角色选择页面。
3. 调用后端开局接口并接收快照。
后端负责:
1. 生成 `runtimeSessionId``runtimeActionVersion`
2. 装配正式初始 `GameState`
3. 装配初始场景、opening act、首遇 NPC、NPC state。
4. 装配初始背包、初始装备、血蓝、货币、技能冷却。
5. 写入 runtime snapshot成为后续 runtime story 的读取来源。
## 接口
新增:
```text
POST /api/runtime/story/sessions
```
请求:
```json
{
"worldType": "CUSTOM",
"customWorldProfile": {},
"character": {},
"runtimeMode": "play",
"disablePersistence": false
}
```
响应:
```json
{
"sessionId": "runtime-...",
"serverVersion": 1,
"snapshot": {
"version": 2,
"savedAt": "...",
"bottomTab": "adventure",
"gameState": {},
"currentStory": null
}
}
```
## 验收
1. `useRpgSessionBootstrap.ts` 不再在 `handleCharacterSelect` 中本地构造完整初始 `GameState`
2. 开局后 `gameState.runtimeSessionId` 来自后端。
3. 开局后 `gameState.currentScene === "Story"`
4. 自定义世界 opening act 能写入 `storyEngineMemory.currentSceneActState`
5. 自定义世界角色 `initialItems` 能进入背包并自动装配可推断槽位。
6. 后端测试覆盖 opening act、首遇 NPC、初始物品、装备。
## 后续
本次仍沿用 runtime story compat 的 JSON `GameState` 桥接形态。后续阶段应继续把 `runtime_story` action 从“快照桥接”推进为 SpacetimeDB 表级状态读写。

View File

@@ -0,0 +1,63 @@
# RPG 运行时战斗后处理后端迁移落地方案2026-04-28
## 目标
本方案承接 `docs/audits/engineering/RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md` 的 4.5 项,专门收口三类仍由前端补真相的逻辑:
1. 战斗胜利 / 切磋完成后的正式 `GameState` 清理。
2. 玩家死亡后的复活场景、血蓝恢复与首场景 act 状态。
3. 战斗结束后章节 act 推进与 `currentStory.deferredOptions` 编排。
## 边界
后端负责:
1. 根据 battle resolver 的 `outcome` 决定 `victory``spar_complete``defeat``escaped` 的最终状态。
2. 写回 `inBattle``currentEncounter``sceneHostileNpcs``currentNpcBattleOutcome``playerHp``playerMana``storyEngineMemory.currentSceneActState`
3. 为死亡复活构造回到首场景的快照,并恢复 `playerHp = playerMaxHp``playerMana = playerMaxMana`
4. 为胜利 / 切磋完成构造只含 `story_continue_adventure` 的当前 story并把真实后续 options 放入 `deferredOptions`
5. 在最后一幕或无需等待继续按钮时直接返回场景旅行 / 常规 fallback options。
前端只负责:
1. 播放 `presentation.battle` 对应动画。
2. 使用 `response.snapshot.gameState``response.snapshot.currentStory` 渲染。
3. 不再调用 `buildPostBattleVictoryState``buildPostBattleVictoryStory``buildRevivedFirstSceneState``buildDeathStory` 作为服务端动作后的正式状态。
## 后端落点
1. `server-rs/crates/module-runtime-story-compat/src/post_battle.rs`
- 增加纯 JSON helper迁移战斗后状态、复活和 scene act 推进。
2. `server-rs/crates/api-server/src/runtime_story/compat.rs`
-`resolve_battle_action` 之后、生成 AI fallback 之前统一调用 post-battle finalizer。
3. `server-rs/crates/api-server/src/runtime_story/compat/presentation.rs`
- 复用现有 option / current story 构造函数。
4. `packages/shared/src/contracts/rpgRuntimeStoryState.ts`
- battle outcome 增加 `defeat`,避免前端类型层把失败误判成非战斗终局。
## 落地补充
1. 后端 post-battle finalizer 在 `resolve_battle_action` 之后、LLM fallback 之前执行,终局战斗不再生成额外 AI 文本。
2. 胜利 / 切磋完成会清理战斗态并推进当前场景 act非最后一幕只展示 `story_continue_adventure`,真实后续动作写入 `deferredOptions`
3. 败北复活会先写回首场景、回满血蓝、重置首场景 act再基于复活后的场景重新生成 `deferredOptions`,避免沿用战斗前旧场景选项。
4. story engine 投影额外接收 battle outcome只有 `victory / spar_complete` 会记录胜利信号,`defeat` 不会被“战斗态从 true 变 false”误判成胜利。
5. 前端 `runServerRuntimeChoiceAction` 的服务端路径不再调用 `postBattleFlow` 构造正式状态;死亡动画仍可短暂播放,但最终 `GameState/currentStory` 只采用后端 hydrated snapshot。
## 本轮收口记录
1. `choiceActions.ts` 删除 `shouldResolveCombatChoiceLocally(...)``battle_* / inventory_use` 不再因战斗可见态回落到本地 continuation。
2. `storyChoiceContinuation.ts``battle_* / inventory_use` 以及被分类为 `battle / escape` 的动作加硬保护,误入时不会裁决掉落、复活、任务推进或战后 story。
3. `storyChoiceRuntime.ts` 删除本地敌对 NPC 战斗奖励 helper前端不再调用 `rollHostileNpcLoot(...)``addInventoryItems(...)` 生成正式战利品。
4. 删除 `postBattleFlow.ts` 与其测试,前端不再保留死亡复活、胜利后 story、deferred options、章节推进的正式构造函数。
5. `choiceActions.test.ts` 覆盖 `battle_use_skill`、stale `battle_attack_basic``inventory_use` 全部进入后端 resolver`storyChoiceRuntime.test.ts` 继续覆盖服务端胜利 / 失败 snapshot 被直接采用。
## 验收
1. Rust 单测覆盖:
- 服务端 battle victory 返回后,`currentEncounter = null``inBattle = false``currentStory.options = [story_continue_adventure]``deferredOptions` 存在。
- 服务端 battle defeat 返回后,玩家复活到首场景,`playerHp/playerMana` 回满,`currentStory` 为死亡复活故事。
2. 前端单测覆盖:
- `runServerRuntimeChoiceAction``victory``defeat` 都直接采用服务端 snapshot/story不再本地构造 post battle / revive 状态。
3. 搜索确认 `src/hooks/rpg-runtime-story` 不再出现 `shouldResolveCombatChoiceLocally``buildPostBattleVictory*``buildRevivedFirstSceneState``buildDeathStory``buildHostileNpcBattleReward`
4. Rust 单测覆盖:
- story engine 对 `defeat` outcome 不写入 `win_battle` 信号和敌压 mutation。

View File

@@ -0,0 +1,164 @@
# RPG 运行时 Story Engine 后端迁移落地方案2026-04-28
## 0. 本轮目标
本轮只收口 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_AUDIT_2026-04-28.md``4.4 P0 story engine / chapter / world mutation` 这条。
目标不是新增一套前端规则,而是让运行时动作完成后由 `server-rs` 统一写回:
1. `storyHistory`
2. `storyEngineMemory`
3. `chapterState`
4. `currentScenePreset.mutationStateText / currentPressureLevel / description`
前端只能展示这些后端字段,不能继续在 hook 中运行 `chapterDirector / threadSignalRouter / worldMutationRouter` 等正式状态机。
## 1. 后端落点
### 1.1 `module-runtime-story-compat`
新增 `story_engine.rs`,作为无 HTTP、无 `AppState` 的纯 JSON projector。
职责:
1. 确保 `storyEngineMemory` 最小结构存在。
2. 按上一帧与下一帧快照生成 story signals。
3. 基于信号推进 active thread、recent signal。
4. 基于当前场景和任务生成 `ChapterState`
5. 基于章节和信号生成 `WorldMutation`
6. 把 mutation 投影到当前场景展示字段。
7. 追加最小 chronicle、journey beat、continue digest。
### 1.2 `api-server runtime_story compat`
`resolve_runtime_story_action` 在动作确定性结算和 `storyHistory` 写入后,统一调用 projector再持久化快照。
这样即使前端只提交 `functionId/payload`,正式叙事记忆也由后端结果生成。
## 2. 前端收口
### 2.1 `progressionActions.ts`
保留:
1. 展示层 loading/error。
2. encounter 入场动画。
3. 调用后端生成 story 或 fallback story。
移除:
1. `applyStoryEngineEchoes`
2. 本地章节任务补发。
3. 本地 thread signal、companion reaction、chapter、journey beat、world mutation、QA、release gate 等编排。
### 2.2 `storyContextBuilder.ts`
保留 prompt context 适配职责,但只能读取后端已存在的字段:
1. `state.chapterState`
2. `state.storyEngineMemory.currentChapter`
3. `state.storyEngineMemory.currentJourneyBeat`
4. `state.storyEngineMemory.worldMutations`
5. `state.currentScenePreset`
禁止继续导入并运行 story engine director。
## 3. 验收标准
1. `src/hooks/rpg-runtime-story/progressionActions.ts` 不再导入 `services/storyEngine/*`
2. `src/hooks/rpg-runtime-story/storyContextBuilder.ts` 不再导入 `services/storyEngine/*`
3. `resolve_runtime_story_action` 返回的 snapshot 中包含后端写入的 `storyEngineMemory.currentChapter`
4. 场景动作后 `currentScenePreset.mutationStateText` 由后端 projector 写入。
5. `cargo test -p module-runtime-story-compat story_engine --manifest-path server-rs/Cargo.toml` 通过。
6. `cargo test -p api-server runtime_story --manifest-path server-rs/Cargo.toml` 通过。
7. 前端相关 vitest 与编码检查通过。
## 4. 本轮落地记录
### 4.1 后端已落地
1. `server-rs/crates/module-runtime-story-compat/src/story_engine.rs` 新增确定性 projector。
2. `server-rs/crates/api-server/src/runtime_story/compat.rs` 在 action resolve 写入 `storyHistory` 后调用 projector再保存 snapshot。
3. `server-rs/crates/api-server/src/runtime_story/compat/tests.rs` 新增 route 边界测试,覆盖响应 snapshot 中的:
- `chapterState.id`
- `storyEngineMemory.currentChapter.id`
- `quests[].chapterId`
- `currentScenePreset.mutationStateText`
- `storyEngineMemory.worldMutations`
### 4.2 前端已收口
1. `src/hooks/rpg-runtime-story/progressionActions.ts` 不再执行本地 story engine echo、chapter、journey beat、world mutation 编排。
2. `src/hooks/rpg-runtime-story/storyContextBuilder.ts` 不再导入 `services/storyEngine/*`只读取后端快照中已有的章节、旅程、mutation、chronicle、companion reaction 等字段。
3. prompt context 中 `visibilitySlice / sceneNarrativeDirective / goalStack / activeScenarioPack / activeCampaignPack` 暂不在前端重建,等待后端后续模块正式写入后直接透传。
### 4.3 验证结果
已通过:
1. `cargo test -p module-runtime-story-compat story_engine --manifest-path server-rs\Cargo.toml`
2. `cargo test -p api-server runtime_story --manifest-path server-rs\Cargo.toml`
3. `cargo test -p api-server runtime_story_route_boundary_projects_story_engine_state --manifest-path server-rs\Cargo.toml`
4. `npm run test -- src/hooks/rpg-runtime-story/storyRequestCoordinator.test.ts src/hooks/rpg-runtime-story/storyRequestRuntime.test.ts src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.test.tsx src/hooks/rpg-runtime-story/choiceActions.test.ts src/hooks/rpg-runtime-story/storyInteractionCoordinator.test.ts`
5. `npx eslint src/hooks/rpg-runtime-story/storyContextBuilder.ts src/hooks/rpg-runtime-story/progressionActions.ts --max-warnings 0`
6. `cargo fmt --manifest-path server-rs\Cargo.toml --all --check`
已发现的非本轮阻塞:
1. `npm run typecheck` 当前被既有 NPC 交易、背包/锻造 UI、测试 fixture、`src/services/ai.ts` 缺 import 等错误拦截。
2. `npm run test -- src/hooks/rpg-runtime-story` 当前有 1 个 `storyChoiceRuntime.test.ts` 战斗死亡/复活断言失败,属于审计后续 `4.5` post-battle 迁移范围。
### 4.4 NPC 聊天半量快照容错补丁
用户复测角色聊天时,点击 NPC 聊天选项后触发:
`Cannot read properties of undefined (reading 'length')`
复查调用链确认,后端 story engine projector 已经成为 `storyEngineMemory` 的主写入方,但部分快照或旧存档可能只携带 `currentChapter / worldMutations` 等增量字段,没有补齐 `activeThreadIds / recentCarrierIds / discoveredFactIds` 等数组字段。前端在 `syncNpcNarrativeState()` 中把半量对象当完整 `StoryEngineMemoryState` 消费,直接读取 `activeThreadIds.length`,导致 NPC 选项点击后的好感与叙事记忆同步中断。
本轮只做消费边界容错,不恢复前端 story engine 状态机:
1. `visibilityEngine.ts` 增加 `normalizeStoryEngineMemoryState()`,以 `createEmptyStoryEngineMemoryState()` 为基底补齐数组字段,同时保留后端快照已有字段。
2. `syncNpcNarrativeState()``appendStoryEngineCarrierMemory()` 在读写叙事记忆前统一归一化,避免半量快照在 NPC 聊天、物品回声等路径里崩溃。
3. `buildEncounterVisibilitySlice()``buildQuestVisibilitySlice()` 直接消费外部 memory 时也先归一化,保证 visibility 层独立调用时口径一致。
4. 新增 `echoMemory.test.ts` 回归用例,覆盖只有 `currentChapter`、缺少 `activeThreadIds` 的后端投影快照。
验证:
1. `npm run test -- src/services/storyEngine/echoMemory.test.ts src/services/storyEngine/visibilityEngine.test.ts`
2. `npm run check:encoding -- src/services/storyEngine/echoMemory.ts src/services/storyEngine/visibilityEngine.ts src/services/storyEngine/echoMemory.test.ts docs/technical/RPG_RUNTIME_STORY_ENGINE_BACKEND_MIGRATION_2026-04-28.md`
### 4.5 story prompt context 后端 projector 收口
本轮继续收口 `RPG_FRONTEND_SCRIPT_BACKEND_MIGRATION_COMPLETION_CHECK_2026-04-28.md` 中仍未完成的 `story engine / prompt context / AI story 请求编排`
1. `server-rs/crates/module-runtime-story-compat/src/prompt_context.rs` 新增 `build_runtime_story_prompt_context(...)`,基于后端持久化 `gameState` 投影:
- 场景描述、mutation、压力等级。
- encounter / NPC 好感、披露阶段、可谈话题、首次接触姿态。
- conversationSituation / conversationPressure / talkPriority。
- chapter、journey beat、worldMutations、chronicle、party relationship notes。
2. `POST /api/runtime/story/initial``POST /api/runtime/story/continue` 支持新主链 payload
- `sessionId`
- `clientVersion`
- `choice`
- `lastFunctionId`
- `observeSignsRequested`
- `recentActionResult`
- `requestOptions`
3. 后端收到 `sessionId` 后只从服务端 runtime snapshot 读取 `worldType / playerCharacter / sceneHostileNpcs / storyHistory / prompt context`;旧 `worldType / character / history / context` 字段仅保留兼容,不作为正式主链来源。
4. `runtime_chat_plain.rs``runtime_chat.rs` 同步支持 `sessionId`角色私聊、NPC 对话、NPC 单轮聊天、招募对话的 prompt context 也由后端快照投影;前端只继续提交对话草稿、目标角色、玩家发言和必要 UI 临时项。
5. `src/hooks/rpg-runtime-story/storyContextBuilder.ts` 缩减为 session 元信息适配器,不再推导 `conversationSituation / conversationPressure / NPC disclosure / partyRelationshipNotes / scene pressure` 等正式上下文。
6. `src/services/aiService.ts` 在存在 `runtimeSessionId`story initial/continue 只提交 session 轻量 payload聊天接口只附带 `sessionId` 与对话输入,不再上传完整 `StoryGenerationContext`
7. `src/hooks/rpg-runtime-story/sessionActions.ts` 领取任务奖励时不再运行前端 `chapterDirector / echoMemory`,只保留旧 UI 层奖励展示所需的本地字段;章节和 `storyEngineMemory.currentChapter` 等正式叙事字段等待后端 action snapshot 刷新。
8. `src/hooks/rpg-runtime-story/useRpgRuntimeNpcInteraction.ts` NPC 聊天闭合后不再调用前端 scene act runtime 推进 `storyEngineMemory.currentSceneActState`,也不再把 `deferredRuntimeState.storyEngineMemory` 写回正式 `GameState`
9. `src/hooks/rpg-runtime-story/choiceActions.ts` 兼容旧 `deferredRuntimeState` 时只允许采用场景字段,不再从 story moment 写入 `storyEngineMemory`
新增验证:
1. `cargo test -p module-runtime-story-compat prompt_context --manifest-path server-rs\Cargo.toml`
2. `cargo test -p shared-contracts runtime_story_ai_request --manifest-path server-rs\Cargo.toml`
3. `cargo test -p api-server runtime_story_initial_uses_server_snapshot_prompt_context_when_session_id_present --manifest-path server-rs\Cargo.toml`
4. `cargo check -p api-server --manifest-path server-rs\Cargo.toml --message-format short`
5. `npm run test -- src/services/ai.test.ts src/hooks/rpg-runtime-story/storyRequestCoordinator.test.ts src/hooks/rpg-runtime-story/useRpgRuntimeStoryController.test.tsx`
6. `npm run test -- src/hooks/rpg-runtime-story/sessionActions.test.ts src/hooks/rpg-runtime-story/choiceActions.test.ts src/hooks/rpg-runtime-story/npcEncounterActions.test.ts`
7. `npx eslint src/hooks/rpg-runtime-story/sessionActions.ts src/hooks/rpg-runtime-story/sessionActions.test.ts src/hooks/rpg-runtime-story/useRpgRuntimeNpcInteraction.ts src/hooks/rpg-runtime-story/choiceActions.ts src/hooks/rpg-runtime-story/choiceActions.test.ts --max-warnings 0`

View File

@@ -112,6 +112,8 @@ src/services/creation-agent/
1. 4 个玩法锚点映射。
2. 输入框占位提示。
3. 生成结果页 action`big_fish_compile_draft`
4. `big_fish_compile_draft` 只负责编译玩法草稿并进入结果页,不在草稿阶段串行生成动作素材。
5. 大鱼吃小鱼的主图、动作、背景都在结果页工坊独立触发;统一进度组件里不再为其草稿阶段展示“生成动作素材”步骤。
### 4.3 拼图