- .claude/rules/registry_calls.md - apps/dag_engine/README.md - apps/dag_engine/app.md - docs/capabilities/INDEX.md - docs/capabilities/systemd.md - docs/execution_standard.md - dev/proposals_e2e_checks_0121/ - docs/capabilities/backends.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.4 KiB
Como invocar funciones del registry — patrones canonicos
Toda invocacion del agente al registry sigue uno de tres patrones. Cualquier otro patron es antipatron auditable. Las invocaciones se loguean en projects/fn_monitoring/apps/call_monitor/operations.db (issue 0085) para alimentar el bucle reactivo.
Patrones canonicos
| Caso | Patron | Cuando |
|---|---|---|
| Inspeccionar (buscar, leer codigo, ver dependencias, listar dominios, leer proposals) | mcp__registry__fn_search / fn_show / fn_code / fn_uses / fn_list_domains / fn_proposal |
SIEMPRE para descubrimiento, lectura de codigo, exploracion. |
| Ejecutar UNA funcion/pipeline con sus args | mcp__registry__fn_run <id> [args] (preferido) o ./fn run <id> [args] (fallback CLI) |
ID conocido + args planos. Despacho automatico por lenguaje. |
| Componer ad-hoc multi-funcion con logica intermedia | Heredoc python/.venv/bin/python3 - <<'PYEOF' ... PYEOF IMPORTANDO funciones del registry |
Solo si hay loops/conditionals/dispatch entre N funciones. Las funciones del registry se importan, no se reescriben. |
Antipatrones prohibidos (audit-targeted)
| Patron | Razon | Sustituir por |
|---|---|---|
sqlite3 registry.db "SELECT ..." para buscar funciones/tipos |
Salta MCP, FTS5 gotchas, sin trazabilidad | mcp__registry__fn_search |
sqlite3 registry.db "SELECT ... FROM proposals" |
Mismo problema | mcp__registry__fn_proposal |
python -c "import metabase; dir(metabase)" para descubrir helpers |
Fuente de verdad = registry, no __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(...) saltando wrapper del registry |
Salta validacion del wrapper y telemetria | Usar wrapper; si firma incompleta, fn proposal add --kind improve_function |
Scripts en temp/ para composiciones que se repiten >2 veces |
Codigo perdido + sin monitoreo | Pipeline en python/functions/pipelines/ o bash/functions/pipelines/ |
from <pkg> import * en heredoc |
Imposible identificar funciones usadas | Imports explicitos from <domain> import <name1>, <name2> |
Excepciones autorizadas para sqlite3 directo
Casos donde el MCP no aplica y sqlite3 registry.db es legitimo:
- Introspeccion de schema:
.schema,.tables,PRAGMA table_info(...),PRAGMA index_list(...). - Agregaciones:
COUNT(*),GROUP BY,SUM(...),AVG(...). - JOINs custom entre tablas que el MCP no expone (
functions JOIN unit_tests ON ...). - Columnas que el MCP no devuelve (rare; preferir proponer ampliacion del MCP).
El hook PreToolUse (.claude/scripts/hook_registry_mcp.sh) ya deja pasar estas excepciones y solo avisa cuando ve sqlite3 registry.db "SELECT ..." plano.
Excepcion: hooks e infraestructura de telemetria (issue 0087)
Los hooks (PreToolUse, PostToolUse, UserPromptSubmit, etc.) y los binarios de infraestructura que sirven al agente (fn_match, fn doctor, call_monitor) pueden leer registry.db directo via sqlite3 o database/sql con conexion read-only. NO estan sujetos a la regla MCP-first porque:
- No son acciones del agente — son inspeccion automatizada del entorno.
- El MCP requiere tool invocation por Claude; un hook no puede invocar tools.
- Latencia objetivo (50-200ms) incompatible con round-trip MCP.
Restricciones:
- SOLO lectura. Conexion debe abrirse con
?mode=roo?_query_only=1. - NUNCA escritura a
registry.dbdesde hooks. - Si un hook necesita escribir (cache, telemetria propia), usa su propia DB (
operations.dbdel app de hooks, o~/.fn_hooks/cache.db).
Esta excepcion es explicita y acotada — no aplica al agente, que sigue regido por la regla MCP-first.
Verificacion previa — fn doctor
Antes de empezar trabajo no trivial sobre el registry, ejecutar fn doctor para confirmar que el ecosistema esta sano:
- Artefactos OK (sin
git_not_initialized,venv_broken_path, etc.). - Services activos cuando se necesiten (
sqlite_api,registry_api,registry_mcp). - Sin drift
pc_locationsvs disco. - Sin drift
uses_functionsvs imports reales.
Si fn doctor reporta service inactive para registry_mcp.service, el MCP estara siendo invocado en modo stdio por Claude Code (normal); el systemd unit solo aplica al modo HTTP. Si el binario no responde, rebuild: cd apps/registry_mcp && CGO_ENABLED=1 go build -tags fts5 -o registry_mcp ..
Tools MCP disponibles
| Tool | Lectura/escritura | Gating |
|---|---|---|
fn_search |
read | siempre on |
fn_show |
read | siempre on |
fn_code |
read | siempre on |
fn_uses |
read | siempre on |
fn_list_domains |
read | siempre on |
fn_proposal |
read | siempre on |
fn_doctor |
read | siempre on |
fn_run |
execute (mutating side-effects) | requiere --enable-run |
fn_create_function |
write | requiere --enable-write |
Heredoc Python — convenciones obligatorias
Cuando el caso 3 (composicion) sea inevitable:
- Imports explicitos desde paquetes del registry. Nunca
import *. - No reescribir la firma de una funcion del registry — importarla.
- Args via env vars o stdin JSON, nunca interpolacion shell directa (inyeccion).
- Output a stdout JSON cuando vaya a ser consumido por el siguiente paso.
- Si el heredoc supera ~30 lineas, extraer a
python/functions/pipelines/. El monitor avisara automaticamente cuando un patron similar se repita >5 veces.
Trazabilidad — bucle reactivo
Cada evento alimenta a call_monitor.db (event-log append-only) y se rollupea en una vista function_stats con contadores por funcion del registry. Tablas event-log:
| Tabla | Captura |
|---|---|
calls |
Cada invocacion (heredoc/mcp/fn_run): function_id, tool_used, duration_ms, success, error_class, args_hash |
code_writes |
Cada Edit/Write sobre archivo del registry: function_id, session_id, lines_added/removed |
test_runs |
Cada go test/pytest que toca codigo del registry: function_id, test_id, passed, duration_ms |
e2e_runs_fn |
Cada check e2e_checks de app que usa la funcion: function_id, app_id, check_id, passed |
violations |
Antipatron detectado: rule_id, session_id, command_snippet, severity |
patterns |
Heredocs clusterizados: pattern_hash, session_ids[], occurrences, representative_snippet |
sessions |
session_id, cwd, started_at, ended_at, health_score, mcp_ratio |
Vista agregada function_stats por function_id:
- Uso:
calls_total,calls_24h/7d/30d/90d,last_used_at - Errores:
errors_total,error_rate,last_error_class,last_error_ts - Performance:
mean_duration_ms,p95_duration_ms - Codigo:
writes_count,last_write_at - Tests:
tests_total,tests_failed,test_fail_rate,last_test_failed_at - E2E:
e2e_total,e2e_failed,e2e_fail_rate,consumer_apps_count - Salud:
violations_caused
Assertions derivadas → proposals automaticas:
| Regla | Threshold | Proposal |
|---|---|---|
| Huerfana absoluta | calls_90d=0 AND writes_count=0 |
deprecate_function |
| Bug prioritario | error_rate>0.1 AND calls_7d>5 |
improve_function (bug) |
| Regresion performance | p95_24h > 1.5 * p95_30d |
improve_function (perf) |
| Test flaky | test_fail_rate>0.1 AND tests_total>10 |
improve_function (flaky) |
| Wrapper saltado | violations_caused>3 |
improve_function (API gap) |
| Patron inline sin funcion | patterns.occurrences>5 AND no match FTS |
new_function con snippet |
| Blast radius alto | e2e_fail_rate>0 AND consumer_apps_count>=3 |
improve_function (critical) |
Datos sensibles: solo args_hash, NUNCA valores concretos. Snippets de error redactados via allowlist.
Capas de monitorizacion (issue 0085)
Cobertura por capa, no todas activas a la vez:
| # | Capa | Activacion | Cobertura |
|---|---|---|---|
| 1 | Hook PostToolUse Bash | siempre (settings.local.json) | mcp, fn_cli_run, edit_registry, violations |
| 2 | Wrapper Python registry_telemetry |
FN_TELEMETRY=1 env var |
heredocs + notebooks Jupyter |
| 3 | Wrapper Bash telemetry_prelude.sh |
source explicito o FN_TELEMETRY=1 |
heredoc bash + apps bash |
| 4 | Interceptor en fn run |
siempre (binario Go) | duration/error real de invocacion CLI |
| 5 | fn doctor copied-code |
comando manual / cron | drift estatico: codigo copiado en apps |
| 6 | function_versions + snapshot |
poblado por fn index + edit-hook |
historial de versiones |
| 7-8 | Build-tag Go / macro C++ | opt-in por app | runtime de app (futuro) |
Boundary: monitorizamos al agente y a invocaciones canonicas. Runtime de apps Go/C++ compiladas queda fuera. Compensar con tests + e2e_checks (issue 0068).
Que NO se monitoriza
- Funcion Go/C++ llamada internamente por app ya compilada.
- Funcion ejecutada por systemd timer / cron / dag_engine step
command:(nofunction:) sin pasar porfn run. Nota: dag_engine steps confunction:SI quedan trazados — el executor invocafn run <id>y guardafunction_idendag_step_results. - Sub-agente (
Agenttool) — sus tools no propagan a hook del padre. - Service de produccion recibiendo HTTP.
Implicacion: una funcion con calls_90d=0 puede ser huerfana real O usada en runtime invisible. Antes de proponer deprecate_function, cruzar con consumer_apps_count > 0 (e2e) o con fn doctor uses-functions (declaraciones estaticas).