## Asset Header

- **Asset ID:** ARQ-XX-AI-SherpaOS-DataModel-EventLedger-v0.1
- **Version:** v00
- **Status:** Draft
- **Owner:** Victor Heredia
- **IntellBank:** IB-XX-Maestro
- **Tipo:** ARQ — Architecture
- **Propósito:** AI Sherpa OS — Data Model & Event Ledger
- **Última actualización:** 2026-04-11

---

# AI Sherpa OS — Data Model & Event Ledger
## Engineering Spec v0.1
**EmpowerLabs / MasterPlaybooks**
Última actualización: 24 marzo 2026

---

## Contexto

Este documento traduce el modelo de datos y el Event Ledger en especificaciones accionables para el equipo de backend.

**Actualización v0.1:** Se incorpora el stack técnico validado basado en el patrón OB1 (Supabase + pgvector + OpenRouter + MCP). Esta implementación sirve simultáneamente al AI Sherpa OS y a Mi Sherpa IA as a Service — misma infraestructura, dos productos.

**Responsable de implementación:** Alex + equipo técnico

**Referencia de implementación:** Patrón OB1 (NateBJones-Projects/OB1) — validado como implementable en días por un solo desarrollador.

---

## Índice

1. Stack técnico concreto
2. Setup inicial — Supabase
3. Schema de base de datos
4. Edge Functions
5. Catálogo de event_type
6. Ejemplos de payload
7. API Endpoints
8. Herramientas MCP
9. Flujo end-to-end
10. Reglas de estado
11. Definition of Done
12. Orden de implementación recomendado

---

## 1. Stack técnico concreto

| Componente | Tecnología | Por qué |
|------------|------------|---------|
| Base de datos | **Supabase** (PostgreSQL) | Todo en uno: DB + auth + storage + realtime + edge functions |
| Vector search | **pgvector** (extensión Postgres) | Búsqueda semántica sin Pinecone/Weaviate — suficiente para esta fase |
| Serverless logic | **Supabase Edge Functions** (Deno) | Metadata extraction + embedding generation al guardar eventos |
| LLM gateway | **OpenRouter** | Multi-modelo (Claude, GPT, Gemini) desde una sola API key |
| Protocolo tools | **MCP** (Model Context Protocol) | Interfaz estándar para que el Sherpa use las herramientas de memoria |
| Autenticación | **Supabase Auth** | JWT + Row Level Security por user_id |
| Aislamiento | **Row Level Security (RLS)** | Cada usuario ve solo sus datos — crítico para multi-tenant |

**Nota:** Este es el mismo stack de Mi Sherpa IA. Si Alex ya tiene un proyecto Supabase corriendo para Mi Sherpa IA, el Sherpa puede usar la misma instancia con tablas adicionales y RLS separado por contexto.

---

## 2. Setup inicial — Supabase

### 2.1 Habilitar extensión pgvector

```sql
-- Ejecutar en SQL Editor de Supabase
create extension if not exists vector;
```

### 2.2 Configurar OpenRouter

```bash
# En variables de entorno de Supabase (Settings → Edge Functions → Secrets)
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=anthropic/claude-3-haiku  # modelo rápido para embeddings/metadata
```

### 2.3 Modelo para embeddings

```bash
# OpenAI embeddings via OpenRouter (1536 dimensiones — compatible con text-embedding-3-small)
EMBEDDING_MODEL=openai/text-embedding-3-small
```

---

## 3. Schema de base de datos

### 3.1 Tabla: users

```sql
create table users (
  user_id uuid primary key default gen_random_uuid(),
  email text unique not null,
  name text,
  profile jsonb default '{}'::jsonb,
  -- profile incluye: goal, industry, role, experience_level
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

-- RLS
alter table users enable row level security;
create policy "users_own_data" on users
  for all using (auth.uid() = user_id);

-- Auto-update timestamp
create trigger update_users_updated_at
  before update on users
  for each row execute function update_updated_at_column();
```

### 3.2 Tabla: sessions

```sql
create table sessions (
  session_id uuid primary key default gen_random_uuid(),
  user_id uuid references users(user_id) not null,
  sherpa_id text not null,  -- ej: 'sherpa-nom035', 'sherpa-recruitment'
  playbook_id text,         -- MasterPlaybook en ejecución
  started_at timestamptz default now(),
  ended_at timestamptz,
  summary text,             -- generado automáticamente al cerrar
  metadata jsonb default '{}'::jsonb
);

-- RLS
alter table sessions enable row level security;
create policy "sessions_own_data" on sessions
  for all using (auth.uid() = user_id);
```

### 3.3 Tabla: events ← EL CORAZÓN DEL SISTEMA

```sql
create table events (
  event_id uuid primary key default gen_random_uuid(),
  user_id uuid references users(user_id) not null,
  session_id uuid references sessions(session_id),

  -- Clasificación del evento
  event_type text not null,      -- ver catálogo completo abajo
  event_category text not null,  -- interaction | progress | artifact | signal | commercial

  -- Datos del evento
  payload jsonb default '{}'::jsonb,

  -- Embedding semántico generado automáticamente
  embedding vector(1536),

  -- Trazabilidad
  created_at timestamptz default now(),

  -- Contexto del playbook (opcional)
  playbook_id text,
  module_id text
);

-- RLS
alter table events enable row level security;
create policy "events_own_data" on events
  for all using (auth.uid() = user_id);

-- Índice para búsqueda semántica
create index events_embedding_idx on events
  using ivfflat (embedding vector_cosine_ops)
  with (lists = 100);

-- Índice para queries por usuario + tiempo
create index events_user_created_idx on events (user_id, created_at desc);
```

### 3.4 Tabla: state (derivado del Event Ledger)

```sql
create table state (
  user_id uuid primary key references users(user_id),

  -- Estado del usuario
  active_goal text,
  current_stage text,
  progress_score integer default 0,  -- 0-100

  -- Niveles derivados
  engagement_level text default 'medium',  -- high | medium | low
  readiness_level text default 'low',      -- high | medium | low
  risk_level text default 'low',           -- high | medium | low (riesgo de abandono)

  -- Siguiente acción recomendada
  next_best_action text,
  next_best_action_payload jsonb default '{}'::jsonb,

  -- Metadata
  last_event_id uuid,
  updated_at timestamptz default now()
);

-- RLS
alter table state enable row level security;
create policy "state_own_data" on state
  for all using (auth.uid() = user_id);
```

### 3.5 Tabla: memories ← MEMORY LAYER (patrón OB1)

```sql
create table memories (
  memory_id uuid primary key default gen_random_uuid(),
  user_id uuid references users(user_id) not null,

  -- Contenido — debe ser autocontenido y tener sentido fuera de contexto
  content text not null,

  -- Embedding para búsqueda semántica
  embedding vector(1536),

  -- Metadata extraída automáticamente por LLM
  metadata jsonb default '{}'::jsonb,
  -- metadata incluye: people[], topics[], action_items[], type, source_session_id

  -- Tipo de memoria
  memory_type text default 'episodic',  -- episodic | identity | inference | summary

  -- Trazabilidad
  source_session_id uuid references sessions(session_id),
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

-- RLS
alter table memories enable row level security;
create policy "memories_own_data" on memories
  for all using (auth.uid() = user_id);

-- Índice para búsqueda semántica
create index memories_embedding_idx on memories
  using ivfflat (embedding vector_cosine_ops)
  with (lists = 100);

-- Auto-update timestamp
create trigger update_memories_updated_at
  before update on memories
  for each row execute function update_updated_at_column();
```

### 3.6 Tabla: artifacts

```sql
create table artifacts (
  artifact_id uuid primary key default gen_random_uuid(),
  user_id uuid references users(user_id) not null,
  session_id uuid references sessions(session_id),

  artifact_type text not null,  -- plan | summary | checklist | framework | report
  title text,
  content jsonb not null,

  created_at timestamptz default now()
);

-- RLS
alter table artifacts enable row level security;
create policy "artifacts_own_data" on artifacts
  for all using (auth.uid() = user_id);
```

### 3.7 Función de utilidad: update_updated_at_column

```sql
create or replace function update_updated_at_column()
returns trigger as $$
begin
  new.updated_at = now();
  return new;
end;
$$ language plpgsql;
```

---

## 4. Edge Functions

### 4.1 on-event-created (trigger automático)

Se ejecuta cada vez que se inserta un evento. Genera embedding y extrae metadata.

```typescript
// supabase/functions/on-event-created/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from "https://esm.sh/@supabase/supabase-js@2"

serve(async (req) => {
  const { event_id, user_id, event_type, payload } = await req.json()

  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )

  // 1. Generar texto para el embedding
  const textToEmbed = `${event_type}: ${JSON.stringify(payload)}`

  // 2. Generar embedding via OpenRouter
  const embeddingResponse = await fetch('https://openrouter.ai/api/v1/embeddings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('OPENROUTER_API_KEY')}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: 'openai/text-embedding-3-small',
      input: textToEmbed
    })
  })

  const { data } = await embeddingResponse.json()
  const embedding = data[0].embedding

  // 3. Actualizar el evento con el embedding
  await supabase
    .from('events')
    .update({ embedding })
    .eq('event_id', event_id)

  // 4. Recalcular state
  await recalculateState(supabase, user_id)

  return new Response(JSON.stringify({ success: true }))
})
```

### 4.2 on-session-end (genera memory al cerrar sesión)

```typescript
// supabase/functions/on-session-end/index.ts
serve(async (req) => {
  const { session_id, user_id } = await req.json()

  // 1. Obtener todos los eventos de la sesión
  const { data: events } = await supabase
    .from('events')
    .select('*')
    .eq('session_id', session_id)
    .order('created_at', { ascending: true })

  // 2. Generar resumen de sesión via LLM (OpenRouter)
  const summary = await generateSessionSummary(events)

  // 3. Guardar resumen en sessions
  await supabase
    .from('sessions')
    .update({ summary, ended_at: new Date().toISOString() })
    .eq('session_id', session_id)

  // 4. Crear memoria episódica
  const memoryContent = buildMemoryContent(summary, events)
  const metadata = await extractMetadata(memoryContent)
  const embedding = await generateEmbedding(memoryContent)

  await supabase.from('memories').insert({
    user_id,
    content: memoryContent,
    embedding,
    metadata,
    memory_type: 'episodic',
    source_session_id: session_id
  })

  return new Response(JSON.stringify({ success: true, summary }))
})
```

### 4.3 search-memories (búsqueda semántica)

```typescript
// supabase/functions/search-memories/index.ts
serve(async (req) => {
  const { query, user_id, limit = 5 } = await req.json()

  // 1. Generar embedding del query
  const queryEmbedding = await generateEmbedding(query)

  // 2. Búsqueda por similitud coseno
  const { data: memories } = await supabase.rpc('search_memories', {
    query_embedding: queryEmbedding,
    user_id_param: user_id,
    match_count: limit
  })

  return new Response(JSON.stringify({ memories }))
})
```

```sql
-- Función SQL para búsqueda semántica
create or replace function search_memories(
  query_embedding vector(1536),
  user_id_param uuid,
  match_count int default 5
)
returns table (
  memory_id uuid,
  content text,
  metadata jsonb,
  similarity float
)
language sql stable as $$
  select
    memory_id,
    content,
    metadata,
    1 - (embedding <=> query_embedding) as similarity
  from memories
  where user_id = user_id_param
  order by embedding <=> query_embedding
  limit match_count;
$$;
```

---

## 5. Catálogo de event_type

### Interaction
| event_type | Descripción |
|------------|-------------|
| `user_message_submitted` | Usuario envía un mensaje al Sherpa |
| `sherpa_response_generated` | Sherpa genera una respuesta |
| `user_goal_declared` | Usuario declara explícitamente un objetivo |
| `user_constraint_declared` | Usuario declara una limitante o restricto |
| `session_started` | Inicio de sesión |
| `session_ended` | Cierre de sesión |

### Progress
| event_type | Descripción |
|------------|-------------|
| `module_started` | Usuario inicia un módulo del MasterPlaybook |
| `module_completed` | Usuario completa un módulo |
| `task_completed` | Usuario completa una tarea específica |
| `milestone_reached` | Usuario alcanza un hito del playbook |
| `content_consumed` | Usuario consumió un contenido (video, artículo) |

### Artifact
| event_type | Descripción |
|------------|-------------|
| `plan_generated` | Sherpa generó un plan de acción |
| `summary_generated` | Sherpa generó un resumen |
| `checklist_generated` | Sherpa generó un checklist |
| `framework_applied` | Sherpa aplicó un framework al contexto del usuario |

### Signal (derivado — generado por el State Engine)
| event_type | Descripción |
|------------|-------------|
| `engagement_increase_detected` | Señal positiva de engagement |
| `engagement_drop_detected` | Señal de caída de engagement (riesgo abandono) |
| `blocker_detected` | Usuario expresó un bloqueo |
| `readiness_increase_detected` | Usuario está más listo para siguiente paso |

### Commercial (especialización)
| event_type | Descripción |
|------------|-------------|
| `pricing_interest_detected` | Usuario preguntó por precio |
| `urgency_detected` | Usuario expresó urgencia |
| `call_intent_detected` | Usuario quiere hablar con alguien |
| `upgrade_interest_detected` | Usuario preguntó por tier superior |

---

## 6. Ejemplos de payload por evento

### user_goal_declared
```json
{
  "goal_text": "Quiero implementar NOM-035 en mi empresa de 80 personas antes de junio",
  "goal_type": "compliance",
  "timeframe": "90_days",
  "priority": "high",
  "company_size": "80_employees"
}
```

### module_completed
```json
{
  "module_id": "NOM035-001",
  "module_name": "Diagnóstico inicial NOM-035",
  "completion_time_seconds": 1240,
  "score": 85,
  "playbook_id": "sherpa-nom035-v1"
}
```

### blocker_detected
```json
{
  "blocker_text": "No tenemos presupuesto asignado para esto todavía",
  "blocker_type": "budget",
  "severity": "medium",
  "raw_message": "El problema es que no tenemos..."
}
```

### pricing_interest_detected
```json
{
  "phrase": "¿Cuánto costaría implementar el programa completo?",
  "interest_level": "high",
  "context": "Al terminar el módulo de diagnóstico",
  "related_offer": "Programa NOM-035 Premium"
}
```

---

## 7. API Endpoints

### POST /events
Registra un evento en el ledger. El embedding se genera automáticamente via edge function.

```
Body:
{
  "user_id": "uuid",
  "session_id": "uuid",
  "event_type": "module_completed",
  "event_category": "progress",
  "payload": { ... },
  "playbook_id": "sherpa-nom035-v1",  // opcional
  "module_id": "NOM035-001"            // opcional
}

Response:
{
  "event_id": "uuid",
  "status": "created",
  "state_updated": true
}
```

### GET /state/{user_id}
Obtiene el estado consolidado del usuario.

```
Response:
{
  "user_id": "uuid",
  "active_goal": "Implementar NOM-035 antes de junio",
  "current_stage": "diagnostic",
  "progress_score": 35,
  "engagement_level": "high",
  "readiness_level": "medium",
  "risk_level": "low",
  "next_best_action": "complete_module_2",
  "next_best_action_payload": {
    "module_id": "NOM035-002",
    "message": "Estás listo para el siguiente módulo: Política de Prevención"
  },
  "updated_at": "2026-03-24T..."
}
```

### GET /recommendations/{user_id}
Obtiene recomendaciones activas para el usuario.

```
Response:
{
  "recommendations": [
    {
      "type": "next_module",
      "priority": "high",
      "title": "Continuar con Módulo 2",
      "action": "navigate_to_module",
      "payload": { "module_id": "NOM035-002" }
    },
    {
      "type": "resource",
      "priority": "medium",
      "title": "Guía de diagnóstico rápido",
      "action": "show_resource"
    }
  ]
}
```

### POST /session/start
```
Body: { "user_id": "uuid", "sherpa_id": "sherpa-nom035", "playbook_id": "..." }
Response: { "session_id": "uuid", "started_at": "..." }
```

### POST /session/end
Cierra sesión, genera resumen y crea memoria episódica automáticamente.
```
Body: { "session_id": "uuid", "user_id": "uuid" }
Response: { "summary": "...", "memory_id": "uuid", "duration_seconds": 840 }
```

### POST /memories/search
Búsqueda semántica en la Memory Layer.
```
Body: { "user_id": "uuid", "query": "¿Qué objetivos declaró el usuario?", "limit": 5 }
Response: { "memories": [{ "content": "...", "similarity": 0.87, "metadata": {...} }] }
```

---

## 8. Herramientas MCP

El Sherpa accede a la infraestructura via estas 6 herramientas MCP:

| Herramienta | Descripción |
|-------------|-------------|
| `capture_event` | Registrar un evento en el Event Ledger |
| `get_user_state` | Obtener estado actual del usuario |
| `get_recommendations` | Obtener recomendaciones activas |
| `capture_memory` | Guardar una memoria episódica manualmente |
| `search_memories` | Búsqueda semántica en el Memory Layer |
| `list_recent_memories` | Listar memorias recientes del usuario |

---

## 9. Flujo end-to-end

```
1. Sherpa recibe mensaje del usuario
   └→ capture_event: user_message_submitted

2. Sherpa consulta contexto
   └→ search_memories: "¿qué sé de este usuario?"
   └→ get_user_state: estado actual

3. Sherpa genera respuesta contextualizada
   └→ capture_event: sherpa_response_generated

4. Usuario completa módulo
   └→ capture_event: module_completed
   └→ State Engine recalcula automáticamente

5. State Engine detecta progreso
   └→ capture_event: engagement_increase_detected (derivado)
   └→ next_best_action actualizado

6. Usuario menciona presupuesto
   └→ capture_event: pricing_interest_detected
   └→ Integration Layer notifica al CRM

7. Sesión termina
   └→ POST /session/end
   └→ Resumen generado automáticamente
   └→ Memoria episódica creada
   └→ Próxima sesión inicia con contexto completo
```

---

## 10. Reglas de recálculo de estado

```
State se recalcula cuando:
- Se registran N eventos nuevos (N=5 por defecto)
- Se cierra una sesión
- Se detecta una señal comercial

Lógica de engagement_level:
- HIGH: módulo_completado en últimos 7 días Y tiempo_sesión > 10min
- LOW: sin eventos en últimos 14 días O blocker_detected sin resolución
- MEDIUM: resto de casos

Lógica de readiness_level:
- HIGH: progress_score > 60 AND engagement_level = HIGH
- LOW: progress_score < 20 OR blocker_detected reciente
- MEDIUM: resto de casos

Lógica de risk_level:
- HIGH: engagement_drop_detected en últimas 2 sesiones
- MEDIUM: sin actividad en 7-14 días
- LOW: actividad regular

Prioridad de eventos: recientes > históricos (decay function)
```

---

## 11. Definition of Done

### Fase 1 — Event Ledger v0.1
- [ ] Proyecto Supabase creado con extensión pgvector habilitada
- [ ] Schema completo ejecutado sin errores (users, sessions, events, state)
- [ ] RLS configurado y verificado por user_id
- [ ] Edge function `on-event-created` deployada y funcional
- [ ] POST /events funcional — evento persistido + embedding generado
- [ ] GET /state/{user_id} funcional — estado calculado automáticamente
- [ ] Flujo end-to-end ejecutable: session_start → event → state_update
- [ ] Al menos 5 event_types del catálogo implementados y probados

### Fase 2 — Memory Layer v0.1
- [ ] Tabla `memories` creada con embedding vector(1536)
- [ ] Edge function `on-session-end` deployada y funcional
- [ ] Función SQL `search_memories` funcionando con cosine similarity
- [ ] POST /session/end genera resumen + memoria episódica automáticamente
- [ ] POST /memories/search retorna resultados relevantes
- [ ] 6 herramientas MCP operativas y documentadas

---

## 12. Orden de implementación recomendado

```
Día 1:
├── Crear proyecto Supabase
├── Habilitar pgvector
├── Ejecutar schema: users + sessions + events + state
└── Verificar RLS

Día 2:
├── Edge function: on-event-created (embedding generation)
├── POST /events funcional
└── GET /state/{user_id} básico

Día 3:
├── Schema: memories
├── Edge function: on-session-end (summary + memory creation)
├── POST /session/start y POST /session/end
└── Prueba flujo completo end-to-end

Día 4:
├── Función SQL: search_memories
├── POST /memories/search
├── Herramientas MCP (6 herramientas)
└── Prueba de búsqueda semántica

Día 5:
├── Integration Layer: webhook hacia CRM para señales comerciales
├── Documentación de API para Juan Carlos
└── Demo interno del flujo completo
```

---

## Cierre técnico

Este spec permite a Alex y su equipo implementar el núcleo completo del Sherpa OS y de Mi Sherpa IA as a Service en una sola semana, usando una sola plataforma (Supabase), sin dependencias externas complejas.

Sin esto, el sistema queda en prompts.
Con esto, se convierte en infraestructura.

La misma infraestructura que sirve al Sherpa hoy, sirve a Mi Sherpa IA as a Service mañana — y eso es exactamente lo que hace que esta inversión valga doble.

---

*Documento: ai_sherpa_os_data_model_event_ledger_v0.1.md | EmpowerLabs | 24 marzo 2026*
