DEV Community

Cover image for A failed compaction wiped my AI session, so I built it infinite memory
geco
geco

Posted on

A failed compaction wiped my AI session, so I built it infinite memory

It was a Tuesday morning. My OpenCode session had 378,755 tokens of context — weeks of trading-bot work. I hit /compact. The model choked: 3 output tokens, empty summary, finish: length. The compaction had failed, and the memory plugin I was using stored everything inside that same compaction call. Result: total amnesia. Zero memories. The session was effectively lobotomized.

That failure taught me the one design rule that matters for agent memory: never store memories only inside the compaction call. Save incrementally, while you work — so a failed compaction costs you a summary, not your past.

So I built opencode-mempalace-persistence (v2.1.0, MIT) on top of MemPalace: every conversation is auto-saved to a local vector palace — zero cloud — and injected back into future prompts, across compactions and across sessions.

Demo: a decision filed on Monday is recalled verbatim by a different session on Thursday

How it works

  • Auto-inject recall — identity + relevant memories land in every prompt via experimental.chat.messages.transform. Zero model discipline required.
  • AI checkpoints — every 15 messages the model files topics, decisions and quotes via MemPalace MCP tools (diary + knowledge graph). The hook decides when, the model decides what — same pattern as the official Claude Code hooks.
  • Pre-compaction emergency save — before every compaction, everything essential is filed first, and core memory is re-attached to the summary so it survives.
  • Exit save — quitting (even Ctrl+C) mines the last turns synchronously.
  • Per-project wings — bot-api sessions never leak into mobile-app memories.
  • Backfill — OPENCODE_MEMPALACE_BACKFILL=1 mines your full history once (I backfilled 9,448 messages / 1,735 drawers from a dead session).
  • Bundled recall skill — question-driven search-before-answer protocol, adapted from the official MemPalace skill. Search results are quoted verbatim, never paraphrased.

Install (3 steps)

1. Install MemPalace:

uv tool install "mempalace>=3.3.5"
mempalace init ~/opencode-memory
Enter fullscreen mode Exit fullscreen mode

2. Plugin + MCP server in ~/.config/opencode/opencode.json:

{
  "plugin": ["opencode-mempalace-persistence"],
  "mcp": {
    "mempalace": {
      "type": "local",
      "command": ["mempalace-mcp"],
      "enabled": true
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Identity in ~/.mempalace/identity.txt (two lines about you), restart OpenCode. Done — from here it saves on its own: every 15 messages, on idle, on exit, before every compaction.

Why MemPalace and not summaries?

Summarizers distill and discard. MemPalace stores the verbatim transcript plus a knowledge graph with time-valid facts (kg_supersede/kg_invalidate when the world changes). When a new session asks "why does auth use Postgres instead of Redis?", it gets the original decision with its reasoning — including the part where the engineer who decided it is on holiday.

Links

If you use OpenCode and compaction anxiety keeps you up at night, give it a try — issues and PRs welcome. What memory setup are you running? I'm genuinely curious what survives in other people's stacks.

Top comments (3)

Collapse
 
mihai_leanzero profile image
Mihai Perdum •

The design choice that stands out is saving incrementally instead of only inside the compaction call - that's the actual fix, not a bigger context window or a smarter summarizer. A much smaller version of the same idea: this desk's own action ledgers are plain append-only jsonl, so a crash mid-run can only ever truncate the tail, never corrupt what's already written. One question on kg_supersede - if a session dies mid-write, before the supersede resolves, does the stale fact just sit there until the next backfill catches it, or is there a check on read too?

Collapse
 
geco_50 profile image
geco •

Great question — supersede is atomic at the shared boundary (single SQLite transaction), so a mid-write crash rolls back to the old fact: stale but present, never half-written. Reads are validity-window only, no liveness check — the stale fact reads as current until the model revisits it. And you're right to suspect the backfill: it mines transcripts into drawers, KG facts only come from live MCP calls, so a crashed supersede waits for the next model touch, not the next backfill. I should document that gap — thanks for spotting it.

Collapse
 
geco_50 profile image
geco •

Small follow-up: I documented this properly in v2.7.0 — new "Durability notes (drawers vs KG)" section (github.com/geco/opencode-mempalace...) in the README. Your question is literally what prompted it.