Merge branch 'codex/dev' into codex/backend-rewrite-spacetimedb

# Conflicts:
#	docs/technical/README.md
#	server-node/src/modules/assets/qwenSpriteRoutes.ts
#	src/components/CustomWorldResultView.test.tsx
#	src/components/CustomWorldResultView.tsx
#	src/components/custom-world-agent/CustomWorldAgentDraftDetailPanel.tsx
#	src/components/game-shell/PreGameSelectionFlow.agent.interaction.test.tsx
#	src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModalImpl.tsx
#	src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx
#	src/components/rpg-entry/RpgEntryCharacterSelectView.tsx
#	src/components/rpg-entry/RpgEntryHomeView.tsx
#	src/services/apiClient.ts
#	src/tools/QwenSpriteSheetTool.tsx
This commit is contained in:
2026-04-21 20:16:01 +08:00
477 changed files with 38047 additions and 26570 deletions

View File

@@ -251,8 +251,8 @@ custom-world-library
| `anchorContent / creatorIntent / anchorPack / lockState` 被直接塞进 legacy profile | 创作态元数据 | 会跟随自动保存一起写进作品库 profile但 runtime 并不以这些字段为正式运行时输入 | 当前更像创作态元数据泄漏进运行时 profile |
| `qualityFindings` | session / contract 字段 | contract、session store、测试里存在但没形成生成、渲染、发布阻断闭环 | 当前主链悬空 |
| `checkpoints` | session 字段 | session store 会记录,但主工作区和结果页没有真实展示入口 | 当前主链悬空 |
| `suggestedActions` | session 字段 | session 会生成,但工作区没有 `QuickActions` 面板 | 当前主链悬空 |
| `pendingClarifications` | session 字段 | session 有数据,但澄清面板未接入主工作区 | 当前主链悬空 |
| `suggestedActions` | session 字段 | session 会生成,但当前正式工作区没有对应消费面;旧 `QuickActions` 面板已在 `2026-04-21` 判定退出当前版本主链并物理删除 | 当前主链悬空 |
| `pendingClarifications` | session 字段 | session 有数据,但当前正式工作区没有对应消费面;旧澄清面板已在 `2026-04-21` 判定退出当前版本主链并物理删除 | 当前主链悬空 |
| `operations` 历史 | session 字段 | 主工作区只展示当前 `activeOperation` 横幅,不展示完整历史 | 当前主链弱消费 |
| `roleAssetSummaryLabel / cover* / counts` 等 works 字段 | works 聚合字段 | 后端能返回,但主平台 create tab 没走 `works` 入口 | 当前主链弱消费 |
@@ -266,11 +266,11 @@ custom-world-library
| --- | --- | --- |
| 结果页直接生成 playable/story/landmark | `CustomWorldResultView.tsx` 仍可直接调用 AI 生成 | 与 Agent 对象精修链重复,且不会同步回 session |
| 结果页直接编辑 `CustomWorldProfile` | `CustomWorldEntityEditorModal` 仍挂在结果页 | 把结果页继续维持成旧编辑器,而不是 Agent 流程的收口层 |
| 旧 `custom-world/sessions` 世界生成 | `aiService.generateCustomWorldProfile()` 仍完整可用 | 与 Agent 八锚点世界创建重复 |
| 旧 `custom-world/sessions` 世界生成 | `2026-04-20` 审计时仍完整可用;`2026-04-21` 已完成物理删除 | 与 Agent 八锚点世界创建重复;当前遗留问题已转为文档口径清理 |
| 作品库 `publish/unpublish` 与 Agent `publish_world` | 两套“发布”概念并行 | 一套作用于 library profile一套想作用于 Agent session但后者还未打通 |
| 结果页自动保存 | `generatedCustomWorldProfile` 变化时自动 `upsertCustomWorldProfile()` | 让“草稿保存”“作品库存档”“正式发布”语义混在一起 |
## 7.2 冗余或未接线组件
## 7.2 冗余或已退出当前版本主链的组件
`src/components/custom-world-agent/CustomWorldAgentWorkspace.tsx` 当前只真正接了:
@@ -280,7 +280,7 @@ custom-world-library
4. `CustomWorldAgentThread`
5. `CustomWorldAgentComposer`
但同目录下已经存在且主工作区未接线组件包括
`2026-04-21` 清理前,同目录下还存在一组未接线组件:
1. `CustomWorldAgentLockBar.tsx`
2. `CustomWorldAgentDraftDrawer.tsx`
@@ -291,6 +291,25 @@ custom-world-library
7. `CustomWorldAgentClarificationPanel.tsx`
8. `CustomWorldGenerateEntityModal.tsx`
其中:
1. `CustomWorldAgentDraftDrawer.tsx` 已在批次 A 清理中删除
2. `CustomWorldAgentLockBar.tsx`
3. `CustomWorldAgentDraftDetailPanel.tsx`
4. `CustomWorldAgentQuickActions.tsx`
5. `CustomWorldAgentSummaryPanel.tsx`
6. `CustomWorldAgentIntentSummaryPanel.tsx`
7. `CustomWorldAgentClarificationPanel.tsx`
8. `CustomWorldGenerateEntityModal.tsx`
已在批次 D 清理中判定为退出当前版本主链,并完成物理删除。
因此这里的审计结论需要更新为:
1. 它们不再属于“待接线组件”
2. 它们属于已确认退场的旧副面板链
3. 当前版本如果还要补 `suggestedActions / pendingClarifications / draftCards` 的消费面,应基于新的主链设计重新定义,而不是默认把旧面板接回来
另外,`src/components/custom-world-home/CustomWorldCreationHub.tsx` 也已存在,但平台 `create` tab 还没有把它接成主入口。
---
@@ -345,16 +364,16 @@ Agent session
2. 只有 `publish_world` 成功后,才产出正式 `CustomWorldProfile` 并允许主入口进入世界。
3. `qualityFindings / blocker` 必须在 foundation draft 完成、资产写回后、publish 前持续重跑。
## P1决定旧 world session 流程的命运
## P1继续做旧 world session 链的文档收口
当前最容易继续制造重复复杂度的是旧 `custom-world/sessions` 链。
`2026-04-21` 更新:
建议二选一:
`custom-world/sessions` 链已经完成物理删除。
1. 明确保留为“快速世界生成兼容模式”,但从主入口降级。
2. 明确进入淘汰路径,逐步下线 `generateCustomWorldProfile()` 这条旧链。
因此这里不再是“保留还是淘汰”的开放问题,而是:
不建议继续让它和 Agent 八锚点链同时作为主入口长期并存。
1. 继续清理由这条旧链残留在审计、PRD、知识图谱中的过时口径
2. 把当前正式主链与仍保留的兼容层边界写清楚
## P1把 works 创作中心接回主平台
@@ -365,16 +384,23 @@ Agent session
3. 已发布 profile 通过“进入世界”或“查看详情”进入。
4. `myEntries` 退回为作品库子集,而不是 create tab 的唯一数据源。
## P1补齐 Agent workspace 的最小闭环
## P1为悬空 session 字段重新定义最小闭环
建议优先接上
`2026-04-21` 更新
1. `CustomWorldAgentQuickActions`
2. `CustomWorldAgentDraftDrawer`
3. `CustomWorldAgentDraftDetailPanel`
4. `CustomWorldAgentClarificationPanel`
原文这里建议把旧 `QuickActions / DraftDrawer / DraftDetailPanel / ClarificationPanel` 接回主工作区。
如果这几个面板不接上,`suggestedActions / pendingClarifications / draftCards` 这些 session 字段会长期处于悬空状态。
但这些旧副面板已经在当前版本收口判断中被明确认定为:
1. 不属于现行主链
2. 不再作为当前版本默认待落地项
3. 已完成物理删除
因此当前更准确的建议应该是:
1. 如果 `suggestedActions / pendingClarifications / draftCards` 仍要进入正式主流程,需要先重新定义符合当前极简工作区的消费方式
2. 不应再以“把旧副面板接回来”作为默认方案
3. 在没有新主链设计前,这些字段继续标记为“主链悬空”
## P2等主链收口后再清桥接字段

View File

@@ -0,0 +1,141 @@
# 工程死分支清理执行记录 A2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应:
- `docs/planning/ENGINEERING_DEAD_CODE_AND_HIDDEN_BRANCH_CLEANUP_PLAN_2026-04-21.md`
- 其中的 `P0 + 批次 A`
本批次只做一件事:
**先清理高置信度、低耦合、无正式入口的小型孤岛与残留壳子。**
这批对象有一个共同特征:
1. 当前没有正式运行时引用
2. 没有当前主链计划要接回
3. 删除后有明确替代路径,或者本身只是历史占位
因此这批次不碰运行时真相链、不碰鉴权链、不碰任务物品主链,只先做低风险去噪。
---
## 1. 本批次已处理对象
## 1.1 已删除文件
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 | 验证口径 |
| --- | --- | --- | --- | --- |
| `src/services/customWorldPresentation.stub.ts` | 无引用占位 stub | 文件本身就是占位实现,且正式逻辑已由 `customWorldPresentation.ts` 承接 | `src/services/customWorldPresentation.ts` | 符号级检索确认正式调用方都指向正式实现 |
| `src/services/typewriter.ts` | 无引用 helper 残留 | 独立 helper 已失效,正式链路已在 `storyPresentation.ts` / `storyRenderingHelpers.ts` 等处内联或迁移 | `src/hooks/story/storyPresentation.ts``src/hooks/story/storyRenderingHelpers.ts` | `getTypewriterDelay` 调用点未指向该文件 |
| `src/prompts/customWorldOrchestratorPrompts.ts` | 前端孤岛 prompt 壳 | 当前无正式 import正式主编排 prompt 已收口到后端 prompt 目录,前端 `ai.ts` 也保留自己的现行实现 | `server-node/src/prompts/customWorldOrchestratorPrompts.ts``src/services/ai.ts` | 全仓检索仅剩文档引用,无代码消费 |
| `src/prompts/storyOrchestratorPrompts.ts` | 前端孤岛 prompt 壳 | 当前无正式 import剧情语言修复 prompt 已由后端 prompt 目录承接,前端当前执行路径不依赖该文件 | `server-node/src/prompts/storyOrchestratorPrompts.ts``src/services/ai.ts` | 全仓检索仅剩文档引用,无代码消费 |
| `src/components/custom-world-home/CustomWorldCreationLauncherModal.tsx` | 无入口 UI 壳层 | 最近两轮工程审计都确认无运行时引用,当前平台主流程未接这条入口 | 当前平台正式入口链 | 文件级检索确认无组件 import |
| `src/components/custom-world-agent/CustomWorldAgentLauncherModal.tsx` | 无入口 UI 壳层 | Agent 创作主流程已切到当前工作区链路,这个旧 modal 没有接线价值 | 当前 Agent 工作区主链 | 文件级检索确认无组件 import |
| `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx` | 无入口 UI 壳层 | 只有孤立 UI 实现,没有正式调用链,也不在当前结果页 / 工作区主链中 | 当前 Agent 工作区与结果页正式链 | 文件级检索确认无组件 import |
---
## 2. 本批次为什么先删这 7 个
这批文件适合先处理,不是因为它们最大,而是因为它们最清晰:
1. **没有正式入口。**
本轮检索没有发现主工程 import。
2. **删除后不会形成职责空洞。**
要么已有正式替代路径,要么本身只是历史占位。
3. **不会误伤当前重点链路。**
这批不涉及运行时快照、鉴权、任务、物品、AI 正式编排主链。
4. **可以最快降低目录噪音。**
先把真假并存的壳子删掉,后面做批次 B/C/D 时判断成本会更低。
---
## 3. 本批次暂不处理对象
以下对象虽然已进入首轮台账,但本批次暂不删除:
1. `src/components/GameShell.tsx`
2. `src/components/custom-world-home/CustomWorldCreationHub.tsx`
3. `src/hooks/story/storyBootstrap.ts`
4. `src/hooks/useEquipmentFlow.ts`
5. `src/hooks/useForgeFlow.ts`
6. `src/hooks/useInventoryFlow.ts`
7. `src/data/buildTagSimilarity.generated.ts`
暂缓原因分别是:
1. 仍属于旧主流程 / 旧 flow 级别对象,删除前要先核对更多历史依赖和替代路径
2. 部分对象仍有测试引用或更大的上下文耦合
3. `buildTagSimilarity.generated.ts` 虽无正式业务 import但属于生成产物处理前还要确认脚本链与文档链
这批对象更适合进入:
1. `批次 B旧 flow / 旧 shell / 旧 hook`
2. 或独立的数据产物复核批次
---
## 4. 本批次同步更新的文档
本批次除了删文件,还同步做了文档回填:
1. 新增本执行记录,说明本批删了什么、为什么删、哪些对象暂缓
2. 更新 `docs/audits/engineering/README.md`,把这份执行记录加入当前审计入口
这样做的目的,是避免再次出现:
1. 代码删了
2. 但审计入口还是旧状态
3. 后续开发又从旧清单里重复判断一遍
---
## 5. 验证方式
本批次验证采用两层口径:
## 5.1 删除前验证
1. 文件级检索确认无正式 import
2. 符号级检索确认关键导出没有被主链消费
3. 结合 `2026-04-20` 工程审计交叉确认这些对象已被标记为高置信度孤岛
## 5.2 删除后验证
建议至少执行:
1. `npm run check:encoding`
2. `npm run build`
说明:
- 当前仓库已知 `typecheck``lint` 仍处于红线阶段,因此本批不把它们作为“由本批引入的新失败”判断口径
- 本批主要验证目标是:删除小残留后,不产生新的导入断裂和构建断裂
---
## 6. 本批次结果判断
本批次完成后,工程至少获得了 3 个直接收益:
1. `src/prompts/``src/services/``src/components/custom-world-*` 中少了一批无入口孤岛
2. 当前目录里“看起来像正式入口,其实已经废弃”的误导性对象减少
3. 后续可以把精力集中到真正高价值的批次 B/C/D而不是继续被小残留分散判断成本
---
## 7. 下一批建议
建议严格按计划继续往下推进:
1. 批次 B`GameShell``storyBootstrap``useEquipmentFlow``useForgeFlow``useInventoryFlow`
2. 批次 C`runtimeStoryCoordinator``runtimeStoryService``apiClient`
3. 批次 D`npcEncounterActions``questDirector``runtimeItemAiDirector``ai.ts`
一句话总结本批次:
**先把最确定的死分支和占位壳子清掉,让主工程少一些假入口、假主源、假能力,再进入更重的主链收口。**

View File

@@ -0,0 +1,145 @@
# 工程死分支清理执行记录 B2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应清洗计划中的:
- `批次 B旧 flow / 旧 shell / 旧 hook`
本批次聚焦的不是小型 stub而是
**已经退出正式主流程、但仍占着高辨识度命名和旧职责心智的壳层与流程 Hook。**
这类文件如果继续留在仓库里,问题比小 helper 更大,因为它们会持续制造误判:
1. 新人会以为它们还是正式入口
2. 后续开发会误判“应该往这里接逻辑”
3. review 时会多出一层“旧主链是不是还活着”的判断成本
---
## 1. 本批次已处理对象
## 1.1 已删除文件
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 | 验证口径 |
| --- | --- | --- | --- | --- |
| `src/components/GameShell.tsx` | 旧主流程壳层残留 | 当前正式壳层已由 `src/components/game-shell/GameShellRuntime.tsx` 承接,旧文件无正式 import | `src/components/game-shell/GameShellRuntime.tsx``src/hooks/useGameShellRuntime.ts` | 全仓检索未发现对旧 `GameShell` 组件的正式消费 |
| `src/hooks/story/storyBootstrap.ts` | 旧启动流程 Hook 残留 | 当前主剧情启动链已不再调用该 Hook继续保留只会误导人以为它还是故事初始化入口 | 当前 story runtime / coordinator 链 | 全仓检索未发现 `useStoryBootstrap` 消费方 |
| `src/hooks/useEquipmentFlow.ts` | 旧装备流程 Hook 残留 | 当前正式背包与装备链未消费该 Hook属于旧流程实现残留 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
| `src/hooks/useForgeFlow.ts` | 旧锻造流程 Hook 残留 | 当前正式锻造入口未通过该 Hook 进入主链,保留会制造旧流程错觉 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
| `src/hooks/useInventoryFlow.ts` | 旧背包使用流程 Hook 残留 | 当前主流程未消费该 Hook属于旧状态推进实现残留 | 当前 inventory / runtime 正式链 | 符号级检索仅命中定义文件自身 |
---
## 2. 为什么这批要紧跟批次 A 处理
批次 A 清掉的是“小型假入口”。
批次 B 清掉的是“高辨识度旧主链”。
这批必须紧跟着做,原因是:
1. 它们虽然比 stub 更大,但引用关系同样清楚
2. 它们的误导性比小残留更强
3. 不先处理这批,后面做批次 C/D 时,很容易继续有人拿旧 flow Hook 当候选接线点
一句话讲:
**批次 A 是去噪,批次 B 是拔掉旧路牌。**
---
## 3. 本批次删除后的结构变化
本批次完成后,仓库里的流程心智会更清楚:
1. 游戏壳层正式入口继续收敛到 `src/components/game-shell/**`
2.`GameShell.tsx` 不再和 `GameShellRuntime.tsx` 并存
3. 旧的装备 / 锻造 / 背包单独 flow Hook 不再伪装成还在生效的正式实现
4.`storyBootstrap` 不再和当前 story runtime 链并存
这会直接减少两类误判:
1. “是不是还有旧主流程没迁完”
2. “我是不是应该把新逻辑继续补进这些旧 Hook”
---
## 4. 本批次暂不处理对象
虽然批次 B 已经处理了旧 shell / old flow / old bootstrap但以下对象仍暂缓
1. `src/components/custom-world-home/CustomWorldCreationHub.tsx`
2. `src/data/buildTagSimilarity.generated.ts`
3. 批次 C 的运行时真相链:
- `src/hooks/story/runtimeStoryCoordinator.ts`
- `src/services/runtimeStoryService.ts`
- `src/services/apiClient.ts`
4. 批次 D 的混合执行层:
- `src/hooks/story/npcEncounterActions.ts`
- `src/services/questDirector.ts`
- `src/services/runtimeItemAiDirector.ts`
- `src/services/ai.ts`
暂缓原因很明确:
1. 这些对象要么仍在当前正式链上
2. 要么涉及运行时真相与鉴权边界
3. 不能按“无引用旧壳”同一口径直接删除
---
## 5. 本批次验证方式
## 5.1 删除前验证
1. 全仓检索 `GameShell` 旧组件消费方,确认当前正式壳层已切到 `game-shell/` 目录
2. 全仓检索 `useStoryBootstrap`
3. 全仓检索旧装备 / 锻造 / 背包 flow Hook 导出的 handler 名称
4. 交叉确认当前正式主链入口已存在替代实现
## 5.2 删除后验证
建议至少执行:
1. `npm run check:encoding`
2. `npm run build`
如果这两项通过,说明:
1. 删除没有引入新的导入断裂
2. 主工程构建链仍然成立
---
## 6. 本批次结果判断
本批次完成后,工程获得的直接收益是:
1. 旧主流程壳层不再和现行壳层并存
2. 旧流程 Hook 不再占据 `src/hooks/` 的主路径注意力
3. 当前正式入口和历史残留的边界更清楚
4. 后续开发更不容易把新逻辑接回旧流程壳子
---
## 7. 下一批建议
建议下一步进入真正有结构价值的收口:
1. `批次 C运行时真相收口`
- `runtimeStoryCoordinator`
- `runtimeStoryService`
- `apiClient`
2. `批次 D任务 / 物品 / AI 混合执行层收口`
- `npcEncounterActions`
- `questDirector`
- `runtimeItemAiDirector`
- `ai.ts`
一句话总结本批次:
**这一步不是在“删几个没用 Hook”而是在把已经退场的旧主流程壳层和旧 flow 路牌从主工程里真正拔掉,让现行架构不再和历史壳子并排站着。**

View File

@@ -0,0 +1,202 @@
# 工程死分支清理执行记录 C2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
这份记录对应清洗计划中的:
- `批次 C运行时真相收口`
但这次不是“一口气把运行时真相链全删干净”,而是先做其中最明确、风险最低、最不该继续拖的那一段:
1. **收掉前端本地自动登录用户名 / 密码真相**
2. **把登录恢复改成优先依赖服务端 session / refresh**
同时,这一批也明确记录了一件事:
**运行时快照前置写入链当前还不能直接砍。**
原因不是“不想动”,而是服务端当前 `runtime story` 动作入口仍然以远端快照作为执行基线。
在后端 contract 没先改好之前,前端不能假装自己已经退出这条链。
---
## 1. 本批次已处理对象
## 1.1 已收口的鉴权链
| 文件 | 处理动作 | 本批结论 |
| --- | --- | --- |
| `src/services/apiClient.ts` | 删除本地自动登录用户名 / 密码存取逻辑 | 前端不再保存 auto auth 账号密码 |
| `src/services/authService.ts` | 去掉对本地游客凭证的读写依赖 | 自动游客登录改为仅本次生成凭证,不再长期落本地 |
| `src/components/auth/AuthGate.tsx` | 去掉“必须先有本地 access token 才尝试恢复”的前置假设 | 登录恢复改为优先尝试服务端 `getCurrentAuthUser()` / refresh session |
| `src/services/authService.test.ts` | 改写游客自动登录相关断言 | 验证改为“生成临时凭证并完成登录”,而不是“落本地账号密码” |
| `src/components/auth/AuthGate.test.tsx` | 改写登录恢复 mock | 验证改为“先尝试服务端会话恢复,再决定是否走游客兜底” |
---
## 2. 本批次为什么先做这段
这批优先级高,是因为它同时满足 4 条:
1. **风险明确。**
浏览器保存自动登录用户名 / 密码,本身就不符合“前端只做表现、后端负责鉴权真相”的方向。
2. **替代路径已经存在。**
后端已经有 refresh session cookie 与 `getCurrentAuthUser()`,不是没有可替代能力。
3. **改动边界清楚。**
这一段主要落在前端鉴权恢复逻辑和测试,不会直接波及运行时战斗、任务、物品、剧情主链。
4. **收益直接。**
一旦收掉,前端就少了一份最不该长期保留的高风险真相。
一句话讲:
**这一步先把“浏览器记住游客账号密码再重登”这条假真相链拔掉。**
---
## 3. 本批次明确没做的事
## 3.1 没有直接删除 `runtimeStoryCoordinator.ts` 里的前置 `putSaveSnapshot(...)`
这不是漏做,而是明确暂缓。
当前复核结果是:
1. `server-node/src/modules/story/storyActionService.ts`
2. `server-node/src/routes/runtimeRoutes.ts`
3. `server-node/src/repositories/runtimeRepository.ts`
这条后端链当前仍然通过远端快照读取运行时状态,再执行:
1. `getRuntimeStoryState`
2. `resolveRuntimeStoryAction`
也就是说,当前真实情况不是“前端多写了一份完全没用的镜像”,而是:
**前端在提交动作前先把当前状态写回远端快照,后端再基于这份快照执行业务动作。**
在这个 contract 没先升级为“前端只发 action后端自己持有完整 session 真相”之前,前端不能直接把这一步砍掉。
否则会出现:
1. 动作请求仍在走
2. 但服务端读取到的执行基线不完整
3. 最后不是收口真相,而是把主链打断
## 3.2 没有删除 `runtimeStoryService.ts` / `runtimeStoryCoordinator.ts` 的快照再水合逻辑
这一步本轮也做了复核,结论是:
1. 我曾尝试把 `runtimeStoryCoordinator.ts` 中对服务端返回快照的重复再水合去掉
2. 但对应的 `runtimeStoryCoordinator` 测试立即暴露出:当前后端返回的快照在部分战斗场景下还不是完整水合态
3. 说明前端当前这层再水合仍然有现实职责,不是纯多余代码
所以这一步本批明确结论是:
**暂不删除,等后端快照 contract 先补完整后再做。**
---
## 4. 本批次验证结果
本批次已完成的定向验证:
1. `npx vitest run src/services/authService.test.ts`
2. `npx vitest run src/components/auth/AuthGate.test.tsx`
3. `npx vitest run src/hooks/story/runtimeStoryCoordinator.test.ts`
4. `npm run check:encoding`
结果:
1. `authService` 测试通过
2. `AuthGate` 测试通过
3. `runtimeStoryCoordinator` 测试通过
4. 编码检查通过
另外执行了:
1. `npm run build`
结果:
构建产物生成成功,但 `build-gate` 仍因主包 chunk warning 拦截失败。
当前失败点仍是已知的主包体积问题:
- `AuthenticatedApp-*.js` 超过当前 warning 门槛
这属于仓库当前既有工程问题,不是本批次引入的新断裂。
## 4.1 2026-04-21 补充修正:会话探测 401 自触发循环
在这批收口完成后,前端又暴露出一条更细的鉴权恢复回路问题:
1. `AuthGate` 启动时会调用 `getCurrentAuthUser()` 探测现有会话
2. `/api/auth/me` 返回 `401` 时,`apiClient.ts` 会默认广播一次 `AUTH_STATE_EVENT`
3. `AuthGate` 自己又监听这个事件并重新 `hydrate()`
4. 最终形成 `hydrate -> /auth/me 401 -> emit -> hydrate` 的自触发循环
这条链的问题不在“是否允许 401”而在
**会话探测请求把“未登录态探测”错误地当成了“全局登录态变更”。**
因此这里补了一条更细粒度的约束:
1. `apiClient.ts` 新增 `notifyAuthStateChange` 选项,默认仍保持原有广播行为
2. `getCurrentAuthUser()` 作为会话探测请求,显式关闭这类 401 广播
3. 真实登录、登出、刷新成功后,仍保留全局鉴权变更通知
这样修完后:
1. `AuthGate` 仍会优先尝试服务端会话恢复
2. 无会话时会正常落回未登录分支
3. 不会因为探测型 401 把自己重新唤醒并刷爆控制台
---
## 5. 本批次完成后的实际收益
这一步完成后,工程在鉴权边界上有了两个明确改善:
1. **前端不再保存自动登录用户名 / 密码。**
浏览器只保留 access token本地高风险游客凭证真相已经收掉。
2. **登录恢复逻辑更接近服务端为真相源。**
`AuthGate` 不再假设“没有本地 token 就一定还没登录”,而是优先尝试服务端会话恢复。
这意味着前端鉴权链已经从:
```text
本地用户名/密码 -> 再次 entry -> 拿 token
```
进一步收到了:
```text
refresh session / 当前会话 -> 恢复用户
兜底时才创建一次游客凭证
```
---
## 6. 本批次后续建议
要继续完成批次 C下一步不该直接在前端硬删而应该先补后端 contract
1.`runtime story` 动作链逐步摆脱“前端先写远端快照”的依赖
2. 让服务端自己持有更完整的运行时 session 真相
3. 等后端返回快照已经稳定水合后,再删前端的重复再水合
换句话说,批次 C 的后半段应该拆成:
1. **C-1鉴权真相收口**
本批已完成
2. **C-2运行时快照 contract 后端化**
需要先改后端
3. **C-3前端镜像写入与重复水合退场**
依赖 C-2
---
## 7. 一句话总结
**批次 C 这一轮已经先把“浏览器长期保存游客账号密码”这条最不该存在的鉴权假真相链收掉了;而运行时快照前置写入这条链经过复核确认仍受后端 contract 约束,不能在服务端未先补齐前硬砍。**

View File

@@ -0,0 +1,56 @@
# 工程死分支清理执行记录 D2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次继续清理上一轮复核后剩余的低风险数据产物与测试占位:
1. 未接入业务的生成产物
2. 只为测试替换真实实现的空 stub
3. 支撑这些残留的配置与脚本
---
## 1. 已删除对象
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `src/data/buildTagSimilarity.generated.ts` | 未接入业务的生成产物 | 运行时代码不 importBuild 相似度当前由 `buildTags.ts` 中的属性亲和度逻辑计算 | `src/data/buildTags.ts` |
| `scripts/generate-build-tag-similarity.py` | 已无输出目标的生成脚本 | 只负责生成已删除的矩阵文件,继续保留会误导后续开发恢复旧主源 | `src/data/buildTags.ts` 的手工审表逻辑 |
| `src/data/customWorldCharacterLoadout.stub.ts` | 测试专用空 stub | 只通过 `vitest.config.ts` alias 替换真实实现;真实实现已经稳定存在 | `src/data/customWorldCharacterLoadout.ts` |
---
## 2. 同步更新
本批次同步移除了:
1. `vitest.config.ts` 中指向 `customWorldCharacterLoadout.stub.ts` 的 alias
2. `BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md` 中把旧 generated 矩阵描述为当前文件的表述
3. 清理计划里对 `buildTagSimilarity.generated.ts` 的未处理状态说明
---
## 3. 验证口径
删除前已确认:
1. `buildTagSimilarity.generated.ts` 无运行时代码引用
2. `customWorldCharacterLoadout.stub.ts` 只被 `vitest.config.ts` alias 引用
3. 真实 `customWorldCharacterLoadout.ts` 仍被 `characterPresets.ts``npcInteractions.ts` 使用,不能删除
删除后建议验证:
1. `npm run check:encoding`
2. 与自定义世界开局物品相关的测试
---
## 4. 当前结论
本批次完成后,剩余清理对象已经不再适合按“无引用直接删”推进。后续如果继续清,需要先改 contract 或主链职责:
1. 运行时快照真相链
2. 任务 / 物品 / AI 混合执行层
3. 大型主流程组件继续拆分,而不是直接删除

View File

@@ -0,0 +1,117 @@
# 工程死分支清理执行记录 E2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次承接批次 D继续清掉已经退出 RPG 游戏创作主流程、RPG 运行时玩法主流程、平台基本功能主流程的历史壳层。
本批次不处理仍需后端 contract 先收口的对象,例如:
1. `src/services/questDirector.ts`
2. `src/services/runtimeItemAiDirector.ts`
3. `src/hooks/rpg-runtime-story/runtimeStoryCoordinator.ts`
4. `src/services/apiClient.ts`
这些对象仍属于“前端越界逻辑继续后端化”的后续批次,不按无引用文件直接删除。
---
## 1. 删除判定口径
本批只删除满足下面条件之一的对象:
1. 无运行时入口、无脚本入口、无当前路由挂载。
2. 已有现行正式实现,旧文件只剩 re-export / facade / 兼容命名。
3. 只被测试验证旧壳自身,且该测试不再服务当前主流程门禁。
4. 文档已明确该对象处于“后续只允许收缩、不再接新逻辑”的兼容残留状态。
---
## 2. 本批次已处理对象
| 文件 | 判定 | 删除原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `server-node/src/routes/rpgCreationAgentRoutes.ts` | 旧命名 re-export | 当前后端正式路由直接使用 `customWorldAgent.ts` | `server-node/src/routes/customWorldAgent.ts` |
| `server-node/src/routes/rpgWorldGalleryRoutes.ts` | 空路由骨架 | 世界广场实际列表和详情已经进入世界库路由 | `server-node/src/routes/rpg-entry/rpgWorldLibraryRoutes.ts` |
| `server-node/src/services/RpgAgentOrchestrator.ts` | 旧命名 re-export | 当前正式上下文直接使用 `CustomWorldAgentOrchestrator` | `server-node/src/services/customWorldAgentOrchestrator.ts` |
| `server-node/src/services/RpgAgentSessionStore.ts` | 旧命名 re-export | 当前正式上下文直接使用 `CustomWorldAgentSessionStore` | `server-node/src/services/customWorldAgentSessionStore.ts` |
| `server-node/src/services/customWorldWorkSummaryService.ts` | 旧兼容入口 | 测试和路由已改为直接使用 RPG 命名服务 | `server-node/src/services/RpgWorldWorkSummaryService.ts` |
| `server-node/src/services/customWorldAgentPublishGateService.ts` | 旧发布门禁实现 | 当前 action executor 与作品库发布链已统一走 PublishingService | `server-node/src/services/customWorldAgentPublishingService.ts` |
| `server-node/src/services/customWorldAgentPublishService.ts` | 旧发布实现 | 当前发布链不再编译旧 legacy result profile | `server-node/src/services/customWorldAgentPublishingService.ts` |
| `server-node/src/modules/custom-world/runtime-profile/runtimeProfileCompiler.ts` | 旧 facade | runtime profile 已拆到目录模块并由 `index.ts` / `runtimeProfile.ts` 承接 | `server-node/src/modules/custom-world/runtime-profile/index.ts` |
| `server-node/src/bridges/legacyBuildRuntimeBridge.ts` | 无引用旧桥 | 后端 runtime build / equipment 已直接在正式模块内使用 | `server-node/src/modules/runtime/**` |
| `server-node/src/bridges/legacyRuntimeItemResolutionBridge.ts` | 旧桥 | runtime item 解析服务一并删除,正式运行时使用 `runtimeItemModule.ts` | `server-node/src/modules/runtime-item/runtimeItemModule.ts` |
| `server-node/src/modules/runtime-item/runtimeItemResolutionService.ts` | 无正式入口 wrapper | 只被 barrel 和自身测试引用,未挂入 Express 运行时主链 | `server-node/src/modules/runtime-item/runtimeItemModule.ts` |
| `server-node/src/modules/**/index.ts` | 无引用 barrel | 这些 barrel 没有被当前后端入口消费,反而制造“公共模块入口仍存在”的错觉 | 直接 import 具体正式模块 |
| `server-node/src/routes/rpg-*/index.ts` | 无引用 barrel | 当前 Express app 直接 import 具体 route 文件 | `server-node/src/app.ts` 中的具体路由 |
| `server-node/src/repositories/rpg-*/index.ts` | 无引用 barrel | 当前上下文直接 import 具体 repository | `server-node/src/server.ts` 中的具体仓储 |
| `src/components/DeveloperTeamModal.tsx` | 无入口 UI | 平台主流程没有打开该弹窗的入口 | 无替代 UI删除历史壳 |
| `src/components/LazySkillEffectPreview.tsx` | 无入口 lazy 壳 | 正式技能预览直接使用 `SkillEffectPreview` | `src/components/SkillEffectPreview.tsx` |
| `src/components/npcVisualEditorModel.ts` | 旧 NPC 形象写回模型 | 当前 RPG 创作编辑器使用 `CustomWorldNpcVisualEditor` 与结果页新入口 | `src/components/CustomWorldNpcVisualEditor.tsx``src/components/rpg-creation-editor/**` |
| `src/components/npcVisualEditorPersistence.ts` | 旧 NPC 形象写回持久层 | 只被旧持久化测试引用,正式编辑入口已迁移 | `src/components/rpg-creation-editor/**` |
| `src/components/rpg-creation-*/index.ts` | 无引用 barrel | 当前入口直接 import 具体 facade 文件barrel 没有主流程消费 | 直接 import `RpgCreation*` 具体文件 |
| `src/components/rpg-creation-editor/CustomWorldSceneChapterEditorSection.tsx` | 旧 facade | 当前编辑器 section 直接在 `RpgCreationEntityEditorShared.tsx` 中分发 | `src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx` |
| `src/data/editorValidation.ts` | 旧预设编辑器校验 | 当前主流程和内容门禁不再调用 | `scripts/validate-overrides.ts`、后端 editor API |
| `src/editor/shared/EditorNotice.tsx` | 无入口共享 UI | 只被同批删除的 FormFields 使用 | 无替代 UI删除历史编辑器壳 |
| `src/editor/shared/FormFields.tsx` | 无入口共享 UI | 旧编辑器共享表单未接主流程 | 当前 RPG 编辑器组件内聚在 `rpg-creation-editor/**` |
| `src/editor/shared/SectionCard.tsx` | 无入口共享 UI | 旧编辑器卡片未接主流程 | 当前 RPG 编辑器组件内聚在 `rpg-creation-editor/**` |
| `src/hooks/rpg-runtime-story/npcEncounterActions.ts` | 旧 wrapper | 正式实现已在 `useRpgRuntimeNpcInteraction.ts`,测试已改到正式文件 | `src/hooks/rpg-runtime-story/useRpgRuntimeNpcInteraction.ts` |
| `src/hooks/rpg-runtime-story/openingAdventure.ts` | 旧前端开局特殊流程 | 开局营地对白已由后端 `RpgRuntimeStoryActionDomain` 和当前 story context 承接 | `server-node/src/modules/rpg-runtime-story/RpgRuntimeStoryActionDomain.ts` |
| `src/hooks/rpg-runtime-story/storyCampCompanion.ts` | 旧前端营地同伴 helper | 只剩旧开局流程和自身测试引用,正式开局上下文已迁到当前 runtime story 链 | 后端 runtime story action domain 与 `storyContextBuilder.ts` |
| `src/hooks/rpg-runtime-story/storyRenderingHelpers.ts` | 无入口旧渲染 helper | 当前正式 story presentation 不再 import | `src/hooks/rpg-runtime-story/storyPresentation.ts` |
| `src/prompts/questPrompts.ts` | 前端 prompt 残留 | Quest prompt 真相已迁到后端 | `server-node/src/prompts/questPrompts.ts` |
| `src/prompts/runtimeItemPrompts.ts` | 前端 prompt 残留 | Runtime item prompt 真相已迁到后端 | `server-node/src/prompts/runtimeItemPrompts.ts` |
| `src/services/questPrompt.ts` | 前端 prompt re-export | 只指向同批删除的前端 prompt | `server-node/src/prompts/questPrompts.ts` |
| `src/services/runtimeItemAiPrompt.ts` | 前端 prompt re-export | 只指向同批删除的前端 prompt | `server-node/src/prompts/runtimeItemPrompts.ts` |
| `src/services/storyEngine/contentDependencyGraph.ts` | 实验性孤岛 | 只被自身测试引用,没有主流程消费 | 后续如需要重新设计到后端 story graph 服务 |
---
## 3. 同步调整
1. `customWorldAgentPhase2/3/4` 测试改为直接实例化 `RpgWorldWorkSummaryService`
2. `customWorldWorkSummaryService.integration.test.ts` 改为直接覆盖 `RpgWorldWorkSummaryService`
3. `npcEncounterActions.test.ts` 改为直接覆盖 `useRpgRuntimeNpcInteraction.ts`,不再通过旧 wrapper。
4. `story_opening_camp_dialogue` 的 function catalog 执行路径改为后端 runtime action domain不再指向已删除旧前端文件。
5. NPC function catalog 中 `npc_chat / npc_help / npc_leave / npc_fight / npc_spar / npc_preview_talk` 的 executor 路牌改到现行 `useRpgRuntimeNpcInteraction.ts`
---
## 4. 本批次暂缓对象
以下对象仍然保留,原因是它们不是“无引用死代码”,而是需要下一轮按 contract 或主链职责迁移:
1. `src/services/questDirector.ts`
2. `src/services/runtimeItemAiDirector.ts`
3. `src/services/ai.ts`
4. `src/data/sceneObservation.ts`
5. `server-node/ecosystem.config.cjs`
6. `server-node/src/scripts/syncCustomWorldSavedProfileAssets.ts`
其中 `ecosystem.config.cjs` 被部署脚本直接使用;`sceneObservation.ts` 被内容 smoke 脚本验证;`syncCustomWorldSavedProfileAssets.ts` 是一次性运维脚本,后续要单独按运维脚本治理口径确认是否归档。
---
## 5. 验证口径
本批删除后建议验证:
1. `npm run check:encoding`
2. `npx tsx --test server-node/src/services/customWorldWorkSummaryService.integration.test.ts`
3. `npx vitest run src/hooks/rpg-runtime-story/npcEncounterActions.test.ts`
4. `npm run server-node:build`
5. `npm run build`
如果 `npm run build` 仍被既有 chunk warning 拦截,需要单独记录为既有门禁问题,不归因到本批删除。
---
## 6. 当前结论
本批次进一步删除了“旧命名入口、旧 facade、旧 prompt 前端镜像、无入口编辑器壳层”这批容易误导后续开发的文件。
后续清理不应继续按“静态无引用”直接推进,而应进入两类工作:
1. 运行时 / 任务 / 物品 / AI 的后端 contract 收口。
2. RPG 创作编辑器与运行时热点文件的职责拆分。

View File

@@ -0,0 +1,91 @@
# 工程死分支清理执行记录 F2026-04-21
更新时间:`2026-04-21`
## 0. 本批次目标
本批次承接批次 E 的验证结果,继续处理删除后暴露出的最后一组高置信残留:
1. 已经没有任何代码入口引用的前端任务生成 director。
2. 只被内容 smoke 牵住、但不再是正式运行时入口的旧观察文案 helper。
3. 带有固定用户、固定 session、固定 profile 的一次性历史同步脚本。
4. 清理后暴露出的 function catalog 契约覆盖缺口。
本批次仍然不按文件名直接删除 `legacy` 命名对象。经核对,`server-node/src/bridges/legacyInventoryRuntimeBridge.ts``legacyNpcTask6Bridge.ts``legacyQuestProgressBridge.ts``legacyQuestRuntimeBridge.ts``legacyRuntimeItemBridge.ts``legacyTreasureRuntimeBridge.ts` 仍被后端战斗、背包、任务、宝藏主链直接引用,不能按历史命名硬删。
---
## 1. 删除判定口径
本批删除对象必须同时满足:
1. 修正 `.js -> .ts` 后端源码解析、前端懒加载入口解析后,仍不可从正式入口到达。
2. 全仓库代码引用扫描没有正式入口引用。
3. 如只被 smoke 或测试牵住,先把 smoke / 测试改到当前正式主链,再删除旧对象。
4. 删除后通过对应门禁验证,没有新增悬空 import。
---
## 2. 本批次已处理对象
| 文件 | 判定 | 删除 / 调整原因 | 替代路径 / 当前真相源 |
| --- | --- | --- | --- |
| `src/services/questDirector.ts` | 无代码入口残留 | 正式 quest 生成已由后端 `/api/runtime/quests/generate``questService.ts` 承接,前端当前没有任何 import | `server-node/src/services/questService.ts``server-node/src/modules/quest/runtimeQuestModule.ts` |
| `src/data/sceneObservation.ts` | 旧观察文案 helper | 只被 `scripts/smoke-content.ts` 引用,正式观察动作已走 `idle_observe_signs` function 与运行时 story continuation | `src/data/functionCatalog/state/idleObserveSigns.ts``src/hooks/rpg-runtime-story/storyChoiceContinuation.ts` |
| `server-node/src/scripts/syncCustomWorldSavedProfileAssets.ts` | 一次性硬编码运维脚本 | 脚本内固定用户、session、profile只服务历史补丁没有 CLI 参数和当前运维入口 | 无替代;如未来需要,按参数化运维脚本重新设计 |
---
## 3. 同步调整
1. `scripts/smoke-content.ts` 不再 import 旧 `sceneObservation.ts`,改为通过 `resolveFunctionOption('idle_observe_signs', ...)` 验证当前正式 function 目录。
2. `packages/shared/src/contracts/rpgRuntimeContracts.test.ts` 不再验证已移除的旧 `story` façade改为直接验证当前拆分契约。
3. `src/data/functionCatalog/` 补齐仍在后端运行时契约中的 function 文档:
- `battle_attack_basic`
- `battle_use_skill`
- `npc_chat_quest_offer_view`
- `npc_chat_quest_offer_replace`
- `npc_chat_quest_offer_abandon`
4. `battle_attack_basic``battle_use_skill` 只作为后端契约文档登记,不进入 `STATE_FUNCTION_DEFINITIONS`,避免前端本地候选池生成缺少 `runtimePayload.skillId` 的假技能 option。
---
## 4. 本批次暂缓对象
以下对象经本批复核后继续保留:
1. `server-node/src/services/customWorldAgentRepositoryTestHelpers.ts`
2. `server-node/src/services/customWorldAgentTestHelpers.ts`
3. `server-node/src/testFixtures/runtimeCharacter.ts`
4. `server-node/src/testHttp.ts`
这些文件不属于正式运行时入口但当前被后端测试、smoke 与路由边界门禁使用。它们不是 RPG 创作 / 运行时玩法主流程代码,但仍是平台基本质量门禁的一部分,不能在“删除冗余业务代码”批次里直接硬删。
另保留:
1. `src/services/runtimeItemAiDirector.ts`
2. `src/services/ai.ts`
3. `src/services/apiClient.ts`
这些文件仍被当前主链或前端 SDK 入口引用,后续如继续压缩,必须先完成对应 contract / SDK 拆分,不按无引用规则删除。
---
## 5. 验证结果
本批已通过:
1. `npx vitest run src/data/functionCatalog/functionCatalog.test.ts packages/shared/src/contracts/rpgRuntimeContracts.test.ts`
2. `npx tsx scripts/smoke-content.ts`
3. `npm run check:encoding`
并额外确认:
1. 全仓库代码中不再引用 `sceneObservation``questDirector``syncCustomWorldSavedProfileAssets`
2. `buildStateFunctionDefinitions()` 中不会出现 `battle_attack_basic` / `battle_use_skill`,这两个 function 只由后端运行时 option 池下发。
---
## 6. 当前结论
本批次后,静态入口扫描中剩余的高置信“不可达源码”已经收敛为测试辅助、测试夹具和 smoke helper。继续删除前需要先重构测试基础设施或迁移剩余前端 SDK而不应再按文件名或历史命名直接硬删。

View File

@@ -0,0 +1,553 @@
# 前端应迁后端逻辑审计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/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.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。**

View File

@@ -4,21 +4,42 @@
## 当前推荐入口
1. [CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md](./CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md)
1. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_F_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_F_2026-04-21.md)
这一版是第六批落地记录,聚焦删除无入口 `questDirector`、旧观察文案 helper、一次性硬编码同步脚本并补齐后端运行时 function catalog 契约覆盖。
2. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_E_2026-04-21.md)
这一版是第五批落地记录,聚焦旧命名 re-export、空路由骨架、旧发布服务、前端 prompt 镜像与无入口编辑器壳层的物理删除。
3. [FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md](./FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md)
这一版是本轮前端越界逻辑专项审计,专门汇总当前仍应继续迁到 Express 后端的运行时、鉴权、生成编排与本地真相残留。
4. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_D_2026-04-21.md)
这一版是第四批落地记录,聚焦未接入业务的数据生成产物、测试专用 stub 与对应配置残留出清。
5. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_C_2026-04-21.md)
这一版是第三批落地记录,聚焦鉴权真相收口,先移除前端保存自动登录用户名/密码的本地真相,并明确运行时快照前置写入为什么当前还不能硬砍。
6. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_B_2026-04-21.md)
这一版是第二批落地记录,聚焦旧主流程壳层、旧 bootstrap 和旧 inventory / forge / equipment flow Hook 的正式出清。
7. [ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md](./ENGINEERING_DEAD_CODE_CLEANUP_BATCH_A_2026-04-21.md)
这一版是第一批落地记录聚焦高置信度小型孤岛、prompt 壳子、stub 和无入口 modal 的首轮清理。
8. [CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md](./CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md)
这一版是面向当前仓库状态的优化点盘点,适合直接拿来排优先级和拆执行批次。
2. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
9. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md)
这一版是对 `2026-04-19` 基线的当前仓库复核,明确哪些问题已经处理、哪些表述需要纠正、热点又迁移到了哪里。
3. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
10. [ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md](./ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-19.md)
这一版保留原始问题快照和执行回填,适合回看“为什么会有这轮清理与边界收口”。
4. [ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md)
11. [ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md)
这一版最适合作为当前工程基线,重点从“是否真正绿色”“门禁有没有覆盖真实风险”来判断仓库状态。
5. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md)
12. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md)
适合回看运行时主链路、Story/Combat 边界、分层过渡期问题。
6. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md)
13. [ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md](./ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md)
适合看第一轮系统性工程扫描,了解最早的问题基线。
## 融合结论
- 最新专项审计已经把“前端哪些逻辑还该后移到后端”收敛到 6 类:运行时快照、本地 token、本地浏览历史、NPC 委托换单、quest/runtime item 混合编排、浏览器 AI orchestration。
- 工程大清洗已经开始进入实际执行阶段,首批高置信度小型孤岛和残留壳子已开始清理。
- 第二批已经开始清理旧主流程壳层与旧 flow Hook当前主工程的“现行入口”和“历史入口”边界正在变得更清楚。
- 第三批已经先完成鉴权真相收口的一段,前端不再保存自动登录用户名/密码;运行时快照链仍需先补后端 contract再继续往前删。
- 第四批已经继续收掉未接入业务的数据生成产物、测试专用 stub 与对应脚本/配置残留,主工程里的“假数据主源”进一步减少。
- 第五批已经继续收掉旧命名 re-export、空路由骨架、旧发布 service、前端 prompt 镜像与无入口编辑器壳层,主工程里的“假入口”和“假 prompt 主源”进一步减少。
- 第六批已经继续收掉无入口 `questDirector`、旧观察文案 helper、一次性硬编码同步脚本并修复 function catalog 对后端运行时契约的覆盖缺口。
- 当前仓库已经完成“旧 dev 插件链路删除、根目录噪音清理、`server-node -> src/**` 反向依赖切断”这批第一阶段任务。
- 当前如果想直接判断“今天先优化什么”,优先看 `CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md`
- 当前的新重点已经进一步收敛到三类:未接线孤岛模块、前端残留的运行时/鉴权真相、热点向 prompt/runtime profile/平台入口壳层迁移。