Prune stale docs and update .hermes content

Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
2026-05-15 06:24:07 +08:00
parent 2eded08bc7
commit 3cb3efb4d0
708 changed files with 4033 additions and 142328 deletions

View File

@@ -1,232 +0,0 @@
# M4 Runtime Inventory State Query 设计2026-04-22
更新时间:`2026-04-22`
## 0. 文档目标
本文件只冻结当前 `M4` 的一个最小新增切片:
**新增 `GET /api/runtime/sessions/:runtimeSessionId/inventory`,让 Axum 能从 `SpacetimeDB` 同步读取当前玩家在指定 `runtime_session` 下的 `inventory_slot` 真相态,不提前承诺旧 `GameState.playerInventory / playerEquipment` 全量兼容。**
本轮目标不是把旧前端背包 view model 一次性全迁到 Rust也不是把 `inventory_use / craft / dismantle / reforge` 一次性补齐。
---
## 1. 为什么先做这个切片
当前 inventory 主链已经具备:
1. `module-inventory` 已冻结 `inventory_slot``apply_inventory_mutation` 的首版领域 contract。
2. `spacetime-module` 已有 `inventory_slot` 真相表与 `apply_inventory_mutation` reducer。
3. `treasure / quest / battle` 奖励物品已经能同步写入 `inventory_slot`
真正缺的部分是:
1. 背包真相态还没有稳定查询入口。
2. `api-server` 还不能按 `runtime_session_id + 当前用户` 同步返回当前背包与装备状态。
3. 后续如果要把背包面板、装备面板或 runtime story projection 收口到 `SpacetimeDB`,需要先有一个最小 inventory query 切片可复用。
因此本轮先补“只读查询”能力,不提前跳到更重的旧前端状态兼容。
---
## 2. 当前冻结范围
本轮只包含以下能力:
1. 新增公开接口:`GET /api/runtime/sessions/:runtimeSessionId/inventory`
2. 认证方式Bearer JWT
3. 查询 scope`runtime_session_id + 当前登录用户`
4. 数据来源:`SpacetimeDB procedure get_runtime_inventory_state`
5. 返回体只包含:
- `runtimeSessionId`
- `actorUserId`
- `backpackItems`
- `equipmentItems`
本轮明确不做:
1. 不兼容旧 `GameState.playerInventory`
2. 不兼容旧 `GameState.playerEquipment`
3. 不补按 `story_session_id` 或全局用户维度的 inventory 查询
4. 不做 inventory public subscription / view
5. 不在查询链路里拼装 quest / npc / battle / story_event
6. 不提前做 `inventory_use / craft / dismantle / reforge`
---
## 3. 为什么按 `runtime_session_id + 当前用户` 查询
当前 `inventory_slot` 的主作用域字段是:
1. `runtime_session_id`
2. `actor_user_id`
3. `story_session_id`
本轮选择 `runtime_session_id + actor_user_id` 作为最小 query scope原因如下
1. `inventory_slot` 当前是运行态背包真相,不是单个 story session 的临时投影。
2. 同一 `runtime_session` 下的宝箱、任务、战斗奖励都已经按这个 scope 汇入同一张表。
3. 旧前端背包面板语义本质上也是“当前运行态玩家的背包”,不是“某个 story session 的局部背包”。
4. 若后续确实需要更细的 `story_session` 投影,应通过上层 façade 或专门 view 提供,而不是先把真相查询 scope 做窄。
---
## 4. 接口 contract
### 4.1 请求
- 方法:`GET`
- 路径:`/api/runtime/sessions/:runtimeSessionId/inventory`
- 认证:必须携带 Bearer JWT
- 路径参数:
- `runtimeSessionId`:目标运行时会话 ID
### 4.2 成功响应
成功响应延续当前 `api-server` 统一 envelope`data` 字段结构为:
```json
{
"runtimeSessionId": "runtime_xxx",
"actorUserId": "user_xxx",
"backpackItems": [
{
"slotId": "invslot_xxx",
"containerKind": "backpack",
"slotKey": "invslot_xxx",
"itemId": "consumable_heal_potion",
"category": "消耗品",
"name": "疗伤药",
"description": "用于恢复少量气血。",
"quantity": 3,
"rarity": "common",
"tags": ["healing"],
"stackable": true,
"stackKey": "heal_potion",
"equipmentSlotId": null,
"sourceKind": "treasure_reward",
"sourceReferenceId": "treasure_xxx",
"createdAt": "2026-04-22T00:00:00.000000Z",
"updatedAt": "2026-04-22T00:01:00.000000Z"
}
],
"equipmentItems": [
{
"slotId": "invslot_weapon_xxx",
"containerKind": "equipment",
"slotKey": "weapon",
"itemId": "weapon_trial_blade",
"category": "武器",
"name": "试作短剑",
"description": "一柄适合入门者的短剑。",
"quantity": 1,
"rarity": "rare",
"tags": ["weapon"],
"stackable": false,
"stackKey": "weapon_trial_blade",
"equipmentSlotId": "weapon",
"sourceKind": "quest_reward",
"sourceReferenceId": "quest_xxx",
"createdAt": "2026-04-22T00:00:00.000000Z",
"updatedAt": "2026-04-22T00:05:00.000000Z"
}
]
}
```
### 4.3 错误响应
当前延续 runtime/profile/story 查询已有策略:
1. `SpacetimeClientError::Runtime(_)` 映射为 `400`
2. 其他 `SpacetimeClientError` 映射为 `502`
3. 错误 `details.provider`
- 参数构建或本地语义错误:`runtime-inventory`
- 下游 `SpacetimeDB` 失败:`spacetimedb`
---
## 5. 分层职责
### 5.1 `module-inventory`
职责:
1. 冻结 `RuntimeInventoryStateQueryInput`
2. 冻结 `RuntimeInventoryStateSnapshot`
3. 冻结 `RuntimeInventorySlotRecord`
4. 负责 builder、validator 与 record 映射
不负责:
1. HTTP 路径解析
2. JWT 鉴权
3. 旧前端背包 view model 编译
### 5.2 `spacetime-module`
职责:
1. 读取指定 `runtime_session_id + actor_user_id` 下的 `inventory_slot`
2.`container_kind` 拆成 `backpackItems / equipmentItems`
3. 复用 `module-inventory` 的 query input / snapshot 结构
4. 通过 `get_runtime_inventory_state` procedure 返回当前真相态
### 5.3 `spacetime-client`
职责:
1. 构造 `RuntimeInventoryStateQueryInput`
2. 调用 `get_runtime_inventory_state`
3. 把返回结果映射为稳定 Rust record
### 5.4 `api-server`
职责:
1. 暴露 `GET /api/runtime/sessions/:runtimeSessionId/inventory`
2. 做 Bearer JWT 鉴权
3. 从 token 中注入 `actorUserId`
4. 把 inventory record 映射到 JSON payload
---
## 6. 排序规则
为了保证前端和后续 façade 读到稳定顺序,本轮冻结以下排序口径:
1. `backpackItems`
- 先按 `slot_key`
- 再按 `slot_id`
2. `equipmentItems`
- 先按装备位固定顺序:`weapon -> armor -> relic`
- 再按 `slot_id`
当前不在 query 层额外做“按稀有度”“按分类”“按来源”的排序投影。
---
## 7. 验收口径
本轮验收只要求以下几点:
1. `module-inventory` 已补 inventory query contract 与最小测试
2. `spacetime-module` 已新增 `get_runtime_inventory_state` procedure
3. `spacetime-client` 已能同步读取 inventory 真相态
4. `api-server` 已真实挂出 `GET /api/runtime/sessions/:runtimeSessionId/inventory`
5. `cargo check -p module-inventory -p spacetime-module -p spacetime-client -p api-server` 可通过
6. `npm run check:encoding` 已执行,确保新增中文文档与接口文件没有编码损坏
---
## 8. 后续边界
这条最小 inventory query 落地后,后续再继续拆下一层:
1. 评估是否需要补 `story session` 局部 inventory projection
2. 评估是否需要把 inventory query 接成旧背包面板 view model
3. 再继续补 `inventory_use / craft / dismantle / reforge`
4. 最后再考虑 inventory subscription / public view
在这些 contract 未冻结前,不应把当前接口误称为“旧背包系统已完整迁移完成”。