53a3cdbda9
- .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>
148 lines
9.4 KiB
Markdown
148 lines
9.4 KiB
Markdown
## 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=ro` o `?_query_only=1`.
|
|
- NUNCA escritura a `registry.db` desde hooks.
|
|
- Si un hook necesita escribir (cache, telemetria propia), usa su propia DB (`operations.db` del 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_locations` vs disco.
|
|
- Sin drift `uses_functions` vs 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:
|
|
|
|
1. **Imports explicitos** desde paquetes del registry. Nunca `import *`.
|
|
2. **No reescribir** la firma de una funcion del registry — importarla.
|
|
3. **Args via env vars o stdin JSON**, nunca interpolacion shell directa (inyeccion).
|
|
4. **Output a stdout JSON** cuando vaya a ser consumido por el siguiente paso.
|
|
5. **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:`** (no `function:`) sin pasar por `fn run`. Nota: dag_engine steps con `function:` SI quedan trazados — el executor invoca `fn run <id>` y guarda `function_id` en `dag_step_results`.
|
|
- Sub-agente (`Agent` tool) — 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).
|