Skip to content

bridge.curateIndex() overwrites hand-curated MEMORY.md with stub when memory folder uses Claude Code's native filename convention (@claude-flow/memory@3.0.0-alpha.12, alpha.13) #1556

@dr-snob

Description

@dr-snob

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

  1. Project has @claude-flow/cli installed (which transitively installs @claude-flow/memory)
  2. Project's .claude/settings.json has the standard Stop hook calling auto-memory-hook.mjs sync
  3. 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
  4. Run any Claude Code session and let the Stop hook fire once
  5. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions