# MindSpace Remote 页面同步与缩略图回归守卫 日期:2026-07-05 ## 事故症状 1. **缩略图 500**:`GET /api/mindspace/v1/pages/:pageId/thumbnail` 返回 500;日志/storage 为 `ENOENT`,但 `MindSpace//public/*.html` 仍存在。 2. **页面列表缺新生成页**:103 生产 `MINDSPACE_SERVER_ADAPTER=remote` 时,Agent 新生成的 `public/*.html` 不出现在「最近页面 / 全部页面」。 3. **误判**:容易把缺页当成「MindSpace 路径解析错误」或「改回 local adapter 就能修缩略图」——两者根因不同。 ## 根因 ### 缩略图 500 - `h5_page_records` / `h5_asset_versions.storage_key` 指向 `data/mindspace/users/.../*.md`。 - 磁盘上该 storage 文件可能已丢失或未写入(历史 sync、清理、quota、迁移)。 - 页面 snapshot 仍保留 `relative_path: public/index.html`,工作区 HTML 是真实内容来源。 - `loadPageThumbnailContext` / `getPage` 若**只读 storage**,会在 remote 8082 上直接抛错 → Portal 返回 500。 ### Remote 下缺页 - Portal `GET /mindspace/v1/pages` 会调用 `syncUserGeneratedPages()`,将 `public/*.html` 入库。 - 曾存在 `if (adapterKind === 'remote') return;`,导致 **8082 独立部署后 sync 永不执行**。 - 测试环境多为 `local`,sync 正常 → 生产/测试行为不一致。 ## 必须保留的行为 ### 1. Workspace HTML fallback(读内容) 在 `mindspace-pages.mjs` 中,读取页面 content / 缩略图 HTML 时: 1. 优先读 `storage_key` 对应文件; 2. 若 `ENOENT` 且 `page_type === 'html'` 且 snapshot 有 `relative_path` → 回退读 `resolvePublishDir(h5Root, userId)` 下对应 HTML; 3. `getPage` 与 `renderThumbnail` **共用** `readPageContentWithWorkspaceFallback`,禁止只修一处。 ### 2. Remote 也执行 page sync 1. 抽出 `mindspace-page-sync-service.mjs` → `pageSyncService.syncUserGeneratedPages(userId)`; 2. 写入 `mindspace-server-adapter-contract.mjs`:`pageSyncService` binding; 3. **Local**:8082 / Portal 内直接跑 `syncGeneratedPagesFromPublicAssets`; 4. **Remote**:Portal 通过 RPC 调 8082 的 `pageSyncService.syncUserGeneratedPages`; 5. **`server.mjs` 禁止**在 remote 模式 early-return 跳过 sync。 触发点(保持不变): - `GET /mindspace/v1/pages` 列表前 - 聊天 Finish 后(`onAfterFinish`) ### 3. 103 拓扑约束 - Portal:`8081`,`MINDSPACE_SERVER_ADAPTER=remote` - MindSpace Service:`8082`,跑 **local adapter**(含 pageSyncService + storage + h5Root) - 用户 HTML:`/Users/john/Project/Memind/MindSpace//public/` - Storage:`data/mindspace/users//...` **禁止**用「改回 Portal local adapter」作为生产长期方案;只可用于本地对比验证。 ## 排查入口 ```bash # 1. 页面 DB 记录 vs storage vs workspace mysql ... # h5_page_records + h5_page_versions.source_snapshot_json + h5_asset_versions.storage_key # 2. storage 是否存在 ls -la data/mindspace/users//pages//versions/ # 3. workspace HTML 是否存在 ls -la MindSpace//public/ # 4. 8082 健康与 adapter curl -s http://127.0.0.1:8082/health # 5. 缩略图 RPC(需登录态;或直接在 8082 侧调 pageService.renderThumbnail) curl -I 'https://m.tkmind.cn/api/mindspace/v1/pages//thumbnail' ``` ## 改相关文件前必跑 ```bash npm run verify:mindspace-page-sync-guards node --test mindspace-page-sync-service.test.mjs mindspace-pages.test.mjs ``` ## Checklist(发 103 前) - [ ] `mindspace-pages.mjs` 含 workspace fallback - [ ] `pageSyncService` 在 contract + local runtime + remote proxy 中齐全 - [ ] `server.mjs` 无 remote sync skip - [ ] **8082 与 Portal 均发布**(8082 承载 renderThumbnail + sync) - [ ] 用真实 pageId 验证 thumbnail 200 + 列表含新页 ## 相关单测 - `mindspace-page-sync-service.test.mjs` - `mindspace-pages.test.mjs`(storage 缺失时 thumbnail / getPage fallback) - `mindspace-page-sync.test.mjs`(public dir → DB) ## 代码内标记 搜索 `REGRESSION GUARD: mindspace-page-sync-thumbnail` 定位内联说明。