Summary
AutoMemoryBridge.curateIndex() in @claude-flow/memory@3.0.0-alpha.12 and @3.0.0-alpha.13 silently overwrites a hand-curated MEMORY.md with a single-line stub (# Claude Flow V3 Project Memory\n) when the project's memory folder uses Claude Code's native <type>_<topic>.md filename convention instead of the 7 hardcoded filenames the package expects.
The bug fires on every Stop hook tick (i.e. end of every assistant turn), so any custom MEMORY.md content is destroyed within seconds.
Affected versions
@claude-flow/memory@3.0.0-alpha.11
@claude-flow/memory@3.0.0-alpha.12
@claude-flow/memory@3.0.0-alpha.13 (confirmed via diff — curateIndex() byte-identical to alpha.12)
- Pulled in transitively by
@claude-flow/cli@^3.5.44 through ^3.5.75 (current latest)
Reproduction
- Project has
@claude-flow/cli installed (which transitively installs @claude-flow/memory)
- Project's
.claude/settings.json has the standard Stop hook calling auto-memory-hook.mjs sync
- Memory folder at
~/.claude/projects/<encoded-project-path>/memory/ has hand-curated MEMORY.md with section headings that DON'T match the 7 hardcoded topic filenames
- Run any Claude Code session and let the
Stop hook fire once
- Result:
MEMORY.md is now # Claude Flow V3 Project Memory\n and nothing else
Root cause
In node_modules/@claude-flow/memory/dist/auto-memory-bridge.js:
Line ~22 — hardcoded 7-file map:
const DEFAULT_TOPIC_MAPPING = {
'project-patterns': 'patterns.md',
'debugging': 'debugging.md',
'architecture': 'architecture.md',
'performance': 'performance.md',
'security': 'security.md',
'preferences': 'preferences.md',
'swarm-results': 'swarm-results.md',
};
Line ~290 — curateIndex() only reads those 7 files:
async curateIndex() {
await this.ensureMemoryDir();
const sections = {};
for (const [category, filename] of Object.entries(this.config.topicMapping)) {
const topicPath = path.join(this.config.memoryDir, filename);
if (existsSync(topicPath)) {
const content = await fs.readFile(topicPath, 'utf-8');
const summaries = extractSummaries(content);
if (summaries.length > 0) sections[category] = summaries;
}
}
// ... pruneSectionsToFit + buildIndexLines + writeFile
await fs.writeFile(this.getIndexPath(), lines.join('\n'), 'utf-8');
}
Line ~687 — buildIndexLines() returns just the title when sections is empty:
function buildIndexLines(sections, topicMapping, sectionOrder) {
const lines = ['# Claude Flow V3 Project Memory', ''];
// empty sections → no further appends
return lines;
}
Line ~193 — syncToAutoMemory() calls this.curateIndex() internally, so commenting out the explicit bridge.curateIndex() call in auto-memory-hook.mjs doSync() (the obvious workaround) is not sufficient — the internal call still fires.
Why every Claude Code project hits this
Claude Code's native auto-memory system (per Anthropic's documentation) writes files using a <type>_<topic>.md convention:
session_2026-04-07_margin_ranges_and_momo.md
project_finance_module_with_momo.md
feedback_no_secrets_in_output.md
reference_planning_structure.md
user_role.md
idea_dynamic_island_mac.md
None of these match the 7 filenames in DEFAULT_TOPIC_MAPPING. The intersection is empty for any project that uses Claude Code's native auto-memory system, which means sections is always empty and MEMORY.md is always overwritten with the stub.
In my case I had a 14k MEMORY.md with 17 hand-curated sections. It was being silently nuked on every assistant turn for an unknown period (probably weeks). I only noticed when I tried to write new content to MEMORY.md and watched it get reset within the same conversation turn.
Recovery was possible only because importFromAutoMemory() had been writing the previous content into the backend auto-memory-store.json keyed auto-memory:MEMORY.md:<heading>. I extracted ~70 entries, sorted by createdAt, grouped by heading, and reconstructed the original 17-section index. Without that backend snapshot, the content would have been permanently lost.
Workaround (for users who hit this in production)
In <project>/.claude/helpers/auto-memory-hook.mjs doSync(), monkey-patch the bridge instance right after creation:
const bridge = new memPkg.AutoMemoryBridge(backend, bridgeConfig);
// WORKAROUND: bridge.curateIndex() overwrites hand-curated MEMORY.md with a stub
// when the memory folder uses Claude Code's native filename convention. See ruvnet/ruflo#XXXX.
// JS instance properties shadow prototype methods, so this no-op shadows BOTH the
// explicit doSync() call AND the internal call from syncToAutoMemory() line 193.
bridge.curateIndex = async () => { /* no-op */ };
try {
const syncResult = await bridge.syncToAutoMemory();
// ...
}
Suggested fixes (pick one)
Option A — Make curateIndex() non-destructive when sections is empty:
if (Object.keys(sections).length === 0) {
// Nothing to curate — leave existing MEMORY.md alone instead of stubbing it
return;
}
Option B — Add user-configurable topicMapping that's actually read from .claude-flow/config.yaml. Currently auto-memory-hook.mjs readConfig() only parses boolean enabled flags, not the topicMapping object.
Option C — Detect Claude Code's native <type>_<topic>.md convention and route those files into the index automatically by parsing the filename prefix as the category.
Option D — Document this incompatibility prominently in the README, recommending users disable curateIndex() if they're already using Claude Code's native auto-memory system.
My vote: Option A is the safest minimal fix. A function called "curate" should never destroy content. Refusing to write an empty index is the principle-of-least-surprise fix.
Environment
- Node: v24.12.0
- npm: v11.12.1
- macOS Darwin 25.1.0 (Apple Silicon)
@claude-flow/cli: v3.5.75
@claude-flow/memory: v3.0.0-alpha.12 (also tested alpha.13 — same bug)
ruflo global: v3.5.75
- Claude Code: v2.1.94
Summary
AutoMemoryBridge.curateIndex()in@claude-flow/memory@3.0.0-alpha.12and@3.0.0-alpha.13silently overwrites a hand-curatedMEMORY.mdwith a single-line stub (# Claude Flow V3 Project Memory\n) when the project's memory folder uses Claude Code's native<type>_<topic>.mdfilename convention instead of the 7 hardcoded filenames the package expects.The bug fires on every
Stophook tick (i.e. end of every assistant turn), so any custom MEMORY.md content is destroyed within seconds.Affected versions
@claude-flow/memory@3.0.0-alpha.11@claude-flow/memory@3.0.0-alpha.12@claude-flow/memory@3.0.0-alpha.13(confirmed via diff —curateIndex()byte-identical to alpha.12)@claude-flow/cli@^3.5.44through^3.5.75(current latest)Reproduction
@claude-flow/cliinstalled (which transitively installs@claude-flow/memory).claude/settings.jsonhas the standardStophook callingauto-memory-hook.mjs sync~/.claude/projects/<encoded-project-path>/memory/has hand-curatedMEMORY.mdwith section headings that DON'T match the 7 hardcoded topic filenamesStophook fire onceMEMORY.mdis now# Claude Flow V3 Project Memory\nand nothing elseRoot cause
In
node_modules/@claude-flow/memory/dist/auto-memory-bridge.js:Line ~22 — hardcoded 7-file map:
Line ~290 —
curateIndex()only reads those 7 files:Line ~687 —
buildIndexLines()returns just the title whensectionsis empty:Line ~193 —
syncToAutoMemory()callsthis.curateIndex()internally, so commenting out the explicitbridge.curateIndex()call inauto-memory-hook.mjs doSync()(the obvious workaround) is not sufficient — the internal call still fires.Why every Claude Code project hits this
Claude Code's native auto-memory system (per Anthropic's documentation) writes files using a
<type>_<topic>.mdconvention:session_2026-04-07_margin_ranges_and_momo.mdproject_finance_module_with_momo.mdfeedback_no_secrets_in_output.mdreference_planning_structure.mduser_role.mdidea_dynamic_island_mac.mdNone of these match the 7 filenames in
DEFAULT_TOPIC_MAPPING. The intersection is empty for any project that uses Claude Code's native auto-memory system, which meanssectionsis always empty andMEMORY.mdis always overwritten with the stub.In my case I had a 14k MEMORY.md with 17 hand-curated sections. It was being silently nuked on every assistant turn for an unknown period (probably weeks). I only noticed when I tried to write new content to MEMORY.md and watched it get reset within the same conversation turn.
Recovery was possible only because
importFromAutoMemory()had been writing the previous content into the backendauto-memory-store.jsonkeyedauto-memory:MEMORY.md:<heading>. I extracted ~70 entries, sorted bycreatedAt, grouped by heading, and reconstructed the original 17-section index. Without that backend snapshot, the content would have been permanently lost.Workaround (for users who hit this in production)
In
<project>/.claude/helpers/auto-memory-hook.mjsdoSync(), monkey-patch the bridge instance right after creation:Suggested fixes (pick one)
Option A — Make
curateIndex()non-destructive whensectionsis empty:Option B — Add user-configurable
topicMappingthat's actually read from.claude-flow/config.yaml. Currentlyauto-memory-hook.mjs readConfig()only parses boolean enabled flags, not the topicMapping object.Option C — Detect Claude Code's native
<type>_<topic>.mdconvention and route those files into the index automatically by parsing the filename prefix as the category.Option D — Document this incompatibility prominently in the README, recommending users disable
curateIndex()if they're already using Claude Code's native auto-memory system.My vote: Option A is the safest minimal fix. A function called "curate" should never destroy content. Refusing to write an empty index is the principle-of-least-surprise fix.
Environment
@claude-flow/cli: v3.5.75@claude-flow/memory: v3.0.0-alpha.12 (also tested alpha.13 — same bug)rufloglobal: v3.5.75