claude-mem Phase 08 排障手册:UI/UX 与开发体验的六项修复——ID 一致性、SSE 重连、模型徽标与插件自愈
claude-mem 的 Phase 08(Triage-08)专项聚焦于 Web UI、插件安装体验与开发者行为中六类"不丢数据但侵蚀用户信任"的中优先级问题:观察 ID 不一致、/reload-plugins 后 SSE 直播中断、模型名缺失、Claude Code 升级导致插件丢失、失效的 setup hook 引用等。本文以仓库中的分诊剧本 .maestro/playbooks/2026-03-12-CM-Issues-PRs/2026-03-12-Issues-PRs-Triage/TRIAGE-08-UI-UX-DX.md 为骨架,逐项给出排查路径、修复方案,并结合当前仓库源码(MCP 服务、SSE 客户端、hook 配置、测试)说明每项问题在代码中的真实落点,读完后可独立完成该阶段的修复与回归验证。
Phase 08 的定位与前置条件
Phase 08 处理的是"Medium — UI/UX & Developer Experience"类别的问题。剧本开头明确指出:这些问题都不是数据丢失级 bug,但"错位的 ID、失效的直播更新、静默的安装失败会让 claude-mem 显得不可靠"。涉及 issue 为 #1339、#1331、#1265、#1268、#1340、#1332,并要求 Phases 01–07 已完成(即 同目录下的 TRIAGE-01 至 TRIAGE-07 阶段已落地)才进入本阶段。
六项任务可归纳为三条主线:
- 数据一致性主线:Web UI 展示的 observation ID 与
get_observationsMCP 工具接受的 ID 必须统一(#1339); - 实时性主线:
/reload-plugins之后 SSE 直播必须能自动恢复(#1331); - 安装与配置健壮性主线:卡片展示模型名(#1265)、插件升级后自愈(#1268)、清理失效的 setup.sh 引用(#1340,顺带核查 OpenClaw 插件配置 #1332)。
修复 #1339:Web UI 观察 ID 与 get_observations 不一致
问题本质
Web viewer 中显示的 observation ID 与 get_observations MCP 工具返回/接受的 ID 对不上,导致会话上下文中"按 ID 访问记忆"的提示变成误导。剧本给出的判断是:错位大概率发生在展示 ID(行号或数组下标)与数据库 id 主键列之间。修复目标是让 UI 显示 get_observations 实际接受的那个 ID——即数据库主键 observations.id,而非索引;如果 UI 目前展示的是顺序展示编号,应把真实 ID 一并显示出来(例如 #58640 这种与会话上下文索引一致的格式),同时核对 SSE 广播中的 ID 是否也一致。
源码落点
get_observations 工具的定义在 src/servers/mcp-server.ts 中,其 inputSchema 明确声明 ids 是数字数组(items: { type: 'number' })且为必填项,handler 将参数转发到 worker 的 /api/observations/batch 端点:
{
name: 'get_observations',
description: 'Step 3: Fetch full details for filtered IDs. Params: ids (array of observation IDs, required), orderBy, limit, project',
inputSchema: {
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'number' },
description: 'Array of observation IDs to fetch (required)'
}
},
required: ['ids'],
...
},
handler: async (args) => {
return await callWorker('/api/observations/batch', { body: args });
}
}
该 worker 端点在 src/services/worker/http/routes/DataRoutes.ts 中注册,并对请求体做 schema 校验:
app.post('/api/observations/batch', validateBody(observationsBatchSchema), this.handleGetObservationsByIds.bind(this));
也就是说,ID 契约的权威定义在 MCP 工具 schema 与 worker 的 observationsBatchSchema 校验之间。排查时应先读 src/ui/viewer/ 下的 viewer 源码,确认卡片组件渲染的是哪个字段;再读上面这条工具链路,确认 /api/observations/batch 按什么列(主键 id)查询。只要 UI 与 SSE 事件(new_observation 的 data.observation.id)统一使用主键列,#1339 即闭环。
另需注意运行时前提:src/servers/mcp-server.ts 中存在运行时守卫逻辑,当 CLAUDE_MEM_RUNTIME=server 与 worker 两种运行时切换时,legacy 工具(search/timeline/get_observations)的可用性会受约束,回归测试时应固定运行时模式再验证 ID。
修复 #1331:/reload-plugins 之后 SSE 直播停止
问题机制
Claude Code 的 /reload-plugins 会触发 hook 重连,连带把 viewer 的 SSE 连接打断,而 UI 侧若无自动重连,观察的直播更新就永久停摆。剧本给出的排查锚点是在 worker HTTP 路由中搜索 SSE、EventSource、text/event-stream;修复优先放在 viewer 客户端:
- 在 SSE 客户端代码里添加
EventSource.onerror处理器,在 2 秒延迟后自动重连; - 重连成功后通过 REST 拉取最新 observations 补齐断档期间漏掉的数据;
- 若 SSE 端点本身在 reload 时被销毁,则服务端要保证 SSE 端点的生命周期独立于插件 reload;
- 动手前先搜索 viewer 里已有的重连逻辑,避免重复实现。
当前仓库的实现状态
当前仓库中,viewer 的 SSE 客户端 src/ui/viewer/hooks/useSSE.ts 已具备自动重连骨架,可以据此对照剧本要求做差距分析:
- 连接目标为
API_ENDPOINTS.STREAM,其值为/stream(见 src/ui/viewer/constants/api.ts); onerror中先close()旧连接,再经setTimeout延时后调用connect()重建;- 重连延迟常量
SSE_RECONNECT_DELAY_MS当前取值为 3000 ms(src/ui/viewer/constants/timing.ts),而剧本建议的是 2 秒——两者都是合理的工程取值,若按剧本调参只需改这一处常量; - 消息分发覆盖五类
StreamEvent:initial_load(项目列表)、new_observation、new_summary、new_prompt、processing_status(处理状态与队列深度),新事件一律unshift到列表头部。
从源码结构看,剧本第 2 点"重连后走 REST 补洞"是当前实现中可补强的一环:onopen 里目前只清理重连定时器,尚未额外发起 REST 全量补齐请求;initial_load 事件承担了初始项目列表的填充,但 observations 的断档补齐依赖服务端重连时的初始推送是否完整。此外 src/services/worker/http/routes/ViewerRoutes.ts 中存在 text/event-stream 相关实现,是服务端"让 SSE 端点独立于插件 reload 存活"的修改落点。
需求 #1265:在 observation/summary 卡片上显示模型名
剧本给出的实现路径是一条先查存储、再查入口、最后打通管线的数据流排查:
- 先确认 DB 是否已有模型信息:在 src/services/sqlite/ 的 observation 类型定义与 store 中搜索
model。当前仓库的 SQLite 存储层以 src/services/sqlite/SessionStore.ts 为核心,模型字段是否已在 observations 表结构中,需要以该 store 的实际 schema 为准; - DB 里没有,再看 hook 输入:在 src/cli/types.ts 中搜索
model,确认NormalizedHookInput是否携带了当前会话使用的模型名——hook 是 observation 写入管线的第一站,输入里有的字段才有资格被透传; - 可用但未存储时:给 observations 表加列,并沿 handler 链路把字段线程化(thread through)到写入点;
- UI 呈现:在 viewer 卡片组件上以低调徽标(subtle badge)形式展示模型名,剧本给出的示例文案是
claude-sonnet-4-6。
仓库中已存在"observed model"相关的基础设施线索,例如 tests/transcripts/observed-model-extraction.test.ts 验证的模型名提取逻辑,说明模型识别在转录解析链路中已有部分能力可以复用;但在动手改卡片组件前,仍应以第 1、2 步的搜索结果决定是"读现有列"还是"加列并透传",避免凭空假设字段存在。
修复 #1268:Claude Code 升级后插件消失的自愈机制
问题与修复方案
Claude Code 更新可能破坏插件的符号链接或安装结构,导致插件"消失"。剧本要求的修复是在 SessionStart hook 中加一个轻量自检,在正常操作之前:
- 检查插件文件是否还在预期安装路径
~/.claude/plugins/marketplaces/thedotmack/; - 若缺失,自动重跑 marketplace 同步;
- 记录警告日志:
Plugin installation repaired after Claude Code update。
关键约束是轻量:只允许 existsSync() 级别的检查,禁止每次启动都做全量 sync。同步逻辑本体在 scripts/sync-marketplace.cjs,配套还有 scripts/restart-marketplace-worker.cjs 与发布流程里的 build-and-sync 脚本(见下文验证章节)。
从现有 hooks.json 看插件定位机制
plugin/hooks/hooks.json 中每条 hook 命令都内嵌了一段"插件根目录发现"shell 逻辑,这为自愈设计提供了现成的参照:它按优先级枚举候选目录——CLAUDE_PLUGIN_ROOT / PLUGIN_ROOT 环境变量指向的路径、~/.claude/plugins/cache/thedotmack/claude-mem/[0-9]*/ 下按版本号数字排序(含 .orphaned_at 孤儿标记过滤)的缓存版本、以及 ~/.claude/plugins/marketplaces/thedotmack/plugin 兜底路径——然后选择第一个包含 scripts/ 与关键脚本(如 bun-runner.js、worker-service.cjs、version-check.js)的目录,找不到时以 claude-mem: plugin scripts not found 报错退出。
从源码结构看,#1268 的自愈逻辑正可以挂接在这套发现机制之上:发现阶段本身已经能识别"缓存版本损坏、只剩 marketplaces 路径"的降级情况,因此自检只需在 SessionStart 的 worker-service.cjs start 之前确认关键脚本 existsSync() 成立,不成立时触发 scripts/sync-marketplace.cjs 重新同步即可,与现有 shell 定位逻辑天然兼容。
修复 #1340:清理 hooks.json 中失效的 setup.sh 引用
问题与排查步骤
hooks.json 曾引用一个在 v10.5.5 中已不存在的 scripts/setup.sh。剧本给出的处理分支:
- 若脚本是有意删除的,就从 hooks.json 里删掉该 setup hook 条目;
- 若是误删,就用
git log --oneline --all -- scripts/setup.sh查历史,确认其功能是否已迁移到其他脚本(剧本推测大概率是smart-install.js),再把 hook 指向正确的脚本; - 同时核查 OpenClaw 插件配置(#1332)是否受同一缺失脚本问题影响——OpenClaw 侧的配置见 openclaw/openclaw.plugin.json 与 openclaw/install.sh,可对照检查其中是否有指向已删除脚本的入口。
当前仓库的落地状态
这一项在仓库中已可见完整闭环:
- CHANGELOG.md 中明确记载
Removed legacy setup.sh script; - 当前 plugin/hooks/hooks.json 的
Setuphook 已不再引用任何.sh脚本,而是直接调用version-check.js(即 plugin/scripts/version-check.js),走的是上面提到的插件根目录发现 +node "$_P/scripts/version-check.js"的路径; - 回归保障由 tests/infrastructure/plugin-distribution.test.ts 提供,测试用例
should not reference removed setup.sh in Setup hook直接断言hooks.json内容不包含setup.sh,相邻用例还断言 Setup hook 必须调用version-check.js。
这套"删除脚本 + 更新 hook 指向 + 用测试锁住引用"的组合,正是 #1340 类"悬空引用"问题的标准处理范式,处理 #1332 时可复用同一思路。
收尾验证:测试、构建与 UI 回归
剧本要求的验收清单三步:
npm test,全部通过。以 package.json 为准,仓库实际脚本定义为bun test tests(即 npm scripttest底层跑的是 bun 测试器),所有新增行为(如 SSE 重连、插件自愈日志)建议补充对应测试用例;npm run build-and-sync。package.json 中该脚本展开为npm run build && npm run sync-marketplace && node scripts/restart-marketplace-worker.cjs——构建、市场同步、重启 marketplace worker 一条龙,恰好覆盖 #1268 自愈涉及的分发链路;- 打开
http://localhost:37777的 viewer UI,验证 observation 以正确 ID 展示。仓库中 37777 号端口在 src/npx-cli/cmem-memory-credentials.ts 中被定义为HOST_OBSERVER_DEFAULT_PORT默认端口,与剧本给出的验证地址一致;验证时重点核对卡片 ID 与get_observations返回 ID 的逐一相等,以及/reload-plugins之后 SSE 是否在 3 秒左右自动恢复推送。
小结
Phase 08 的六个条目虽然分散在 UI、hook、MCP、安装脚本不同层面,但共享同一个工程原则:用户可见的标识符(ID、模型名)、实时通道(SSE)与安装状态(插件路径)必须在客户端展示与服务端契约之间保持单一事实来源。ID 对齐以 src/servers/mcp-server.ts 的 get_observations schema 为锚,SSE 恢复以 src/ui/viewer/hooks/useSSE.ts 的重连常量为锚,安装自愈以 plugin/hooks/hooks.json 的插件发现逻辑与 scripts/sync-marketplace.cjs 为锚,悬空引用则用 tests/infrastructure/plugin-distribution.test.ts 这类断言测试长期锁定。按剧本顺序逐项落地并通过三步验收后,这一阶段对"用户信任"的损耗即被系统性修复。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00