# M4:story action 与 gameplay reducer 任务清单 ## 0. 当前执行基线 本阶段与当前仓库里的 RPG 入口与运行时主链重构直接对应,统一以以下文档为准: 1. [../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md) 2. [../docs/technical/FRONTEND_TO_BACKEND_MIGRATION_EXECUTION_PLAN_2026-04-21.md](../docs/technical/FRONTEND_TO_BACKEND_MIGRATION_EXECUTION_PLAN_2026-04-21.md) 3. [../docs/technical/M4_RPG_RUNTIME_STORY_SPACETIMEDB_BASELINE_2026-04-21.md](../docs/technical/M4_RPG_RUNTIME_STORY_SPACETIMEDB_BASELINE_2026-04-21.md) 当前任务清单只维护 Axum / SpacetimeDB 重写侧的后端迁移项,不再把旧 `GameShell / runtimeRoutes.ts / storyActionService.ts` 命名视为新架构目标。 ### 当前进展(`2026-04-22`) 本阶段首轮已先把 `server-rs` 从“只有 `module-story` 占位目录”推进到“SpacetimeDB 侧 story 会话基座真实可编译”: 1. 已新增 `server-rs/crates/module-story` 真实 crate。 2. 已冻结 `story_session / story_event` 的首版领域类型、状态枚举和字段校验 helper。 3. 已在 `server-rs/crates/spacetime-module` 中新增 `story_session`、`story_event` 两张表。 4. 已新增 `begin_story_session`、`continue_story` 两个 reducer,形成最小会话事件链。 5. 已新增 `begin_story_session_and_return`、`continue_story_and_return` 两个 procedure,形成可同步返回快照的最小 story session contract。 6. 已重新执行 `spacetime generate`,把 `story_session / story_event` Rust bindings 刷入 `spacetime-client/src/module_bindings`。 7. 已在 `server-rs/crates/spacetime-client` 中新增 `begin_story_session(...)`、`continue_story(...)` facade。 8. 已在 `server-rs/crates/api-server` 中新增: - `POST /api/story/sessions` - `POST /api/story/sessions/continue` 9. 已执行 `cargo check -p module-story -p spacetime-module -p spacetime-client -p api-server` 并通过。 6. 已新增 `docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `battle_state` 与 `resolve_combat_action` 的首版字段与规则口径。 7. 已新增 `server-rs/crates/module-runtime-item` 真实 crate。 8. 已冻结 `treasure_record` 的首版领域类型、完整奖励物品快照和字段校验规则。 9. 已在 `server-rs/crates/spacetime-module` 中新增 `treasure_record` 表。 10. 已新增 `resolve_treasure_interaction` reducer 与 `resolve_treasure_interaction_and_return` procedure,并把宝箱奖励同步写入 `inventory_slot`。 11. 已新增 `docs/technical/M4_RPG_RUNTIME_INVENTORY_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `inventory_slot` 与 `apply_inventory_mutation` 的首版字段与规则口径。 12. 已新增 `server-rs/crates/module-inventory` 真实 crate。 13. 已在 `server-rs/crates/spacetime-module` 中新增 `inventory_slot` 表。 14. 已新增 `apply_inventory_mutation` reducer,形成最小背包主链。 15. 已新增 `docs/technical/M4_MODULE_NPC_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `npc_state`、`resolve_npc_social_action` 与 `resolve_npc_interaction` 的首版字段与交互口径。 16. 已新增 `server-rs/crates/module-npc` 真实 crate。 17. 已在 `server-rs/crates/spacetime-module` 中新增 `npc_state` 表。 18. 已新增 `upsert_npc_state`、`resolve_npc_social_action`、`resolve_npc_interaction` 及对应 procedure。 19. 已新增 `docs/technical/M4_MODULE_NPC_COMBAT_ORCHESTRATION_BASELINE_2026-04-21.md`,冻结 `npc_fight / npc_spar` 到 `battle_state` 的最小联合编排口径。 20. 已在 `server-rs/crates/spacetime-module` 中新增 `resolve_npc_battle_interaction_and_return` procedure,把 NPC 开战交互与 battle 初始化写入串到同一事务。 15. 已新增 `docs/technical/M4_MODULE_PROGRESSION_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `player_progression / chapter_progression` 的首版字段、成长曲线与章节预算口径。 16. 已新增 `server-rs/crates/module-progression` 真实 crate。 17. 已在 `server-rs/crates/spacetime-module` 中新增 `player_progression`、`chapter_progression` 两张表。 18. 已新增 `get_player_progression_or_default`、`grant_player_progression_experience`、`upsert_chapter_progression`、`apply_chapter_progression_ledger_entry` 及对应 procedure。 19. 已新增 `docs/technical/M4_RPG_RUNTIME_QUEST_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `quest_record / quest_log / apply_quest_signal` 的首版字段、日志口径与交付状态流转规则。 20. 已新增 `server-rs/crates/module-quest` 真实 crate。 21. 已在 `server-rs/crates/spacetime-module` 中新增 `quest_record`、`quest_log` 两张表。 22. 已新增 `accept_quest`、`apply_quest_signal`、`acknowledge_quest_completion`、`turn_in_quest` reducer,形成最小任务闭环。 23. 已执行 `cargo test -p module-quest`、`cargo check -p spacetime-module`、`cargo check -p api-server` 与全量 `cargo check` 并通过。 24. 已新增 `docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md`,冻结任务交付与战斗胜利到成长系统的联动口径。 25. 已把 `turn_in_quest` 接到 `player_progression / chapter_progression` 的最小经验写入。 26. 已把 `resolve_combat_action(Victory)` 接到 `player_progression / chapter_progression` 的最小经验写入。 27. 已把 `turn_in_quest.reward.items` 接到 `inventory_slot` 发物链,形成任务交付的最小物品奖励闭环。 28. 已新增 `docs/technical/M4_RPG_RUNTIME_STORY_SESSION_STATE_QUERY_DESIGN_2026-04-22.md`,冻结最小 `story state` 查询切片,只开放 `storySession + storyEvents` 真相态查询。 29. 已在 `server-rs/crates/api-server` 中挂出 `GET /api/story/sessions/:storySessionId/state`,通过 `spacetime-client.get_story_session_state(...)` 读取 `SpacetimeDB procedure` 返回的会话快照与事件流。 30. 已新增 `docs/technical/M4_COMBAT_REWARD_INVENTORY_INTEGRATION_2026-04-22.md`,冻结 `battle_state.reward_items` 与 `resolve_combat_action(Victory)` 发物到 `inventory_slot` 的最小联动口径。 31. 已新增 `docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md`,冻结最小 `battle state` 查询切片,只开放单个 `battleState` 真相态查询。 32. 已在 `server-rs/crates/spacetime-module` 中新增 `get_battle_state` procedure,按 `battle_state_id` 返回当前战斗快照。 33. 已在 `server-rs/crates/spacetime-client` 中新增 `get_battle_state(...)` facade,供 Axum 同步读取 battle 真相态。 34. 已在 `server-rs/crates/api-server` 中挂出 `GET /api/story/battles/:battleStateId`,通过 `spacetime-client.get_battle_state(...)` 返回单战斗快照。 35. 已在 `server-rs/crates/spacetime-client` 中新增 `resolve_npc_battle_interaction(...)` facade,把 `resolve_npc_battle_interaction_and_return` procedure 映射为稳定 Rust record,供 Axum 直接消费。 36. 已在 `server-rs/crates/api-server` 中挂出 `POST /api/story/npc/battle`,当前只接受 `npc_fight / npc_spar`,同步返回 `npcInteraction + battleState`。 37. 已执行 `cargo check -p spacetime-client -p api-server` 并通过,完成 `module-npc -> spacetime-client -> api-server` 的最小 NPC 开战同步返回链闭环。 38. 已重新执行 `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes`,把 `get_battle_state`、`battle_state.reward_items` 与 `custom_world_agent_session` 相关 bindings 刷入 `spacetime-client/src/module_bindings`。 39. 已把 `server-rs/crates/spacetime-client/src/lib.rs` 中原本占位返回错误的 `get_battle_state(...)` 改成真实 procedure 调用,当前 battle query 已不再停留在 facade stub。 40. 已再次执行 `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 与 `cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`,当前 battle/story 新链路在编译层已恢复通过。 41. 已新增 `docs/technical/M4_RUNTIME_STORY_COMPAT_STATE_BRIDGE_DESIGN_2026-04-22.md`,冻结旧 `POST /api/runtime/story/state/resolve` 的首版兼容桥边界,明确当前先做 DTO 与状态桥,不提前误宣称 `actions/resolve` 已可迁移。 42. 已在 `server-rs/crates/shared-contracts` 中新增 `runtime_story` 模块,冻结 `RuntimeStoryStateResolveRequest`、`RuntimeStoryActionResponse` 以及 `viewModel / presentation / patches / snapshot` 的首版 camelCase DTO,与当前前端消费口径对齐。 43. 已恢复并重建 `server-rs/crates/api-server/src/runtime_story.rs`,把上一轮误删留下的中间态收口回可编译实现。 44. 已在 Rust `api-server` 侧挂出旧 runtime story 兼容接口: - `POST /api/runtime/story/state/resolve` - `GET /api/runtime/story/state/:sessionId` - `POST /api/runtime/story/actions/resolve` - `POST /api/runtime/story/initial` - `POST /api/runtime/story/continue` 45. `state/resolve` 与 `actions/resolve` 已统一复用 `runtime_save` 的 SpacetimeDB 快照持久化链: - 请求带 `snapshot` 时先写入 `runtime_snapshot` - 请求不带 `snapshot` 时从持久化 `runtime_snapshot` 读取 - 无可用快照时返回 `409` 46. `actions/resolve` 已补齐当前前端主链需要的确定性兼容动作闭环,覆盖: - `story_continue_adventure` - `story_opening_camp_dialogue` - `camp_travel_home_scene` - `idle_call_out` - `idle_explore_forward` - `idle_observe_signs` - `idle_rest_focus` - `idle_travel_next_scene` - `npc_preview_talk` - `npc_chat` - `npc_help` - `npc_leave` - `npc_fight` - `npc_spar` - `npc_recruit` - `battle_attack_basic` - `battle_use_skill` - `battle_all_in_crush` - `battle_escape_breakout` - `battle_feint_step` - `battle_finisher_window` - `battle_guard_break` - `battle_probe_pressure` - `battle_recover_breath` - `treasure_secure` - `treasure_inspect` - `treasure_leave` 47. `actions/resolve` 已补 `clientVersion` 与 `gameState.runtimeActionVersion` 的冲突校验、动作后版本自增、`storyHistory` 追加和 snapshot 回写。 48. `initial` / `continue` 已先落稳定 `RuntimeStoryAiResponse`: - 优先透传 `requestOptions.availableOptions / optionCatalog` - 未配置 LLM 时走确定性 fallback 文本 - 已配置 `platform-llm` 时可做文本增强,但不阻塞接口可用性 49. 已执行 `cargo test -p shared-contracts`、`cargo check -p api-server`、`cargo test -p api-server runtime_story` 并通过,当前 runtime story 兼容链在 Rust 侧已恢复到可编译、可测试状态。 50. 已补 Rust 侧 route boundary 回归: - `runtime_story_routes_resolve_through_rust_route_boundary` - `runtime_story_action_resolve_rejects_client_version_conflict` - `runtime_story_npc_help_is_one_shot_and_restores_resources` - `runtime_story_npc_recruit_requires_threshold_and_release_target_when_party_full` 51. 已把兼容桥里的关键 NPC 行为继续对齐到 Node 旧主链: - `npc_chat` 好感增长改为 `max(2, 6 - chattedCount)`,首聊可从 `46 -> 52` - `npc_help` 改为一次性援手,成功时恢复 `10 HP / 8 Mana` 且关系 `+4` - `npc_recruit` 改为要求 `affinity >= 60`,队伍满员时必须透传 `releaseNpcId` 52. 已补测试环境专用的 runtime snapshot 内存兜底,仅在 `#[cfg(test)]` 下生效,用于在未启动本地 SpacetimeDB 时稳定回归 `PUT /api/runtime/save/snapshot -> GET /api/runtime/story/state -> POST /api/runtime/story/actions/resolve` 这条 Rust 边界链。 53. 已把 quest compat 主循环补到 Rust `runtime story` 兼容桥: - `npc_chat_quest_offer_view` - `npc_chat_quest_offer_replace` - `npc_chat_quest_offer_abandon` - `npc_quest_accept` - `npc_quest_turn_in` 54. 已把 quest offer 对话态的 `currentStory.npcChatState.pendingQuestOffer` 与前端面板依赖的 `runtimePayload.npcChatQuestOfferAction` 一并回填到 Rust compat 回包,保证现有 quest 面板入口不回退。 55. 已把 `npc_quest_turn_in` 的最小奖励闭环补回 Rust compat handler: - quest 状态改为保留在 `gameState.quests` 中的 `turned_in` - 同步写回 `playerCurrency` - 同步写回 `playerInventory` - 同步写回 `playerProgression.totalXp / level / xpToNextLevel / lastGrantedSource` - 同步写回 NPC `affinity` 56. 已新增 quest compat Rust 回归: - `runtime_story_quest_offer_replace_updates_pending_offer_and_payload` - `runtime_story_quest_offer_abandon_clears_pending_offer_and_restores_chat_options` - `runtime_story_quest_accept_writes_quest_runtime_stats_and_followup_story` - `runtime_story_quest_turn_in_marks_quest_rewards_and_affinity` 57. 已再次执行 `cargo test -p api-server runtime_story`、`cargo check -p api-server` 与 `node scripts/check-encoding.mjs` 并通过,当前 quest compat 已恢复到可编译、可回归状态。 当前验证边界补充: 1. `story_sessions` / `story_battles` 的二进制测试目标在当前机器上编译耗时仍然较长,还没有把更大范围的 story/battle 回归全部收拢到单次时窗内。 2. `node scripts/check-encoding.mjs` 已再次执行并通过,当前本轮涉及的中文文件编码未被写坏。 3. 当前可以确认的是: - `module -> generated bindings -> spacetime-client -> api-server` 的编译链已打通 - Rust `runtime story` compat route boundary 与关键 NPC 主循环规则已有回归覆盖 - 真正的 `resolve_story_action / sync_runtime_snapshot_projection` 真相链仍未完成 当前这轮仍未扩到真正的 SpacetimeDB `resolve_story_action` / `sync_runtime_snapshot_projection` 真相 reducer,也还没有完成前端默认切流到 Rust `api-server`。当前已完成的是“旧 `/api/runtime/story/*` 兼容接口在 Rust 侧的快照桥与确定性动作闭环”,后续 `M4` 继续推进真相态替换与前端切换。 ## 1. SpacetimeDB gameplay 表 - [x] 设计 `story_session` - [x] 设计 `story_event` - [x] 设计 `npc_state` - [x] 设计 `quest_record` - [x] 设计 `inventory_slot` - [x] 设计 `treasure_record` - [x] 设计 `battle_state` - [x] 设计 `player_progression` - [x] 设计 `chapter_progression` ## 2. 核心 reducer - [ ] 设计 `resolve_story_action` - [x] 设计 `continue_story` - [x] 设计 `begin_story_session` - [ ] 设计 `sync_runtime_snapshot_projection` - [x] 设计 `apply_quest_signal` - [x] 设计 `apply_inventory_mutation` - [x] 设计 `resolve_npc_interaction` - [x] 设计 `resolve_treasure_interaction` - [x] 设计 `resolve_combat_action` - [x] 设计 `update_progression_state` ## 3. 当前主链模块落位 - [ ] 迁移 `rpg-entry` 配套后端入口能力 - [ ] 迁移 `rpg-profile` 资料域 - [x] 迁移 `rpg-runtime-story` - [x] 迁移 `combat` - [ ] 迁移 `inventory` - [ ] 迁移 `npc` - [x] 迁移 `progression` - [x] 迁移 `quest` - [x] 迁移 `runtime-item` - [ ] 迁移 runtime snapshot 归一化、view model compiler 与状态同步规则 ## 4. 兼容接口 - [x] 兼容 `POST /api/runtime/story/actions/resolve` - [x] 兼容 `GET /api/runtime/story/state/:sessionId` - [x] 兼容 `POST /api/runtime/story/state/resolve` - [x] 兼容 `POST /api/runtime/story/initial` - [x] 兼容 `POST /api/runtime/story/continue` 补充说明: 1. 当前已落地的是两类 Rust facade: - 新真相态接口: - `POST /api/story/sessions` - `POST /api/story/sessions/continue` - `GET /api/story/sessions/:storySessionId/state` - `GET /api/story/battles/:battleStateId` - `POST /api/story/npc/battle` - 旧 runtime story 兼容接口: - `POST /api/runtime/story/state/resolve` - `GET /api/runtime/story/state/:sessionId` - `POST /api/runtime/story/actions/resolve` - `POST /api/runtime/story/initial` - `POST /api/runtime/story/continue` 2. 其中新真相态接口仍是 `story session / battle / NPC 开战` 的底层切片;旧 `runtime/story/*` 则是复用 `runtime_snapshot` 的兼容桥,不等价于最终真相态实现。 3. 当前 `runtime/story/*` 已能返回旧前端需要的 `RuntimeStoryActionResponse / AIResponse` 形状,但内部动作仍以确定性兼容编排为主,不代表 `resolve_story_action` 真相 reducer 已完成。 4. 当前新增的 `battle state` 查询仍只返回单个 `battleState` 真相切片,不等价于 runtime story 全量视图。 5. 后续 `M4` 仍需把兼容桥逐步替换成真正的 story action / snapshot projection 真相链。 ## 5. ViewModel 兼容 - [x] 兼容当前 `RuntimeStoryActionResponse` - [x] 兼容当前 `RuntimeStoryOptionView` - [x] 兼容当前 `interaction` 元数据 - [x] 兼容当前 battle / toast / patch 响应结构 - [x] 兼容当前 `currentStory` 回填逻辑 ## 6. 阶段验收 - [x] 当前前端 story 选项点击后可走新后端闭环 - [ ] NPC / quest / treasure / combat 主循环行为不回退 - [x] `story state` 恢复链可用 - [ ] 后端边界与当前 `rpgEntry -> rpgSession -> rpgRuntime -> rpgRuntimeStory -> rpgProfile` 口径一致 - [x] 旧 Node 版 story route 回归用例完成平移 阶段验收补充说明: 1. `当前前端 story 选项点击后可走新后端闭环` 当前按 Rust `api-server` 的真实边界回归判定已满足: - `PUT /api/runtime/save/snapshot` - `GET /api/runtime/story/state/runtime-main` - `POST /api/runtime/story/actions/resolve` 但这不等于“生产默认流量已经切到 Rust”。 2. `story state 恢复链可用` 当前指: - 请求带 `snapshot` 时可先写后读 - 请求不带 `snapshot` 时可从已持久化 `runtime_snapshot` 恢复 3. `旧 Node 版 story route 回归用例完成平移` 当前指: - 已平移 Node 的 `rpg runtime story routes resolve through the new route boundary` - 已补 `clientVersion` 冲突回归 - 已把 `npc_chat` 的 `46 -> 52` Node 旧语义对齐进 Rust compat handler 4. `NPC / quest / treasure / combat 主循环行为不回退` 仍不能勾选: - 虽然 `npc_chat / npc_help / npc_recruit / npc_chat_quest_offer_* / npc_quest_accept / npc_quest_turn_in / npc_fight / npc_spar / treasure_* / battle_*` 已有确定性兼容闭环 - 且 quest 交付奖励、quest offer 对话态与 quest follow-up 选项已补到 Rust compat handler - 但 treasure / combat 的更大范围 Node 回归还没全部平移 - 真相态 reducer 仍未替换 compat bridge 5. `后端边界与当前 rpgEntry -> ...` 仍不能勾选: - 前端真实调用链已对齐 `/api/runtime/story/*` - 但“默认走 Rust server”的联调证据仍未冻结