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,458 +0,0 @@
# 拼图玩法创作与运行态最小落地技术方案
日期:`2026-04-22`
## 1. 文档目的
本文件承接 PRD《AI 原生拼图玩法创作工具与玩法系统》,冻结本轮拼图玩法在当前平台内的最小完整闭环。
本轮目标不是抽象一个通用拼图编辑器,也不是额外搭建一个脱离平台的新小游戏站点,而是在现有平台壳层内新增独立 `puzzle` 玩法域,跑通下面这条主链:
1. 平台创作入口选择拼图玩法
2. Agent-first 对话收束 5 个高杠杆视觉锚点
3. 编译结果页草稿
4. 在结果页编辑关卡名、标签并生成候选拼图图片
5. 发布作品进入拼图广场
6. 玩家从广场进入第 1 关
7. 后端初始化 `3x3 / 4x4` 棋盘
8. 后端裁决交换、合并、拖动、拆分与通关
9. 通关后根据“标签相似度 `70%` + 同作者 `30%`”推荐下一关
## 2. 本轮明确不做
1. 不做异形拼块、旋转拼块、道具、体力、倒计时。
2. 不做新的平台站点或新的全局导航系统。
3. 不做前端本地推荐、前端本地裁决、前端本地持久化真相。
4. 不做复杂图片模型编排;首版图片生成沿用 `api-server` 的占位资产生成方式,保证完整链路可跑通。
5. 不把拼图玩法继续挂在 `customWorld``rpgWorld` 或 RPG runtime 旧语义下。
6. 不扩到拼图排行榜、社交评论、收藏、复盘系统。
## 3. 分层边界
### 3.1 前端
前端继续使用当前 `React + TypeScript + Vite` 平台壳层,只负责:
1. 展示拼图创作中心、Agent 工作区、结果页、广场、运行时画布。
2. 发起聊天、结果页编辑、发布、开始游戏、交换与拖动请求。
3. 基于后端快照渲染棋盘、HUD、选中态与合并反馈。
前端禁止:
1. 自行判断下一关推荐。
2. 自行判断拼块是否应当合并。
3. 自行判断合并块是否应当拆分。
4. 自行判断通关。
### 3.2 Axum
`server-rs/crates/api-server` 负责:
1. 对外暴露 `/api/runtime/puzzle-*` HTTP 接口。
2. 鉴权、请求上下文、错误 envelope。
3. 结果页占位图片生成与静态资产落盘。
4. 调用 `spacetime-client` 读写拼图玩法真相态。
### 3.3 SpacetimeDB
`server-rs/crates/spacetime-module` 负责:
1. 存储拼图 Agent session / message。
2. 存储已发布拼图作品 profile。
3. 存储拼图运行态 run snapshot。
4. 通过 procedure 同步返回 session / works / gallery / runtime 快照。
### 3.4 纯领域 crate
新增 `server-rs/crates/module-puzzle`,承载:
1. 5 个锚点与会话阶段的纯领域模型。
2. 草稿编译、标签规范化、发布校验。
3. `3x3 / 4x4` 棋盘初始化。
4. 交换、合并、拖动、拆分、通关与下一关推荐算法。
## 4. 共享契约
### 4.1 TypeScript shared contracts
`packages/shared/src/contracts/` 新增:
1. `puzzleAgentSession.ts`
2. `puzzleAgentDraft.ts`
3. `puzzleAgentActions.ts`
4. `puzzleResultPreview.ts`
5. `puzzleWorkSummary.ts`
6. `puzzleRuntimeSession.ts`
这些文件分别承载:
1. Agent session / message / anchor pack
2. 结果页草稿与候选图片
3. Agent actions 与 works/gallery mutation request
4. 结果页 publish gate / preview
5. owner-only works 与 gallery card
6. runtime run / board / swap / drag / next-level contract
### 4.2 Rust shared contracts
`server-rs/crates/shared-contracts/src/` 新增:
1. `puzzle_agent.rs`
2. `puzzle_works.rs`
3. `puzzle_gallery.rs`
4. `puzzle_runtime.rs`
Rust DTO 只承载对前端公开的 HTTP contract不直接泄露 `module-puzzle` 内部实现细节。
## 5. Spacetime 表与 procedure
本轮保持“最小闭环优先”,作品与运行时仍以结构化字段 + `snapshot_json` 组合持久化,不额外拆出更多高耦合表。
### 5.1 `puzzle_agent_session`
字段:
1. `session_id`
2. `owner_user_id`
3. `seed_text`
4. `current_turn`
5. `progress_percent`
6. `stage`
7. `anchor_pack_json`
8. `draft_json`
9. `last_assistant_reply`
10. `published_profile_id`
11. `created_at`
12. `updated_at`
### 5.2 `puzzle_agent_message`
字段:
1. `message_id`
2. `session_id`
3. `role`
4. `kind`
5. `text`
6. `created_at`
### 5.3 `puzzle_work_profile`
字段:
1. `profile_id`
2. `owner_user_id`
3. `source_session_id`
4. `author_display_name`
5. `level_name`
6. `summary_text`
7. `theme_tags_json`
8. `cover_image_src`
9. `cover_asset_id`
10. `anchor_pack_json`
11. `publication_status`
12. `play_count`
13. `updated_at`
14. `published_at`
### 5.4 `puzzle_runtime_run`
字段:
1. `run_id`
2. `owner_user_id`
3. `entry_profile_id`
4. `current_profile_id`
5. `cleared_level_count`
6. `current_level_index`
7. `current_grid_size`
8. `played_profile_ids_json`
9. `previous_level_tags_json`
10. `snapshot_json`
11. `updated_at`
12. `created_at`
### 5.5 Procedure
本轮全部使用 procedure 同步返回快照,避免 Axum 再次读 private table
1. `create_puzzle_agent_session`
2. `get_puzzle_agent_session`
3. `submit_puzzle_agent_message`
4. `compile_puzzle_agent_draft`
5. `save_puzzle_generated_images`
6. `select_puzzle_cover_image`
7. `publish_puzzle_work`
8. `list_puzzle_works`
9. `get_puzzle_work_detail`
10. `update_puzzle_work`
11. `list_puzzle_gallery`
12. `get_puzzle_gallery_detail`
13. `start_puzzle_run`
14. `get_puzzle_run`
15. `swap_puzzle_pieces`
16. `drag_puzzle_piece_or_group`
17. `advance_puzzle_next_level`
## 6. 结果页图片生成策略
本轮后续已经接入 `api-server` 统一资产链路:拼图候选图由 `api-server` 调用图像服务生成,再以 OSS 对象作为持久化真值SpacetimeDB 只保存候选图 URL、assetId 与 prompt snapshot。
1. 每次生成 2 张候选图。
2. 候选图通过 `api-server` 写入 OSS兼容展示路径统一为 `/generated-puzzle-assets/...`,禁止再落到仓库 `public/` 目录。
3. Axum 把候选图 URL、assetId、prompt snapshot 回写到 Spacetime session draft。
4. 陶泥儿主在结果页选择其中 1 张作为正式图。
这样可以保证:
1. 结果页图片生成、重生、应用正式图完整可用。
2. 发布链有正式图片可校验。
3. 不再依赖本地 `public/` 占位目录,避免开发工作区混入运行时生成文件。
### 6.1 发布前编辑真相补充
结果页允许陶泥儿主在发布前直接编辑:
1. `关卡名`
2. `摘要`
3. `题材标签`
这 3 个字段不能只停留在前端临时态。
本轮冻结为:
1. `publish_puzzle_work` 允许直接携带 `levelName / summary / themeTags`
2. `spacetime-module` 在发布事务内先把这些字段覆盖回 session draft 真相
3. 覆盖后的 draft 再参与发布校验与 profile 持久化
这样可以避免额外新增一条“草稿轻量编辑 procedure”同时确保结果页编辑内容会真实进入广场作品与后续运行时 HUD。
## 7. 运行态规则冻结
### 7.1 难度推进
```ts
function resolvePuzzleGridSize(clearedLevelCount: number): 3 | 4 {
return clearedLevelCount >= 3 ? 4 : 3;
}
```
### 7.2 棋盘初始化
1. 根据正式图片与网格规格生成 `pieceId -> correctRow/correctCol`
2. 随机打乱到非完成态。
3. 生成初始 `mergedGroups = []`,再执行一次正确连接检查。
### 7.3 正确连接
若两个拼块在当前棋盘中四向相邻,且它们在原图上的正确位置也以同方向相邻,则视为正确连接。
所有正确连接链通过并查集合并为 `mergedGroup`
### 7.4 拖动与拆分
1. 单块拖到单块位置:执行交换。
2. 合并块拖到任意目标锚点:保持内部相对布局整体重排。
3. 若合并块整体平移后覆盖到多个单块,被覆盖单块必须与合并块腾出的原格子做一对一交换,禁止把多个单块回填到同一个源格。
4. 一对一交换必须满足:
- 每个被覆盖单块只移动一次。
- 每个被腾出的源格只接收一个被覆盖单块。
- 若腾出的源格数量与被覆盖单块数量不一致,本次拖动视为非法,不更新棋盘。
3. 单块拖到合并块占据位置:先拆分目标合并块,再执行交换,最后重算合并。
### 7.5 通关
当所有拼块回到正确位置,或全盘只剩一个覆盖全部拼块的合并组时,标记当前关卡 `cleared`
### 7.6 下一关推荐
固定公式:
```ts
finalScore = tagSimilarityScore * 0.7 + sameAuthorScore * 0.3;
```
标签相似度首版使用规范化标签集合的 Jaccard。
同分裁决顺序:
1. `tagSimilarityScore` 更高
2. 当前 run 未出现过
3. `play_count` 更低
4. `updated_at` 更近
## 8. 前端接入
### 8.1 平台入口
只改现有平台壳层:
1. 在创作类型弹层新增“拼图玩法”。
2. 新增拼图专属 stage不改 RPG runtime 主链。
### 8.2 组件目录
新增:
1. `src/components/puzzle-agent/`
2. `src/components/puzzle-result/`
3. `src/components/puzzle-gallery/`
4. `src/components/puzzle-runtime/`
### 8.3 服务目录
新增:
1. `src/services/puzzle-agent/`
2. `src/services/puzzle-works/`
3. `src/services/puzzle-gallery/`
4. `src/services/puzzle-runtime/`
本轮全部走 HTTP facade不引入新的前端 Spacetime 直连。
### 8.4 当前前端最小落地补充
当前实现固定走下面这条最小链路:
1. `PlatformEntryCreationTypeModal` 选择 `puzzle`
2. `PuzzleAgentWorkspace` 收束锚点并触发 `compile_puzzle_draft`
3. `PuzzleResultView` 编辑 `levelName / summary / themeTags`
4. 图片生成通过独立 `PuzzleImageStudioModal` 触发,不在结果页内联堆叠
5. 发布后跳转 `PuzzleGalleryDetailView`
6. 从详情进入 `PuzzleRuntimeShell`
创作中心作品展示冻结为:
1. 拼图作品也是平台作品,和其他创作作品共用同一套列表项样式。
2. 创作中心不再保留独立“拼图玩法作品模块”。
3. 拼图作品仅通过 `拼图` 标签与题材标签区分,不额外拆出第二块作品区。
4. 创作中心仍保留统一“新建作品”入口,由创建类型弹层继续分流到 RPG / 拼图玩法。
运行时前端表现冻结为:
1. 使用正式封面图按 `correctRow / correctCol` 做真实网格切片渲染
2. 点击两块时仅前端维护轻量选中态,真正交换以后端返回快照为准
3. 拖动统一采用 pointer 事件,兼顾网页端与移动端
4. 不在前端计算合并、拆分、通关与下一关推荐
## 9. 验收与检查
完成后至少执行:
1. `npm run check:encoding`
2. `npm run typecheck`
3. `npm run test`
4. `cargo check -p module-puzzle`
5. `cargo check -p shared-contracts`
6. `cargo check -p spacetime-module`
7. `npm run spacetime:generate`
8. `cargo check -p spacetime-client`
9. `cargo check -p api-server`
如果检查中发现拼图主链缺口,继续补齐;如果已经满足 PRD 主链和上述检查,不再追加额外玩法能力。
## 10. 2026-04-22 最终验收记录
本轮已按“最小完整闭环、禁止超出需求过度实现”完成拼图玩法主链落地,并补齐收尾检查。
### 10.1 已落地主链
1. 平台创作中心可选择 `puzzle` 玩法入口。
2. `PuzzleAgentWorkspace` 已接入 Agent-first 锚点收束与草稿编译。
3. `PuzzleResultView` 已支持最小结果页编辑与独立图片生成弹层。
4. 发布后作品可进入拼图广场与详情页。
5. `PuzzleRuntimeShell` 已按正式封面图真实切片渲染 `3x3 / 4x4` 关卡。
6. 交换、拖动、拆分、合并、通关、下一关推荐真相全部以后端快照为准。
### 10.2 本轮额外修复的验收阻塞
在最终验收阶段,补齐了与拼图主链无直接业务耦合、但会阻塞仓库整体检查的基线问题:
1. `typecheck` 基线类型不兼容。
2. `AccountModal` 测试 mock 字段落后于最新鉴权契约。
3. `customWorld` 存档归一化中场景连接方向未收敛到强类型。
4. 结果页生成资源在签名 URL 尚未返回时会短暂空白,已调整为先展示原路径占位,再异步替换签名读地址。
### 10.3 实际通过的检查
1. `npm run check:encoding`
2. `npm run typecheck`
3. `npm run test`
4. `cargo check -p module-puzzle`
5. `cargo check -p shared-contracts`
6. `cargo check -p spacetime-module`
7. `cargo check -p spacetime-client`
8. `cargo check -p api-server`
### 10.4 冻结说明
截至本次验收,拼图玩法已满足 PRD 要求的最小产品闭环;未继续扩展排行榜、提示、体力、异形拼块、倒计时、前端本地裁决等超出本轮需求的能力。
## 11. 2026-04-26 运行态机制补齐记录
本次按 PRD 第 9 章补齐拼图运行态的未完成机制,落点保持在 `server-rs/crates/module-puzzle` 领域层;前端本地兜底只同步表现和离线闭环,不改变后端真相源。
### 11.1 棋盘初始化
1. `build_initial_board_with_seed` 使用种子化洗牌生成初始棋盘,不再固定左移一格。
2. 正式 run 的种子由 `runId + profileId + levelIndex + gridSize` 派生;由于每次进入都会创建新的 `runId`,同一作品多次进入也会得到不同打乱样式。
3. 洗牌后若极端情况下仍为完成态,强制旋转一次,保证新关卡不是已完成局面。
4. 初始棋盘不得存在任何原图相邻块互相贴边;初始化会多次洗牌筛选,若极端情况下未命中,则使用确定性约束搜索兜底,避免开局出现局部连续结构。
5. `module-puzzle` 与本地 fallback 的测试都必须直接断言初始棋盘不存在原图相邻贴边对,不能只检查 `mergedGroups = []`
### 11.2 局部重算与合并
1. 交换后只把源格、目标格和四向邻格纳入重算范围。
2. 拖动后把源格、目标格、被移动合并块边界格、被拆分目标合并块格子纳入重算范围。
3. 对受影响范围内的旧合并组先拆回单块,再按正确四向相邻关系重新生成合并组;未受影响的旧合并组保留。
4. 每次生成快照时统一重编号合并组,避免保留组与新组出现重复 `groupId`
### 11.3 拖动与拆分
1. 单块拖到单块位置时执行交换。
2. 合并块拖动时保持内部相对布局,以被拖动块作为锚点整体平移。
3. 单块拖入合并块占据位置时,先拆分目标合并块,再完成本次交换,最后按受影响范围重新合并。
### 11.4 本次新增验证
1. `cargo test -p module-puzzle` 覆盖:每次 run 不同打乱、正确相邻自动合并、单块拖入合并块拆分目标组。
2. `npm run test -- src/services/puzzle-runtime/puzzleLocalRuntime.test.ts` 覆盖:本地兜底每次启动不同打乱、交换后正确相邻自动合并、通关后推进下一关。
3. `npm run check:encoding` 已通过,确认中文文档未被编码写坏。
## 12. 2026-04-26 二次运行态缺口补齐
本次继续按 PRD 第 9.12 与第 14.4 节收敛两个剩余缺口:
1. 通关判定必须同时支持“所有拼块回到正确位置”和“所有拼块汇成一个覆盖全盘的大合并块”。领域层以 `all_tiles_resolved` 作为唯一对外真相,但其计算来源必须包含这两个条件。
2. 运行态底部不再常驻玩法说明文字,只保留短状态反馈、错误反馈和下一关动作;点击/拖动规则不写成长期 UI 文案。
### 12.1 合并块可见性修正
用户反馈“正确连接的块自动合并没有看到”后,确认原实现只把已合并格子染成绿色,仍按单块逐格渲染,视觉上无法形成“合并块”。本次运行态画布改为:
1. 根据 `mergedGroups` 计算合并块外接矩形。
2. 原单格位置让位为透明占位。
3. 在棋盘上叠加一个跨格整体层,内部仍按原图切片拼接,但外边框、阴影和拖动事件都属于同一个合并块。
4. 合并块整体层以组内第一块作为拖动锚点,继续沿用后端/本地运行态的合并块拖动裁决。
### 12.2 拖动可用性修正
用户反馈“没有办法拖动拼图块”后,确认原交互只在 pointer move 超过阈值后记录 `dragging = true`,没有持续记录当前指针位置,也没有把拖动中的块做视觉平移;移动端还可能被浏览器默认触控手势抢占。修正如下:
1. `dragState` 持续记录 `currentX/currentY`,拖动中按指针偏移对单块或合并块做 `translate3d` 跟手反馈。
2. 棋盘与合并块交互层增加 `touch-none select-none`,避免移动端滚动、选中文本等默认行为打断拖动。
3. 松手后仍只提交 `pieceId + targetRow + targetCol`,最终交换、合并、拆分和通关继续以后端/本地运行态快照为准。
### 12.3 合并块外轮廓描边修正
用户反馈“合并的块的边界显示要描边自己的块的边界,不要搞一个正方形或者矩形的边界”后,移除合并块外接矩形 `ring` 层。运行态现在按合并组真实占据格逐格判断四向邻居:某一边没有同组合并格时才画该边描边,同组内部相邻边不画线。这样 L 形、长条或其他非矩形合并块只显示自身外轮廓,拖动热区仍只覆盖真实拼块格。
后续反馈要求合并块边界也要圆角后,外轮廓描边补充按四个角判断:只有相邻两条外露边同时存在的真实外轮廓角才应用圆角,内部拼接角保持直角且不显示分界线。
2026-05-01 追加修正:合并块的圆角不能继续依赖逐格 `border-radius` 叠加。运行时应根据合并组真实占据格提取一条整体 SVG 轮廓路径,外凸角和内凹角都通过同一条路径生成二次贝塞尔圆角;合并块图片层也必须用这条整体路径裁剪,避免 L 形、阶梯形凹口处仍露出直角图片像素。
### 12.4 第二关后打乱规则旁路修正
用户反馈“从第二关开始打乱规则像是完全相同”后,检查发现 `api-server` 的本地下一关 fallback 仍使用旧版 `build_local_puzzle_board` 固定左移一格,没有复用 `module-puzzle` 的种子化初始化规则。该路径会在图库/正式推荐不可用、由 API 临时构造下一关时触发。
修正后 `api-server` 本地下一关构造改为调用 `module_puzzle::build_initial_board_with_seed`,种子由 `runId + profileId + levelIndex + gridSize` 派生;因此第二关、第三关以及后续 fallback 关卡也会得到不同布局,并继续满足“开局没有原图相邻块贴边”的约束。