An agent vault that wrote memory down for months and never read it back before starting work. This is the loop that closed: remember → retrieve before acting → forget on purpose.
The Problem
The vault had good raw material: 31 archived handoff files, a decisions log, a small knowledge graph, and a consolidation pass that tidied all of it on command. None of it was ever read at the moment it would have helped, which is the first minute of a task.
So the same ground got re-walked. A polish pass shipped in August, and the verdict on it was that it did not look like an upgrade. Nothing in the system would surface that verdict to the next session about to do a polish pass. Facts had no way to be superseded, only rewritten. Skills carried no record of whether they were approved or abandoned. And nothing was ever forgotten, so the memory that existed kept growing until the index outgrew its own load limit and started dropping its last forty lines silently.
The Three-Layer Architecture
The architecture spans three distinct layers, implemented entirely as plain files: Markdown, YAML frontmatter, and JSONL. No vector databases, no remote embeddings, and no third-party memory services.
| Layer | Store | Contract & Scope | Answers |
|---|---|---|---|
| Episodic | memory/episodes.jsonlmemory/episodes.archive.jsonl | Append-only experience log validated against schema; archived after 90 days unless pinned. | ”Have we tried something like this before, and how did it go?” |
| Semantic | brain/nodes/*.mdbrain/schema.yaml | Entity-relation-value triples on Facts & Decisions; lifecycle states (active, superseded, stale). | ”What is true, and is it still true?” |
| Procedural | .claude/skills/*/SKILL.md | Lifecycle frontmatter mirroring security rules; usage telemetry and candidate tracking. | ”Which way of working is approved, used, or retired?“ |
1. Episodic Schema
Every session and queue run emits a single line of structured JSON validated on write by scripts/memory/memory.mjs. Malformed or duplicate entries are rejected immediately.
id: ep-YYYYMMDD-HHMM-<slug>
ts: ISO-8601
task: string # One line: what was requested
scope: string # Target project slug or 'vault'
approach: string # One line: how it was attempted
outcome: success | failure | partial
key_decisions: string[] # Max 5 decisions, one line each
errors: string[] # Max 5 error strings
resolution: string # One line fix or empty
evidence_refs: string[] # Links to queue results, handoff anchors, or commit SHAs
tags: string[] # Searchable keyword facets
pinned: boolean # If true, exempted from 90-day archive pass
status: active | archived
agent_id: string # Optional; writer identity for memory-aware routing
The system launched with 63 historical episodes backfilled from handoffs, each explicitly tagged backfill to preserve audit integrity.
2. Semantic Node Lifecycle & Minimal Ontology
Facts and decisions are recorded as Markdown nodes under brain/nodes/ validated against brain/schema.yaml. The schema introduces formal lifecycle tracking and disambiguation fields:
status: active | superseded | stale | flagged
confirmed_at: YYYY-MM-DD
superseded_by: string # Target node ID if superseded
entity: string # Target entity (e.g., 'wanessalabs-com')
relation: string # Predicate: prefers | uses | bans | owns | depends_on | located_at | decided
value: string # The statement or rule value
This triple structure allows newer facts to automatically supersede older contradictory entries without human intervention.
3. Procedural Skill Lifecycle
Every tool and workflow under .claude/skills/*/SKILL.md carries version and vetting metadata:
version: 1
status: pending | approved | deprecated | superseded
last_used: YYYY-MM-DD
uses: number
superseded_by: string
Status strictly mirrors .claude/rules/security.md. Skills are never promoted automatically: when /dream observes three or more successful episodes sharing a tag pattern over 90 days, it outputs a proposal to memory/review/skill-candidates.md for human approval.
Retrieval Before Acting (recall-before-task)
Before any work begins, the orchestrator queries past experience. The answer is injected as a concise, high-density context block ($\le 400$ tokens) formatted as [outcome] task → approach → resolution (id).
[RECALL] 2 relevant episodes:
• [success] polish wanessalabs-games hub -> replaced box-shadow with transform/opacity -> 60fps achieved (ep-20260923-1430-games-cwv)
• [partial] add audio to kalimba -> web audio api blocked on mobile gesture -> unlock on first tap (ep-20260921-1100-kalimba-audio)
Retrieval Mechanics
- Pipeline Hooks: Wired as step zero in three critical entrypoints:
- Session startup (
/resumestep 5.5). - Project initialization (
/project <path>). - Task execution (
/queue-runbeforeorchestrator-runner).
- Session startup (
- Scoring Formula: $$\text{Score} = (1 \times \text{text token overlap}) + (2 \times \text{tag overlap}) + (3 \times \text{scope match})$$
- The Two-Token Precision Floor: Early prototypes produced noisy matches where a single generic word like
"session"returned five confident hallucinations. Requiring a minimum of two overlapping tokens cut noise to zero. A compact or empty block is the correct response to a vague query. - Latency: Linear scans across active JSONL records complete in $< 15\text{ms}$ in Node.js, making external vector stores or indexing daemons unnecessary.
Forgetting, Which Is Not Deleting
The forgetting engine runs inside /dream (and in preview mode via /dream-dry-run), enforcing strict retention rules:
| Entity | Trigger Condition | Action |
|---|---|---|
| Episodes | status: active, pinned: false, age $> 90$ days | Set status: archived, move to memory/episodes.archive.jsonl. Reversible. |
| Brain Nodes | confirmed_at $> 90$ days old | Set status: stale. Never deleted or archived. |
| Stale Nodes | Node reaffirmed by claude-mem in a session | confirmed_at refreshed; next pass restores status: active. |
| Facts & Decisions | Two active nodes with identical entity + relation | Older node receives status: superseded, superseded_by: <newer id>. |
| Contradictions | Colliding Pillars, Playbooks, design rules, or negations | Flagged to memory/review/contradictions.md as - [ ] <A> vs <B>: <why>. |
The Contradiction Detector
The detector scans active nodes, skills, and design documents for hard rule conflicts. It successfully isolates:
- Design rule collisions: An authoring rule specifying a serif font (
Lora) versus an anti-pattern rule forbidding model-default serifs. - Library bans: Banned animation packages (
GSAP) flagged when introduced into minimalist route builds. - Negation parsing: Distinguishes between positive mentions, code-span examples, and negative constraints (
"No GSAP, Lottie, or three.js").
Single-Writer Ownership Matrix
To prevent file contention, data corruption, and multi-agent race conditions, every memory artifact has a declared single writer:
| Artifact | Single Writer | Permitted Readers & Proposers |
|---|---|---|
HANDOFF.md | /session-end (auto-primer) | dream proposes via memory/proposals/handoff.md |
~/.claude/.../MEMORY.md | claude-mem | dream proposes via memory/proposals/memory.md |
brain/nodes/* (status) | dream-keeper | Read-only to all |
brain/nodes/* (confirmed_at) | claude-mem (reaffirmation only) | Read-only to all |
brain/schema.yaml | Human | dream proposes in digest |
memory/episodes.jsonl (Append) | orchestrator-runner / /session-end | Subagents forbidden |
memory/episodes.jsonl (Archive) | dream-keeper (forgetting pass) | Exclusive pass |
review/contradictions.md | dream-keeper (append) / Human (tick) | Machine never auto-resolves |
tasks/todo.md | /session-end | dream-keeper forbidden |
Memory agents (auto-primer, dream-keeper, claude-mem) operate in an exclusive memory lane and never run concurrently.
Proof & Verification
The test harness (scripts/memory-tests/) runs 15 automated test suites against isolated fixtures, never touching live production records:
amnesia.test: Confirms synthetic episodes are indexed and returned in top-5 recall blocks.contradiction.test: Verifies that Facts auto-supersede while Pillars and design conflicts are flagged.staleness.test: Verifies that unpinned 100-day-old episodes archive while pinned episodes remain active.isolation.test: Confirms that project scopes never leak memory into unrelated projects.frontmatter-lint: Validates schema conformance across all brain nodes and skills.
The entire memory loop—remembering experiences, recalling past solutions, and pruning stale information—operates deterministically as plain text in git.