Files
fn_registry/.claude/CLAUDE.md
T
egutierrez 750b7abcd5 chore: auto-commit (97 archivos)
- .claude/CLAUDE.md
- .claude/agents/fn-recopilador/SKILL.md
- .claude/rules/INDEX.md
- .claude/rules/cpp_apps.md
- bash/functions/infra/build_cpp_windows.sh
- cpp/CMakeLists.txt
- cpp/PATTERNS.md
- cpp/framework/app_base.cpp
- cpp/framework/app_base.h
- dev/issues/README.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:11:24 +02:00

22 KiB

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, apps, projects, analysis, vaults, pc_locations. Regenerable con fn index (excepto proposals y pc_locations).
  • operations.db (por app en apps/*/) — entities, relations, executions, assertions. Datos vivos.

Sync entre PCs: fn sync sincroniza datos no regenerables (proposals, apps, projects, analysis, vaults, pc_locations) contra registry_api en https://registry.organic-machine.com. Config: ~/.fn_pc (identidad del PC), FN_REGISTRY_API (URL con basicAuth), REGISTRY_API_TOKEN (token).

Sub-repos: cada app y cada analysis es su propio repo Gitea en dataforge/<basename> con branch master (ver ADR 0002). Los slash commands /full-git-push y /full-git-pull orquestan push/pull/clone de fn_registry + todos los sub-repos + fn sync. /full-git-push auto-inicializa apps/analyses sin .git via ensure_repo_synced_bash_infra. Los vaults/ y subrepos/ NO entran en este flujo.

Artefactos: termino paraguas para apps, analysis, vaults, projects y playgrounds — todo lo que NO es codigo reutilizable. Usa "artefacto" cuando una afirmacion aplica a varios tipos a la vez para no repetir la lista. Ver .claude/rules/artefactos.md y .claude/rules/playgrounds.md.

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

Migraciones SQLite obligatorias: todo cambio de schema en cualquier .db (apps, operations.db, registry.db) va en migrations/NNN_*.sql numerado. Aditivo, idempotente, aplicado al arrancar via embed.FS. Nunca borrar .db ni modificar migraciones existentes. Aplica retroactivamente. Ver .claude/rules/db_migrations.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.

Usa SIEMPRE el MCP registry (regla por defecto)

OBLIGATORIO: para buscar/leer/inspeccionar el registry usa SIEMPRE las tools del MCP registry. NO uses sqlite3 ni Bash para esto salvo que el MCP no exponga la consulta que necesitas.

Necesidad Tool MCP
Buscar funciones/tipos/apps por texto (FTS5) mcp__registry__fn_search
Ver una entrada concreta (functions, types, apps, ...) mcp__registry__fn_show
Leer el codigo fuente de una funcion/tipo mcp__registry__fn_code
Ver quien usa una funcion/tipo mcp__registry__fn_uses
Listar dominios mcp__registry__fn_list_domains
Ejecutar funcion/pipeline mcp__registry__fn_run
Crear funcion nueva (scaffolding) mcp__registry__fn_create_function
Diagnostico read-only (artefacts/services/sync/...) mcp__registry__fn_doctor

Razones: menos tokens, output estructurado, FTS5 escapado bien (sin gotchas de column:"valor"), permisos pre-aprobados, no requiere cd ni paths absolutos a registry.db.

Cuando si caer a sqlite3 (Bash): SOLO si el MCP no cubre el caso — JOINs custom entre tablas, agregaciones (COUNT/GROUP BY), introspeccion de schema (.schema, PRAGMA table_info), o columnas que el MCP no expone. En ese caso, los patrones FTS5 estan documentados abajo.

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: mcp__registry__fn_code (preferido) o SELECT code FROM functions WHERE id = '...' (fallback).

Busquedas FTS5 (cuando uses sqlite3 como fallback): 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.

# 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, usa mcp__registry__fn_search (preferido) o consulta FTS5 con sqlite3 (fallback). No asumas que no existe sin consultar primero.

Escapado FTS5 (gotcha): despues de column: el valor debe ser un solo token alfanumerico ASCII (underscores OK). Cualquier otro caracter (-, ., :, espacios) rompe el parser con no such column: X o syntax error near ".". Encierra el valor en comillas dobles dentro del MATCH:

# MAL: description:single-page  →  "no such column: page"
# MAL: description:embed.FS     →  'syntax error near "."'
# BIEN:
sqlite3 registry.db "SELECT id FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'description:\"single-page\" OR description:\"embed.FS\"');"

Tokens multi-palabra tambien necesitan comillas: description:"react router".

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_idfunctions.id

pc_locations — columnas: id, entity_type, entity_id, pc_id, dir_path, status, notes, created_at, updated_at

  • Mapa de ubicaciones por PC: donde esta cada app/analysis/project/vault en cada maquina
  • entity_type: app, analysis, project, vault
  • status: active, missing, archived
  • Se puebla con fn sync, NO con fn index
  • Consultas: SELECT * FROM pc_locations WHERE pc_id = 'home-wsl'

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
  cpp/apps/               # Apps C++ standalone (sin proyecto). Ej: chart_demo, shaders_lab. Indexadas igual que apps/
  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
  temp/                   # Workspace efimero — pruebas, APIs, prototipos (gitignored, no indexado)
  <artefacto>/playground/ # Prototipo rapido dentro de un artefacto padre (analysis/app/proyecto). No se indexa

Build

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

CLI

# 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

# Doctor: diagnostico read-only del registry y artefactos
fn doctor                           # Corre todos los checks
fn doctor artefacts                 # git/venv/app.md/upstream de cada app y analysis
fn doctor services                  # apps tag 'service' + systemctl + puerto
fn doctor sync                      # drift pc_locations BD vs disco
fn doctor uses-functions            # imports reales vs uses_functions del app.md
fn doctor unused                    # funciones del registry sin consumidores
fn doctor --json                    # salida JSON (cualquier subcomando)
# Ver .claude/rules/fn_doctor.md para mapeo subcomando → funcion + acciones derivadas.

# 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]

# Sync entre PCs
fn sync                             # Push+pull completo contra el servidor
fn sync status                      # Estado local: PC, API, conteos
fn sync locations                   # Mapa de ubicaciones en todos los PCs
# Config: ~/.fn_pc (identidad PC), FN_REGISTRY_API (URL), REGISTRY_API_TOKEN (token)
# URL con basicAuth: export FN_REGISTRY_API="https://user:pass@registry.organic-machine.com"

# 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

Un solo comando deja todo listo: carpetas, venv, paquetes, launcher, MCP, kernel startup, analysis.md con frontmatter y, si va en un proyecto, fn index final.

# Analisis suelto (analysis/{nombre}/)
fn run init_jupyter_analysis finanzas
fn run init_jupyter_analysis ml scikit-learn torch

# Analisis dentro de un proyecto (projects/{proyecto}/analysis/{nombre}/)
fn run init_jupyter_analysis --project aurgi sale_prices --desc "Comprobacion precios"
fn run init_jupyter_analysis --project fn_monitoring coverage polars --tags "monitoring,coverage"

Flags del pipeline:

  • --project <nombre> — crea el analisis dentro de projects/{nombre}/analysis/ y ejecuta fn index al final. El proyecto debe existir (projects/{nombre}/project.md).
  • --desc "..." — descripcion que se escribe en el frontmatter de analysis.md.
  • --tags "a,b,c" — tags CSV que se escriben en el frontmatter.

NUNCA uses mv para mover un analisis de analysis/ a projects/{proyecto}/analysis/ despues de crearlo. Al mover, el .venv/bin/activate queda con el path antiguo hardcodeado y el launcher falla con ERROR: jupyter-collaboration no esta instalado. Si esto pasa: rm -rf .venv && uv sync dentro del directorio nuevo. La forma correcta es siempre crear con --project desde el inicio.

El pipeline init_jupyter_analysis_bash_pipelines (v1.1.0) compone 9 funciones atomicas del registry.

Usar un analisis

# 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:

# 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