diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index fc5c974b..6c837914 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -2,6 +2,46 @@ Registry personal de codigo reutilizable con busqueda FTS. Diseñado para composicion funcional y agentes. +El sistema tiene **dos bases de datos SQLite**: +- **registry.db** — conocimiento estatico: que funciones y tipos existen, como se componen, y que mejoras se proponen. Vive en la raiz del repositorio. Regenerable con `fn index` (excepto proposals). +- **operations.db** — conocimiento dinamico por proyecto: que entities existen, como se relacionan, que ejecuciones se han hecho, y que calidad tienen los datos. Vive en cada proyecto que usa el registry. + +--- + +## Arquitectura: dos BDs, un bucle + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ registry.db (central) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ functions │ │ types │ │proposals │ │ +│ │ (120+) │ │ (31+) │ │(auto/man)│ │ +│ └──────────┘ └──────────┘ └──────────┘ │ +│ ↑ busca funciones ↑ crea proposals │ +└───────┼────────────────────────────┼────────────────────────────────┘ + │ │ +┌───────┼────────────────────────────┼────────────────────────────────┐ +│ ↓ usa en relaciones │ reactive loop │ +│ operations.db (por proyecto) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ +│ │ entities │→ │relations │ │executions│→ │assertions │ │ +│ │ │ │ │ │ │ │ → assertion_results│ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │relation_inputs│ │types_snapshot│ │ +│ └──────────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +**Bucle autonomo:** EJECUTAR → EVALUAR → REACCIONAR → PROPONER +1. Un pipeline se ejecuta → se registra en `executions` +2. Las `assertions` activas de la entity se evaluan automaticamente +3. Si critical falla → entity pasa a `corrupted` + se crea `proposal` en registry +4. Si warning falla → entity pasa a `stale` +5. El humano revisa proposals y decide si implementar mejoras + +--- + ## Explorar el registry (USAR SIEMPRE) La BD SQLite `registry.db` es tu mapa del repositorio. Antes de escribir codigo, SIEMPRE consultala para saber que existe, evitar duplicados y descubrir funciones reutilizables. @@ -36,33 +76,324 @@ sqlite3 registry.db "SELECT domain, COUNT(*) FROM functions GROUP BY domain;" # Ver todo el schema sqlite3 registry.db ".schema" + +# Ver proposals pendientes +sqlite3 registry.db "SELECT id, kind, status, title FROM proposals WHERE status = 'pending';" ``` La BD usa WAL mode — puedes leerla mientras se escribe sin bloqueos. +--- + +## Tablas: registry.db + +### functions (27 columnas) + +Cada funcion registrada: su firma, purity, dependencias, y metadata. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | `{name}_{lang}_{domain}` | +| name | TEXT | snake_case | +| kind | TEXT | `function` / `pipeline` / `component` | +| lang | TEXT | `go` / `python` / `typescript` / `sql` | +| domain | TEXT | `core` / `finance` / `datascience` / `cybersecurity` / ... | +| version | TEXT | semver | +| purity | TEXT | `pure` / `impure` | +| signature | TEXT | firma completa | +| description | TEXT | que hace y cuando usarla | +| tags | JSON[] | etiquetas | +| uses_functions | JSON[] | IDs de funciones que invoca | +| uses_types | JSON[] | IDs de tipos que recibe | +| returns | JSON[] | IDs de tipos que devuelve (no tipos nativos) | +| returns_optional | INT | 0/1 | +| error_type | TEXT | ID del tipo de error (obligatorio si impure) | +| imports | JSON[] | dependencias externas | +| example | TEXT | codigo de ejemplo extraido del .md | +| tested | INT | 0/1 | +| tests | JSON[] | nombres de tests | +| test_file_path | TEXT | ruta al archivo de test | +| file_path | TEXT | ruta relativa al .go/.py/.tsx | +| props | JSON[] | solo components: PropDef[] | +| emits | JSON[] | solo components: eventos emitidos | +| has_state | INT? | solo components: nullable | +| framework | TEXT | solo components: react/vue/... | +| variant | JSON[] | solo components: variantes | +| created_at, updated_at | TEXT | RFC3339 | + +### types (13 columnas) + +Tipos algebraicos: product (struct) y sum (interface/union). + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | `{name}_{lang}_{domain}` | +| name | TEXT | snake_case | +| lang | TEXT | lenguaje | +| domain | TEXT | dominio | +| version | TEXT | semver | +| algebraic | TEXT | `product` / `sum` | +| definition | TEXT | codigo fuente del tipo | +| description | TEXT | descripcion | +| tags | JSON[] | etiquetas | +| uses_types | JSON[] | IDs de tipos que compone (sin auto-ref) | +| file_path | TEXT | ruta relativa | +| created_at, updated_at | TEXT | RFC3339 | + +### proposals (11 columnas) + +Mejoras propuestas al registry. Las crea el agente (reactive loop) o el humano. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | `proposal_{timestamp}` o manual | +| kind | TEXT | `new_function` / `new_type` / `improve_function` / `improve_type` / `new_pipeline` | +| target_id | TEXT | ID de la funcion/tipo a mejorar (obligatorio para improve_*) | +| title | TEXT | titulo corto | +| description | TEXT | detalle | +| evidence | JSON{} | datos que justifican la propuesta (assertion failures, metrics, etc) | +| status | TEXT | `pending` / `approved` / `rejected` / `implemented` | +| created_by | TEXT | quien creo (agente, humano, reactive_loop) | +| reviewed_by | TEXT | quien reviso | +| created_at, updated_at | TEXT | RFC3339 | + +### FTS5 (busqueda full-text) + +- `functions_fts` — indexa: id, name, description, tags, signature, domain +- `types_fts` — indexa: id, name, description, tags, domain +- `proposals_fts` — indexa: id, title, description, evidence + +Sincronizados automaticamente via triggers (INSERT/UPDATE/DELETE). + +--- + +## Tablas: operations.db + +### entities (12 columnas) + +Instancia concreta de un tipo del registry dentro de un proyecto. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | identificador unico en el proyecto | +| name | TEXT | nombre descriptivo | +| type_ref | TEXT | ID del tipo en registry (ej: `ohlcv_go_finance`) | +| status | TEXT | `active` / `stale` / `corrupted` / `archived` | +| description | TEXT | que representa esta entity | +| domain | TEXT | dominio | +| tags | JSON[] | etiquetas | +| source | TEXT | origen de los datos (obligatorio) | +| metadata | JSON{} | campos del tipo instanciados con valores reales | +| notes | TEXT | notas libres | +| created_at, updated_at | TEXT | RFC3339 | + +### relations (17 columnas) + +Conexion/transformacion entre entities. Puede ser causal (via funcion) o semantica. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | identificador | +| name | TEXT | nombre descriptivo | +| from_entity | TEXT | entity origen | +| to_entity | TEXT | entity destino (obligatorio) | +| via | TEXT | ID de funcion del registry que transforma (si vacio = semantica) | +| description | TEXT | que hace esta relacion | +| purity | TEXT | `pure` / `impure` / `` | +| direction | TEXT | `unidirectional` / `bidirectional` / `inverse` | +| weight | REAL | 0.0-1.0, importancia | +| status | TEXT | `designed` / `implemented` / `tested` / `running` / `deprecated` | +| started_at, ended_at | TEXT | ciclo de vida | +| order | INT | para secuencias | +| tags | JSON[] | etiquetas | +| notes | TEXT | notas | +| created_at, updated_at | TEXT | RFC3339 | + +### relation_inputs (5 columnas) + +Multi-input para relaciones N→1 (joins, merges, agregaciones). + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | | +| relation_id | TEXT FK | referencia a relations.id (CASCADE delete) | +| entity_id | TEXT FK | referencia a entities.id | +| role | TEXT | rol semantico del input (ej: "left", "right", "config") | +| order | INT | orden de procesamiento | + +### types_snapshot (7 columnas) + +Copia inmutable de un tipo del registry en el momento de uso. Hace operations.db autonoma. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | mismo ID que en registry.types | +| version | TEXT | version capturada | +| lang | TEXT | lenguaje | +| algebraic | TEXT | product/sum | +| definition | TEXT | codigo fuente capturado | +| description | TEXT | descripcion capturada | +| snapped_at | TEXT | cuando se hizo el snapshot | + +### executions (12 columnas) + +Cada ejecucion de un pipeline. Memoria de comportamiento del sistema. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | `exec_{timestamp}` o manual | +| pipeline_id | TEXT | ID de funcion del registry (ej: `tick_to_ohlcv_go_finance`) | +| relation_id | TEXT | relacion asociada (opcional) | +| status | TEXT | `success` / `failure` / `partial` | +| started_at | TEXT | inicio (obligatorio) | +| ended_at | TEXT | fin (nullable si en progreso) | +| duration_ms | INT | auto-calculado si started_at y ended_at presentes | +| records_in | INT | registros de entrada (nullable) | +| records_out | INT | registros de salida (nullable) | +| error | TEXT | mensaje de error si fallo | +| metrics | JSON{} | metricas custom (ej: `{"mean_close": 42000}`) | +| created_at | TEXT | RFC3339 | + +### assertions (9 columnas) + +Regla de calidad formal sobre una entity. Evaluable automaticamente contra metadata. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | | +| entity_id | TEXT FK | referencia a entities.id | +| name | TEXT | nombre descriptivo | +| kind | TEXT | tipo libre: `range`, `null`, `statistical`, `consistency`, `freshness`, o custom | +| rule | TEXT | expresion SQL evaluable (ver motor de evaluacion abajo) | +| severity | TEXT | `critical` / `warning` / `info` | +| description | TEXT | que verifica | +| active | INT | 0/1 — solo las activas se evaluan | +| created_at | TEXT | RFC3339 | + +### assertion_results (7 columnas) + +Historial de evaluaciones de assertions. + +| Columna | Tipo | Descripcion | +|---|---|---| +| id | TEXT PK | | +| assertion_id | TEXT FK | referencia a assertions.id | +| execution_id | TEXT | referencia a executions.id (vacio si eval manual) | +| status | TEXT | `pass` / `fail` / `skip` | +| value | JSON{} | datos capturados en el momento de evaluacion | +| message | TEXT | detalle del resultado | +| evaluated_at | TEXT | RFC3339 | + +### FTS5 operations + +- `entities_fts` — indexa: id, name, description, tags, domain +- `assertions_fts` — indexa: id, name, description, rule + +--- + +## Motor de evaluacion de assertions + +Las rules se escriben como expresiones SQL. Campos sin prefijo se reescriben automaticamente a `json_extract(metadata, '$.campo')`: + +``` +close > 0 → json_extract(metadata, '$.close') > 0 +low <= close AND close <= high → json_extract(metadata, '$.low') <= ... +open IS NOT NULL → json_extract(metadata, '$.open') IS NOT NULL +``` + +Si la rule ya usa `json_extract`, se deja como esta. Palabras SQL (AND, OR, NOT, IS, NULL, BETWEEN, etc) y funciones SQLite (datetime, abs, max, min, etc) no se reescriben. + +**Kinds documentados** (puedes añadir nuevos sin tocar schema): + +| Kind | Descripcion | Ejemplo de rule | +|---|---|---| +| `range` | Valor dentro de rango | `close BETWEEN 0 AND 1000000` | +| `null` | Campo no nulo | `open IS NOT NULL` | +| `consistency` | Relacion entre campos | `low <= close AND close <= high` | +| `freshness` | Datos recientes | `json_extract(metadata, '$.ts') > datetime('now', '-1 hour')` | +| `statistical` | Desviacion estadistica | (evaluar externamente, registrar manual) | + +**Dos modos:** +- **Auto**: `fn ops assertion eval --entity-id X` ejecuta rules SQL contra `entities.metadata` +- **Manual**: `fn ops assertion result add` registra resultados de assertions que el sistema no puede evaluar + +**Bucle reactivo** (con `--react`): +- `fn ops assertion eval --entity-id X --react` evalua Y reacciona: + - Critical fail → entity.status = `corrupted` + auto-crea proposal en registry.db + - Warning fail → entity.status = `stale` (solo si era `active`) + - Info fail → sin cambio + +--- + +## Sistema de migraciones + +Ambas BDs usan un sistema de migraciones con `embed.FS`: + +``` +registry/migrations/ + 001_init.sql # functions + types + FTS + 002_proposals.sql # proposals + FTS + +fn_operations/migrations/ + 001_init.sql # entities + relations + relation_inputs + types_snapshot + FTS + 002_executions_assertions.sql # executions + assertions + assertion_results + FTS +``` + +- Tabla `schema_migrations` en cada BD rastrea versiones aplicadas +- `CREATE TABLE IF NOT EXISTS` + transacciones por migracion = idempotente +- Al hacer `Open()` se aplican automaticamente las migraciones pendientes +- Para añadir una nueva migracion: crear `NNN_nombre.sql` en la carpeta correspondiente + +--- + ## Estructura del repositorio ``` fn-registry/ - functions/ # Codigo y docs de funciones - core/ # Utilidades genericas (filter, map, pipeline, retry...) - finance/ # Indicadores, riesgo, IO de mercado - datascience/ # Estadistica, DSP, IO de datos - cybersecurity/ # Crypto, analisis de red, IO de seguridad - pipelines/ # Composiciones de funciones, siempre impuras - components/ # Componentes React (.tsx) - types/ # Tipos algebraicos (product y sum) - core/ # Result, Option, Pair, Error - finance/ # OHLCV, Tick, BollingerResult, DrawdownResult - datascience/ # OutlierResult - cybersecurity/ # CIDRBlock, ThreatResult, PortResult - registry/ # Libreria Go: modelos, SQLite, parser, indexer, validacion - cmd/fn/ # CLI - docs/ # Schema de documentacion v1.0 (fuente de verdad del diseño) - docs/templates/ # Plantillas de frontmatter para function, pipeline, component, type - registry.db # Indice SQLite FTS5+WAL (regenerable, NO commitear) + functions/ # Codigo y docs de funciones + core/ # Utilidades genericas (filter, map, pipeline, retry...) + finance/ # Indicadores, riesgo, IO de mercado + datascience/ # Estadistica, DSP, IO de datos + cybersecurity/ # Crypto, analisis de red, IO de seguridad + pipelines/ # Composiciones de funciones, siempre impuras + components/ # Componentes React (.tsx) + types/ # Tipos algebraicos (product y sum) + core/ # Result, Option, Pair, Error + finance/ # OHLCV, Tick, BollingerResult, DrawdownResult + datascience/ # OutlierResult + cybersecurity/ # CIDRBlock, ThreatResult, PortResult + registry/ # Paquete Go: modelos, SQLite, parser, indexer, validacion, migraciones + models.go # Function, Type, Proposal structs + enums (Kind, Purity, Algebraic, ProposalKind, ProposalStatus) + db.go # Open/Close/Drop + WAL + migraciones + store.go # CRUD: Insert/Get/Update/Delete/List/Search para functions, types, proposals + validate.go # ValidateFunction, ValidateType, ValidateProposal + parser.go # ParseFunctionMD, ParseTypeMD (YAML frontmatter) + indexer.go # Index() — two-pass: parse → validate refs → insert + migrate.go # Motor de migraciones (embed.FS) + migrations/ # Archivos .sql numerados + fn_operations/ # Paquete Go: operations database (libreria, NO apps) + models.go # Entity, Relation, RelationInput, TypeSnapshot, Execution, Assertion, AssertionResult + enums + db.go # Open/Close/Drop/Conn + store.go # CRUD para todas las tablas + validate.go # ValidateEntity, ValidateRelation, ValidateExecution, ValidateAssertion, DetectCycle + operations.go # Alto nivel: InsertEntityWithSnapshot, InsertRelationSafe, React, ExecuteAndReact + eval.go # Motor de evaluacion: rewriteRule, EvalAssertion, EvalEntityAssertions + migrate.go # Motor de migraciones + migrations/ # Archivos .sql numerados + apps/ # Aplicaciones ejecutables (TUIs, CLIs) — modulos Go independientes + docker_tui/ # TUI fullscreen para gestionar Docker + pipeline_launcher/ # TUI para lanzar pipelines y registrar ejecuciones + cmd/fn/ # CLI + main.go # Subcomandos: index, search, list, show, add, ops, proposal + ops.go # fn ops: entity, relation, graph, snapshot, execution, assertion + proposal.go # fn proposal: add, list, show, update + docs/ # Specs de diseño (fuente de verdad del schema) + docs/templates/ # Plantillas de frontmatter + registry.db # Indice SQLite FTS5+WAL (regenerable con fn index, excepto proposals) ``` +--- + ## Build ```bash @@ -71,6 +402,78 @@ CGO_ENABLED=1 go test -tags fts5 ./... CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/ ``` +--- + +## CLI completo (cmd/fn) + +### Registry + +```bash +fn index # Regenera registry.db desde los .md +fn search "texto" # Busqueda FTS en functions + types +fn search -k function -p pure -d core "slice" +fn list # Lista todo +fn list -d finance -k function # Lista por dominio y kind +fn show filter_slice_go_core # Muestra entrada completa +fn add -k function # Muestra template para copiar +``` + +### Proposals + +```bash +fn proposal add --kind new_function --title "..." --created-by agent [--target-id ] [--evidence '{}'] [--description "..."] +fn proposal list [-k kind] [-s status] +fn proposal show +fn proposal update --status approved [--reviewed-by lucas] +``` + +### Operations + +```bash +fn ops init [path] # Crea operations.db en el directorio +fn ops help # Ayuda + +# Entities +fn ops entity add --id --name --type-ref --source [--metadata '{}'] [--domain d] [--tags t1,t2] +fn ops entity list [--domain d] [--status s] +fn ops entity show +fn ops entity delete + +# Relations +fn ops relation add --id --name --from --to [--via ] [--direction uni] [--status designed] +fn ops relation list [--from ] +fn ops relation show +fn ops relation delete + +# Graph +fn ops graph # ASCII graph de entities y relations + +# Snapshots +fn ops snapshot list # Lista type snapshots +fn ops snapshot check # Compara snapshots vs registry actual +fn ops snapshot update |--all # Re-snapshot desde registry + +# Executions +fn ops execution add --pipeline-id --status success [--started-at ] [--ended-at ] [--records-in N] [--records-out N] [--metrics '{}'] [--error "msg"] +fn ops execution list [--pipeline-id ] [--relation-id ] [-s status] +fn ops execution show + +# Assertions +fn ops assertion add --entity-id --name "close positivo" --kind range --rule "close > 0" --severity critical [--description "..."] +fn ops assertion list [--entity-id ] [--active] [--inactive] +fn ops assertion show # Incluye ultimos 5 resultados +fn ops assertion delete +fn ops assertion eval --entity-id [--execution-id ] [--react] # Evalua assertions activas + +# Assertion results (registro manual) +fn ops assertion result add --assertion-id --status pass|fail|skip [--execution-id ] [--value '{}'] [--message "..."] +fn ops assertion result list [--assertion-id ] [--execution-id ] +``` + +`FN_REGISTRY_ROOT` env var permite que `fn ops` acceda a registry.db desde cualquier directorio. + +--- + ## Reglas para añadir funciones nuevas ### Antes de crear @@ -129,12 +532,12 @@ Explicacion adicional si es necesario. | Regla | Condicion | |---|---| -| Pipeline siempre impuro | `kind: pipeline` -> `purity: impure` + `uses_functions` no vacio | -| Pura sin side effects | `purity: pure` -> `returns_optional: false` + `error_type: ""` | -| Impura declara errores | `purity: impure` -> `error_type` obligatorio (usar `error_go_core`) | -| Tests coherentes | `tested: true` -> `test_file_path` y `tests` obligatorios | +| Pipeline siempre impuro | `kind: pipeline` → `purity: impure` + `uses_functions` no vacio | +| Pura sin side effects | `purity: pure` → `returns_optional: false` + `error_type: ""` | +| Impura declara errores | `purity: impure` → `error_type` obligatorio (usar `error_go_core`) | +| Tests coherentes | `tested: true` → `test_file_path` y `tests` obligatorios | | Referencias validas | `uses_functions`, `uses_types`, `returns`, `error_type` deben apuntar a IDs existentes | -| Component tiene framework | `kind: component` -> `framework` obligatorio, `returns` vacio (usar `emits`) | +| Component tiene framework | `kind: component` → `framework` obligatorio, `returns` vacio (usar `emits`) | | Rutas relativas | `file_path` siempre relativa a la raiz, nunca absoluta | | IDs unicos | Formato `{name}_{lang}_{domain}`, colisiones rechazadas | @@ -154,6 +557,8 @@ CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/ && ./fn index ./fn show {name}_{lang}_{domain} ``` +--- + ## Reglas para añadir tipos nuevos Cada tipo requiere dos archivos: `types/{domain}/{name}.go` y `types/{domain}/{name}.md`. @@ -174,6 +579,8 @@ file_path: "types/{domain}/{name}.go" --- ``` +--- + ## Convenciones - **IDs:** `{name}_{lang}_{domain}` (ej: `filter_slice_go_core`) @@ -182,6 +589,10 @@ file_path: "types/{domain}/{name}.go" - **Tipos en firmas:** usar tipos nativos (float64, []float64, string) para evitar imports circulares entre paquetes de funciones. Documentar los tipos del registry en `uses_types`/`returns` del .md - **Purity:** puras en el centro, impuras en los bordes. Una pura NUNCA depende de una impura - **Stubs impuros:** si la implementacion real requiere dependencias externas no disponibles, crear stub con `return ..., fmt.Errorf("not implemented")` y documentar completamente el .md +- **Assertions:** kind es texto libre — puedes inventar nuevos kinds sin tocar schema +- **Proposals:** las crea el bucle reactivo automaticamente (created_by: reactive_loop) o el humano/agente manualmente + +--- ## Fuentes de verdad @@ -191,15 +602,49 @@ file_path: "types/{domain}/{name}.go" | Documentacion | archivos .md junto al codigo | | Diseño del schema | carpeta docs/ | | Indice de busqueda | registry.db (regenerable con `fn index`) | +| Proposals | registry.db tabla proposals (NO regenerable, son datos vivos) | +| Entities, relations | operations.db por proyecto (datos vivos) | +| Executions, assertions | operations.db por proyecto (datos vivos) | -## CLI (cmd/fn) +**Importante:** `fn index` regenera functions y types desde los .md pero NO toca proposals. Las proposals, entities, relations, executions, assertions y assertion_results son datos vivos que persisten. -```bash -fn index # Regenera registry.db desde los .md -fn search "texto" # Busqueda FTS -fn search -k function -p pure -d core "slice" -fn list # Lista todo -fn list -d finance -k function # Lista por dominio y kind -fn show filter_slice_go_core # Muestra entrada completa -fn add -k function # Muestra template para copiar +--- + +## Codigo Go: patrones clave + +### JSON en columnas TEXT + +Arrays y objetos se guardan como JSON serializado en columnas TEXT: + +```go +marshalStrings([]string) string // ["a","b"] → string +unmarshalStrings(string) []string // string → ["a","b"] +marshalJSON(map[string]any) string // {k:v} → string +unmarshalJSON(string) map[string]any ``` + +### Validacion + +Acumula errores, retorna nil o *ValidationError: + +```go +func ValidateX(x *X) *ValidationError { + var errs []string + if x.Name == "" { errs = append(errs, "name required") } + if len(errs) > 0 { return &ValidationError{ID: x.ID, Errors: errs} } + return nil +} +``` + +### Migraciones + +```go +//go:embed migrations/*.sql +var migrationsFS embed.FS + +func migrate(conn *sql.DB) error { ... } // aplica pendientes en transacciones +``` + +### Cycle detection + +Solo relaciones causales (via != "") se verifican. BFS desde to_entity buscando from_entity. diff --git a/docs/fn-registry-system-complete.md b/docs/fn-registry-system-complete.md new file mode 100644 index 00000000..41193562 --- /dev/null +++ b/docs/fn-registry-system-complete.md @@ -0,0 +1,437 @@ +# fn-registry — Sistema de conocimiento componible para agentes autónomos + +Schema completo · Bucle autónomo · Optimizaciones para agentes + +--- + +## Visión general del sistema + +| | fn_registry | fn_operations | +|---|---|---| +| **Naturaleza** | Conocimiento estático | Conocimiento dinámico | +| **Pregunta** | ¿Qué existe y qué puedo hacer? | ¿Qué ha ocurrido y cómo se conecta? | +| **Tablas** | `functions` · `types` · `proposals` | `entities` · `relations` · `relation_inputs` · `types_snapshot` · `executions` · `assertions` · `assertion_results` | +| **Scope** | Compartido entre todos los proyectos | Local a cada proyecto — un `.db` por proyecto | +| **Ontología** | Intensional — define conceptos en abstracto | Extensional — instancias concretas con valores reales | + +--- + +## REGISTRY + +### Tabla: functions + +Almacena funciones atómicas, pipelines y componentes React. Un pipeline es `kind: pipeline`, siempre impuro, que orquesta otras funciones. Un componente es `kind: component` con campos adicionales para su API visual. + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Formato: `{name}_{lang}_{domain}` ej: `filter_slice_go_core` | +| `name` | string | Nombre de la función o pipeline. | Obligatorio. snake_case. | +| `kind` | enum | Clasificación. | `function` \| `pipeline` \| `component` | +| `lang` | enum | Lenguaje. | `go` \| `python` \| `sql` \| `typescript` \| ... | +| `domain` | string | Namespace de dominio. | `finance`, `dsp`, `core`, `io`... | +| `version` | string | Versión semántica. | Semver: `1.0.0` | +| `purity` | enum | Side effects. | `pure` \| `impure` | +| `signature` | string | Firma completa. | Con parámetros y tipos de retorno. | +| `description` | text | Qué hace, por qué existe, cuándo usarla. | Obligatorio. | +| `tags` | []string | Etiquetas de búsqueda. | | +| `uses_functions` | []string | Funciones del registry que invoca. | IDs validados. | +| `uses_types` | []string | Tipos que recibe como parámetros. | IDs validados. | +| `returns` | []string | Tipos que emite. | IDs validados. | +| `returns_optional` | bool | Si el retorno puede estar ausente. | Siempre `false` en puras. | +| `error_type` | string | Tipo de error que puede emitir. | Obligatorio en impuras. | +| `imports` | []string | Dependencias externas. | | +| `example` | text | Ejemplo de uso. | Preferiblemente compilable. | +| `tested` | bool | Si tiene test. | `true` → `test_file_path` obligatorio. | +| `tests` | []string | Lista de tests aplicados. | | +| `test_file_path` | string | Ruta al archivo de test. | Relativa a raíz del registry. | +| `file_path` | string | Ruta al archivo de implementación. | Relativa a raíz del registry. | +| `created_at` | datetime | Fecha de creación. | ISO 8601. Automático. | +| `updated_at` | datetime | Fecha de última modificación. | ISO 8601. Automático. | + +#### Campos adicionales — kind: component + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `props` | []PropDef | API de entrada. | `{ name, type, required, description }` | +| `emits` | []string | Eventos hacia el padre. | Ej: `[onClick, onChange]`. No son `returns`. | +| `has_state` | bool | Gestiona estado interno. | `true` → `purity: impure` automáticamente. | +| `framework` | enum | Framework de UI. | `react`. Obligatorio en `kind: component`. | +| `variant` | []string | Variantes disponibles. | Ej: `[primary, ghost, danger]` | + +#### Reglas de integridad — functions + +| Condición | Regla | +|---|---| +| `kind: pipeline` | `purity` siempre `impure`. `uses_functions` no puede estar vacío. | +| `kind: component` | `framework` obligatorio. `returns` vacío — usar `emits`. | +| `purity: pure` | `returns_optional: false`. `error_type` vacío. Una pura que devuelve opcional debe modelarse como tipo suma. | +| `purity: impure` | `error_type` obligatorio. | +| `tested: true` | `test_file_path` y `tests` obligatorios. | +| `uses_functions[]` | Todos los IDs deben existir en `functions`. | +| `uses_types[]` | Todos los IDs deben existir en `types`. | +| `has_state: true` | `purity: impure` automáticamente en componentes. | + +--- + +### Tabla: types + +Los tipos son el contrato entre funciones. Permiten al agente razonar sobre composabilidad sin leer implementaciones. + +**Tipo producto** — todos los campos siempre presentes. Modela datos y entidades. Valores posibles = N₁ × N₂ × ... × Nₙ + +**Tipo suma** — un caso activo a la vez. Modela estados, resultados y alternativas. Valores posibles = N₁ + N₂ + ... + Nₙ + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Formato: `{name}_{lang}_{domain}` ej: `ohlcv_go_finance` | +| `name` | string | Nombre del tipo. | PascalCase recomendado. | +| `lang` | enum | Lenguaje. | Mismo enum que `functions`. | +| `domain` | string | Namespace de dominio. | | +| `version` | string | Versión semántica. | Semver: `1.0.0` | +| `algebraic` | enum | Categoría algebraica. | `product` \| `sum` | +| `definition` | text | Definición completa. | Código real. | +| `description` | text | Qué modela y cuándo usarlo. | Obligatorio. | +| `tags` | []string | Etiquetas de búsqueda. | | +| `uses_types` | []string | Tipos que compone internamente. | IDs validados. | +| `file_path` | string | Ruta al archivo. | Relativa a raíz del registry. | +| `created_at` | datetime | Fecha de creación. | ISO 8601. Automático. | +| `updated_at` | datetime | Fecha de última modificación. | ISO 8601. Automático. | + +#### Reglas de integridad — types + +| Condición | Regla | +|---|---| +| `algebraic: product` | Todos los campos siempre presentes. Modela datos. | +| `algebraic: sum` | Un caso activo a la vez. Modela estados y resultados. | +| `uses_types[]` | Solo IDs existentes. Sin auto-referencias. | + +--- + +### Tabla: proposals + +El agente escribe propuestas de mejora al registry cuando assertions fallan o métricas son pobres. El humano revisa y aprueba. El registry crece de forma controlada. + +Las proposals viven en **registry** — no en operations — porque su destino es el conocimiento estático compartido entre todos los proyectos. + +``` +operations → detecta el problema (assertions, assertion_results) +registry → propuesta de solución (proposals) — beneficia a todos los proyectos +``` + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | | +| `kind` | enum | Tipo de propuesta. | `new_function` \| `new_type` \| `improve_function` \| `improve_type` \| `new_pipeline` | +| `target_id` | string | ID del elemento a mejorar si aplica. | Vacío para propuestas nuevas. Referencia a `functions.id` o `types.id`. | +| `title` | string | Título corto. | Obligatorio. | +| `description` | text | Qué propone y por qué. | El agente explica su razonamiento. | +| `evidence` | json | Datos que justifican la propuesta. | Ej: `assertion_ids` que fallaron, métricas de `executions`. | +| `status` | enum | Estado de revisión. | `pending` \| `approved` \| `rejected` \| `implemented` | +| `created_by` | string | Quién propone. | Ej: `agent`, `human`. | +| `reviewed_by` | string | Quién revisó. | Vacío si `pending`. | +| `created_at` | datetime | Fecha de la propuesta. | ISO 8601. | +| `updated_at` | datetime | Fecha de última modificación. | ISO 8601. | + +--- + +## OPERATIONS + +### Tabla: entities + +Una entity es una instancia concreta de un tipo del registry dentro del contexto de un proyecto. + +``` +type_ref → lo que la entidad ES (heredado del registry) +metadata → valores concretos del tipo (la instancia real) +``` + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único en el proyecto. | Formato: `{name}_{context}` ej: `ticks_btcusdt_2024` | +| `name` | string | Nombre semántico. | Obligatorio. snake_case. | +| `type_ref` | string | ID del tipo del registry. | Referencia a `types.id`. Obligatorio. | +| `status` | enum | Estado actual. | `active` \| `stale` \| `corrupted` \| `archived` | +| `description` | text | Rol en el contexto del proyecto. | | +| `domain` | string | Área de dominio. | Ej: `market_data`, `auth`. | +| `tags` | []string | Etiquetas de búsqueda. | | +| `source` | string | Origen del dato. | Obligatorio. Ej: `binance_api`, `pipeline_output`. | +| `metadata` | json | Valores concretos de los campos del tipo. | Ej: si `type_ref` es `person_go_core` con campos `name` y `dni`, aquí va `{"name":"Juan","dni":"32435"}`. Es la instancia real del tipo. | +| `notes` | text | Contexto extra libre. | Para el agente o para ti. | +| `created_at` | datetime | Fecha de registro. | ISO 8601. Automático. | +| `updated_at` | datetime | Fecha de modificación. | ISO 8601. Automático. | + +#### Valores de status — entities + +| Valor | Significado | +|---|---| +| `active` | Dato en uso, actualizado y fiable. | +| `stale` | Puede estar desactualizado. El agente verifica antes de usar. | +| `corrupted` | Problemas de integridad. El agente no lo usa como input en ningún pipeline activo. | +| `archived` | Ya no se usa. Se conserva por trazabilidad histórica. | + +#### Reglas de integridad — entities + +| Condición | Regla | +|---|---| +| `type_ref` | Debe existir en `fn_registry.types.id` o en `types_snapshot.id`. | +| `source` | Obligatorio. Todo dato tiene un origen conocido. | +| `metadata` | JSON válido. Contiene los valores de los campos definidos por el tipo del registry. | +| `status: corrupted` | El agente nunca usa esta entity como input de una relation activa. | + +--- + +### Tabla: types_snapshot + +Copia local de los tipos del registry en el momento de su primer uso. Hace `operations.db` completamente autónomo — no necesita `registry.db` en runtime ni en producción. + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | ID original del tipo en el registry. | | +| `version` | string | Versión en el momento del snapshot. | Semver. Inmutable tras el snapshot. | +| `lang` | string | Lenguaje. | | +| `algebraic` | enum | `product` \| `sum`. | | +| `definition` | text | Definición copiada. | Inmutable. | +| `description` | text | Descripción copiada. | Inmutable. | +| `snapped_at` | datetime | Cuándo se hizo el snapshot. | ISO 8601. Automático. | + +--- + +### Tabla: relations + +Describe cómo una entidad se conecta o transforma en otra. El campo `name` expresa el significado humano de la conexión. `via` es opcional — vacío para relaciones puramente semánticas. + +``` +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) se usa `relation_inputs` en lugar de `from_entity`. + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Formato: `{from}__to__{to}__via__{fn}` | +| `name` | string | Etiqueta semántica de la relación. | Ej: `CONOCE A`, `TRANSFORMA`, `PRODUCE`, `DEPENDE DE`. Obligatorio. | +| `from_entity` | string | Entidad origen para relaciones 1-a-1. | Opcional si se usa `relation_inputs`. | +| `to_entity` | string | Entidad destino. | Obligatorio. | +| `via` | string | Función del registry que transforma. | Opcional — vacío si relación semántica. | +| `description` | text | Qué ocurre en este contexto. | | +| `purity` | enum | Naturaleza de la transformación. | `pure` \| `impure`. Consistente con `via.purity` si informado. | +| `direction` | enum | Dirección semántica. | `unidirectional` \| `bidirectional` \| `inverse` | +| `weight` | float | Ponderación opcional. | Rango `0.0 – 1.0`. | +| `status` | enum | Estado del flujo. | `designed` \| `implemented` \| `tested` \| `running` \| `deprecated` | +| `started_at` | datetime | Cuándo empezó el flujo realmente. | Distinto de `created_at`. | +| `ended_at` | datetime | Cuándo terminó. | Vacío si sigue activo. | +| `order` | int | Posición en secuencia. | Opcional. | +| `tags` | []string | Etiquetas. | | +| `notes` | text | Observaciones y contexto histórico. | El agente lee esto. | +| `created_at` | datetime | Fecha de documentación. | ISO 8601. Automático. | +| `updated_at` | datetime | Fecha de modificación. | ISO 8601. Automático. | + +#### Valores de direction + +| Valor | Significado | +|---|---| +| `unidirectional` | A → B. Un solo sentido. Valor por defecto. | +| `bidirectional` | A ↔ B. Simétrica. Ej: `CONOCE A`. | +| `inverse` | Documentada de B a A, lectura natural A a B. Para linaje inverso. | + +#### Valores de status — relations + +| Valor | Significado | +|---|---| +| `designed` | Planificado. En arquitectura pero sin código. | +| `implemented` | Código escrito. No verificado aún. | +| `tested` | Verificado. Los datos fluyen correctamente. | +| `running` | Activo en producción. | +| `deprecated` | Ya no se usa. Se mantiene por trazabilidad. | + +#### Reglas de integridad — relations + +| Condición | Regla | +|---|---| +| `name` | Obligatorio. | +| `from_entity` o `relation_inputs` | Al menos uno presente. Una relation sin origen es inválida. | +| `via` | Si informado, debe existir en `fn_registry.functions.id`. | +| `weight` | Si informado, rango `0.0 – 1.0`. | +| `started_at` / `ended_at` | Si ambos informados, `started_at` anterior a `ended_at`. | +| **Sin ciclos causales** | El CLI valida ciclos solo en relations con `via` informado. Las relaciones semánticas sin `via` pueden ser bidireccionales libremente. | + +--- + +### Tabla: relation_inputs + +Para relaciones que consumen múltiples entidades simultáneamente — joins, merges, agregaciones multi-fuente. Cuando se usa esta tabla, `from_entity` en `relations` se deja vacío. + +``` +join(ticks_btcusdt, metadata_binance) → ENRIQUECE → ohlcv_enriquecido + ↑ role: "base" ↑ role: "lookup" +``` + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Automático. | +| `relation_id` | string | Relation a la que pertenece. | Referencia a `relations.id`. Obligatorio. | +| `entity_id` | string | Entidad que actúa como input. | Referencia a `entities.id`. Obligatorio. | +| `role` | string | Rol semántico en la relación. | Ej: `base`, `lookup`, `filter`, `left`, `right`. Obligatorio. | +| `order` | int | Orden si la relación es sensible al orden. | Opcional. | + +#### Reglas de integridad — relation_inputs + +| Condición | Regla | +|---|---| +| `role` | Obligatorio. Sin rol el agente no puede razonar sobre cómo se usa el input. | +| Mínimo 2 inputs | Si se usa esta tabla, al menos 2 entradas. Si hay una sola usar `from_entity`. | + +--- + +### Tabla: executions + +Registra cada ejecución de un pipeline. Es la memoria de comportamiento del sistema — el agente consulta `executions` para detectar degradación, comparar rendimiento histórico y justificar `proposals`. + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Automático. | +| `pipeline_id` | string | Función/pipeline ejecutado. | Referencia a `functions.id` en registry. | +| `relation_id` | string | Relation que disparó la ejecución. | Referencia a `relations.id`. | +| `status` | enum | Resultado. | `success` \| `failure` \| `partial` | +| `started_at` | datetime | Inicio real de la ejecución. | ISO 8601. | +| `ended_at` | datetime | Fin de la ejecución. | ISO 8601. | +| `duration_ms` | int | Duración en milisegundos. | | +| `records_in` | int | Registros de entrada procesados. | | +| `records_out` | int | Registros de salida producidos. | | +| `error` | text | Mensaje de error si falló. | Vacío si `success`. | +| `metrics` | json | Métricas clave del output. | Ej: `{"mean_close":42000,"nulls":0}` | +| `created_at` | datetime | Fecha de registro. | ISO 8601. Automático. | + +--- + +### Tabla: assertions + +Reglas de calidad formales sobre entities. Condiciones computables que el agente evalúa automáticamente tras cada ejecución. No texto libre — condiciones que devuelven `pass` o `fail`. + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | Ej: `ohlcv_close_positive` | +| `entity_id` | string | Entity sobre la que aplica. | Referencia a `entities.id`. | +| `name` | string | Nombre legible. | Ej: `close debe ser positivo` | +| `kind` | enum | Tipo de assertion. | `range` \| `null` \| `statistical` \| `consistency` \| `freshness` | +| `rule` | text | Condición formal evaluable. | SQL o expresión. Ej: `close > 0 AND close < open * 3` | +| `severity` | enum | Qué ocurre si falla. | `critical` \| `warning` \| `info` | +| `description` | text | Por qué existe esta regla. | Conocimiento de dominio que la justifica. | +| `active` | bool | Si se evalúa en cada ejecución. | Permite desactivar sin borrar. | +| `created_at` | datetime | Fecha de creación. | ISO 8601. | + +#### Tipos de assertion + +| Kind | Descripción y ejemplo | +|---|---| +| `range` | El valor debe estar dentro de un rango. Ej: `close BETWEEN 0 AND 1000000` | +| `null` | El campo no puede ser nulo. Ej: `open IS NOT NULL` | +| `statistical` | No debe desviarse más de N desviaciones estándar. Ej: `zscore(close) < 3` | +| `consistency` | Relación entre campos del mismo registro. Ej: `low <= close AND close <= high` | +| `freshness` | Los datos no pueden ser más antiguos de X. Ej: `max(ts) > now() - 1h` | + +#### Lógica de severidad + +| Severidad | Acción automática | +|---|---| +| `critical` | `entity.status = corrupted`. El agente no usa esta entity. Escribe en `proposals`. | +| `warning` | `entity.status = stale`. El agente la usa con precaución y lo documenta. | +| `info` | No cambia `status`. Solo registra en `assertion_results` para análisis histórico. | + +--- + +### Tabla: assertion_results + +Historial de cada evaluación de assertion. Permite al agente detectar tendencias — "esta assertion falla el 20% de las veces en ejecuciones nocturnas". + +| Campo | Tipo | Descripción | Notas / Restricciones | +|---|---|---|---| +| `id` | string | Identificador único. | | +| `assertion_id` | string | Assertion evaluada. | Referencia a `assertions.id`. | +| `execution_id` | string | Ejecución que disparó la evaluación. | Referencia a `executions.id`. | +| `status` | enum | Resultado. | `pass` \| `fail` \| `skip` | +| `value` | json | Valor concreto evaluado. | Lo que tenía el dato cuando falló. | +| `message` | text | Descripción del fallo. | Generado automáticamente. | +| `evaluated_at` | datetime | Cuándo se evaluó. | ISO 8601. | + +--- + +## El bucle autónomo + +El sistema completo habilita un ciclo de mejora continua donde el agente construye, ejecuta, analiza y propone mejoras sin intervención humana salvo en la aprobación final. + +### 1. CONSTRUIR +- Agente consulta registry → recupera funciones testeadas por FTS sobre `name`, `description`, `tags`. +- Razona sobre composabilidad comparando `returns` con `uses_types`. +- Prioriza funciones puras para el núcleo, aísla impuras en los bordes. +- Registra el pipeline en operations como `status: designed → implemented`. + +### 2. EJECUTAR +- Pipeline corre → inserta registro en `executions` con `duration_ms`, `records_in`, `records_out`, `metrics`. +- `operations.relations.status = running`. +- Si falla → `execution.status = failure`, `error` capturado. + +### 3. RECOPILAR +- Entities se pueblan — `metadata` contiene los valores concretos de los campos del tipo. +- `types_snapshot` garantiza que `operations.db` es autónomo sin `registry.db`. +- El agente actualiza `entity.status` según los datos recibidos. + +### 4. ANALIZAR +- Agente evalúa todas las `assertions` activas sobre las entities producidas. +- Compara `metrics` de la ejecución actual con `executions` históricas. +- `critical` falla → `entity.status = corrupted`. +- `warning` falla → `entity.status = stale`. +- Resultados en `assertion_results` con `value` concreto para debugging. + +### 5. MEJORAR +- Si assertions fallan o métricas degradan → agente escribe en `proposals`. +- `proposals.evidence` referencia los `assertion_ids` y `execution_ids` que lo justifican. +- El humano revisa `proposals.status: pending → approved → implemented`. +- El registry crece de forma controlada y trazable. + +### 6. REPEAT +- Con el registry mejorado el agente construye mejor en la siguiente iteración. +- Cada ciclo acumula conocimiento — funciones más robustas, tipos más precisos, assertions más finas. +- El sistema mejora continuamente sin resetear el contexto acumulado. + +--- + +## Optimizaciones para el agente + +| Área | Mejora | Beneficio para el agente | +|---|---|---| +| Búsqueda en registry | Índice FTS5 sobre `name + description + tags + signature` | Recuperación en <5ms sin embeddings | +| `types_snapshot` | Copiar tipo completo al primer uso | `operations.db` completamente autónomo sin `registry.db` | +| Ciclos en grafo | Validar solo relations con `via` informado (causales) | Relaciones semánticas bidireccionales permitidas | +| `assertions` | Evaluar automáticamente tras cada `execution` | Agente detecta degradación sin intervención humana | +| `metadata` en entities | Almacenar instancia real del tipo como JSON | Agente lee valores sin acceder a ficheros externos | +| `proposals.evidence` | Referenciar `assertion_ids` y `execution_ids` concretos | Trazabilidad completa del razonamiento del agente | +| WAL mode SQLite | Activado por defecto en plantilla | Lectura simultánea mientras el agente escribe | +| `version` en `functions` y `types` | Semver explícito | Detección automática de contratos rotos | +| `direction` en relations | `unidirectional / bidirectional / inverse` | Agente recorre linaje hacia adelante y hacia atrás | +| `proposals` en registry | No en operations | Un solo punto de revisión humana para todos los proyectos | + +--- + +## Referencias cruzadas completas + +| Campo origen | → | Destino | +|---|---|---| +| `functions.uses_functions[]` | → | `functions.id` | +| `functions.uses_types[]` | → | `types.id` | +| `functions.returns[]` | → | `types.id` | +| `functions.error_type` | → | `types.id` | +| `types.uses_types[]` | → | `types.id` | +| `proposals.target_id` | → | `functions.id` o `types.id` (opcional) | +| `entities.type_ref` | → | `fn_registry → types.id` / `types_snapshot.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` | +| `executions.pipeline_id` | → | `fn_registry → functions.id` | +| `executions.relation_id` | → | `relations.id` | +| `assertions.entity_id` | → | `entities.id` | +| `assertion_results.assertion_id` | → | `assertions.id` | +| `assertion_results.execution_id` | → | `executions.id` |