# Brain Maintenance — Complete Specification

**For:** Claude Code / coding agent implementation
**Owner:** Assaf Dagan
**Created:** 2026-03-05
**Status:** Ready for implementation

---

## 1. SITUATION

### What exists today

An AI agent team (one human "Assaf," one orchestrator "Kitt," plus sub-agents) runs on OpenClaw on a Hetzner VPS (Ubuntu). The workspace is at `/root/.openclaw/workspace/`.

The agents use a flat-file memory system:

```
/root/.openclaw/workspace/
├── MEMORY.md              # 38 lines. Hand-curated critical facts. Auto-loaded every session.
├── LEARNINGS.md           # 57 lines. Rules from mistakes. Read on boot.
├── AGENTS.md              # Boot sequence, architecture table, write discipline rules. Auto-loaded.
├── SOUL.md                # Persona rules. Auto-loaded.
├── USER.md                # User profile. Auto-loaded.
├── HEARTBEAT.md           # Operational rules, heartbeat checklist. Auto-loaded.
├── IDENTITY.md            # Agent identity. Auto-loaded.
├── docs/reference.md      # Project refs, API keys, IDs. Searched on-demand.
├── memory/
│   ├── qmd/
│   │   └── current.json   # Working memory. Active tasks, decisions, blockers. Updated during work.
│   ├── YYYY-MM-DD.md      # Daily logs. ~20 files currently (Feb 4 – Mar 5). Read on boot (today + yesterday).
│   ├── monthly/           # NEW (created today). Auto-summarized monthly digests.
│   │   ├── 2026-01.md
│   │   └── 2026-02.md
│   └── archive/           # NEW (created today). Raw daily logs older than 30 days.
│       └── (12 files from Jan 22 – Feb 3)
├── scripts/
│   ├── qmd-compact.py     # Moves completed QMD tasks → daily log. Works, simple.
│   ├── memory-compact-monthly.py  # NEW (created today). Keyword-based extraction of daily logs → monthly summaries.
│   └── memory-dedup-check.py      # NEW (created today). String-matching dedup audit on MEMORY.md.
```

External systems:
- **Notion** — Used as a project management / knowledge base. Queried on-demand via `gws` CLI or direct API calls. Notion API key at `/home/clawd/secrets/notion/api_key`. Contains Projects DB, Contacts, Inspiration Library, Trend Intelligence, etc.
- **Discord** — Primary communication channel with Assaf. Messages route through OpenClaw.
- **Gemini** — Powers `memory_search` tool (semantic search across memory/*.md files). API key in `~/.bashrc` as `GEMINI_API_KEY`.

### How memory flows today

```
Session start:
  → OpenClaw auto-loads: AGENTS.md, SOUL.md, IDENTITY.md, USER.md, HEARTBEAT.md, MEMORY.md
  → AGENTS.md mandates reading: QMD, LEARNINGS.md, today's daily log, yesterday's daily log

During work:
  → Agent updates QMD (active tasks, decisions)
  → Agent appends to daily log (decisions, outcomes)
  → If mistake → appends rule to LEARNINGS.md
  → If durable fact → sometimes updates MEMORY.md (inconsistent)
  → Notion queried on-demand (projects, contacts)

Session end / compaction:
  → qmd-compact.py moves completed tasks from QMD → daily log
  → HANDOVER section written to daily log

Periodically (NEW, just created):
  → memory-compact-monthly.py: daily logs >30 days → keyword-extracted monthly summary + archive raw
  → memory-dedup-check.py: audit MEMORY.md for duplicates (string matching only)
```

### What's wrong (validated by research + internal critique)

1. **No memory hygiene enforcement.** MEMORY.md has no cap enforcement (soft target of 40 lines, currently at 21). Daily logs accumulate without automatic compaction. No dedup gate before writes — dedup script exists but isn't wired into any workflow.

2. **Monthly summarization is keyword-only.** `memory-compact-monthly.py` extracts lines containing keywords like "decided," "complete," "blocked." This misses decisions phrased differently, drops context around extracted lines, and has no way to judge importance. A line saying "completed the refactor" gets extracted but a paragraph explaining WHY is lost.

3. **Notion and workspace memory are disconnected.** Decisions made in agent sessions live in daily logs and QMD. Notion has project state and reference data. Neither writes to the other systematically. They drift. An agent can't answer "what was our positioning decision for eToro?" without manually searching both systems.

4. **No failure detection.** If QMD isn't updated, if daily log isn't written, if MEMORY.md has stale facts — nothing alerts anyone. No health checks on the memory system itself.

5. **LEARNINGS.md is a flat list.** 57 lines, partially categorized, no dates on most rules, some rules duplicated in SKILL.md files. Hard to know which rules are still relevant.

---

## 2. WHAT WE WANT (end state)

After implementation, the memory system should:

1. **Self-maintain.** Old daily logs automatically compact. Stale facts get flagged. Duplicates are caught before they enter. No manual cleanup needed.

2. **Have one source of truth per memory type.** Clear ownership:
   - `QMD` = what's happening right now (working memory)
   - `daily logs` = what happened today (episodic memory)
   - `MEMORY.md` = critical facts always loaded (hot cache — hand-curated, NOT auto-generated)
   - `LEARNINGS.md` = how to avoid mistakes (procedural memory)
   - `Notion Agent Memory DB` = long-term knowledge, decisions, project context (semantic memory)

3. **Sync decisions to Notion.** After significant decisions, a structured record is created in a Notion database. This is the permanent record. Daily logs are ephemeral.

4. **Be resilient.** If Notion is down, everything still works — flat files are the operational layer. Notion is the archive/enrichment layer, not a dependency.

5. **Be observable.** A health check script can report: are daily logs being written? Is QMD fresh? When was the last Notion sync? Any stale MEMORY.md entries?

---

## 3. IMPLEMENTATION SPEC

### Phase 1: Fix memory hygiene (scripts + wiring)

**Timeline target:** 1-2 days of Claude Code work

#### Task 1.1: Rewrite `memory-compact-monthly.py` with LLM summarization

The current script does keyword extraction. Replace with a two-pass approach:

**Pass 1 (deterministic):** Group daily logs by month. Concatenate all logs for that month.

**Pass 2 (LLM summary):** Call Gemini API to summarize. The prompt should be:

```
You are summarizing a month of AI agent session logs. Extract:

1. DECISIONS — What was decided and why. Include the date.
2. OUTCOMES — What was completed, shipped, or delivered. Include the date.
3. PROJECT STATE CHANGES — What moved forward, what got stuck.
4. UNRESOLVED — What was left open or blocked at month end.
5. LEARNINGS — Mistakes made, rules created.

Be specific. Include names, project names, dates, and the actual decision/outcome.
Do NOT summarize vaguely. "Decided to use Notion as memory backend (Mar 5)" is good.
"Made several decisions about architecture" is bad.

Keep the summary under 200 lines. If the month was light, keep it under 50.

Here are the daily logs:
---
{concatenated_daily_logs}
---
```

**Environment:** Use `GEMINI_API_KEY` from environment (already in `~/.bashrc`). Model: `gemini-2.5-flash` (fast, cheap). API endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent`.

**Safety:**
- Always keep raw files in `memory/archive/` (never delete originals)
- Write summary to `memory/monthly/YYYY-MM.md`
- If LLM call fails, fall back to current keyword extraction and log a warning
- Print a diff-style preview before moving files (unless `--yes` flag is passed)

**CLI:**
```bash
python3 scripts/memory-compact-monthly.py [--dry-run] [--days 30] [--yes]
```

#### Task 1.2: Wire dedup check into MEMORY.md write flow

The current `memory-dedup-check.py` is a standalone audit tool. That's fine — keep it. But also create a simple function that agents can call mentally (no script needed — just update the write discipline rules).

**Update `AGENTS.md`** Write Discipline section. Change rule 4 from:
```
4. If significant durable fact → update MEMORY.md (during reviews only, not mid-task)
```
To:
```
4. If significant durable fact → run `python3 scripts/memory-dedup-check.py check "the fact"` first → update MEMORY.md only if no duplicate → keep MEMORY.md under 40 content lines
```

#### Task 1.3: LEARNINGS.md cleanup

Rewrite LEARNINGS.md with:
- Date on every rule (YYYY-MM-DD or "legacy" for undated ones)
- Category headers (Execution, System, Communication, Quality, Sub-Agents, Memory, Technical, Discord)
- Remove rules that are now fully encoded in SKILL.md files (check `skills/REGISTRY.md` for overlap)
- Remove rules that are clearly one-time and no longer relevant

**Do NOT change the content of rules that are still relevant.** Just organize and date them.

#### Task 1.4: Memory health check script

Create `scripts/memory-health.py`:

```bash
python3 scripts/memory-health.py
```

Checks:
1. **Daily log exists for today** — warn if missing after 10am Lisbon time
2. **QMD freshness** — warn if `updated_at` is >24 hours old
3. **MEMORY.md line count** — warn if >40 content lines
4. **MEMORY.md dedup** — run quick dedup audit (reuse logic from memory-dedup-check.py)
5. **Daily logs pending compaction** — count logs >30 days old not yet summarized
6. **QMD size** — warn if >50 lines (tasks accumulating)
7. **Notion connectivity** — try a simple Notion API call (list one page from any DB), report success/failure

Output: One-line-per-check, ✅ or ⚠️, with a summary line at the end.

**Notion API:** Use key from `/home/clawd/secrets/notion/api_key`. Endpoint: `https://api.notion.com/v1/databases/{any_known_db_id}/query` with `page_size: 1`. Use the Inspiration Library ID: `2ff330c2-8646-81f0-bbd9-ec474393d7a5`.

### Phase 2: Notion Agent Memory database + sync

**Timeline target:** 2-3 days of Claude Code work
**Dependency:** Phase 1 complete

#### Task 2.1: Create Notion Agent Memory database

Use the Notion API to create a new database. **Start minimal** (Anton's critique: don't over-engineer the schema).

Properties:
| Property | Type | Required | Purpose |
|----------|------|----------|---------|
| Content | Title | Yes | The fact/decision itself |
| Type | Select | Yes | Options: `decision`, `fact`, `rule`, `project-state` |
| Project | Text | No | Project name (free text, not a relation — keep it simple) |
| CreatedAt | Date | Yes | When captured |
| Source | Text | No | Where this came from ("conversation 2026-03-05", "research", etc.) |

That's 5 properties. Not 10. We can add more later when we actually need them.

**Parent page:** Create under a new page called "Agent Memory" in the workspace root, or under an existing "Bot Memory" page if one exists (Notion page ID `2f1330c2-8646-81c6-8c7b-e328bc6466eb` from MEMORY.md).

**API details:**
- Key: read from `/home/clawd/secrets/notion/api_key`
- Endpoint: `POST https://api.notion.com/v1/databases`
- Notion-Version: `2022-06-28`

#### Task 2.2: Create `scripts/notion-memory-sync.py`

This script does two things:

**Write (push decisions to Notion):**
```bash
python3 scripts/notion-memory-sync.py push "We decided to use Notion as semantic memory backend" --type decision --project "brain-maintenance" --source "conversation 2026-03-05"
```

Creates a page in the Agent Memory DB with the given properties.

**Dedup before write:** Before creating, query Notion for entries with similar Content (filter: Content contains first 5 words). If a match exists, print it and skip (unless `--force`).

**Read (pull from Notion to local):**
```bash
python3 scripts/notion-memory-sync.py pull [--type decision] [--project etoro] [--limit 20]
```

Queries the Agent Memory DB with optional filters. Prints results as formatted markdown. Does NOT overwrite MEMORY.md — this is a read tool, not a sync.

**Export critical facts (generate MEMORY.md cache):**
```bash
python3 scripts/notion-memory-sync.py export-critical
```

Queries Notion for entries of all types, formats as bullet list, and prints to stdout. **Does NOT auto-overwrite MEMORY.md.** The agent or human reviews the output and manually updates MEMORY.md if desired. MEMORY.md stays hand-curated — Notion is the archive, not the master of the hot cache.

**Resilience:**
- If Notion API fails, print error and exit non-zero. Never silently succeed.
- 3 retries with 2-second backoff on 429 (rate limit) or 500 errors.
- Timeout: 30 seconds per API call.

#### Task 2.3: Update AGENTS.md Write Discipline

Add to the Write Discipline section:
```
5. If significant decision or durable project fact → also push to Notion:
   `python3 scripts/notion-memory-sync.py push "the fact" --type decision --project "project-name"`
   This is the permanent record. Daily logs are ephemeral.
```

### Phase 3: Wire into heartbeat (optional, do after Phase 1+2 work)

#### Task 3.1: Add to HEARTBEAT.md

Add a new item to the heartbeat checklist:
```
8. **Memory health** — Run `python3 scripts/memory-health.py` — fix any warnings
```

This means every heartbeat (every 10 minutes), the agent checks memory health. That's the observability layer.

#### Task 3.2: Add monthly compaction to heartbeat

Add to HEARTBEAT.md nightly duties:
```
- Run `python3 scripts/memory-compact-monthly.py --yes` if daily logs >30 days exist
```

---

## 4. FILE MAP (what exists, what to create, what to modify)

### Create new:
```
scripts/memory-health.py          # Phase 1, Task 1.4
scripts/notion-memory-sync.py     # Phase 2, Task 2.2
```

### Modify:
```
scripts/memory-compact-monthly.py # Phase 1, Task 1.1 — rewrite with LLM summarization
LEARNINGS.md                      # Phase 1, Task 1.3 — reorganize, add dates
AGENTS.md                         # Phase 1 Task 1.2 + Phase 2 Task 2.3 — update Write Discipline
HEARTBEAT.md                      # Phase 3 — add memory health + compaction to checklist
```

### Keep as-is:
```
scripts/memory-dedup-check.py     # Already works. No changes needed.
scripts/qmd-compact.py            # Already works. No changes needed.
MEMORY.md                         # Do NOT auto-generate. Hand-curated. Updated manually.
memory/qmd/current.json           # Working memory. Existing protocol is fine.
memory/monthly/                   # Directory exists, will be populated by improved compaction.
memory/archive/                   # Directory exists, working.
```

---

## 5. KEY DESIGN DECISIONS (and why)

| Decision | Rationale |
|----------|-----------|
| **MEMORY.md stays hand-curated** | Anton's critique was right: auto-generating the hot cache from Notion creates a silent dependency. A human or agent deliberately choosing "this fact matters enough to always load" is a feature, not a bug. |
| **Notion is archive, not dependency** | If Notion is down, everything still works. Flat files are the operational layer. Notion is where decisions go to be permanent and searchable. |
| **Minimal Notion schema (5 props, not 10)** | We don't need LastVerified, ExpiresAt, Scope, Priority, or Agent attribution yet. Add them when we actually hit a problem they solve. |
| **LLM summarization with fallback** | Keyword extraction misses too much. LLM summary captures intent. But if the API fails, fall back to keyword extraction rather than failing entirely. |
| **No auto-overwrite of MEMORY.md from Notion** | The `export-critical` command prints to stdout. A human decides if/what to update. This prevents silent drift. |
| **Health check, not auto-repair** | The health script reports problems. It doesn't fix them. Fixing requires judgment. |

---

## 6. FAILURE MODES (what can go wrong)

| Failure | Impact | Mitigation |
|---------|--------|------------|
| Gemini API down during monthly compaction | Compaction fails | Fall back to keyword extraction. Log warning. |
| Notion API down during push | Decision not archived | Script exits non-zero. Agent sees error. Can retry later. Daily log still has the decision. |
| Monthly summary drops important context | Lost institutional memory | Raw files kept in archive/. Can always re-summarize. Summary includes "Auto-generated" header so reader knows to check archive if something's missing. |
| MEMORY.md exceeds 40 lines | Context bloat on every session | Health check warns. Dedup check catches duplicates. But enforcement is still manual — this is acceptable at our scale. |
| QMD grows too large | Slow boot, stale tasks | Health check warns at >50 lines. qmd-compact.py already handles this. |
| Notion Agent Memory DB gets polluted with low-quality entries | Noisy retrieval | Dedup-before-write in sync script. Low volume (single user) makes this manageable. |

---

## 7. TESTING

Before considering any script done:

1. **memory-compact-monthly.py** — Run with `--dry-run` first. Verify the LLM summary against the raw daily logs manually. Check that archived files are intact.
2. **memory-health.py** — Run and verify each check produces correct ✅/⚠️. Intentionally break things (delete today's log, inflate QMD) to verify warnings fire.
3. **notion-memory-sync.py push** — Push a test entry. Verify it appears in Notion with correct properties. Push the same entry again — verify dedup catches it.
4. **notion-memory-sync.py pull** — Pull entries. Verify formatting. Test with `--type` and `--project` filters.
5. **LEARNINGS.md** — After reorg, verify no rules were lost by diffing against the original.

---

## 8. WHAT NOT TO DO

- **Do not add a vector database.** We don't need Pinecone, Weaviate, Milvus, or any vector store. Notion's built-in search + Gemini memory_search is sufficient for a single-user system.
- **Do not build a knowledge graph.** No Neo4j. If we need relationships, use Notion relations between databases.
- **Do not adopt Mem0, Zep, or LangGraph.** These are multi-user SaaS memory layers. Wrong abstraction for us.
- **Do not auto-generate MEMORY.md from Notion.** Keep it hand-curated.
- **Do not make Notion a boot dependency.** If Notion is unreachable, the agent should boot normally from flat files.
- **Do not over-engineer the Notion schema.** Start with 5 properties. Add more only when there's a real problem they solve.

---

## 9. ENVIRONMENT & CREDENTIALS

| Resource | Location |
|----------|----------|
| Gemini API key | `GEMINI_API_KEY` env var (in `~/.bashrc`). Value: `AIzaSyDXqYZInk83iVV4mD29pSuHQKbgkiI1x9Q` |
| Notion API key | File: `/home/clawd/secrets/notion/api_key` |
| Notion API version | `2022-06-28` |
| Notion Bot Memory page | `2f1330c2-8646-81c6-8c7b-e328bc6466eb` |
| Notion Inspiration Library DB | `2ff330c2-8646-81f0-bbd9-ec474393d7a5` (used for health check connectivity test) |
| Python | `python3` (3.x, available at `/usr/bin/python3`) |
| Workspace root | `/root/.openclaw/workspace/` |
| Memory directory | `/root/.openclaw/workspace/memory/` |
| Scripts directory | `/root/.openclaw/workspace/scripts/` |

---

## 10. ACCEPTANCE CRITERIA

Phase 1 is done when:
- [ ] `python3 scripts/memory-compact-monthly.py --dry-run` produces an LLM-generated summary (not keyword-only)
- [ ] `python3 scripts/memory-compact-monthly.py` runs end-to-end with fallback if LLM fails
- [ ] `python3 scripts/memory-health.py` reports ✅/⚠️ for all 7 checks
- [ ] LEARNINGS.md is reorganized with dates and categories
- [ ] AGENTS.md Write Discipline includes dedup check instruction

Phase 2 is done when:
- [ ] A "Agent Memory" database exists in Notion with 5 properties
- [ ] `python3 scripts/notion-memory-sync.py push "test fact" --type fact` creates an entry in Notion
- [ ] `python3 scripts/notion-memory-sync.py push "test fact" --type fact` (again) is caught by dedup
- [ ] `python3 scripts/notion-memory-sync.py pull` returns formatted results
- [ ] AGENTS.md Write Discipline includes Notion push instruction

Phase 3 is done when:
- [ ] HEARTBEAT.md includes memory health check
- [ ] HEARTBEAT.md includes monthly compaction in nightly duties