# How Munder Difflin Implements Semantic Memory Layer and Memory Condensation: A Technical Deep Dive

> Explore Munder Difflin's technical deep dive into its semantic memory layer and memory condensation process. Discover how it compresses agent memory while preserving context.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: deep-dive
- Published: 2026-08-20

---

**Munder Difflin combines a markdown-first semantic memory layer powered by the MemPalace CLI with an automated memory condensation process that compresses agent memory files using Claude Haiku to maintain strict size bounds while preserving critical context.**

Munder Difflin is an open-source agent framework that treats markdown files as the source of truth for long-term memory. The system implements both a **semantic memory layer** for fast vector search and a **memory condensation process** to prevent unbounded growth, ensuring agents retain instant access to relevant context without inflated memory files.

## What Is the Semantic Memory Layer?

The semantic memory layer transforms every agent’s plain-text [`memory.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/memory.md) file into a searchable knowledge base using an external **MemPalace** CLI integration. This layer indexes content in the background and exposes millisecond-scale retrieval capabilities through a controlled API.

### MemoryManager Architecture

The `MemoryManager` class in [`src/main/memory.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/memory.ts) (lines 60‑71) orchestrates the semantic layer. It discovers the MemPalace binary, manages a shared palace directory, and runs periodic indexing jobs. The layer activates only when the CLI is installed and the user enables `semanticMemory` in the harness configuration.

**Binary Discovery**

The `bin()` method (lines 80‑112) resolves the `mempalace` executable from PATH or common install locations:

- Linux/macOS: `~/.local/bin/mempalace`
- Windows: `%LOCALAPPDATA%\Programs\Python\Scripts\mempalace.exe`

```typescript
// src/main/memory.ts → lines 80-112
private bin(): string | null {
  // Resolves from PATH or common install locations
  // Returns null if unavailable, triggering graceful degradation
}

```

**Mining Loop Lifecycle**

The `startMineLoop()` method (lines 59‑73) launches a background process that runs `mempalace mine` for each agent whose [`memory.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/memory.md) changed since the last pass. The loop repeats every three minutes (`MINE_INTERVAL_MS`), guarded by a `this.mining` semaphore to prevent overlapping runs.

**Environment Injection**

The `env()` method (lines 38‑44) injects required environment variables into spawned agents:

```typescript
// src/main/memory.ts → lines 38-44
env(): Record<string, string> {
  return {
    MEMPALACE_PALACE_PATH: this.palaceDir,
    MEMPALACE_EMBEDDING_MODEL: 'text-embedding-3-small'
  };
}

```

### Public Search API

The manager exposes two async helpers consumed by the UI:

- `search(query, { wing?, results? })`: Executes `mempalace search` with a 120-second timeout.
- `wakeUp(wing?)`: Runs `mempalace wake-up` to generate a session digest.

Both delegate to a generic `runCli` that spawns the process and kills hung instances.

```typescript
// Perform semantic search from the main process
const { ok, output } = await memory.search(
  'how to reset the git repo',
  { results: 8 }
);

```

## How Memory Condensation Works

**Memory condensation** is the janitorial subsystem that prevents [`memory.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/memory.md) files from growing indefinitely. When a file exceeds configurable byte-size or section-count thresholds, the `MemoryReflector` rewrites it into a structured three-region format.

### The Three-Region Memory Model

Condensed memory files follow a strict schema:

1. **Pinned**: Durable facts marked with special syntax that are never removed.
2. **Condensed**: A synthetic summary generated by a headless Claude Haiku LLM.
3. **Recent**: Verbatim sections from the newest agent interactions.

### Trigger Conditions

The `shouldCondense()` method in [`src/main/reflect.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/reflect.ts) (lines 74‑84) evaluates files against the global budget (`BUDGET_BYTES = 131,072` ≈ 128 KB) and section limits:

```typescript
// src/main/reflect.ts → lines 74-84
private shouldCondense(bytes: number, mem: Memory, sections: Section[]): boolean {
  // Fires when bytes exceed percentage of budget OR section count exceeds sectionTrigger
  // Ignores files below minBytes threshold
}

```

### The Safe Condensation Pipeline

The `MemoryReflector` class (lines 2‑14) implements a safe-first pipeline to prevent data loss during LLM-driven compression:

| Step | Implementation |
|------|---------------|
| **Backup** | Lossless copy written to `hive/backups/<timestamp>/<agent>/memory.md` |
| **Summarize** | Tail sections (`evict`) fed to `runHiddenClaude` with `CONDENSE_SYSTEM` prompt requiring JSON output `{condensed, hoist}` |
| **Rebuild** | `rebuild()` assembles pinned heading, merged pinned lines, condensed summary, and kept recent sections |
| **Verify** | `verify()` checks structural integrity, size reduction (>5%), non-empty condensed block, and preservation of pinned lines |
| **Atomic Swap** | `atomicWrite()` fsyncs to temporary sibling, then renames over original; aborts on any failure |

```typescript
// src/main/reflect.ts → condensation flow (lines 200-244)
await this.summarize(memory, evictSections);  // Calls Claude-Haiku
const rebuilt = rebuild(pinned, condensed, recent);
if (verify({ oldBytes, newBytes, pinned, rebuilt }).ok) {
  await atomicWrite(memoryPath, rebuilt);
}

```

## Practical Implementation Examples

### Enable Semantic Memory

Toggle the feature at runtime via the settings API:

```typescript
// src/renderer/src/components/SettingsModal.tsx (line 232)
await window.cth.updateConfig({ semanticMemory: true });

```

### Trigger Manual Condensation

Force immediate compression for a specific agent:

```typescript
// src/main/reflect.ts → lines 37-45
const results = await reflector.reflectNow('agent-123');
// Returns: { id: 'agent-123', condensed: true, reason: 'condensed', bytesBefore: 140000, bytesAfter: 85000 }

```

### Query the Semantic Index

Retrieve contextually relevant snippets across all agents:

```typescript
const { ok, output, error } = await memory.search(
  'authentication flow errors',
  { wing: 'backend', results: 5 }
);
if (ok) {
  output.forEach(hit => console.log(hit.score, hit.text.slice(0, 200)));
}

```

## Summary

- **Markdown-first persistence**: Agent memories remain human-readable Git-diffable files that survive index failures.
- **External semantic indexing**: The `MemoryManager` integrates MemPalace CLI for millisecond-scale vector search, with automatic binary discovery and 3-minute mining loops.
- **Bounded growth guarantee**: `MemoryReflector` enforces a 128 KB budget per file using Claude Haiku to generate lossy-but-safe summaries.
- **Data-loss prevention**: Condensation uses atomic writes, pre-flight backups, and structural verification before replacing any file.
- **Graceful degradation**: If MemPalace is missing, the system continues with raw markdown only; disabling `semanticMemory` skips all CLI dependencies.

## Frequently Asked Questions

### What happens if the MemPalace CLI is not installed?

The `MemoryManager.available()` check returns `false` when `bin()` cannot resolve the executable. The harness continues operating with plain markdown files, and all search calls return empty results without crashing. Users can install MemPalace later and enable `semanticMemory` via the settings toggle at [`src/renderer/src/components/SettingsModal.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/SettingsModal.tsx).

### How does the condensation process prevent data loss?

The `MemoryReflector` implements a verify-don't-trust pattern. It creates a lossless backup in `hive/backups/` before any mutation, validates the LLM output for JSON schema compliance, checks that new files are at least 5% smaller, and uses `atomicWrite()` with fsync and rename operations. If any step fails, the original file remains untouched and an abort event is logged.

### What is the default size threshold for memory condensation?

The global budget is `BUDGET_BYTES = 131,072` (128 KB). Condensation triggers when a file exceeds a configurable percentage of this budget or when the number of `## ` section headers surpasses `sectionTrigger`. Files below `minBytes` are ignored regardless of section count.

### Can I disable automatic memory condensation?

Yes. While the `MemoryReflector` runs as part of the main process lifecycle, you can set the `sectionTrigger` and budget percentages to artificially high values in the harness configuration, effectively preventing `shouldCondense()` from ever returning `true`. Manual condensation via `reflectNow()` remains available for ad-hoc cleanup.