# fn-registry

Registry personal de codigo reutilizable con busqueda FTS. Diseñado para composicion funcional y agentes.

**Dos bases de datos SQLite:**
- **registry.db** (raiz) — funciones, tipos, proposals. Regenerable con `fn index` (excepto proposals).
- **operations.db** (por app en `apps/*/`) — entities, relations, executions, assertions. Datos vivos.

**Reglas y convenciones:** ver `.claude/rules/INDEX.md`

---

## Explorar el registry (OBLIGATORIO)

**SIEMPRE** consulta registry.db antes de escribir codigo, crear funciones, o responder sobre el registry. No uses grep/glob sobre archivos .go/.md — la BD es la fuente de verdad.

**La BD contiene el codigo y la documentacion completa** de cada funcion y tipo en los campos `code`, `documentation` y `notes`. Estos campos tambien estan indexados en FTS5, asi que puedes buscar dentro del codigo y la documentacion directamente. Para leer el codigo de una funcion: `SELECT code FROM functions WHERE id = '...'`. Para leer su documentacion: `SELECT documentation FROM functions WHERE id = '...'`.

**Busquedas FTS5 obligatorias:** Usa SIEMPRE la tabla FTS5 para buscar tanto por `name` como por `description`. Esto encuentra coincidencias parciales y similares que una busqueda exacta perderia. Usa operadores FTS5: `OR` para ampliar, `*` para prefijos, `NEAR` para proximidad.

```bash
# Busqueda FTS5 por nombre Y descripcion (USAR SIEMPRE ESTE PATRON)
sqlite3 registry.db "SELECT id, kind, purity, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:slice OR description:slice') ORDER BY name;"

# FTS5 con prefijo (encuentra slice, slicing, sliced...)
sqlite3 registry.db "SELECT id, kind, purity, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:slic* OR description:slic*') ORDER BY name;"

# FTS5 en tipos
sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE id IN (SELECT id FROM types_fts WHERE types_fts MATCH 'name:result OR description:result') ORDER BY name;"

# FTS5 por semantica de params (composabilidad)
sqlite3 registry.db "SELECT id, json_extract(params_schema, '$.output') FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'params_schema:retornos');"

# Por dominio
sqlite3 registry.db "SELECT id, purity, signature FROM functions WHERE domain = 'finance' ORDER BY name;"

# Puras de un dominio
sqlite3 registry.db "SELECT id, signature FROM functions WHERE domain = 'core' AND purity = 'pure' ORDER BY name;"

# Tipos por dominio
sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE domain = 'cybersecurity';"

# Dependencias
sqlite3 registry.db "SELECT id, uses_functions, uses_types FROM functions WHERE uses_functions != '[]';"

# Proposals pendientes
sqlite3 registry.db "SELECT id, kind, status, title FROM proposals WHERE status = 'pending';"

# Schema completo
sqlite3 registry.db ".schema"
```

**Regla:** Si necesitas saber si algo existe o hay algo similar, haz la consulta FTS5 sobre la BD. No asumas que no existe sin consultar primero.

### Schema rapido

**functions** — columnas: `id, name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, example, tested, tests, test_file_path, file_path, created_at, updated_at, props, emits, has_state, framework, variant, notes, documentation, code, content_hash, source_repo, source_license, source_file, params_schema`
- `params_schema`: JSON con semántica de inputs/outputs. Formato: `{"params":[{"name":"x","desc":"..."}],"output":"..."}`. Buscable via FTS5.
- Enums: `kind`(function|pipeline|component) `purity`(pure|impure) `lang`(go|py|bash|ps)
- Dominios: core, infra, finance, datascience, cybersecurity, shell, tui, pipelines, browser

**types** — columnas: `id, name, lang, domain, version, algebraic, definition, description, tags, uses_types, file_path, created_at, updated_at, examples, notes, documentation, code, content_hash, source_repo, source_license, source_file`
- Enums: `algebraic`(product|sum)

**unit_tests** — columnas: `id, function_id, name, code, file_path, lang, created_at, updated_at`
- Extraidos automaticamente por `fn index` desde los archivos de test
- FK: `function_id` → `functions.id`

**FTS5 (columnas buscables):**
- `functions_fts`: id, name, description, tags, signature, domain, example, notes, documentation, code, params_schema
- `types_fts`: id, name, description, tags, domain, examples, notes, documentation, code
- `unit_tests_fts`: id, name, code, function_id, lang

---

## Estructura

```
fn-registry/
  functions/{domain}/     # .go + .md por funcion Y tipo Go (core, finance, datascience, cybersecurity)
  functions/pipelines/    # Composiciones, siempre impuras
  types/{domain}/         # Solo .md de tipos (los .go viven en functions/{domain}/)
  python/functions/       # .py + .md por funcion Python
  python/types/           # .py + .md por tipo Python
  bash/functions/         # .sh + .md por funcion Bash (core, infra, io, shell)
  frontend/               # pnpm + vite + react + mantine
  frontend/functions/     # .tsx/.ts + .md (core para TS puro, ui para componentes React)
  frontend/types/         # .ts + .md por tipo
  registry/               # Paquete Go: modelos, SQLite, parser, indexer, validacion, migraciones
  fn_operations/          # Paquete Go: operations database (libreria)
  apps/                   # Apps ejecutables (TUIs, CLIs, scripts) — codigo NO reutilizable, cada una con su operations.db
  analysis/               # Exploraciones Jupyter independientes — cada una con su venv, MCP y kernel conectado al registry
  cmd/fn/                 # CLI principal
  docs/                   # Specs de diseño
  docs/templates/         # Plantillas de frontmatter
```

---

## Build

```bash
CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/
CGO_ENABLED=1 go test -tags fts5 ./...
```

---

## CLI

```bash
# Registry
fn index                            # Regenera registry.db
fn search "texto"                   # FTS en functions + types
fn search -k function -p pure -d core "slice"
fn list [-d domain] [-k kind]
fn show <id>
fn add -k function                  # Template
fn check params                     # Lista funciones sin params_schema

# Ejecutar funciones y pipelines (fn run)
fn run <id_or_name> [args...]       # Ejecuta por ID o nombre
fn run init_metabase --project test # Go pipeline (go run .)
fn run setup_metabase_volume        # Bash pipeline (bash <file>)
fn run metabase_setup_py_infra      # Python (python/.venv/bin/python3 <file>)
fn run my_component_ts_core         # TypeScript (frontend/node_modules/.bin/tsx <file>)
fn run filter_slice_go_core         # Go function con tests (go test -v)
fn run docker_pull_image_go_infra   # Go function sin tests (go vet)
# Despacho por lenguaje:
#   go (con main.go en dir) → go run .
#   go (con tests)          → go test -v -count=1 -tags fts5 ./pkg/
#   go (sin tests)          → go vet -tags fts5 ./pkg/
#   py                      → python/.venv/bin/python3 <file>
#   bash                    → bash <file>
#   ts                      → frontend/node_modules/.bin/tsx <file>
# Si el nombre es ambiguo, muestra los IDs para desambiguar.

# Proposals
fn proposal add --kind new_function --title "..." --created-by agent [--target-id <id>]
fn proposal list [-k kind] [-s status]
fn proposal show <id>
fn proposal update <id> --status approved [--reviewed-by lucas]

# Operations (desde directorio con operations.db)
fn ops init [path]
fn ops entity add|list|show|delete
fn ops relation add|list|show|delete
fn ops graph
fn ops snapshot list|check|update
fn ops execution add|list|show
fn ops assertion add|list|show|delete|eval [--react]
fn ops assertion result add|list
```

`FN_REGISTRY_ROOT` env var permite que `fn ops` acceda a registry.db desde cualquier directorio.

### Uso de fn run por agentes

`fn run` permite ejecutar directamente funciones y pipelines del registry desde la terminal. Usar para:
- Lanzar pipelines con sus argumentos: `./fn run init_metabase --project fn_registry`
- Correr tests de funciones Go: `./fn run filter_slice_go_core`
- Ejecutar scripts Python/Bash del registry sin montar paths manualmente
- Verificar que funciones Go compilan correctamente (go vet)

Entornos usados automaticamente:
- Python: `python/.venv/bin/python3` (venv del proyecto)
- TypeScript: `frontend/node_modules/.bin/tsx` (node del proyecto)
- Go: `go run .` / `go test` / `go vet` con `CGO_ENABLED=1 -tags fts5`
- Bash: `bash` del sistema

---

## Añadir funciones

1. Consulta la BD para verificar que no existe algo similar
2. Crea dos archivos segun el lenguaje:
   - Go: `functions/{domain}/{name}.go` + `.md`
   - Python: `python/functions/{domain}/{name}.py` + `.md`
   - Bash: `bash/functions/{domain}/{name}.sh` + `.md`
   - TypeScript: `frontend/functions/{domain}/{name}.ts` + `.md`
3. Ejecuta `./fn index` y verifica con `./fn show {id}`

Frontmatter del .md — ver template completo en `docs/templates/` o con `fn add -k function`.

Campos `params` y `output` (obligatorios en frontmatter):
- `params`: lista de `{name, desc}` con descripción semántica de cada parámetro (qué representa, unidades, rango)
- `output`: descripción semántica de lo que retorna la función
- Para componentes: solo `output` (ya tienen `props`)
- Se indexan como JSON en `params_schema` y son buscables via FTS5
- `fn check params` lista funciones sin documentar

Reglas de integridad (el indexer las valida):
- Pipeline → siempre impuro + uses_functions no vacio
- Pure → returns_optional: false + error_type: ""
- Impure → error_type obligatorio (usar `error_go_core`)
- tested: true → test_file_path y tests obligatorios
- uses_functions, uses_types, returns, error_type → IDs existentes
- Component → framework obligatorio, returns vacio (usar emits)
- file_path siempre relativa, IDs formato `{name}_{lang}_{domain}`
- Campo `returns` solo para IDs del registry, NO tipos nativos de Go

## Añadir tipos

Dos archivos en directorios separados:
- **Codigo Go:** `functions/{domain}/{name}.go` (junto a las funciones, mismo paquete Go)
- **Metadata .md:** `types/{domain}/{name}.md` con `file_path` apuntando a `functions/{domain}/{name}.go`

Los `.go` de tipos viven en `functions/{domain}/` para que Go los compile en el mismo paquete que las funciones que los usan. Los `.md` se mantienen en `types/{domain}/` para que el indexer los identifique como tipos.

Ver template en `docs/templates/`.

---

## Analysis (exploraciones Jupyter)

Carpeta `analysis/` para exploraciones de datos con Jupyter + agentes Claude. Mismo patron que `apps/` — cada analisis es independiente con su propio venv, MCP y kernel.

**NO es codigo reutilizable** — son investigaciones ad-hoc. Si algo de un analisis resulta util, se extrae como funcion al registry.

### Estructura

```
analysis/
  {tema}/                              # Cada analisis es autonomo
    .venv/                             # Deps propias (gitignored)
    .mcp.json                          # MCP jupyter apuntando a SU venv (gitignored)
    .claude/CLAUDE.md                  # Reglas para agentes en este analisis
    .ipython/profile_default/startup/  # Kernel startup con acceso al registry
      00_fn_registry.py                # Autocarga FN_REGISTRY_ROOT, helpers, sys.path
    notebooks/                         # Notebooks de exploracion
    data/                              # Datos locales (gitignored)
    run-jupyter-lab.sh                 # Launcher Jupyter colaborativo
    pyproject.toml                     # Deps gestionadas con uv
```

### Crear un analisis nuevo

```bash
# Basico
fn run init_jupyter_analysis finanzas

# Con paquetes extra
fn run init_jupyter_analysis ml scikit-learn torch
fn run init_jupyter_analysis duckdb polars duckdb
```

El pipeline `init_jupyter_analysis_bash_pipelines` compone 8 funciones atomicas del registry.

### Usar un analisis

```bash
# Terminal 1: lanzar Jupyter
cd analysis/{tema} && ./run-jupyter-lab.sh

# Terminal 2: abrir Claude con MCP jupyter
cd analysis/{tema} && claude

# Navegador: http://localhost:8888
```

### Acceso al registry desde notebooks

El kernel startup (`00_fn_registry.py`) se ejecuta automaticamente al abrir cualquier notebook y provee:

```python
# Helpers disponibles sin importar nada:
fn_search("slice")           # Busca funciones y tipos por nombre/descripcion
fn_query("SELECT ...")       # SQL directo sobre registry.db
fn_code("filter_list_py_core")  # Codigo fuente de una funcion

# Importar funciones Python del registry directamente:
from core import filter_list, map_list, reduce_list
from finance import sma, ema, rsi
from metabase import MetabaseClient

# Variable de entorno disponible:
import os
os.environ["FN_REGISTRY_ROOT"]  # Raiz del registry
```

### Reglas para agentes en analysis

Cada analisis tiene su `.claude/CLAUDE.md` con reglas especificas:
- Celdas inmutables: nunca modificar celdas existentes, solo anadir nuevas
- Programacion funcional obligatoria: funciones puras, sin mutacion
- Usar MCP jupyter para ejecutar codigo, nunca bash
- Notebooks en `notebooks/`, maximo 50 celdas por notebook
- Dependencias con `uv add`, nunca pip directo

---

## Bucle reactivo: CONSTRUIR → EJECUTAR → RECOPILAR → ANALIZAR → MEJORAR

### 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 nucleo, aisla impuras en los bordes.
- Registra el pipeline en operations como `status: designed → implemented`.
- **BD:** `registry.db` (functions, types) → `operations.db` (relations, entities)

### 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.
- **BD:** `operations.db` (executions, relations)

### 3. RECOPILAR
- Entities se pueblan — `metadata` contiene los valores concretos de los campos del tipo.
- `types_snapshot` garantiza que `operations.db` es autonomo sin `registry.db`.
- El agente actualiza `entity.status` segun los datos recibidos.
- **BD:** `operations.db` (entities, types_snapshot)

### 4. ANALIZAR
- Agente evalua todas las `assertions` activas sobre las entities producidas.
- Compara `metrics` de la ejecucion actual con `executions` historicas.
- `critical` falla → `entity.status = corrupted`.
- `warning` falla → `entity.status = stale`.
- Resultados en `assertion_results` con `value` concreto para debugging.
- **BD:** `operations.db` (assertions, assertion_results, entities.status)

### 5. MEJORAR
- Si assertions fallan o metricas 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.
- **BD:** `registry.db` (proposals)

Codigo: `ExecuteAndReact()` en `fn_operations/operations.go` ejecuta pasos 2-4.
CLI: `fn ops assertion eval --entity-id X --react` ejecuta pasos 4-5.
Las assertion rules son expresiones SQL. Campos sin prefijo se reescriben a `json_extract(metadata, '$.campo')`.

---

## Fuentes de verdad

| Que | Donde |
|---|---|
| Codigo | .go / .py / .tsx |
| Metadata | .md junto al codigo |
| Schema de BDs | `sqlite3 *.db ".schema"` o `docs/` |
| Indice | registry.db (`fn index`) |
| Proposals, entities, executions, assertions | datos vivos en sus BDs |
