Cada projects/<name>/ es ahora su propio repo Gitea con branch master, versionando solo sus docs de nivel-project. apps/*/ y analysis/*/ siguen como sub-repos hijos independientes (excluidos por el .gitignore del project). /full-git-push|pull los manejan via discover_git_repos. Cierra el gap de docs de nivel-project sin versionar. Aplicado a web_scraping, fn_monitoring, message_bus.
29 KiB
fn-registry
Registry personal de codigo reutilizable con busqueda FTS. Diseñado para composicion funcional y agentes.
Objetivos del registry (Norte) — Issues 0086 + 0087
4 metricas optimizadas por el bucle reactivo (visibles en Monitor tab del registry_dashboard):
- MAXIMIZAR
Reg %— porcentaje de calls del agente que golpean una funcion del registry (function_id != ''). Cada bash inline o heredoc que reescribe logica baja el ratio. Target: subir cada semana. - MEJORAR uso del registry por Claude — el agente debe encontrar y usar funciones existentes antes de escribir codigo. Indicadores:
MCP(mcp/heredoc/fn run) sube; violations baja. Si Claude no encuentra una funcion por busqueda mediocre, mejorardescription/tags/params_schemade esa funcion. - ACELERAR tareas comunes via funciones nuevas — patrones inline repetidos >2 veces ->
fn-constructorcrea la funcion, Claude la usa el siguiente turno. Velocidad medida en pasos (turnos) por tarea. Pattern detection: tab Monitor +mcp__registry__fn_proposal action="list". - PROMOVER COMPOSICIONES A PIPELINES (issue 0087) — el registry no crece inflando funciones, crece promoviendo secuencias A→B(→C) que se repiten con exito a pipelines one-shot. Hoy
bank_login + bank_make_transfer(2 calls). Mananabank_transfer_oneshot(1 call). Misma capacidad, mitad de pasos. Detectado por telemetria de secuencias encall_monitor. Una funcion que hace bien UNA cosa NO necesita crecer — lo que crece es el catalogo de composiciones probadas.
Auto-discovery zero-second-lookup: cada .md debe ser autosuficiente — ## Ejemplo lanzable + ## Cuando usarla + ## Gotchas (impuras). Descubrir = lanzar, sin segunda lectura. Ver .claude/rules/function_growth_and_self_docs.md.
Cualquier decision tecnica que choque con estos objetivos esta mal priorizada. Ejemplo: un bash heredoc rapido hoy que reinventa logica = penaliza objetivos 1 y 3 manana.
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, cada analysis y cada project es su propio repo Gitea en dataforge/<basename> con branch master (ver ADR 0002). apps/*, analysis/* y projects/* estan en el .gitignore del repo padre — el codigo de cada app vive en apps/<name>/.git/. Cada projects/<name>/ es a su vez un sub-repo que versiona solo sus docs de nivel-project (project.md, CONVENTIONS.md, ...) con un .gitignore interno que excluye apps/*/ y analysis/*/ (sub-repos hijos). Ver .claude/rules/projects.md. 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. Gotcha worktrees: si creas una app nueva dentro de un git worktree del repo padre, haz git init dentro de apps/<name>/ ANTES de limpiar el worktree, sino el codigo se pierde (apps/* gitignored). REGLA DURA: el repo padre NUNCA trackea contenido de artefactos hijos (apps/analysis/projects) — solo .gitkeep. Nada de git add -f sobre esos paths: deja el padre permanentemente dirty (doble-tracking). Auditoria + fix en .claude/rules/apps_subrepo.md. Ver .claude/rules/apps_subrepo.md.
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
Slash commands: /commands lista todos los slash commands del repo agrupados por namespace (global + projects). Project commands viven en projects/<p>/.claude/commands/ y se exponen como /<project>:<cmd> via symlink. Ver .claude/rules/project_commands.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.
Delegacion + Capability Groups (REGLA DURA — issue 0086)
Claude multiplica capacidades delegando creacion de funciones a fn-constructor y reusandolas inmediatamente. NO escribir logica reutilizable inline.
Flujo obligatorio (mismo turno)
- Detectar gap. Si vas a escribir >=5 lineas de logica reutilizable inline -> STOP.
- Spawn
fn-constructorviaAgent(subagent_type="fn-constructor", ...). Sin preguntar al usuario. - Paralelo: si hay >1 funcion independiente -> una sola llamada al Agent tool con N tool_use blocks paralelos en mismo mensaje. NO serializar.
- Tag de grupo obligatorio (
notebook,metabase,deploy, etc.). Verdocs/capabilities/INDEX.md. fn index+ importar + invocar en mismo turno. No dejar funcion huerfana recien creada.- Auto-verificar:
fn doctor uses-functions+fn doctor unusedsi tocas >=3 funciones nuevas.
Capability groups
Cluster de >=3 funciones que comparten dominio operativo. Cada grupo tiene tag plano + pagina madre docs/capabilities/<grupo>.md con: lista de funciones, ejemplo canonico end-to-end, fronteras.
Antes de buscar funciones sueltas en una tarea de dominio conocido: lee docs/capabilities/<grupo>.md para cargar el cluster entero en un solo read. Filtro MCP: mcp__registry__fn_search query="" tag="<grupo>".
Reglas completas: .claude/rules/delegation.md + .claude/rules/capability_groups.md.
Telemetria CAPABILITY-GROWTH
Cada turno el hook UserPromptSubmit inyecta CAPABILITY-GROWTH: created_this_session=X used=Y orphan=Z. Si orphan>0 -> integra la funcion antes de cerrar turno o documenta por que.
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.
La BD contiene el codigo y la documentacion completa de cada funcion y tipo en los campos code, documentation y notes. Tambien indexados en FTS5 — buscas dentro del codigo directamente. Para leer codigo: mcp__registry__fn_code <id>.
Ejemplos MCP (usa estos, NO sqlite3)
Cada llamada MCP se registra en call_monitor (issue 0085). Cada sqlite3 registry.db "SELECT ..." queda fuera del bucle reactivo y dispara el hook PreToolUse.
# Busqueda basica por nombre/descripcion (FTS5 detras)
mcp__registry__fn_search query="slice"
# Filtros: kind, purity, domain, lang
mcp__registry__fn_search query="filter" kind="function" purity="pure" domain="core"
# Prefijo FTS5 — encuentra slice/slicing/sliced
mcp__registry__fn_search query="slic*"
# Buscar tipos
mcp__registry__fn_search query="result" entity="types"
# Apps
mcp__registry__fn_search query="kanban" entity="apps"
# Listar dominios
mcp__registry__fn_list_domains
# Ver una entrada concreta (functions, types, apps, analysis, proposals...)
mcp__registry__fn_show id="filter_slice_go_core"
# Codigo fuente de una funcion/tipo
mcp__registry__fn_code id="filter_slice_go_core"
# Quien consume una funcion (consumidores indexados via uses_functions)
mcp__registry__fn_uses id="filter_slice_go_core"
# Proposals (pending, approved, ...)
mcp__registry__fn_proposal action="list" status="pending"
mcp__registry__fn_proposal action="show" id="<proposal_id>"
# Diagnostico read-only del registry (artefacts/services/sync/uses-functions/unused/cpp-apps)
mcp__registry__fn_doctor subcommand="artefacts"
mcp__registry__fn_doctor subcommand="sync"
Escapado FTS5 (gotcha cuando pasas query libre): valores con -, ., :, espacios rompen el parser FTS5 si los expones como column:valor. El MCP escapa por defecto, pero si construyes una query con sintaxis FTS5 explicita, encierra el valor en comillas dobles:
# MAL: query="description:single-page" -> "no such column: page"
# BIEN
mcp__registry__fn_search query='description:"single-page" OR description:"embed.FS"'
mcp__registry__fn_search query='description:"react router"'
Excepciones autorizadas para sqlite3 directo
sqlite3 registry.db SOLO es legitimo si el MCP no expone la consulta:
- Introspeccion de schema:
.schema,.tables,PRAGMA table_info(...),PRAGMA index_list(...). - Agregaciones:
COUNT(*),GROUP BY,SUM(...),AVG(...). - JOINs custom entre tablas (ej.
functions JOIN unit_tests ON ...) no expuestos por el MCP.
Cualquier SELECT ... FROM functions/types/apps/proposals WHERE ... plano se hace via MCP. El hook PreToolUse avisa si ve sqlite3 registry.db "SELECT ...".
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 indexdesde los archivos de test - FK:
function_id→functions.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, vaultstatus: active, missing, archived- Se puebla con
fn sync, NO confn index - Consultas:
mcp__registry__fn_doctor subcommand="sync"(drift PC vs disco) osqlite3 registry.db "SELECT ... GROUP BY pc_id"SOLO para agregaciones que el MCP no expone
FTS5 (columnas buscables):
functions_fts: id, name, description, tags, signature, domain, example, notes, documentation, code, params_schematypes_fts: id, name, description, tags, domain, examples, notes, documentation, codeunit_tests_fts: id, name, code, function_id, lang
Como invocar funciones del registry (CANONICO)
Tres patrones, uno por caso de uso. Toda invocacion del agente se loguea en projects/fn_monitoring/apps/call_monitor/operations.db para alimentar el bucle reactivo (issue 0085).
| Caso de uso | Patron canonico | Cuando usar |
|---|---|---|
| Inspeccionar registro (buscar, leer codigo, ver dependencias, dominios) | mcp__registry__fn_search / fn_show / fn_code / fn_uses / fn_list_domains |
SIEMPRE para descubrimiento. Reemplaza sqlite3 registry.db "SELECT ..." inline. |
| Ejecutar UNA funcion o pipeline con sus args | mcp__registry__fn_run <id> [args] (preferido) o ./fn run <id> [args] (fallback CLI) |
Cuando hay UN id conocido a lanzar. Despacho automatico por lenguaje. Salida estructurada. |
| Componer ad-hoc varias funciones con logica intermedia | Heredoc python/.venv/bin/python3 - <<'PYEOF' ... PYEOF IMPORTANDO funciones del registry |
Solo cuando hay loops/conditionals/dispatch entre N funciones. Las funciones del registry se importan, no se reescriben. |
Regla decisiva: antes de cada bloque de codigo, decide caso. Si dudas entre 2 y 3, casi siempre es 2 (un MCP run con args). Si el caso 3 se repite con el mismo shape >5 veces entre sesiones, es candidato a pipeline en python/functions/pipelines/.
Antipatrones prohibidos
| Patron | Por que es malo | Sustituir por |
|---|---|---|
sqlite3 registry.db "SELECT ..." para buscar funciones/tipos |
Salta MCP, FTS5 gotchas, sin trazabilidad. Hook PreToolUse ya avisa. | mcp__registry__fn_search |
python -c "import metabase; print(dir(metabase))" o help(metabase) para descubrir helpers |
La fuente de verdad es el registry, no el __init__.py |
mcp__registry__fn_search "metabase" + mcp__registry__fn_show <id> |
| Heredoc que reescribe logica que ya existe como funcion del registry | Reinvento + perdida de capitalizacion | Buscar primero; si falta, delegar a fn-constructor (no escribir inline) |
client._http.request(...) directo cuando hay wrapper en el registry |
Salta validacion del wrapper y telemetria | Usar wrapper; si la firma no cubre el caso, proponer extension via fn proposal add |
Scripts en temp/ para composiciones que se repiten |
Codigo se pierde y no se monitoriza | Pipeline en python/functions/pipelines/ o pipeline Bash en bash/functions/pipelines/ |
Imports from <pkg> import * en heredoc |
Imposible saber que funcion del registry se uso | Imports explicitos from <domain> import <name1>, <name2> |
claude -p o subprocess(["claude", "-p", ...]) para obtener una respuesta del modelo |
Lento (cold start ~7-15s, carga MCP + CLAUDE.md), caro, sin control de tools | ask_llm (grupo claude-direct, API directa, arranque 0). Ver regla llm_invocation.md |
Excepciones autorizadas para sqlite3 directo (no requieren MCP): .schema, .tables, PRAGMA table_info, COUNT(*) GROUP BY, JOINs custom entre tablas que el MCP no expone.
Trazabilidad y bucle reactivo
Hook PostToolUse en .claude/settings.local.json parsea cada comando Bash + cada mcp__registry__* y escribe en la operations.db del call_monitor. Datos consumidos por:
- Tab "Claude usage" en
registry_dashboard— top funciones, latencias, error rate, huerfanas concalls_90d=0. - Fase MEJORAR del bucle reactivo — patrones inline repetidos generan proposals
new_functioncon evidencia (session_ids + snippets). Funciones con error_rate alto y muchas llamadas suben en prioridad de bugfix. - Auditoria de reglas — assertions sobre
violation_count,mcp_ratio,heredoc_repetition. Si fallan critical → proposal "actualizar CLAUDE.md / prompt del agente".
Datos sensibles: solo se guarda args_hash, NUNCA valores concretos de argumentos.
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 services-spec # audita bloque `service:` del app.md (issue 0105)
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 vetconCGO_ENABLED=1 -tags fts5 - Bash:
bashdel sistema
Añadir funciones
mcp__registry__fn_search query="<nombre|desc>"para verificar que no existe algo similar- 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
- Go:
- Ejecuta
./fn indexy 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 tienenprops) - Se indexan como JSON en
params_schemay son buscables via FTS5 fn check paramslista 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
returnssolo 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}.mdconfile_pathapuntando afunctions/{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 deprojects/{nombre}/analysis/y ejecutafn indexal final. El proyecto debe existir (projects/{nombre}/project.md).--desc "..."— descripcion que se escribe en el frontmatter deanalysis.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
returnsconuses_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
executionsconduration_ms,records_in,records_out,metrics. operations.relations.status = running.- Si falla →
execution.status = failure,errorcapturado. - BD:
operations.db(executions, relations)
3. RECOPILAR
- Entities se pueblan —
metadatacontiene los valores concretos de los campos del tipo. types_snapshotgarantiza queoperations.dbes autonomo sinregistry.db.- El agente actualiza
entity.statussegun los datos recibidos. - BD:
operations.db(entities, types_snapshot)
4. ANALIZAR
- Agente evalua todas las
assertionsactivas sobre las entities producidas. - Compara
metricsde la ejecucion actual conexecutionshistoricas. criticalfalla →entity.status = corrupted.warningfalla →entity.status = stale.- Resultados en
assertion_resultsconvalueconcreto para debugging. - BD:
operations.db(assertions, assertion_results, entities.status)
5. MEJORAR
- Si assertions fallan o metricas degradan → agente escribe en
proposals. proposals.evidencereferencia losassertion_idsyexecution_idsque 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 |