AutoMemoryBridge.curateIndex() destroys a user-maintained MEMORY.md once the bridge writes its first topic file
- Dominant language
- TypeScript
- Stars
- 72.7k
- Forks
- 8.6k
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 83
Description
## Summary
`AutoMemoryBridge.curateIndex()` rebuilds `MEMORY.md` from the `DEFAULT_TOPIC_MAPPING` files
alone. When the configured `memoryDir` is Claude Code's own
`~/.claude/projects//memory` — which is what `resolveAutoMemoryDir()` targets by design —
that file is the **user's hand-maintained memory index**, and it is silently replaced by a
stub on the first sync that writes a topic file.
Measured on a copy of a real memory directory:
```
MEMORY.md before: 75 lines (hand-curated, 49 links to topic files)
MEMORY.md after : 6 lines
index links lost: 49
```
The `#1556` guard prevents this only while **no** `DEFAULT_TOPIC_MAPPING` file exists. The
bridge creates the first one itself (`patterns.md`), so the guard stops applying after the very
first insight, and every later sync rewrites the index.
## Why this is becoming reachable now
On Windows this has been masked by the drive-letter slug bug (#2423, Bug 3): the resolved
directory is illegal, `ensureMemoryDir()` throws `ENOENT`, and `syncToAutoMemory()` aborts
before `curateIndex()` runs. In other words the path bug has been accidentally protecting
users' memory indexes. **Anyone who applies the #2423 fix inherits this data loss.** I hit
exactly that sequence: fixed the slug, then checked what the now-working sync would write
before pointing it at anything real.
## Reproduction
Against a directory that already contains a user-written `MEMORY.md` and topic files whose
names are *not* in `DEFAULT_TOPIC_MAPPING` (Claude Code's own memory folders use descriptive
per-topic filenames, which is why none of them match the hardcoded mapping):
```js
import { AutoMemoryBridge } from '@claude-flow/memory';
const bridge = new AutoMemoryBridge(backend, { memoryDir: DIR, syncMode: 'manual' });
await bridge.syncToAutoMemory(); // 1) no topic file yet -> #1556 guard holds, MEMORY.md intact
bridge.recordInsight({
category: 'project-patterns',
summary: 'anything',
source: 'repro',
confidence: 0.95,
});
await bridge.syncToAutoMemory(); // 2) creates patterns.md, then rewrites MEMORY.md
```
After step 2:
```
# Claude Flow V3 Project Memory
## Project Patterns
- anything
- See `patterns.md` for details
```
Everything the user wrote is gone. There is no backup and no prompt.
## Environment
- Windows 11, Node 24
- `@claude-flow/memory@3.0.0-alpha.22`, local `node_modules`
- Reproduced on a sandbox copy; nothing live was harmed
## Suggested fixes (any one of these closes the hole)
1. **Never rebuild an index the bridge did not author.** Write a marker when `curateIndex()`
creates `MEMORY.md`, and refuse to overwrite a file that lacks it (report via an event
instead, as the existing `index:skipped` path already does).
2. **Preserve unknown content.** Curate only inside a delimited block
(`` … ``) and leave everything outside it
untouched — this also makes the feature co-exist with Claude Code's own index rather than
competing for the same file.
3. **Do not default into Claude Code's directory.** Default `memoryDir` to a package-owned
folder (e.g. `.claude-flow/auto-memory`) and require opt-in to write into
`~/.claude/projects//memory`.
4. At minimum: back the file up (`MEMORY.md.bak`) before the first destructive rewrite, and
document in the README that the bridge takes ownership of `MEMORY.md`.
Option 2 seems the most useful in practice, since the stated goal of ADR-048 is to feed Claude
Code's memory system rather than replace its index.
## Workaround
Pass an explicit `memoryDir` pointing at a package-owned directory, so `curateIndex()` can only
ever rewrite a file the bridge itself owns:
```js
const bridgeConfig = {
memoryDir: join(PROJECT_ROOT, '.claude-flow', 'auto-memory'),
workingDir: PROJECT_ROOT,
syncMode: 'on-session-end',
};
```
Related: #2423 (Windows auto-memory bugs — the slug fix there is the trigger for this one),
#1556 (the partial guard), #2282.
Contributor guide
Research direction
Start at AutoMemoryBridge.syncToAutoMemory() and curateIndex(), then inspect the existing index:skipped path and the #1556 guard. Reproduce the two-sync sequence with a hand-maintained MEMORY.md and a recorded insight. Done means a later sync does not erase user content when the bridge did not author the index, while the existing skip behavior remains observable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100