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:
@@ -1,553 +0,0 @@
|
||||
# 前端应迁后端逻辑审计(2026-04-21)
|
||||
|
||||
更新时间:`2026-04-21`
|
||||
|
||||
## 0. 审计目标
|
||||
|
||||
这份文档只回答一个问题:
|
||||
|
||||
**当前前端代码里,哪些逻辑已经明显越过“前端只做表现,Express 后端负责逻辑、数据与存储”的边界,应该继续迁到后端。**
|
||||
|
||||
本轮不改业务代码,只做:
|
||||
|
||||
1. 基于当前仓库状态给出高置信度候选点
|
||||
2. 标明代码证据
|
||||
3. 给出迁移优先级
|
||||
4. 说明迁移后前端应该保留什么、移走什么
|
||||
|
||||
---
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
结合当前代码与已有边界文档,前端里仍有 7 类逻辑应该继续后移:
|
||||
|
||||
1. **运行时快照前置写入与本地镜像解释**
|
||||
2. **鉴权 token 的浏览器本地真相**
|
||||
3. **平台浏览历史的本地真相与迁移状态**
|
||||
4. **NPC 待接委托“换单”仍由前端直接触发正式生成**
|
||||
5. **quest/runtime item 的双环境混合编排**
|
||||
6. **浏览器侧大型 AI orchestration 与 prompt/repair/fallback 主链**
|
||||
7. **NPC 招募对白之后的正式结算链路**
|
||||
|
||||
一句话判断:
|
||||
|
||||
**当前前端已经不是最早那种“大量主算”的状态,但仍然保留了运行时镜像、生成编排和部分正式真相。后端边界还需要再收一轮,前端才算真正退回表现层。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 审计依据
|
||||
|
||||
### 2.1 文档依据
|
||||
|
||||
1. `docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
|
||||
2. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
|
||||
3. `docs/technical/RUNTIME_STORY_BACKEND_BOUNDARY_MIGRATION_2026-04-19.md`
|
||||
4. `docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md`
|
||||
5. `docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md`
|
||||
|
||||
### 2.2 当前代码依据
|
||||
|
||||
1. `src/hooks/story/runtimeStoryCoordinator.ts`
|
||||
2. `src/services/apiClient.ts`
|
||||
3. `src/services/platformBrowseHistory.ts`
|
||||
4. `src/components/game-shell/PreGameSelectionFlow.tsx`
|
||||
5. `src/hooks/story/npcEncounterActions.ts`
|
||||
6. `src/services/questDirector.ts`
|
||||
7. `src/services/runtimeItemAiDirector.ts`
|
||||
8. `src/services/ai.ts`
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前高置信度应后移逻辑
|
||||
|
||||
## 3.0 本轮已完成后移
|
||||
|
||||
以下链路已在本轮或上一轮连续落地中完成后移,不再属于“仍残留在前端”的正式主链:
|
||||
|
||||
1. access token 浏览器本地真相
|
||||
2. browse history 本地真相
|
||||
3. runtime story 前置 `PUT /runtime/save/snapshot`
|
||||
4. NPC 待接委托 `replace / abandon / accept`
|
||||
5. custom world profile 正式浏览器入口
|
||||
6. `questDirector` / `runtimeItemAiDirector` 浏览器正式编排
|
||||
7. NPC 招募正式结算
|
||||
|
||||
其中 NPC 招募已从“前端本地改 companions / roster / npcStates / storyHistory”收回到后端 runtime action。
|
||||
|
||||
## 3.1 运行时快照前置写入仍在前端
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/hooks/story/runtimeStoryCoordinator.ts` 当前仍存在以下链路:
|
||||
|
||||
1. `syncRuntimeSnapshot(...)`
|
||||
2. `syncRuntimeSnapshot(...)` 内部直接调用 `putSaveSnapshot(...)`
|
||||
3. `loadServerRuntimeOptionCatalog(...)` 在请求 `getRuntimeStoryState(...)` 之前先写本地快照
|
||||
4. `resolveServerRuntimeChoice(...)` 在请求 `resolveRuntimeStoryAction(...)` 之前先写本地快照
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/hooks/story/runtimeStoryCoordinator.ts:21`
|
||||
2. `src/hooks/story/runtimeStoryCoordinator.ts:25`
|
||||
3. `src/hooks/story/runtimeStoryCoordinator.ts:36`
|
||||
4. `src/hooks/story/runtimeStoryCoordinator.ts:99`
|
||||
|
||||
### 当前问题
|
||||
|
||||
这意味着运行时正式动作发起前,前端仍会先落一份自己的快照真相,再去请求后端。
|
||||
|
||||
这条链的问题不是“有没有缓存”,而是:
|
||||
|
||||
1. 前端仍在承担正式提交前的状态镜像
|
||||
2. 快照解释权没有完全收回到后端
|
||||
3. 运行时主链仍处于“本地镜像 + 服务端会话”并存状态
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. 运行时快照写入
|
||||
2. 快照版本解释
|
||||
3. 动作提交前的状态一致性校验
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 当前展示用的 view model
|
||||
2. 可选的只读恢复缓存
|
||||
3. 纯表现态的 loading / transition / animation state
|
||||
|
||||
### 优先级
|
||||
|
||||
`P0`
|
||||
|
||||
---
|
||||
|
||||
## 3.2 鉴权 token 仍由前端 localStorage 持有真相
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/services/apiClient.ts` 当前仍直接访问 `window.localStorage` 保存 access token:
|
||||
|
||||
1. `getStoredAccessToken()`
|
||||
2. `setStoredAccessToken(...)`
|
||||
3. `clearStoredAccessToken(...)`
|
||||
4. `withAuthorizationHeaders(...)` 直接从本地 token 组装请求头
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/services/apiClient.ts:333`
|
||||
2. `src/services/apiClient.ts:341`
|
||||
3. `src/services/apiClient.ts:362`
|
||||
4. `src/services/apiClient.ts:382`
|
||||
|
||||
### 当前问题
|
||||
|
||||
第三批清理已经收掉了“自动登录用户名/密码”本地真相,但 access token 仍然由浏览器长期持有。
|
||||
|
||||
这在当前项目边界下仍有两个问题:
|
||||
|
||||
1. 正式鉴权真相仍没有完全收回后端 session 边界
|
||||
2. 前端 SDK 仍然负担 token 生命周期的关键部分
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. session / refresh / cookie 真相
|
||||
2. 鉴权状态续期
|
||||
3. token 更新与失效策略
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 当前是否已登录的展示态
|
||||
2. 统一的请求封装
|
||||
3. 401 后的 UI 响应
|
||||
|
||||
### 优先级
|
||||
|
||||
`P0`
|
||||
|
||||
---
|
||||
|
||||
## 3.3 平台浏览历史仍是“前端本地历史 + 后端回填”的双真相
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/services/platformBrowseHistory.ts` 当前仍维护一整套本地历史真相:
|
||||
|
||||
1. `readPlatformBrowseHistory(...)`
|
||||
2. `writePlatformBrowseHistory(...)`
|
||||
3. `hasPendingPlatformBrowseHistoryMigration(...)`
|
||||
4. `markPlatformBrowseHistoryMigrated(...)`
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/services/platformBrowseHistory.ts:77`
|
||||
2. `src/services/platformBrowseHistory.ts:103`
|
||||
3. `src/services/platformBrowseHistory.ts:151`
|
||||
4. `src/services/platformBrowseHistory.ts:164`
|
||||
|
||||
`src/components/game-shell/PreGameSelectionFlow.tsx` 当前仍显式做:
|
||||
|
||||
1. 先 `writePlatformBrowseHistory(...)`
|
||||
2. 再调用 `upsertProfileBrowseHistory(...)`
|
||||
3. 同步成功后 `markPlatformBrowseHistoryMigrated(...)`
|
||||
4. 启动阶段读取 `readPlatformBrowseHistory(...)`
|
||||
5. 根据 `hasPendingPlatformBrowseHistoryMigration(...)` 决定是否补同步
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/components/game-shell/PreGameSelectionFlow.tsx:383`
|
||||
2. `src/components/game-shell/PreGameSelectionFlow.tsx:392`
|
||||
3. `src/components/game-shell/PreGameSelectionFlow.tsx:394`
|
||||
4. `src/components/game-shell/PreGameSelectionFlow.tsx:433`
|
||||
5. `src/components/game-shell/PreGameSelectionFlow.tsx:466`
|
||||
|
||||
### 当前问题
|
||||
|
||||
这条链已经不是单纯缓存,而是:
|
||||
|
||||
1. 本地历史存储
|
||||
2. 本地同步标记
|
||||
3. 后端历史持久化
|
||||
|
||||
三套状态同时存在。
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. 浏览历史唯一持久化真相
|
||||
2. 历史去重、排序、截断
|
||||
3. 迁移完成标记
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 展示缓存
|
||||
2. 弱网下的临时 optimistic UI
|
||||
3. 刷新后重新拉取远端结果
|
||||
|
||||
### 优先级
|
||||
|
||||
`P1`
|
||||
|
||||
---
|
||||
|
||||
## 3.4 NPC 待接委托“换单”仍由前端直接发起正式生成
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/hooks/story/npcEncounterActions.ts` 当前仍保留:
|
||||
|
||||
1. `replacePendingNpcQuestOffer = async () => { ... }`
|
||||
2. 内部直接调用 `generateQuestForNpcEncounter(...)`
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/hooks/story/npcEncounterActions.ts:1561`
|
||||
2. `src/hooks/story/npcEncounterActions.ts:1595`
|
||||
|
||||
### 当前问题
|
||||
|
||||
聊天后是否挂出待接委托已经后移,但“换一份委托”这条分支仍然是:
|
||||
|
||||
1. 前端组装上下文
|
||||
2. 前端决定调用生成
|
||||
3. 前端直接把结果写回当前 story UI
|
||||
|
||||
这仍属于正式运行时任务编排没有收干净。
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. NPC 待接委托换单决策
|
||||
2. 是否允许换单
|
||||
3. 换单后的任务草案生成
|
||||
4. 对应聊天态快照回填
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 点击“换一份委托”
|
||||
2. loading / error 展示
|
||||
3. 消费后端返回的新 pending quest offer
|
||||
|
||||
### 优先级
|
||||
|
||||
`P0`
|
||||
|
||||
---
|
||||
|
||||
## 3.5 questDirector 仍是前端 SDK 与生成编排混合体
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/services/questDirector.ts` 当前同时承担:
|
||||
|
||||
1. `generateQuestForNpcEncounter(...)`
|
||||
2. 浏览器路径 `requestJson('/api/runtime/quests/generate')`
|
||||
3. 非浏览器路径 `requestChatMessageContent(...)`
|
||||
4. 本地 `compileQuestIntentToQuest(...)` fallback
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/services/questDirector.ts:213`
|
||||
2. `src/services/questDirector.ts:242`
|
||||
3. `src/services/questDirector.ts:267`
|
||||
4. `src/services/questDirector.ts:256`
|
||||
5. `src/services/questDirector.ts:281`
|
||||
6. `src/services/questDirector.ts:293`
|
||||
|
||||
### 当前问题
|
||||
|
||||
这类文件虽然浏览器正式路径已经优先走后端,但职责仍混在一起:
|
||||
|
||||
1. 前端 SDK
|
||||
2. Quest prompt 编排
|
||||
3. Quest intent 解析
|
||||
4. deterministic fallback compile
|
||||
|
||||
这会导致边界长期模糊,也让前端仍像“半个服务端”。
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. quest intent 生成
|
||||
2. prompt 组装
|
||||
3. JSON 解析
|
||||
4. fallback compile
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. `requestGenerateQuest(...)` 这类轻量 SDK
|
||||
2. 请求参数组装
|
||||
3. 结果消费
|
||||
|
||||
### 优先级
|
||||
|
||||
`P1`
|
||||
|
||||
---
|
||||
|
||||
## 3.6 runtimeItemAiDirector 仍是前端 SDK 与意图生成混合体
|
||||
|
||||
### 代码证据
|
||||
|
||||
`src/services/runtimeItemAiDirector.ts` 当前同时承担:
|
||||
|
||||
1. `generateRuntimeItemAiIntents(...)`
|
||||
2. 浏览器路径 `requestJson('/api/runtime/items/runtime-intent')`
|
||||
3. 非浏览器路径 `requestChatMessageContent(...)`
|
||||
4. 本地 `buildRuntimeItemAiIntent(...)` fallback
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/services/runtimeItemAiDirector.ts:84`
|
||||
2. `src/services/runtimeItemAiDirector.ts:94`
|
||||
3. `src/services/runtimeItemAiDirector.ts:118`
|
||||
|
||||
### 当前问题
|
||||
|
||||
它和 `questDirector` 是同类问题:
|
||||
|
||||
1. 正式浏览器路径已经走后端
|
||||
2. 但前端文件仍然承担完整生成逻辑认知
|
||||
3. 文件职责仍然是双环境混合
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. runtime item intent prompt
|
||||
2. 模型调用
|
||||
3. 结果解析与 fallback
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 轻量请求 SDK
|
||||
2. 结果到 UI 的映射
|
||||
|
||||
### 优先级
|
||||
|
||||
`P1`
|
||||
|
||||
---
|
||||
|
||||
## 3.7 `src/services/ai.ts` 仍是浏览器侧正式 AI orchestration 热点
|
||||
|
||||
### 代码证据
|
||||
|
||||
当前 `src/services/ai.ts` 仍直接承担以下正式链路:
|
||||
|
||||
1. `requestChatMessageContent(...)`
|
||||
2. `requestPlainTextCompletionFromClient(...)`
|
||||
3. `streamPlainTextCompletionFromClient(...)`
|
||||
4. `generateCustomWorldProfile(...)`
|
||||
5. `generateInitialStory(...)`
|
||||
6. `generateNextStep(...)`
|
||||
7. `streamNpcChatDialogue(...)`
|
||||
8. `streamNpcRecruitDialogue(...)`
|
||||
|
||||
对应位置:
|
||||
|
||||
1. `src/services/ai.ts:1732`
|
||||
2. `src/services/ai.ts:1868`
|
||||
3. `src/services/ai.ts:2038`
|
||||
4. `src/services/ai.ts:2339`
|
||||
5. `src/services/ai.ts:2447`
|
||||
6. `src/services/ai.ts:2487`
|
||||
7. `src/services/ai.ts:2529`
|
||||
8. `src/services/ai.ts:2570`
|
||||
|
||||
并且文件内仍保留:
|
||||
|
||||
1. JSON repair
|
||||
2. prompt 组装
|
||||
3. response normalize
|
||||
4. fallback/offline 响应
|
||||
5. 角色聊天建议与摘要生成
|
||||
|
||||
### 当前问题
|
||||
|
||||
这说明浏览器端并不只是“请求一个后端接口”,而是还在承担:
|
||||
|
||||
1. prompt source
|
||||
2. 生成策略
|
||||
3. 错误修复
|
||||
4. fallback 编排
|
||||
5. 多类业务场景的正式 AI 出口
|
||||
|
||||
这与“前端只做表现”存在明确冲突。
|
||||
|
||||
### 迁移建议
|
||||
|
||||
后端继续承接:
|
||||
|
||||
1. story / npc / recruit / custom-world 的 prompt 编排
|
||||
2. JSON repair
|
||||
3. fallback 策略
|
||||
4. streaming orchestration
|
||||
5. 模型调用与日志
|
||||
|
||||
前端只保留:
|
||||
|
||||
1. 轻量 AI SDK
|
||||
2. SSE 文本流展示
|
||||
3. UI fallback 呈现
|
||||
|
||||
### 优先级
|
||||
|
||||
`P0`
|
||||
|
||||
---
|
||||
|
||||
## 3.8 NPC 招募对白之后的正式结算链路已完成后移
|
||||
|
||||
### 本轮前状态
|
||||
|
||||
迁移前,`src/hooks/story/npcInteraction.ts` 中的 `buildRecruitmentOutcome / executeRecruitment / startRecruitmentSequence` 仍在前端本地正式结算:
|
||||
|
||||
1. 改 `npcStates`
|
||||
2. 改 `companions`
|
||||
3. 改 `roster`
|
||||
4. 清 `currentEncounter / inBattle / sceneHostileNpcs`
|
||||
5. 直接写 `storyHistory`
|
||||
6. 再触发后续剧情推进
|
||||
|
||||
这与“前端只做表现,所有正式逻辑、数据都放到 Express 后端”直接冲突。
|
||||
|
||||
### 本轮后状态
|
||||
|
||||
本轮已完成:
|
||||
|
||||
1. `server-node/src/modules/story/runtimeSession.ts`
|
||||
- 正式承接完整 `companions`
|
||||
- 正式承接 `roster`
|
||||
2. `server-node/src/modules/npc/npcInteractionService.ts`
|
||||
- `npc_recruit` 已支持正常入队
|
||||
- `npc_recruit` 已支持满员换队招募
|
||||
3. `src/hooks/story/npcInteraction.ts`
|
||||
- 前端只保留招募对白流式展示
|
||||
- 正式招募结算改为调用后端 runtime action
|
||||
|
||||
### 当前判断
|
||||
|
||||
这一项已不再属于前端残留正式逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 4. 可以暂时保留在前端的部分
|
||||
|
||||
下面这些内容即使和上述模块同文件出现,也不属于必须后移的对象:
|
||||
|
||||
1. 面板开关、loading、error、streaming 文本展示
|
||||
2. 动画时间线、过场状态、临时 UI 回显
|
||||
3. 表单草稿、筛选词、排序选项
|
||||
4. 只影响表现、不影响正式真相的 view model 拼接
|
||||
|
||||
迁移时要注意:
|
||||
|
||||
**不是把所有前端代码都往后端搬,而是把“正式状态解释、规则裁决、生成编排、持久化真相”搬走。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐迁移顺序
|
||||
|
||||
## 5.1 第一阶段
|
||||
|
||||
先收最危险的正式真相:
|
||||
|
||||
1. `runtimeStoryCoordinator.ts`
|
||||
2. `apiClient.ts`
|
||||
3. `npcEncounterActions.ts` 里的 quest replace 分支
|
||||
|
||||
原因:
|
||||
|
||||
1. 这三处最直接影响运行时真相和动作主链
|
||||
2. 不先收这些,前端仍然不是纯表现层
|
||||
|
||||
## 5.2 第二阶段
|
||||
|
||||
再拆双环境混合服务:
|
||||
|
||||
1. `questDirector.ts`
|
||||
2. `runtimeItemAiDirector.ts`
|
||||
3. `platformBrowseHistory.ts`
|
||||
|
||||
原因:
|
||||
|
||||
1. 这几处已经有后端承接基础
|
||||
2. 迁移成本相对可控
|
||||
|
||||
## 5.3 第三阶段
|
||||
|
||||
最后继续压缩浏览器 AI orchestration:
|
||||
|
||||
1. `src/services/ai.ts`
|
||||
2. 相关 prompt builder / repair helper / offline fallback
|
||||
|
||||
原因:
|
||||
|
||||
1. 这部分体量大
|
||||
2. 链路多
|
||||
3. 更适合在前两阶段把 contract 稳住后集中拆
|
||||
|
||||
---
|
||||
|
||||
## 6. 建议产出物
|
||||
|
||||
如果后续按这份文档继续落地,建议每一批都至少同步产出:
|
||||
|
||||
1. 一份落地文档,说明迁移了哪条链
|
||||
2. 一组 contract/route 变更说明
|
||||
3. 一组前端 SDK 收缩说明
|
||||
4. 一组防回退测试
|
||||
|
||||
---
|
||||
|
||||
## 7. 一句话结论
|
||||
|
||||
当前前端最需要继续后移的,不是零散小工具,而是:
|
||||
|
||||
**运行时快照前置写入、鉴权 token、本地浏览历史真相、NPC 委托换单、quest/runtime item 双环境混合编排,以及 `src/services/ai.ts` 里仍然留在浏览器的正式 AI orchestration。**
|
||||
Reference in New Issue
Block a user