Operator's Guide

OpenClaw Memory Management –
What breaks, and how to fix it.

MEMORY.md, daily files, and the three-phase dreaming cycle. What actually happens in production after weeks of continuous operation — and the hygiene practices that keep your agent's memory clean instead of ballooning quietly in the background.

By Sora Labs April 2026 ~18 min read
What's in this guide
  1. Why memory is where autonomous agents break
  2. The four layers of OpenClaw memory
  3. The append-only rule for MEMORY.md
  4. Daily memory file hygiene
  5. The memory protocol every cron job needs
  6. Dreaming: how it works
  7. How dreaming broke my memory files
  8. The heartbeat duplication trap
  9. Editing memory files safely
  10. Reindexing and health checks
  11. Mistakes I made so you don't have to
01

Why memory is where autonomous agents break

Every failure mode I've hit running an autonomous agent 24/7 traces back to memory. Not the model. Not the channels. Not the tools. Memory.

When your agent runs one session, memory barely matters — the conversation holds everything it needs. When your agent runs forty-eight heartbeats a day, six cron jobs, and an overnight consolidation pass for weeks on end, memory becomes the system. It decides what the agent remembers, what it forgets, what it repeats, and what it never figures out.

Get it right and the agent compounds. Every session builds on the last. It stops repeating itself, stops asking the same questions, starts catching patterns you hadn't noticed yourself.

Get it wrong and you don't see it immediately. Daily memory files grow from 30 lines to 500. Token usage climbs. The agent gets slower and repeats itself more. One morning you open a file and realize you've been paying for your agent to re-read the same paragraph twelve times a day.

This guide is the reference I wish I'd had when I started. It covers the parts of OpenClaw memory nobody documents until something breaks — and the specific things that broke for me in production, with the fixes that actually held.

02

The four layers of OpenClaw memory

OpenClaw memory isn't one thing. It's four layers, each with a different purpose, lifespan, and failure mode. If you don't know which layer you're looking at, you can't reason about what's going wrong.

┌─────────────────────────────────────────────────────────┐ │ 1. Working context (in-session) │ │ what the model sees right now — disappears at exit │ ├─────────────────────────────────────────────────────────┤ │ 2. Daily notes (memory/YYYY-MM-DD.md) │ │ running context — 2–3 lines per cron, loaded at start │ ├─────────────────────────────────────────────────────────┤ │ 3. Long-term memory (MEMORY.md) │ │ durable facts — append-only, loaded every session │ ├─────────────────────────────────────────────────────────┤ │ 4. Semantic index (memory-core) │ │ vector search — used by memory_search, not editable │ └─────────────────────────────────────────────────────────┘

Working context

What the model sees in a single turn — the system prompt, the user message, loaded files, tool results. It disappears when the session ends. You don't manage this directly; OpenClaw decides what to load based on the session type.

Daily notes — memory/YYYY-MM-DD.md

One file per day. This is where cron jobs and heartbeats append short summaries of what they did. Today's and yesterday's daily notes are loaded automatically at the start of every session, which is what gives your agent "what happened recently" context without dragging the whole history along.

This file should stay small. If it's bigger than 50 lines on any given day, something is wrong — and that something is usually section 07 of this guide.

Long-term memory — MEMORY.md

The single file that lives at the workspace root. This is your agent's durable memory — facts, decisions, preferences, lessons learned. It loads at the start of every DM session, so anything in here is always available to the agent. Treat this like it matters, because it does.

Semantic index

Behind the scenes, the memory-core plugin embeds every memory file and makes them searchable by meaning, not just keyword. When the agent runs memory_search "prospecting results", this is the layer doing the work. You don't edit it directly. You reindex it after meaningful changes to the underlying files.

Key insight

Most memory problems aren't about the files you can see — they're about mismatches between layers. An edit to MEMORY.md doesn't update the semantic index until you reindex. A heartbeat that writes garbage to a daily note pollutes everything that reads from that note. Think in layers.

03

The append-only rule for MEMORY.md

Your agent should never rewrite MEMORY.md. It should only append to it.

This rule sounds academic until the first time an agent decides to "clean up" its memory file and you lose three weeks of accumulated context in a single overnight run. Then it sounds like the most important rule in the workspace.

Put this in OPERATIONS.md so it's in front of the agent every session:

OPERATIONS.md
## Memory rules (non-negotiable)
- MEMORY.md is append-only. Never overwrite, never rewrite sections,
  never "reorganize." New facts go at the bottom as new entries.
- Daily notes (memory/YYYY-MM-DD.md) are the only memory files you
  may edit freely. Keep entries to 2–3 lines per cron run.
- If a fact in MEMORY.md is wrong, add a corrected entry below it.
  Do not edit the original.

The reason is architectural, not stylistic. MEMORY.md gets indexed. It's referenced by other memory artifacts. When your agent rewrites it, stale references break, the semantic index goes out of sync, and dreaming's rehydration step (section 06) quietly starts skipping candidates because the snippets it expected to find no longer exist.

The only thing that should write to MEMORY.md automatically is the deep phase of dreaming, and only when it passes three evidence gates. Everything else — you, the agent, the cron jobs — writes to daily notes.

⚠ The one you'll be tempted to break

Sooner or later you'll look at MEMORY.md and see clutter you want to clean up. Resist. If you must edit it manually, do it as an operator, outside the agent, with a git commit before and after — never let the agent do it. The agent that rewrites memory once will do it again.

04

Daily memory file hygiene

Daily notes should be small. A good daily file is 10–30 lines at the end of the day. A bad one is 500. The difference is what your cron jobs are telling the agent to write.

The 2–3 line rule

Every cron job should append a summary that's short enough to skim. What happened, what changed, what to remember. Not a full status report. Not a recap of every tool call. Not the same template filled in with slightly different numbers. Two or three lines.

memory/2026-04-19.md — good
## 10:00 — prospecting run
Sent 6 outreach emails (4 P3, 2 P1). Two bounced from
construction vertical — domain logged. Pipeline at 23 active.

## 12:00 — reply check
One reply from Edwin at ATS, positive on CTEIG calculator.
Nikki notified via WhatsApp.

## 14:00 — memory sweep
No changes.

Log deltas, not status

This is the single most important rule for keeping daily files small. If your heartbeat prompt says "write a status summary every cycle," the agent will write the same status summary every cycle. Twelve times a day. Every day.

Change the prompt to log deltas — things that actually changed since the last entry. If nothing changed, one line: no changes. Twelve "no changes" lines per day is infinitely less noise than twelve four-sentence status blocks that dreaming will then promote overnight.

HEARTBEAT.md — delta-only prompt
On every heartbeat sweep:

1. Check for new replies in inbox. If any, handle and log.
2. Compare current state to the last heartbeat entry in today's
   daily note.
3. If something changed (new reply, new subscriber, new error,
   pipeline shift), log a 1–2 line delta.
4. If nothing changed, append a single line: "no changes."
5. Never restate status that hasn't moved.

Put persistent status in MEMORY.md, update in place

If there's a number the agent needs to know at all times — active pipeline count, current monthly revenue, number of paying customers — don't have the heartbeat repeat it in every daily note. Put a ## Current status block in MEMORY.md and have the agent update that specific line in place with str_replace. The daily notes stay clean. The status stays current. The agent still sees it every session.

✓ The test

At the end of the day, open your daily note. If you can read the entire file in thirty seconds and actually learn something, it's working. If you're skimming past repeated templates to find the real signal, fix the prompt.

05

The memory protocol every cron job needs

Cron sessions don't inherit instructions from other sessions. Every cron run is a blank slate that gets the system prompt, OPERATIONS.md, today's and yesterday's daily notes, and MEMORY.md — then dies. If you don't explicitly tell the cron job to search memory and log to memory, it won't.

Bake this into every cron message:

cron message template
Before starting: run memory_search for "[relevant topic]" to see
what's been tried or decided before. Factor that context in.

[... actual task ...]

After finishing: append a 2–3 line summary to today's daily
note (memory/YYYY-MM-DD.md) covering what you did, what worked,
what didn't, and any follow-up needed.

The search before, log after loop is what turns a bunch of independent cron jobs into an agent that learns. Without it, each cron run is amnesiac. With it, the prospecting job on Wednesday knows what the prospecting job on Monday tried and adjusts.

Search queries that work, and queries that don't

Semantic search is good but not magic. Vague queries return mediocre results. Specific queries return the thing you want.

memory_search usage
# Weak — too vague, returns anything loosely adjacent
memory_search "prospecting"

# Better — scoped to the actual decision
memory_search "construction outreach reply rates"

# Best — specific enough to match the original entry
memory_search "manual targets file qualified prospects skip verification"
Put the protocol in the playbook, not the message

Long cron messages burn tokens. Instead of pasting the protocol into every cron command, put it in OPERATIONS.md as a rule — "Every cron job runs memory_search before acting and appends to the daily note after." Then every cron message can be shorter because the protocol is already loaded.

06

Dreaming: how it works

Dreaming is OpenClaw's overnight memory consolidation. When enabled, memory-core schedules a managed cron job (default 3 AM daily) that runs three phases in sequence: Light → REM → Deep. Only the deep phase writes to MEMORY.md. The other two stage and reflect.

It's opt-in and off by default. You enable it from chat with /dreaming on or in config under plugins.entries.memory-core.config.dreaming.enabled.

Daily notes + session transcripts + recall traces │ ▼ ┌──────────────────────┐ │ Light Sleep │ stage & dedupe candidates └──────────┬───────────┘ (records reinforcement signals) │ ▼ ┌──────────────────────┐ │ REM Sleep │ extract recurring themes └──────────┬───────────┘ (records more signals) │ ▼ ┌──────────────────────┐ │ Deep Sleep │ score, gate, promote └──────────┬───────────┘ → MEMORY.md │ ▼ DREAMS.md (human-readable diary)

The three phases

Light sleep reads recent daily memory files, dedupes entries, and stages candidates in the short-term recall store. It records "light phase signal" hits that boost ranking later. It does not write to MEMORY.md.

REM sleep looks at everything staged and extracts recurring themes — the candidates that keep showing up across multiple days. It records REM signal hits that further boost ranking. It also does not write to MEMORY.md.

Deep sleep scores every candidate using six weighted signals, applies the reinforcement boosts from the previous two phases, and promotes anything that crosses all three threshold gates. Deep is the only phase that writes to MEMORY.md.

The three gates

A candidate must pass all three to get promoted:

These gates exist specifically to prevent a single noisy entry from getting promoted on weight of frequency alone. A memory has to show up in different contexts to be worth keeping forever.

The rehydration step that saves you

Before deep sleep writes a candidate to MEMORY.md, it rehydrates the snippet from the live daily file. If the original content has been deleted or edited, the promotion is skipped. This is a quiet but important safety mechanism — it's why you can clean up old daily files (section 07) without worrying that dreaming will promote phantom references.

Useful commands

terminal
# Preview what would be promoted without writing
openclaw memory promote

# Apply promotions manually (if not using the managed cron)
openclaw memory promote --apply

# Explain why a specific candidate would or wouldn't promote
openclaw memory promote-explain "construction prospecting"

# Preview REM reflections without writing
openclaw memory rem-harness

# Check overall dreaming health
openclaw memory status --deep
Don't enable dreaming on day one

Dreaming consolidates whatever you feed it. If your daily notes are noisy, it promotes noise. Get your daily hygiene (section 04) and heartbeat prompt (section 08) right first, then turn dreaming on. Otherwise you're automating the bad input.

07

How dreaming broke my memory files

This is the section the rest of the guide has been building toward. It's also the one that will save you the most time.

After upgrading to a newer OpenClaw release, I did the usual post-upgrade checks. A few days later I noticed tokens climbing on every heartbeat. Open ended a daily note expecting 30 lines and found 500+. The pattern repeated every day for two weeks before I actually sat down to read one of these files.

The files were stuffed with staged dreaming candidates that never got cleaned up. Hundreds of lines of this:

memory/2026-04-10.md — the bloat
## Light Sleep

Candidate: heartbeat status check, pipeline 23 active, 0 replies
confidence: 0.62
evidence: memory/2026-04-10.md:14
status: staged

Candidate: heartbeat status check, pipeline 23 active, 0 replies
confidence: 0.61
evidence: memory/2026-04-10.md:28
status: staged

Candidate: heartbeat status check, pipeline 23 active, 0 replies
confidence: 0.63
evidence: memory/2026-04-10.md:42
status: staged

[... 180 more of these ...]

What was actually happening

Two failure modes, compounding:

  1. My heartbeat was running every 2 hours and appending a status summary to the daily file. Same four sentences, twelve times a day.
  2. Dreaming's light phase was reading those daily files, staging candidates, and writing the staged blocks back into the same daily files under ## Light Sleep headers. Some got promoted to MEMORY.md overnight. The ones that didn't pass the gates just sat there. Forever.

On the next sweep, the daily file was scanned again — and now it contained the previous night's staged candidates alongside the real notes. Light phase ingested those as fresh material. Dreaming was eating its own output.

Meanwhile, my MEMORY.md had the same paragraph about pipeline status promoted twenty different times, each with slightly different wording, because dreaming was faithfully doing its job on the duplicate heartbeats I was feeding it.

⚠ This is a known issue, not just a config problem

The feedback loop where dreaming's staged blocks get re-ingested on subsequent sweeps is documented in the OpenClaw repo. The underlying fix is upstream; the operator workaround below is what I used to unblock in the meantime. Check whether your OpenClaw version already strips dreaming metadata during ingestion — if yes, you only need parts 2 and 3 of the fix.

The fix, in order

1. Strip the staged blocks from every old daily file.

A sed pass through the memory/ directory, scoped to anything older than today. This deletes the ## Light Sleep block and everything under it until the next ## header. Daily files dropped from 500+ lines to under 10.

terminal
# Always back up first
cd ~/.openclaw/workspace
cp -r memory memory.backup-$(date +%Y%m%d)

# Strip ## Light Sleep blocks from every file EXCEPT today
TODAY=$(date +%Y-%m-%d)
for f in memory/*.md; do
  [ "$(basename "$f" .md)" = "$TODAY" ] && continue
  sed -i.bak '/^## Light Sleep/,/^## /{/^## Light Sleep/d;/^## /!d;}' "$f"
done

# Do the same for any REM blocks if present
for f in memory/*.md; do
  [ "$(basename "$f" .md)" = "$TODAY" ] && continue
  sed -i.bak '/^## REM Sleep/,/^## /{/^## REM Sleep/d;/^## /!d;}' "$f"
done

# Check the damage
wc -l memory/*.md | sort -n

2. Rewrite MEMORY.md by hand.

Open it, deduplicate the promoted entries, keep one clean version of each unique fact. This is the one and only time you edit MEMORY.md directly — do it as the operator, with a git commit before and after. Don't let the agent near it.

3. Change the heartbeat to log deltas only.

This is the root-cause fix. See section 04. If your heartbeat isn't writing duplicate status blocks, dreaming has nothing to keep re-promoting. The sed cleanup treats the symptom; the delta-only prompt treats the cause.

4. Reindex.

terminal
openclaw memory index --agent main --verbose
openclaw memory status --deep

Before and after

Daily memory files
500+ lines under 30
MEMORY.md
58 lines 45 unique
Total memory dir
bloated ~50% smaller

Dreaming is still on. Still running all three phases. It just stopped being fed twelve copies of the same note every day. Once the input got clean, the output got clean automatically.

✓ The real lesson

The problem wasn't dreaming. It was the heartbeat feeding dreaming duplicate content to consolidate. Fix the input and dreaming works exactly the way it's supposed to. This is true of almost every memory problem you'll hit — the consolidation layer is usually doing its job on whatever you give it.

08

The heartbeat duplication trap

Zooming out from the specific fix in section 07: the heartbeat is the biggest lever you have over memory quality. It runs more often than any cron job. Whatever pattern you set there, your agent repeats it at scale.

The three heartbeat prompts that cause duplication

The prompts that don't

Heartbeat cadence matters

Every 15 minutes is almost always too frequent. Every 30 minutes can work if the prompt is lean. Every 2 hours is the sweet spot for most autonomous agents — frequent enough to catch replies quickly, infrequent enough that the daily note doesn't balloon even when the delta-only rule fails a few times.

Heartbeat vs cron job, when in doubt

If a task takes more than a few seconds of thought, it's a cron job, not a heartbeat. Heartbeats are for reactive checks — new replies, new events, new errors. Deep work (research, outreach, content creation) belongs in scheduled cron jobs with longer timeouts and their own memory protocol.

09

Editing memory files safely

When you do edit memory files manually (or have the agent edit them via str_replace), a few rules save you from quiet corruption.

View line numbers before every edit

Line numbers shift after every removal. If you view a file, remove three lines near the top, and then try to edit based on the line numbers you saw before — you're editing the wrong lines. The fix is to view the file again immediately before any follow-up edit to the same file.

Use str_replace on unique strings, not positions

String-based replacement is resilient to shifting line numbers. Position-based edits are not. If the agent is editing MEMORY.md's ## Current status block, it should match on the specific current value ("active subscribers: 0") and replace it ("active subscribers: 2") — not try to edit line 47.

Reindex after meaningful changes

Direct edits to memory files don't update the semantic index. The agent will still see the old content when it runs memory_search until you reindex.

terminal
# After any manual edit to MEMORY.md or daily notes
openclaw memory index --agent main --verbose

# If something looks off, check the index health
openclaw memory status --deep

# Scoped to a specific agent if you run multiple
openclaw memory status --deep --agent main

Back up before destructive operations

Anytime you're about to run sed, a bulk delete, or manual MEMORY.md surgery: cp -r memory memory.backup-$(date +%Y%m%d) first. The disk cost is nothing. The cost of losing three weeks of accumulated context is real.

10

Reindexing and health checks

The semantic index drifts from the underlying files whenever something changes outside the normal ingestion path — manual edits, bulk deletes, dreaming failures, upgrades. Build a habit of checking.

terminal
# Quick status
openclaw memory status

# Deep status — checks vector/embedding health
openclaw memory status --deep

# Deep status + reindex if dirty + verbose logs
openclaw memory status --deep --index --verbose

# Full reindex on demand
openclaw memory index --agent main --verbose

# Repair stale recall/promotion artifacts
openclaw memory status --fix

When to reindex

Weekly memory health check

Add this to your weekly operator routine, same as you check model auth and channel health:

weekly check
# What does memory look like right now?
wc -l ~/.openclaw/workspace/memory/*.md | sort -n | tail -20
wc -l ~/.openclaw/workspace/MEMORY.md

# Any daily file over 50 lines? Investigate.
# Any daily file with ## Light Sleep blocks? See section 07.

# Index health
openclaw memory status --deep

# Recent dreaming activity
cat ~/.openclaw/workspace/DREAMS.md | tail -50
11

Mistakes I made so you don't have to

Every one of these happened in production and cost real time to diagnose.

Letting the agent rewrite MEMORY.md

Early on, I didn't have the append-only rule documented. The agent, being helpful, decided to "consolidate" MEMORY.md during a slow heartbeat. Lost weeks of context. Now the rule is in OPERATIONS.md in bold, and every cron prompt references it.

Enabling dreaming before fixing the heartbeat

I turned on dreaming expecting magic. What I got was twenty promoted copies of the same pipeline status paragraph. Dreaming amplifies whatever you feed it. If the input is duplicated, the output is twenty duplicates with high confidence scores. Fix the heartbeat first.

Not scoping memory commands to the agent

Running openclaw memory index without --agent main reindexes every configured agent workspace. If you have multiple agents, this does more work than you want and sometimes surfaces errors from workspaces you weren't trying to touch. Always scope: --agent main.

The "preparation loop" failure mode

A specific way agents fail with memory: instead of executing an outbound action, the agent creates an internal markdown file describing what it would do. Five cron runs later, you have five planning documents and zero sent emails. The fix goes in OPERATIONS.md: "Creating an internal file is not a valid outbound action. Every cron must produce at least one external action — an email, a message, a published post — or explicitly log why it couldn't."

Trusting memory summaries without reading the files

Your agent's daily summary of what happened is not a substitute for actually reading the daily file once a week. Summaries miss patterns. I didn't catch the dreaming bloat until I opened a file and counted 500 lines. Automation doesn't replace reading your own logs.

Running destructive memory operations without a backup

Second time I got burned — ran sed with the wrong pattern and stripped content I wanted to keep. No backup. Had to reconstruct MEMORY.md from git (which is why everything lives in git now). cp -r memory memory.backup-$(date +%Y%m%d) is free. Do it.

Ignoring DREAMS.md

The dream diary is the human-readable narrative of what dreaming noticed. It's also the earliest warning that dreaming is learning something wrong. If DREAMS.md reads like it's obsessed with the same three uninteresting things, that's your cue to audit the heartbeat prompt — dreaming is telling you what it thinks your agent cares about, and if that doesn't match reality, the input is noisy.

Ready to make your agent sell?

This guide keeps your agent sharp. Prospector gets it earning.

A complete B2B outreach workflow — prospect research, email templates, pipeline tracking, and a self-improving lessons loop. Built from running production outreach on this exact setup.

Browse Products Subscribe to the Newsletter