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,299 +0,0 @@
# M3browse history Axum + SpacetimeDB 落地设计
日期:`2026-04-21`
关联任务:
- [../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md](../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md)
- [./M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md](./M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md)
关联现状:
- `server-node/src/routes/rpg-profile/rpgProfileRoutes.ts`
- `server-node/src/repositories/runtimeRepository.ts`
- `packages/shared/src/contracts/runtime.ts`
## 1. 文档目的
`02_M3_RUNTIME_PROFILE.md` 已经把 `user_browse_history``browse history` facade 列入 M3但还没有冻结到可直接编码的字段、去重规则、路由兼容方式和错误语义。
本文只解决 `browse history` 这一个最小闭环切片:
1. `user_browse_history` 真相表
2. `GET /api/runtime/profile/browse-history`
3. `POST /api/runtime/profile/browse-history`
4. `DELETE /api/runtime/profile/browse-history`
5. `/api/profile/browse-history` 兼容路径
本文不新建 checklist不替代 [../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md](../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md),只补足本轮编码所需的冻结口径。
## 2. 旧实现冻结口径
当前 Node 侧行为来自:
- `server-node/src/routes/rpg-profile/rpgProfileRoutes.ts`
- `server-node/src/repositories/runtimeRepository.ts`
冻结行为如下。
### 2.1 路由
主路径与兼容路径都必须保留:
1. `GET /api/runtime/profile/browse-history`
2. `POST /api/runtime/profile/browse-history`
3. `DELETE /api/runtime/profile/browse-history`
4. `GET /api/profile/browse-history`
5. `POST /api/profile/browse-history`
6. `DELETE /api/profile/browse-history`
所有路径都要求 Bearer JWT。
### 2.2 数据字段
单条浏览记录字段与 `packages/shared/src/contracts/runtime.ts` 保持一致:
1. `ownerUserId`
2. `profileId`
3. `worldName`
4. `subtitle`
5. `summaryText`
6. `coverImageSrc`
7. `themeMode`
8. `authorDisplayName`
9. `visitedAt`
### 2.3 POST 请求体兼容
`POST` 同时支持两种形态:
1. 单条对象
2. `{ entries: [...] }` 批量对象
批量最多 `100` 条。
### 2.4 归一化规则
旧 Node 仓储不是严格校验,而是宽松归一化:
1. `ownerUserId``profileId``worldName` 去首尾空白后必须非空,否则该条忽略。
2. `subtitle``summaryText``coverImageSrc` 去首尾空白,空串按空值处理。
3. `themeMode` 不做严格枚举校验,未知值统一回落到 `mythic`
4. `authorDisplayName` 空值回落到 `玩家`
5. `visitedAt` 缺失时回落到当前时间。
### 2.5 去重与排序规则
旧 Node 仓储的关键行为必须保持:
1. 去重键:`ownerUserId + profileId`
2. 同一批写入时,先按 `visitedAt DESC` 排序,再去重,只保留最新一条
3. 表内最终查询结果按 `visitedAt DESC` 返回
### 2.6 清空行为
`DELETE` 清空当前用户的全部浏览历史,并返回:
```json
{
"entries": []
}
```
## 3. Rust 落位决议
### 3.1 crate 分工
本切片固定按以下边界落位:
1. `crates/module-runtime`
- 定义 `browse history` DTO、字段校验、去重排序与宽松归一化规则。
2. `crates/spacetime-module`
- 定义 `user_browse_history` 表。
- 提供 `list / upsert / clear` 三个 procedure。
3. `crates/spacetime-client`
- 提供 `list_platform_browse_history`
- 提供 `upsert_platform_browse_history_entries`
- 提供 `clear_platform_browse_history`
4. `crates/shared-contracts`
- 冻结 Axum facade 的请求/响应 DTO。
5. `crates/api-server`
- 提供双路径兼容 facade。
- 保持 envelope / 错误格式与当前 `runtime settings` 一致。
### 3.2 身份边界
本轮仍沿用 Axum Bearer JWT 作为唯一鉴权边界:
1. `require_bearer_auth` 校验 token。
2. 从 claims 中提取 `user_id`
3. `user_id` 作为 procedure 入参传入 SpacetimeDB。
当前阶段不把浏览历史直接暴露给前端直连订阅。
## 4. SpacetimeDB 表设计
### 4.1 表名
`user_browse_history`
### 4.2 字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `browse_history_id` | `String` | 主键,格式为 `user_id:owner_user_id:profile_id` |
| `user_id` | `String` | 当前登录用户 |
| `owner_user_id` | `String` | 被浏览世界所属用户 |
| `profile_id` | `String` | 被浏览世界 profile |
| `world_name` | `String` | 世界名 |
| `subtitle` | `String` | 副标题 |
| `summary_text` | `String` | 摘要 |
| `cover_image_src` | `Option<String>` | 封面图 |
| `theme_mode` | `RuntimeBrowseHistoryThemeMode` | 主题枚举 |
| `author_display_name` | `String` | 作者显示名 |
| `visited_at` | `Timestamp` | 最近访问时间 |
| `created_at` | `Timestamp` | 首次写入时间 |
| `updated_at` | `Timestamp` | 最近更新时间 |
### 4.3 索引
至少保留以下访问路径:
1. 主键 `browse_history_id`
2. `(user_id, visited_at)` 用于当前用户按时间倒序列出
3. `(user_id, owner_user_id, profile_id)` 用于幂等 upsert
### 4.4 设计决议
1. 不额外引入自增 ID直接把幂等键收口成主键。
2. `visited_at` 单独持久化成 `Timestamp`,避免把时间排序退回字符串比较。
3. `theme_mode` 在表内固定为枚举,但 Axum 输入仍宽松接受字符串。
## 5. Procedure 设计
### 5.1 procedure 列表
1. `list_platform_browse_history`
2. `upsert_platform_browse_history_and_return`
3. `clear_platform_browse_history_and_return`
统一返回:
```text
RuntimeBrowseHistoryProcedureResult {
ok
entries
error_message
}
```
### 5.2 行为约束
`list_platform_browse_history`
1. 校验 `user_id`
2. 读取当前用户所有记录
3.`visited_at DESC` 返回
`upsert_platform_browse_history_and_return`
1. 校验 `user_id`
2. 接受单批最多 `100`
3. 先按旧 Node 规则宽松归一化
4. 先按 `visitedAt DESC` 排序,再按 `ownerUserId + profileId` 去重
5.`browse_history_id` 幂等 upsert
6. 返回当前用户完整倒序列表
`clear_platform_browse_history_and_return`
1. 校验 `user_id`
2. 删除当前用户全部记录
3. 返回空列表
## 6. Axum facade 设计
### 6.1 双路径兼容
两组路径必须共用同一组 handler
1. `/api/runtime/profile/browse-history`
2. `/api/profile/browse-history`
只允许路由名不同,不允许行为分叉。
### 6.2 GET
行为:
1. Bearer JWT 校验
2. 读取 claims 中的 `user_id`
3.`spacetime_client.list_platform_browse_history`
4. 返回 `PlatformBrowseHistoryResponse`
### 6.3 POST
行为:
1. Bearer JWT 校验
2. 通过 `serde(untagged)` 同时接单条和批量 shape
3. 不对 `themeMode` 做严格 400 拒绝
4.`ownerUserId``profileId``worldName` 的缺失或空串按旧 Node 路由规则直接返回 `400`
5. 写入成功后返回最新完整列表
### 6.4 DELETE
行为:
1. Bearer JWT 校验
2. 清空当前用户全部记录
3. 返回 `entries: []`
### 6.5 错误映射
1. JSON 解析失败:`400 BAD_REQUEST`
2. DTO 构建失败:`400 BAD_REQUEST`
3. SpacetimeDB 调用失败:`502 BAD_GATEWAY`
4. JWT 缺失或失效:沿用当前 `401 UNAUTHORIZED`
错误 `details.provider` 固定为:
1. `browse-history`
2. `spacetimedb`
## 7. 测试策略
### 7.1 必跑测试
1. `module-runtime`
- 宽松 theme 归一化
- `visitedAt` 默认值
- 去重与倒序逻辑
2. `api-server`
- 未登录返回 `401`
- 兼容路径与主路径一致
- `POST` 同时支持单条和批量
- envelope 打开时错误结构稳定
### 7.2 可选联调测试
保留 `#[ignore]` 的本地 SpacetimeDB 集成测试:
1. `POST -> GET`
2. `DELETE -> GET`
## 8. 本文完成定义
当以下条件成立时,本设计视为完成:
1. `user_browse_history` 表字段、主键和排序规则已冻结。
2. 双路径 facade、请求 shape 和错误契约已冻结。
3. 后续编码不再需要猜测:
- `themeMode` 是否严格校验
- `POST` 是否支持单条/批量双 shape
- 去重时机与排序依据
## 9. 2026-04-22 实际落地进度
1. `module-runtime` 已切换为“API 入口严格校验 + 领域层静默过滤”的旧 Node 对齐模式。
2. `api-server` 已补齐双路径 browse history handler并补 `401``400`、批量 shape、兼容路径一致性测试。
3. 剩余阻塞主要在工作树内其他并行任务带来的 Rust 编译占用与跨模块联调,不属于 browse history 方案本身。