# RFC-001: Lattice — Agent Memory System

**Status:** Draft  
**Created:** 2026-01-31  
**Last Updated:** 2026-02-01

---

## 1. Problem Statement

### 1.1 The Forgetting Problem (Session Amnesia)

Current AI coding agents (Claude Code, Cursor, OpenCode, etc.) suffer from **session amnesia**. Every session starts from zero. Users repeatedly re-explain team conventions, architecture decisions, and personal preferences.

### 1.2 The Core Insight

Human experts don't explicitly "save" knowledge to a database and "query" it later. They:
1.  **Absorb** information implicitly through interaction.
2.  **Resonate** (recall) relevant knowledge automatically when context triggers it.

An ideal agent memory system should work the same way: **Implicit, Automatic, and Context-Aware.**

---

## 2. Core Concepts

### 2.1 Crystallization (隐式结晶)
The process of extracting condensed insights from raw conversation streams.
*   **Input**: Raw conversation logs.
*   **Process**: LLM analyzes the stream to find durable facts, preferences, and decisions.
*   **Output**: "Memories" (Structured insights).
*   **Trigger**: Automatic (e.g., session end, idle time). Users do NOT need to say "save this".

### 2.2 Resonance (共鸣)
The mechanism of surfacing relevant memories based on the current task context.
*   **Input**: Current user query, open files, active terminal output.
*   **Process**: Vector similarity search + Time-weighted re-ranking.
*   **Output**: Relevant memories injected into the Agent's context window.
*   **Trigger**: Automatic (e.g., session start, file open). Users do NOT need to say "search for X".

**Resonance Formula:**
```
Score = VectorSimilarity * 0.85 + TimeFreshness * 0.15
```
*Design Note: Time decay uses logarithmic falloff rather than linear, preventing valuable old memories from being completely buried.*


### 2.3 Scope & Trust
Memories are bounded by **Scope** (usually a Project).
*   **Isolation**: Project A's architecture decisions shouldn't pollute Project B.
*   **Default Behavior**: Project-level isolation. Cross-project retrieval is disabled by default.

*Design Note: The original "Trust Ledger" with 3-confirmation escalation was removed. Analysis showed it creates UX friction (users rubber-stamp or ignore) without proportional safety benefits. Project isolation is the correct default.*

---

## 2A. Design Rationale

This section documents key design decisions and their justifications.

### 2A.1 Why Pure Vector Search (No AST/Symbol Matching)

**Rejected Alternative**: Multi-factor resonance with AST parsing (`Semantic 40% + Symbolic 25% + Technical 20% + Freshness 15%`).

**Decision**: Pure embedding-based search with time decay.

**Rationale**:
1.  **Maintenance Cost**: AST parsers require per-language implementation and break on language updates.
2.  **Diminishing Returns**: Modern code-aware embeddings (CodeBERT successors) capture most symbolic relationships implicitly.
3.  **Future Bet**: Embedding models will continue improving; AST parsers will continue requiring maintenance.

### 2A.2 Why Read-Time Conflict Resolution (No Write-Time Versioning)

**Rejected Alternative**: `supersedes` chain with user-resolved conflicts at write time.

**Decision**: Store all memories with timestamps; let LLM resolve conflicts at retrieval time.

**Rationale**:
1.  **Context Advantage**: The LLM at read-time has full context (current project, current query, all timestamps). The user at write-time does not.
2.  **User Friction**: Write-time resolution interrupts flow and leads to rubber-stamping.
3.  **Recoverability**: If LLM misresolves, user corrects inline. If supersedes chain corrupts, manual audit required.

### 2A.3 Why No Active TTL (Passive Decay Instead)

**Rejected Alternative**: 90-day TTL with background sweeper and explicit archival.

**Decision**: Time-weighted ranking + Hard Cap (LRU eviction).

**Rationale**:
1.  **History is Context**: Deleted memories lose the ability to explain "why we stopped doing X".
2.  **Operational Simplicity**: No background jobs, no state machines, fewer failure modes.
3.  **Agentic Principle**: "Regeneration is cheaper than management." If a memory becomes stale, the agent can re-learn it faster than we can curate it.

### 2A.4 What We Explicitly Do NOT Implement

| Feature | Reason |
|---------|--------|
| Trust Ledger / Cross-Scope Promotion | UX antipattern; project isolation is safer default |
| AST-based Symbol Matching | Maintenance cost exceeds benefit |
| Supersedes Chain / Version Graph | Read-time resolution is superior |
| Active TTL Sweeper | Passive decay + LRU is simpler and sufficient |
| Compliance Monitor with Injection | Deferred to Phase 2; not core to memory function |


---

## 3. System Architecture (High Level)

Lattice is designed as an **Independent Memory Subsystem**. It runs alongside the Agent, observing interactions and injecting context.

```
[ Agent / IDE ]
    │
    ├── (Stream) Interaction Logs ────► [ Lattice Observer ]
    │                                          │
    │                                          ▼
    │                                   [ Crystallizer ]
    │                                          │
    │                                          ▼
    │                                   [ Memory Store ]
    │                                          │
    │                                          ▼
    ◄── (Inject) Relevant Context ────── [ Resonator ]
```

### 3.1 Components

1.  **Observer**: Passive listener that buffers user-agent interactions.
2.  **Crystallizer**: The "Brain" that processes buffers into insights (using LLMs).
3.  **Memory Store**: The "Hippocampus" that stores insights and embeddings.
4.  **Resonator**: The "Retrieval Engine" that matches current context to stored memories.

---

## 4. Support Targets

Lattice aims to be the universal memory layer for two primary agent types:

### 4.1 OpenCode (Plugin Ecosystem)
*   **Context**: A mature, extensible AI IDE with a plugin architecture and native MCP support.
*   **Integration Goal**: "Plug and Play". Lattice should feel like a native feature of OpenCode, utilizing its event hooks (`session.created`, `message.created`) to achieve zero-friction integration.

### 4.2 Self-Developed Agents (Custom Code)
*   **Context**: Future autonomous coding agents built from scratch (e.g., Python-based ReAct loops).
*   **Integration Goal**: "Native Code". Lattice should provide a library/SDK interface that allows these agents to access memory functions directly within their logic loop (e.g., `await memory.resonate(context)`).

---

## 5. Interaction Layer: The Daemon Architecture

Lattice adopts a **Client-Server Architecture** to ensure memory consistency across different tools and workflows.

### 5.1 System Model

```
+-------------------------------------------------------+
|                 LATTICE DAEMON (Core)                 |
|  [SQLite] [Vector DB] [Crystallizer LLM] [Resonator]  |
|                 (Serving HTTP / Socket)               |
+---------------------------^---------------------------+
                            | API Protocol (Unified)
                            |
      +---------------------+---------------------+
      |                     |                     |
[ The Wrapper ]        [ The SDK ]       [ File Watcher ]
(for Blackbox)        (for Custom)       (for Legacy/File)
      |                     |                     |
      v                     v                     v
[ CLI Agent ]         [ Py/Node Agent]    [ Any Text Editor ]
```

### 5.2 The Core: Lattice Daemon
The **Daemon** is the persistent process that owns the database and intelligence logic.
*   **Responsibility**:
    *   Manages the SQLite + Vector Store.
    *   Executes `Resonate` (Search) and `Crystallize` (Extraction) jobs.
    *   Handles concurrency (e.g., multiple agents accessing same memory).
*   **Interface**: Lightweight HTTP/JSON-RPC or Unix Domain Socket.

### 5.3 Client A: The Universal Wrapper (CLI)
Designed for **Closed/Black-box Agents** (e.g., `claude`, `antigravity`).

*   **Command**: `lattice run <agent_command>`
*   **Mechanism**:
    1.  **Injection**: Fetches context from Daemon and writes to `.lattice/context.md` (or env var).
    2.  **Observation**: Spawns the agent process inside a PTY (Pseudo-Terminal) to capture `stdin`/`stdout`.
    3.  **Synchronization**: Streams the conversation log to the Daemon for background crystallization.
*   **User Experience**: Zero setup. Just prefix the command.

### 5.4 Client B: The SDK (Library)
Designed for **Self-Developed Agents** (Python, Node, etc.).

*   **Mechanism**: A "Thin Client" library that connects to the Daemon.
*   **Usage**:
    ```python
    import lattice
    client = lattice.connect() # Auto-connects to local daemon
    
    # Active Recall
    context = client.resonate("auth system")
    
    # Push Interaction
    client.push_log(user="...", agent="...")
    ```
*   **Benefit**: Direct programmatic access to memory features without managing DB locks or LLM calls locally.

### 5.6 Operational Strategy: Zero-Management

Lattice is designed to be **invisible**.

*   **Auto-Spawn**: The Daemon is lazily started by the CLI on first use.
*   **Socket-Based**: Uses Unix Domain Sockets to avoid port conflicts.
*   **Idle-Timeout**: The Daemon automatically shuts down after 2 hours of inactivity to save resources.
*   **Logs**: Rotated logs stored in `~/.lattice/logs/`.
*   **Client Resilience**: The Wrapper (and SDK) treats the Daemon as ephemeral. If a connection fails (e.g., daemon timeout), the client automatically respawns the daemon and retries the request transparently.

---

## 6. (Removed for Redesign)

---


## 6. Implementation Plan (Day 0 Complete)

### Week 1: Core & CLI
**Goal: "Brain in a Box" — A working CLI tool.**
- Project structure (`src/lattice/cli`, `src/lattice/core`)
- Core Models & Protocols
- Google Provider (Unified)
- SQLite Storage (vec0)
- **CLI Implementation (Typer)**

### Week 2: Integration Patterns
**Goal: "Agent Integration"**
- Log-Driven Architecture patterns
- OpenCode Plugin (Shell execution)

### Week 3+: Advanced Features
- Conflict resolution
- Cross-scope trust


---

## 6. SDK Specification

### 6.1 Core Classes

```python
from lattice import Lattice, Session, Memory, Scope

# Initialize Lattice
lattice = Lattice(
    db_path="~/.lattice/memories.db",  # Optional
    embedding_model="local",            # "local" | "openai" | "custom"
    crystallizer_model="gemini-flash",  # LLM for extraction
    
    # Session tracking (solves token doubling problem)
    auto_track_conversations=True,
    max_tracked_turns=50,
    exclude_from_tracking=[r"password", r"api[_-]?key"],
    
    # Compliance monitoring
    compliance_warn_threshold=3,  # Warn after N turns without context call
    on_compliance_violation="warn",  # "log" | "warn" | "inject"
)

# Start a session
session = lattice.start_session(
    project_path="/path/to/project",
    scope_override=None,  # Optional manual scope
)

# Get context for a message
context: list[Memory] = session.get_context(
    message="Add authentication endpoint",
    limit=10,
)

# Record a conversation turn (automatically tracked)
session.record_turn(
    user_message="Add authentication",
    agent_response="I'll create an auth endpoint...",
    tools_called=["read_file", "write_file"],  # For compliance tracking
)

# Crystallize session (uses tracked content, zero token cost)
new_memories: list[Memory] = session.crystallize()

# Search memories
results = lattice.search(
    query="authentication patterns",
    scope=session.scope,
    limit=5,
)

# Forget a memory
lattice.forget(memory_id="mem_xxx")

# Check compliance
report = session.check_compliance()
if report.severity == "critical":
    print(f"Warning: {report.recommendation}")
```

### 6.2 Session Tracking

SDK automatically tracks conversation turns, eliminating the need for Agent to pass conversation content:

```python
class SessionTracker:
    def __init__(self, config: SessionTrackerConfig):
        self.max_turns = config.max_turns  # Default 50
        self.max_content_length = config.max_content_length  # Default 10000
        self.exclude_patterns = config.exclude_patterns  # Privacy filters
    
    def track_turn(self, session_id: str, turn: Turn):
        # Sanitize sensitive content
        sanitized = self.sanitize(turn)
        
        # Store in rolling window
        self.sessions[session_id].append(sanitized)
        
        # Enforce max turns
        if len(self.sessions[session_id]) > self.max_turns:
            self.sessions[session_id].pop(0)
    
    def get_content(self, session_id: str) -> str:
        """Get tracked content for crystallization"""
        return self.format_conversation(self.sessions[session_id])
```

### 6.3 Compliance Monitoring

SDK detects when Agent should call `lattice_context` but hasn't:

```python
class ComplianceMonitor:
    # Trigger patterns - user expects Agent to remember
    TRIGGERS = [
        r"上次|之前|以前|earlier|before|last time",
        r"记得|还记得|remember",
        r"继续|接着|continue|pick up",
        r"我们(说|讨论|聊)过",
        r"我的偏好|my preference",
    ]
    
    def check(self, session_id: str, user_message: str, tools_called: list[str]):
        state = self.get_state(session_id)
        
        # Reset if context was called
        if "lattice_context" in tools_called:
            state.turns_since_context = 0
            return None
        
        state.turns_since_context += 1
        
        # Check for trigger patterns
        triggered = [p for p in self.TRIGGERS if re.search(p, user_message)]
        
        if triggered or state.turns_since_context > self.warn_threshold:
            return ComplianceReport(
                severity="critical" if triggered else "warning",
                recommendation=f"Consider calling lattice_context"
            )
        
        return None
```

**Compliance Injection (Optional):**

When violation detected, SDK can inject reminder into system prompt:

```
⚠️ LATTICE MEMORY REMINDER
The user's message suggests they expect you to remember previous context.
Before responding, consider calling lattice_context to retrieve relevant memories.
```

### 6.4 Session Hooks

For deeper integration, SDK provides hooks:

```python
class LatticeSession:
    # Called automatically when session starts
    on_start: Callable[[Scope], None]
    
    # Called for each turn - can modify context
    on_turn: Callable[[str, list[Memory]], list[Memory]]
    
    # Called when session ends
    on_end: Callable[[list[Turn]], list[Memory]]
    
    # Called when memory conflict detected
    on_conflict: Callable[[Memory, Memory], ConflictResolution]
    
    # Called on compliance violation
    on_compliance_violation: Callable[[ComplianceReport], None]
```

### 6.5 Custom Embedding Provider

```python
from lattice import EmbeddingProvider

class MyEmbedding(EmbeddingProvider):
    def embed(self, text: str) -> list[float]:
        # Custom embedding logic
        return my_model.encode(text)

lattice = Lattice(embedding_provider=MyEmbedding())
```

---

## 7. Lifecycle Management

### 7.1 Crystallization Strategy

**Context Acquisition:** Crystallization requires access to the conversation history. The specific mechanism for acquiring this history (e.g., active tracking vs explicit passing) depends on the Interaction Layer design (TBD).

**Crystallizer Reliability:**

Crystallization is a single point of failure. Mitigations:

| Risk | Mitigation |
|------|------------|
| LLM extracts noise | Confidence scoring, threshold filtering |
| LLM misses insights | User feedback loop, periodic review |
| LLM hallucinates | Provenance tracking, user verification |
| API failure | Retry queue with exponential backoff |

### 7.2 Lifecycle: Passive Decay Model

**Design Decision**: No active TTL. Use time-weighted ranking and LRU eviction.

**Mechanism**:
1.  **Time-Weighted Ranking**: Old memories naturally sink in search results due to `Freshness * 0.15` factor.
2.  **Hard Cap**: When memory count exceeds `MAX_MEMORIES` (default: 5000), oldest/least-accessed memories are evicted.
3.  **No Deletion on Read**: Memories are never automatically deleted; they fade from relevance.

**Conflict Resolution**:
*   Conflicting memories are stored with timestamps.
*   At retrieval time, the prompt instructs the LLM: "Prefer newer memories. If conflict exists, trust the more recent one."
*   User can manually delete via `lattice forget`.


---

## 8. Privacy & Security

### 8.1 Principles

1. **Local-first**: All data in `~/.lattice/`, no cloud sync by default
2. **User control**: Every memory can be inspected, edited, deleted
3. **Explicit boundaries**: Guarded/sealed scopes require user action to breach
4. **No secrets**: API keys, passwords, tokens are never stored (detection + rejection)

### 8.2 Isolation Level Defaults

```yaml
work/*: guarded        # Work projects don't leak to each other by default
personal/*: guarded    # Personal projects are protected
*: open                 # Everything else flows freely
```

Users can override per-scope.

### 8.3 Sensitive Content Detection

```python
SENSITIVE_PATTERNS = [
    r'(?i)(api[_-]?key|secret|password|token)\s*[:=]\s*\S+',
    r'(?i)bearer\s+[a-zA-Z0-9\-_]+',
    r'-----BEGIN\s+(RSA|DSA|EC)?\s*PRIVATE KEY-----',
]

def should_crystallize(content: str) -> bool:
    for pattern in SENSITIVE_PATTERNS:
        if re.search(pattern, content):
            return False  # Never store
    return True
```

---

## 9. Model Requirements

### 9.1 Crystallizer Model

**Purpose:** Extract memorable insights from conversations

**Recommended Choice (2026):** **Gemini 3.0 Flash**

| Capability | Gemini 3.0 Flash | GPT-4o-mini | Claude 3.5 Haiku |
|------------|------------------|-------------|------------------|
| **Cost** | **Lowest** (~$0.05/1M) | Low | Medium |
| **Context** | **2M+ tokens** | 128K | 200K |
| **JSON** | Native Strict Mode | JSON Mode | Good |
| **Latency** | Extremely Fast | Fast | Fast |

### 9.2 Embedding Model

**Purpose:** Generate semantic vectors for resonance calculation

**Design Decision:** **Unified Cloud Provider**

**Selected Model (2026):** **Gemini text-embedding-005** (Default)

| Feature | Why |
|---------|-----|
| **Simplicity** | No `torch` or `transformers` dependencies. Pure API call. |
| **Context** | 2048 dimensions (auto-scaled to 768 for sqlite-vec compatibility) |
| **Performance** | Faster than local CPU inference on most machines. |
| **Cost** | Negligible (bundled with Gemini API usage). |

---

## 10. Open Questions

### 10.1 Resolved in This Design

| Question | Resolution |
|----------|------------|
| Implicit vs explicit memory creation | Implicit write via crystallization |
| Project isolation | Project-level sandbox (no cross-scope by default) |
| Memory lifetime | Passive decay via time-weighted ranking + LRU eviction |
| Cross-scope trust | **Removed**. Project isolation is the correct default. |
| Integration model | Daemon + Wrapper/SDK |
| Conflict handling | Read-time LLM resolution with timestamps |
| Resonance algorithm | Pure vector search (85%) + time decay (15%) |
| **Compute Architecture** | **Unified Cloud (Gemini Native) with Adapter Interface** |


### 10.2 Deferred to Later Phases

| Question | When to Resolve |
|----------|-----------------|
| Cross-project memory sharing | Future RFC (if validated need) |
| Memory consolidation/summarization | Phase 3+ |
| Multi-device sync | Phase 3+ |
| Team/shared memories | Future RFC |
| Compliance Monitor | Phase 2 |


### 10.3 Needs Validation

| Question | How to Validate |
|----------|-----------------|
| Is 85/15 resonance weight optimal? | A/B test, measure precision/recall |
| Does LLM reliably resolve conflicts? | Monitor retrieval quality in production |
| Is 5000 memory cap sufficient? | Usage telemetry |


---

## 11. Implementation Plan

### 11.1 Phases

The implementation follows the "Daemon First" strategy.

#### Phase 1: The Core Daemon (Week 1)
**Goal:** A running server that can store, search, and crystallize.
*   **Models**: `Memory`, `Session`, `InteractionLog`.
*   **Storage**: SQLite with `sqlite-vec`.
*   **API**: FastAP/Uvicorn server definition.
*   **Intelligence**: Gemini 3.0 integration for Crystallizer/Resonator.
*   **Deliverable**: `lattice serve` works and responds to curl requests.

#### Phase 2: The Universal Wrapper (Week 2)
**Goal:** "It just works with Claude."
*   **PTY Recorder**: Python `pty` module implementation to capture stdout/stdin.
*   **ANSI Stripper**: Cleaning terminal logs for LLM consumption.
*   **Context Injector**: Logic to generate `.lattice/context.md`.
*   **Deliverable**: `lattice run claude` captures session and creates memories.

#### Phase 3: SDK & Polish (Week 3)
**Goal:** Developer ergonomics.
*   **Python SDK**: `lattice.Client` wrapper around the HTTP API.
*   **Dashboard**: (Optional) Simple TUI to view running daemon status.

### 11.2 Validation Checkpoints

| Time | Checkpoint | Pass Criteria |
|------|------------|---------------|
| Week 1 End | Daemon Alive | `curl localhost:8000/resonate` returns JSON |
| Week 2 End | Wrapper Works | `lattice run bash` -> commands are remembered |
| Week 3 End | SDK Works | Custom script can push/pull memories |

---

## 12. Success Metrics

| Metric | Target | Measurement |
|--------|--------|-------------|
| Memory precision | >90% | User doesn't forget within 1 day |
| Memory recall | >70% | User doesn't re-state known info |
| Agent compliance | >80% | Agent calls `lattice_context` at session start |
| Transparency usage | >50% | Users inspect memories at least once |
| Feedback rate | >10% | Users provide feedback on surfaced memories |
| Cross-scope usefulness | >60% positive | Promoted memories get used |
| Performance | <100ms retrieval | P95 latency |

---

## 13. References

- [mem0](https://github.com/mem0ai/mem0) — Vector-based agent memory
- [MCP Specification](https://modelcontextprotocol.io/) — Integration protocol
- [CLAUDE.md Pattern](https://docs.anthropic.com) — Static project context

---

## Appendix A: Glossary

| Term | Definition |
|------|------------|
| **Crystallize** | Extract memorable insights from conversation |
| **Resonance** | Contextual similarity score (0-1), computed as `Similarity*0.85 + Freshness*0.15` |
| **Scope** | Knowledge boundary (usually = project) |
| **Daemon** | The core Lattice server process that owns the database |
| **Wrapper** | CLI tool that captures black-box agent interactions |
| **SDK** | Thin client library for custom agent integration |
| **Provenance** | Origin/source of a memory |
| **Passive Decay** | Old memories sink in rankings without explicit deletion |


---

## Appendix B: Example Crystallizer Prompt

```markdown
You are a memory extraction system. Analyze the conversation and extract insights worth remembering across sessions.

## What to Extract
- User preferences explicitly stated ("I prefer...", "We always...")
- Team/project conventions ("Our team uses...", "In this project...")
- Architecture decisions with rationale
- Debugging patterns that resolved issues
- Workflow preferences

## What NOT to Extract
- One-off questions and answers
- Code snippets (code lives in files)
- Temporary debugging state
- Anything containing secrets/credentials

## Output Format
```json
[
  {
    "what": "Brief description of the insight",
    "why": "Why this is worth remembering",
    "scope": "global" | "project",
    "confidence": 0.0-1.0
  }
]
```

If nothing worth remembering, return empty array: []

## Conversation
{conversation}
```

---

## Appendix C: Schema

```sql
-- Vector search extension (sqlite-vec)
-- Requires: pip install sqlite-vec

CREATE TABLE memories (
    id TEXT PRIMARY KEY,
    what TEXT NOT NULL,
    why TEXT NOT NULL,
    type TEXT NOT NULL,  -- fact | preference | decision | lesson
    scope_path TEXT NOT NULL,
    confidence REAL DEFAULT 1.0,
    created_at TIMESTAMP NOT NULL,
    last_accessed TIMESTAMP,
    access_count INTEGER DEFAULT 0,
    source_session TEXT,
    crystallizer_version TEXT DEFAULT 'v1'
);

-- Vector index for semantic search (sqlite-vec)
CREATE VIRTUAL TABLE vec_memories USING vec0(
    embedding float[768]
);

CREATE TABLE scopes (
    id TEXT PRIMARY KEY,
    path TEXT UNIQUE NOT NULL,
    created_at TIMESTAMP NOT NULL
);

CREATE TABLE sessions (
    id TEXT PRIMARY KEY,
    scope_id TEXT NOT NULL,
    started_at TIMESTAMP NOT NULL,
    ended_at TIMESTAMP,
    turns_count INTEGER DEFAULT 0
);

CREATE TABLE interaction_logs (
    id TEXT PRIMARY KEY,
    session_id TEXT NOT NULL,
    capture_type TEXT NOT NULL,  -- transcript | conversation
    content TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL
);

-- NOTE: The following tables from the original design are NOT implemented:
-- - trust_ledger (cross-scope promotion removed)
-- - feedback (deferred to Phase 2)
-- - session_turns (replaced by interaction_logs)
```


---

## Appendix D: (Removed for Redesign)
