# Database Schema

Arandu uses PostgreSQL with pgvector. All tables are created automatically by `memory.initialize()`. This page documents each table for debugging, querying, and understanding the data model.

---

## Core Tables

### memory_events

Immutable audit log. Every `write()` call creates one event.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `content` | TEXT | Raw message text |
| `embedding` | VECTOR | Message embedding |
| `emotion` | VARCHAR | Detected emotion (joy, sadness, anger, etc.) |
| `emotion_intensity` | FLOAT | Emotion intensity 0.0–1.0 |
| `energy_level` | VARCHAR | high, medium, or low |
| `created_at` | TIMESTAMP | When the event was created |

### memory_facts

Versioned factual knowledge. Each fact is a self-contained natural language statement about an entity.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `entity_type` | VARCHAR | Free-form entity type (person, organization, place, etc.) |
| `entity_key` | VARCHAR | Canonical entity key (e.g., `person:carlos`) |
| `entity_name` | VARCHAR | Display name of the entity |
| `attribute_key` | VARCHAR | Optional attribute category |
| `fact_text` | TEXT | The fact in natural language |
| `embedding` | VECTOR | Fact text embedding |
| `confidence` | FLOAT | Extraction confidence 0.0–1.0 |
| `importance` | FLOAT | Base importance score |
| `source_event_id` | UUID | FK to the event that created this fact |
| `supersedes_fact_id` | UUID | FK to the fact this one replaces (UPDATE chain) |
| `valid_from` | TIMESTAMP | When this fact became active |
| `valid_to` | TIMESTAMP | When this fact was superseded (NULL = active) |
| `invalidated_at` | TIMESTAMP | When explicitly invalidated |
| `is_stale` | BOOLEAN | Marked stale by memify |
| `last_confirmed_at` | TIMESTAMP | Last NOOP confirmation |
| `times_retrieved` | INT | Retrieval counter |
| `last_retrieved_at` | TIMESTAMP | Last retrieval time |
| `source_context` | VARCHAR | Origin marker (e.g., `inferred_from_relation` for mirror facts) |
| `cluster_id` | UUID | FK to cluster |
| `created_at` | TIMESTAMP | Row creation time |

### memory_fact_entity_links

Cross-entity links. Each fact is linked to ALL entities it mentions, not just its primary subject.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `fact_id` | UUID | FK to memory_facts (CASCADE delete) |
| `entity_key` | VARCHAR | Entity this fact is linked to |
| `is_primary` | BOOLEAN | True if this is the fact's primary subject |
| `user_id` | TEXT | User identifier |

**Unique constraint:** `(fact_id, entity_key)` — one link per fact-entity pair.

**Indexes:** `(user_id, entity_key)` for retrieval queries, `(fact_id)` for cascade operations.

### memory_entities

Canonical entity records. Created during entity resolution.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `canonical_key` | VARCHAR | Unique key (e.g., `person:carlos`) |
| `display_name` | VARCHAR | Human-readable name |
| `entity_type` | VARCHAR | Free-form type string |
| `embedding_vec` | VECTOR | Entity name embedding |
| `summary_text` | TEXT | LLM-generated summary (from background jobs) |
| `importance_score` | FLOAT | Computed importance 0.0–1.0 |
| `fact_count` | INT | Number of linked facts |
| `is_active` | BOOLEAN | Whether entity is active |

### memory_entity_aliases

Alias cache for fast exact-match entity resolution.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `alias` | VARCHAR | Normalized alias text |
| `canonical_entity_key` | VARCHAR | Resolved entity key |
| `canonical_entity_type` | VARCHAR | Entity type |

**Unique constraint:** `(user_id, alias)` — first-write-wins semantics.

### memory_entity_relationships

Knowledge graph edges between entities.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `source_entity_key` | VARCHAR | Source entity |
| `target_entity_key` | VARCHAR | Target entity |
| `rel_type` | VARCHAR | Relationship type (snake_case, free-form) |
| `strength` | FLOAT | 0.0–1.0, reinforced on repetition |
| `evidence_fact_id` | UUID | FK to the fact supporting this relationship |
| `valid_from` | TIMESTAMP | When created |
| `valid_to` | TIMESTAMP | When invalidated (NULL = active) |
| `invalidated_at` | TIMESTAMP | Cascade invalidation timestamp |

**Unique constraint:** `(user_id, source_entity_key, target_entity_key, rel_type)`.

!!! warning "Relationships are unidirectional"
    `ana → works_at → acme` does NOT create `acme → employs → ana`. Graph retrieval traverses both directions, but the edge itself is one-way.

---

## Supporting Tables

### memory_clusters

Semantic fact clusters (created by background jobs).

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `entity_type` | VARCHAR | Cluster entity type |
| `entity_key` | VARCHAR | Cluster entity key |
| `summary` | TEXT | LLM-generated cluster summary |
| `embedding` | VECTOR | Cluster embedding |
| `created_at` | TIMESTAMP | Creation time |

### memory_meta_observations

Higher-order patterns detected by consolidation.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `observation_type` | VARCHAR | Type: insight, pattern, contradiction, trend, entity_community |
| `content` | TEXT | Observation text |
| `supporting_fact_ids` | JSONB | Array of fact IDs supporting this observation |
| `is_active` | BOOLEAN | Whether still relevant |
| `created_at` | TIMESTAMP | Creation time |

### memory_attribute_registry

Tracks known attribute keys per user.

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `attribute_key` | VARCHAR | Attribute key |
| `first_seen_at` | TIMESTAMP | When first used |

### memory_intentions

User intentions detected from events (experimental).

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User identifier |
| `intention` | TEXT | Detected intention |
| `source_event_id` | UUID | Source event |
| `confidence` | FLOAT | Detection confidence |
| `created_at` | TIMESTAMP | Creation time |
