# fn_operations — Schema de inteligencia operacional

Base de datos local por proyecto. Cada proyecto tiene su propio archivo `.db` independiente.
Referencia IDs del registry central (`fn_registry`) pero no los duplica — solo apunta a ellos.

---

## Propósito

El registry almacena **lo que sabes hacer**.
fn_operations almacena **lo que has hecho y cómo fluye** en un proyecto concreto.

| | fn_registry | fn_operations |
|---|---|---|
| **Naturaleza** | Conocimiento estático | Conocimiento dinámico |
| **Contenido** | Lo que sabes hacer | Lo que has hecho y cómo fluye |
| **Scope** | Compartida entre todos los proyectos | Local a cada proyecto — un `.db` por proyecto |
| **Tablas** | `functions`, `types` | `entities`, `relations`, `relation_inputs` |
| **Cuándo cambia** | Al añadir código al registry | En cada proyecto activo |

---

## Estructura de directorios

```
fn-registry/
  fn_operations/
    fn_operations.md       ← este archivo
    project_template/
      operations.db        ← plantilla vacía con schema y WAL mode aplicados
    migrations/
      001_init.sql         ← script de creación de tablas
```

---

## Tabla: entities

Una entity es una instancia concreta de un tipo del registry dentro del contexto de un proyecto.
No es el tipo en abstracto — es el dato real que fluye: `ticks_btcusdt_2024`, `ohlcv_1h_binance`, `user_session_web`.

El tipo del registry (`type_ref`) aporta conocimiento heredado — campos, estructura, semántica algebraica.
La entity aporta conocimiento contextual — qué rol juega aquí, de dónde viene, qué propiedades específicas tiene.

```
type_ref  → lo que la entidad ES       (heredado del registry)
metadata  → lo que la entidad TIENE    (específico de este contexto)
```

| Campo | Tipo | Descripción | Notas / Restricciones |
|---|---|---|---|
| `id` | string | Identificador único de la entidad en el proyecto. | Formato: `{name}_{context}` ej: `ticks_btcusdt_2024`. Único dentro del proyecto. |
| `name` | string | Nombre semántico de la entidad. | Descriptivo y específico al dominio del proyecto. Obligatorio. |
| `type_ref` | string | ID del tipo del registry que modela esta entidad. | Referencia a `types.id` en `fn_registry`. Obligatorio. |
| `status` | enum | Estado actual de la entidad. | `active` \| `stale` \| `corrupted` \| `archived` |
| `description` | text | Qué representa esta entidad en el contexto del proyecto. | No qué es el tipo en abstracto, sino qué rol juega aquí. |
| `domain` | string | Área de dominio dentro del proyecto. | Ej: `market_data`, `auth`, `reporting`. |
| `tags` | []string | Etiquetas libres para búsqueda y agrupación. | |
| `source` | string | Origen del dato. | Obligatorio. Ej: `binance_api`, `csv_upload`, `user_input`, `pipeline_output`. |
| `metadata` | json | Propiedades específicas del contexto no capturadas por el tipo. | Valores concretos de los campos del tipo. Ej: `{"pair":"BTCUSDT","exchange":"binance"}`. |
| `notes` | text | Contexto extra libre sobre la entidad. | Para el agente o para ti. Decisiones, observaciones, advertencias, historial informal. |
| `created_at` | datetime | Fecha de registro de la entidad. | ISO 8601. Generado automáticamente. |
| `updated_at` | datetime | Fecha de última modificación. | ISO 8601. Actualizado automáticamente. |

### Valores de status — entities

| Valor | Significado |
|---|---|
| `active` | Dato en uso, actualizado y fiable. |
| `stale` | Existe pero puede estar desactualizado. El agente debe verificar antes de usar. |
| `corrupted` | Se sabe que tiene problemas de integridad. No usar en pipelines. |
| `archived` | Ya no se usa activamente. Se conserva por trazabilidad histórica. |

### Reglas de integridad — entities

| Condición | Regla |
|---|---|
| `type_ref` | Debe existir en `fn_registry.types.id`. Sin referencias huérfanas. |
| `id` | Único dentro del proyecto. Formato `{name}_{context}`. |
| `source` | Obligatorio. Todo dato tiene un origen conocido. |
| `name` | Obligatorio. Sin espacios, snake_case. |
| `metadata` | JSON válido. Debe contener los valores de los campos definidos por el tipo del registry. |
| `status: corrupted` | El agente nunca debe usar esta entidad como input de una relation activa. |

---

## Tabla: relations

Una relation describe cómo una entidad se conecta o transforma en otra. Puede documentar una
transformación técnica a través de una función del registry, o una relación puramente semántica
sin función asociada. El campo `name` expresa el significado humano de la conexión.

```
juan (type: person)  →  CONOCE A  →  paula (type: person)
ticks                →  AGREGA    →  ohlcv_1h   (via: tick_to_ohlcv_go_finance)
```

Para relaciones que consumen **múltiples entidades** (joins, merges, agregaciones multi-fuente),
se usa la tabla `relation_inputs` en lugar de `from_entity`. Ver sección correspondiente.

| Campo | Tipo | Descripción | Notas / Restricciones |
|---|---|---|---|
| `id` | string | Identificador único de la relación. | Formato: `{from}__to__{to}__via__{fn}`. Legible sin joins. |
| `name` | string | Etiqueta semántica de la relación. | El verbo o concepto que describe la conexión. Ej: `CONOCE A`, `TRANSFORMA`, `PRODUCE`, `DEPENDE DE`. Obligatorio. |
| `from_entity` | string | ID de la entidad origen para relaciones simples (1-a-1). | Referencia a `entities.id`. Opcional si se usa `relation_inputs`. |
| `to_entity` | string | ID de la entidad destino. | Referencia a `entities.id`. Obligatorio. |
| `via` | string | ID de la función o pipeline del registry que realiza la transformación. | Referencia a `functions.id` en `fn_registry`. Opcional — vacío si la relación es puramente semántica. |
| `description` | text | Qué ocurre en esta conexión en el contexto del proyecto. | No qué hace la función en abstracto, sino qué significa aquí. |
| `purity` | enum | Naturaleza de la transformación. | `pure` \| `impure`. Si `via` está informado, debe ser consistente con `via.purity` en el registry. |
| `direction` | enum | Dirección semántica de la relación. | `unidirectional` \| `bidirectional` \| `inverse`. Ver valores abajo. |
| `weight` | float | Ponderación opcional de la relación. | Rango `0.0 – 1.0`. Permite queryar "relaciones con peso > 0.8". Opcional. |
| `status` | enum | Estado del flujo. | `designed` \| `implemented` \| `tested` \| `running` \| `deprecated` |
| `started_at` | datetime | Cuándo empezó el flujo realmente. | ISO 8601. Distinto de `created_at` — ese es cuándo lo documentaste. |
| `ended_at` | datetime | Cuándo terminó o dejó de ejecutarse el flujo. | ISO 8601. Vacío si sigue activo. |
| `order` | int | Posición en el pipeline si forma parte de una secuencia ordenada. | Opcional. Permite reconstruir el grafo en orden de ejecución. |
| `tags` | []string | Etiquetas libres. | |
| `notes` | text | Observaciones, decisiones de diseño, problemas conocidos. | Libre. El agente lee esto para entender contexto histórico del flujo. |
| `created_at` | datetime | Fecha de registro de la relación. | ISO 8601. Generado automáticamente. |
| `updated_at` | datetime | Fecha de última modificación. | ISO 8601. Actualizado automáticamente. |

### Valores de direction

| Valor | Significado |
|---|---|
| `unidirectional` | A → B. La relación fluye en un solo sentido. Valor por defecto. |
| `bidirectional` | A ↔ B. La relación es simétrica — vale en ambos sentidos. Ej: `CONOCE A`. |
| `inverse` | La relación se documenta de B a A pero su lectura natural es de A a B. Útil para linaje inverso. |

### Valores de status — relations

| Valor | Significado |
|---|---|
| `designed` | Flujo planificado. Existe en el grafo de arquitectura pero no hay código todavía. |
| `implemented` | Código escrito. No verificado en este contexto de proyecto aún. |
| `tested` | Verificado en este proyecto. Los datos fluyen correctamente. |
| `running` | Activo en producción o en uso continuo. |
| `deprecated` | Ya no se usa. Se mantiene por trazabilidad histórica. |

### Reglas de integridad — relations

| Condición | Regla |
|---|---|
| `name` | Obligatorio. Describe el verbo semántico de la relación. |
| `from_entity` | Si informado, debe existir en `entities.id`. Vacío si se usa `relation_inputs`. |
| `from_entity` o `relation_inputs` | Al menos uno de los dos debe estar presente. Una relation sin origen es inválida. |
| `to_entity` | Debe existir en `entities.id`. Obligatorio. |
| `via` | Si informado, debe existir en `fn_registry.functions.id`. |
| `purity` | Si `via` está informado, debe ser consistente con `via.purity` en el registry. |
| `weight` | Si informado, debe estar en el rango `0.0 – 1.0`. |
| `from_entity != to_entity` | Una entidad no puede relacionarse consigo misma. |
| `started_at` / `ended_at` | Si ambos informados, `started_at` debe ser anterior a `ended_at`. |
| `id` | Único dentro del proyecto. |
| **Sin ciclos** | El CLI valida al insertar que la nueva relation no crea un ciclo en el grafo. Si lo crea, la inserción se rechaza. SQLite no detecta ciclos — esta validación ocurre en la capa de aplicación. |

---

## Tabla: relation_inputs

Tabla intermedia para relaciones que consumen **múltiples entidades simultáneamente** — joins,
merges, agregaciones multi-fuente. Cuando una relation usa `relation_inputs`, el campo
`from_entity` en `relations` se deja vacío.

```
join(ticks_btcusdt, metadata_binance)  →  ENRIQUECE  →  ohlcv_enriquecido
     ↑ input role: "base"                                   ↑ to_entity
                     ↑ input role: "lookup"
```

| Campo | Tipo | Descripción | Notas / Restricciones |
|---|---|---|---|
| `id` | string | Identificador único del input. | Generado automáticamente. |
| `relation_id` | string | ID de la relation a la que pertenece este input. | Referencia a `relations.id`. Obligatorio. |
| `entity_id` | string | ID de la entidad que actúa como input. | Referencia a `entities.id`. Obligatorio. |
| `role` | string | Rol semántico de este input en la relación. | Ej: `base`, `lookup`, `filter`, `left`, `right`. Obligatorio. Permite al agente entender cómo se usa cada input. |
| `order` | int | Orden del input si la relación es sensible al orden. | Opcional. Ej: en un join `left`/`right` el orden importa. |

### Reglas de integridad — relation_inputs

| Condición | Regla |
|---|---|
| `relation_id` | Debe existir en `relations.id`. |
| `entity_id` | Debe existir en `entities.id`. |
| `role` | Obligatorio. Sin rol el agente no puede razonar sobre cómo se usa el input. |
| Mínimo 2 inputs | Una relation con `relation_inputs` debe tener al menos 2 entradas. Si solo hay una, usar `from_entity` en su lugar. |

---

## Referencias cruzadas completas

| Campo origen | → | Destino |
|---|---|---|
| `entities.type_ref` | → | `fn_registry → types.id` |
| `relations.from_entity` | → | `entities.id` (opcional) |
| `relations.to_entity` | → | `entities.id` |
| `relations.via` | → | `fn_registry → functions.id` (opcional) |
| `relation_inputs.relation_id` | → | `relations.id` |
| `relation_inputs.entity_id` | → | `entities.id` |

---

## Ejemplo — pipeline de market data

### Entities

| id | type_ref | status | source | metadata |
|---|---|---|---|---|
| `ticks_btcusdt_2024` | `tick_go_finance` | `active` | `binance_api` | `{"pair":"BTCUSDT","exchange":"binance"}` |
| `metadata_binance` | `exchange_meta_go_finance` | `active` | `binance_api` | `{"version":"v3"}` |
| `ohlcv_1h_btcusdt` | `ohlcv_go_finance` | `active` | `pipeline_output` | `{"interval":"1h","pair":"BTCUSDT"}` |
| `sma_20_btcusdt` | `float64_slice_go_core` | `active` | `pipeline_output` | `{"period":20,"field":"close"}` |

### Relations simples

| name | from_entity | to_entity | via | direction | status | weight |
|---|---|---|---|---|---|---|
| `CALCULA` | `ohlcv_1h_btcusdt` | `sma_20_btcusdt` | `sma_go_finance` | `unidirectional` | `tested` | 1.0 |

### Relation con múltiples inputs (relation_inputs)

**Relation:** `id: ticks_y_meta__to__ohlcv__via__enrich`, `name: ENRIQUECE`, `to_entity: ohlcv_1h_btcusdt`

| relation_id | entity_id | role | order |
|---|---|---|---|
| `ticks_y_meta__to__ohlcv__via__enrich` | `ticks_btcusdt_2024` | `base` | 1 |
| `ticks_y_meta__to__ohlcv__via__enrich` | `metadata_binance` | `lookup` | 2 |

### Grafo resultante

```
[ticks_btcusdt_2024] ──┐
                        ├─ ENRIQUECE (impure · running) ──→ [ohlcv_1h_btcusdt]
[metadata_binance]   ──┘                                           ↓
                                                    CALCULA (pure · tested)
                                                           ↓
                                                   [sma_20_btcusdt]
```

---

## Ejemplo — relaciones semánticas puras (sin via)

```
[juan] (type: person)   →  CONOCE A   (bidirectional, weight: 0.9)  →  [paula]
[servicio_auth]         →  DEPENDE DE (unidirectional, weight: 1.0)  →  [bbdd_usuarios]
[modelo_riesgo]         →  ALIMENTA   (unidirectional, weight: 0.7)  →  [dashboard_trading]
```

---

## Escalabilidad

| Fase | BBDD | Cuándo migrar |
|---|---|---|
| Inicial | SQLite + WAL mode | Proyectos personales, un agente escribiendo |
| Crecimiento | PostgreSQL | Múltiples agentes escribiendo concurrentemente |
| Analítico | ClickHouse | Millones de operaciones, dashboards de uso |
