[bug] maker-memory 静默降级到 %TEMP%\cindy-no-session:write 返回成功但数据不入 owner 库,list 返回 ok + 空数组与「空库」不可区分
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 摘要
`cindy_memory` MCP 的 store 根目录在应用启动早期确定。当 owner 作用域尚未就绪时,它会退到 `%LOCALAPPDATA%\Temp\cindy-no-session\\maker-memory\`,**并在该应用实例的整个生命周期内不再重新解析**。
结果是一次**静默数据丢失**:
- `memory_write` 返回成功,但分片落在 `%TEMP%` 下的一次性目录,不进 owner 正库
- `memory_list` 返回 `{"ok": true, "data": []}` —— 与「这个 workdir 还没存过 memory」**完全不可区分**
- `memory_read` 对正库里确实存在的分片返回 `NOT_FOUND`
- 全程**不产生任何日志**(`logs/main-*.log` 里 grep 不到 owner / memory / no-session 相关行)
由于系统提示明确告诉 agent「cindy_memory 是本环境下唯一的持久化路径」,agent 会照此依赖一条实际不持久的通道。
## 影响
实测在一个 workdir 上丢了 **2 条**业务 memory(都在故障期写入、`write` 均返回成功)。`%TEMP%` 会被系统清理,所以这类丢失最终不可恢复。
更棘手的是**可信度污染**:其中一条在 git commit message 里被记为「已入 memory」,事后看那条记录是错的 —— 静默失败会把假事实写进审计痕迹。
同机两个并行会话**同时**中招,说明作用域是 app 实例级而非会话级。
## 观察到的现象
1. `memory_list` → `{"ok": true, "data": []}`
2. `memory_read({filename: "<正库里确实存在的文件>"})` → `NOT_FOUND`
3. 正库 `%APPDATA%\Cindy\owners\\maker-memory\\` 状态健康:`fts.db` 的 `integrity_check` = ok、`memory_fts` 76 行、78 个 `.md` 分片、`MEMORY.md` 前一天正常重建
4. 实际被使用的根:`%LOCALAPPDATA%\Temp\cindy-no-session\\maker-memory\\` —— 只有 `fts.db` / `meta.json`,**零分片**
5. 该目录的 `meta.json` 里 `absPath` **是正确的 workdir 绝对路径** → workdir 解析正常,**错的是 owner 作用域**
## 时间线证据
```
10:39:12 Cindy.exe (PID ) 启动
10:40:41 cindy-no-session//maker-memory/ 创建 ← 启动后约 90 秒
11:42 一个会话开始 → 拿到的已经是这个根
14:02:59 某会话 memory_write 成功 → 落进 %TEMP%(第 1 条丢失)
14:13:05 同上(第 2 条丢失)
14:15:17 重启 Cindy(新 PID)
之后 memory_list 正常返回 78 条;新 PID 的 no-session 目录零分片
```
`cindy-no-session` 目录按 Cindy 主进程 PID 命名,实测**近乎每次启动都会生成一个**(该机器上累计 21 个实例目录),但绝大多数是空壳 —— 也就是说兜底根的创建很常见、真正吃掉写入是偶发,因此极难被发现。
## 已排除的原因
本次故障发生在一次断电后的冷启动。逐条排查后,下列断电相关机制**均已排除**:
- 身份文件损坏:`.owner-namespace-claim-v1.json` 完好,`ownerKey` 正确、`complete: true`
- 零长度文件损坏:只有 `.verified` / `LOCK` / `*-journal` 这类正常标记
- 上次进程留下 stale claim / PID 复用碰撞:`.dev-instances/` 里 3 条残留记录对应的 PID 均已 dead
同时有一条反证表明 owner 在故障期是**可解析的**:故障当天有其他组件正常读写了 owner 正库(其 `fts.db` 的 WAL 被 checkpoint 并入主库,而诊断侧只以 `mode=ro` 打开过、无写权限)。
因此推测:**启动竞态** —— memory 子系统在 owner 作用域套上之前就把根缓存住了;冷启动(大量进程同时启动、登录态需重新拉取)会放大命中概率。**具体为何未就绪需上游确认。**
## 期望行为(按优先级)
1. 🔴 **拿不到 owner 时应报错,而不是静默兜底**。仓库里已有 `MAKER_MEMORY_NOT_READY` 这个错误码,「宁可拒绝不要假装」的模式已存在,owner 缺失路径也应走它
2. 🔴 **owner 就绪后重新解析根**,或至少在切换后迁移/合并已写入兜底根的数据
3. 🟡 **降级必须可见**:`memory_write` / `memory_list` 的返回里带 `degraded: true` + 实际 store 路径。当前 `ok:true + []` 与「空库」不可区分,是这个 bug 最难识破的地方
4. 🟡 **落一条日志**。这么严重的降级目前零日志
## 给其他撞到的人:诊断与抢救
```bash
# 诊断:memory_list 返空时
find "$LOCALAPPDATA/Temp" -maxdepth 5 -type d -name maker-memory
# 命中 cindy-no-session 即为本 bug
# 抢救
# 1) 把该目录下的 *.md(MEMORY.md 除外)拷到持久位置
# 2) 重启 Cindy
# 3) 经 memory_write 重新写入 —— 不要直接拷进正库目录,会让 FTS5 索引与文件不同步
```
## 环境
- Windows 11 Pro (26200)
- Cindy 桌面版(`%LOCALAPPDATA%\Programs\Cindy\Cindy.exe`)
- maker-memory 模式,单 owner
- 一个 git 仓库 workdir,正库内 78 个分片
---
**关联**:#1652(maker-memory 接管时不检测同 workdir 已有的原生 per-cwd memory)—— 同属「持久化层静默失效」类。
Contributor guide
Research direction
Start at the maker-memory initialization and owner-scope resolution paths used by memory_write, memory_list, and memory_read. Check how MAKER_MEMORY_NOT_READY is handled when the owner is unavailable, then trace when the store root is cached. Done means unavailable owners cannot silently write to a temporary root, and the affected behavior is covered by tests or observable diagnostics.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- sqlite, typescript
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100