makecindy / makecindy/cindy

[bug] maker-memory 静默降级到 %TEMP%\cindy-no-session:write 返回成功但数据不入 owner 库,list 返回 ok + 空数组与「空库」不可区分

Open
#2,341 3 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.