Skip to content

Data Model

Engram’s data layer is built on Lattice, a Swift ORM backed by SQLite. All models use Lattice’s @Model macro for automatic schema management, migrations, observation, and sync.

The central model. Each row represents one atomic piece of knowledge.

erDiagram
    Memory {
        string content "Full-text indexed memory text"
        string topic "Category: architecture, debugging, preferences..."
        string project "Project scope or 'global'"
        string source "Origin: conversation, code-review, file path"
        vector embedding "384-dim semantic vector (paraphrase-MiniLM-L6-v2)"
        date createdAt "When the memory was stored"
        date lastAccessedAt "Last recall hit"
        date expiresAt "Auto-filter from recall after this date"
        int accessCount "Number of times recalled"
        int importance "1-5 ranking boost (0 = unset)"
        bool isPrivate "Excluded from team sync"
    }

content — the memory text itself. Indexed by both FTS5 (keyword search) and sqlite-vec (vector search). Kept atomic — one concept per memory.

embedding — a 384-dimensional float vector computed by the local sentence transformer. Used for cosine similarity search. Memories with empty embeddings are rejected at storage time.

topic — a category label used for organization and filtered recall. Common topics: preferences, architecture, debugging, patterns, conventions. Custom topics are encouraged for project-specific categories.

project — scoping for recall ranking. Same-project memories rank higher. Use "global" for cross-project knowledge (user preferences, language patterns, workflow conventions).

importance — a 1-5 rating that boosts recall ranking by up to 20%. Set explicitly on store, or adjusted by maintenance. Importance 0 is special: used by consolidate to deprioritize originals after they’ve been summarized.

isPrivate — when true, this memory never leaves the personal graph. It’s excluded from team contribution databases regardless of project exposure settings.

expiresAt — memories past this date are filtered from recall results but not deleted. Set via expires_in_days on store. Defaults to distant future (permanent).

A directed relationship between two memories, forming the knowledge graph.

erDiagram
    Edge {
        uuid sourceGlobalId "Source memory UUID"
        uuid targetGlobalId "Target memory UUID"
        string relation "relatesTo, contradicts, supersedes, derivedFrom, partOf, summarizedBy"
        date createdAt "When the edge was created"
    }

    Memory ||--o{ Edge : "source"
    Memory ||--o{ Edge : "target"

Edges reference memories by globalId (UUID), not by database primary key. This is essential for the multi-database architecture:

  • Your personal graph is one SQLite database
  • Your synced memories are in another
  • Team contribution databases are separate per-member
  • The consolidated team graph is yet another

Integer primary keys would collide across these databases. UUIDs are globally unique, so an edge created in your personal graph remains valid when that memory syncs to the team.

RelationDirectionSemantic
relatesToA → BA is associated with B
contradictsA → BA conflicts with B (bidirectional in practice)
supersedesA → BA replaces B (A is newer)
derivedFromA → BA was concluded from B
partOfA → BA is a component of B
summarizedByA → BA was consolidated into B

Edges are idempotent — creating a duplicate edge is a no-op.

Saves work-in-progress state for task continuity across sessions.

FieldTypeDescription
taskNameStringHuman-readable task identifier
planStringStructured plan (numbered steps, checkboxes)
progressStringWhat’s done and what’s next
contextStringFile paths, key decisions, blockers
statusStringactive, paused, or completed
projectStringProject scope

Checkpoints let an AI agent pick up exactly where it left off, even in a new session. The plan and progress fields are freeform text designed for the agent to read and act on.

Groups memories into narrative sessions (see Episode Workflows).

FieldTypeDescription
titleStringDescriptive session name
summaryStringWhat was attempted, what worked, what was decided
statusStringactive or ended
projectStringProject scope

Memories stored during an active episode are automatically linked to it via part_of edges. The summary (written at episode end) is the primary value for future recall — it captures the narrative arc of a focused work session.

~/.claude/memory.sqlite
├── memories (FTS5 + vec0 indexed)
├── edges
├── checkpoints
├── episodes
└── sync_config
~/.claude/
├── memory.sqlite # Personal graph (local-only memories)
├── memory_synced.sqlite # Sync target (flows to/from server)

Both databases are queried on every recall — results are merged by relevance score. The split exists so that local-only memories never touch the sync pipeline.

/team-storage/mobile-team/
├── alice_contribution.db # Alice's exposed memories
├── bob_contribution.db # Bob's exposed memories
├── carol_contribution.db # Carol's exposed memories
└── mobile_team.db # AI-consolidated team graph

Each team member’s contribution database contains memories from their exposed projects. The consolidated team graph is produced by the AI consolidation agent and is the highest-signal source in team recall.