chore: auto-commit (799 archivos)
- .claude/CLAUDE.md - .claude/commands/subagentes.md - .claude/rules/INDEX.md - .mcp.json - bash/functions/cybersecurity/analyze_dns.md - bash/functions/cybersecurity/audit_http_headers.md - bash/functions/cybersecurity/audit_ssh_config.md - bash/functions/cybersecurity/check_firewall.md - bash/functions/cybersecurity/detect_suspicious_users.md - bash/functions/cybersecurity/encrypt_file.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,3 +30,6 @@ Reglas operativas del proyecto. Cada archivo es una regla independiente.
|
||||
| 24 | [feature_flags.md](feature_flags.md) | TBD: feature flags para mergear codigo incompleto sin romper master. Patrones por stack (Go/TS/Bash/Py), branch-by-abstraction, anti-patrones |
|
||||
| 25 | [db_migrations.md](db_migrations.md) | Migraciones SQLite obligatorias para cualquier cambio de schema. Aditivas, idempotentes, archivos numerados. Nunca borrar .db ni modificar migraciones existentes |
|
||||
| 26 | [e2e_validation.md](e2e_validation.md) | Contrato `e2e_checks` en `app.md` consumido por fn-analizador (fase 4 del bucle reactivo). Issue 0068 |
|
||||
| 27 | [registry_calls.md](registry_calls.md) | Patrones canonicos para invocar funciones del registry (MCP inspect / MCP run / heredoc compose), antipatrones, excepciones, telemetria. Issue 0085 |
|
||||
| 28 | [delegation.md](delegation.md) | Si vas a escribir logica reutilizable inline -> spawn fn-constructor inmediato + tag de grupo + usar en mismo turno. Issue 0086 |
|
||||
| 29 | [capability_groups.md](capability_groups.md) | Tags planos + paginas madre `docs/capabilities/<grupo>.md` para desbloquear clusters de funciones en un read. Issue 0086 |
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
## Capability groups: tags + paginas madre en docs/capabilities/
|
||||
|
||||
Un **capability group** es un cluster de >=3 funciones del registry que comparten un dominio operativo (ej. `notebook`, `metabase`, `deploy`). Cada grupo tiene un **tag plano** (sin prefijo) y una **pagina madre** en `docs/capabilities/<grupo>.md`. La pagina madre desbloquea el conjunto entero en un solo read.
|
||||
|
||||
### Para que existen
|
||||
|
||||
Sin grupos, Claude redescubre funciones via FTS5 una a una cada sesion ("¿como interactuo con Jupyter? ¿como subo deploy?"). Con grupos, Claude lee `docs/capabilities/<grupo>.md` y carga las 5-10 funciones del cluster con su ejemplo canonico — menos turnos perdidos en discovery.
|
||||
|
||||
### Convencion de tag
|
||||
|
||||
- **Slug del grupo** = tag plano. Ej: `notebook`, `metabase`, `android-emu`.
|
||||
- **No prefijos** (`cap:`, `group:`). Ya hay namespacing implicito porque convivirian con tags semanticos sueltos.
|
||||
- **Una funcion puede llevar varios tags de grupo** si pertenece a dos clusters (raro pero valido).
|
||||
- Filtro MCP: `mcp__registry__fn_search query="" tag="notebook"` lista el grupo.
|
||||
|
||||
### Cuando crear grupo nuevo
|
||||
|
||||
- **Minimo 3 funciones** afines. Con 2 no compensa pagina madre — quedan tags sueltos.
|
||||
- **Dominio operativo claro**: el grupo debe ser describible en 1 frase ("operar Jupyter colaborativo", "deploy via SSH+systemd").
|
||||
- **Frontera neta** con grupos existentes. Si solapa con otro -> reorganizar, no duplicar.
|
||||
|
||||
### Como crear grupo
|
||||
|
||||
1. Anadir el tag al frontmatter `.md` de >=3 funciones afines. `fn index` lo registra.
|
||||
2. Crear `docs/capabilities/<grupo>.md` con plantilla:
|
||||
- **Lista de funciones**: tabla `ID | firma corta | que hace`.
|
||||
- **Ejemplo canonico**: 1-2 bloques de codigo end-to-end con los IDs reales.
|
||||
- **Fronteras**: que NO cubre el grupo.
|
||||
- **Prerequisitos** y **notas** si aplica.
|
||||
3. Anadir fila al `docs/capabilities/INDEX.md`.
|
||||
4. Correr `fn doctor capabilities` para auditar drift.
|
||||
|
||||
### Auto-generacion
|
||||
|
||||
`fn doctor capabilities --update` (TBD) reescribe la tabla de funciones de cada pagina madre preservando bloques curated (`Ejemplo canonico`, `Fronteras`, `Notas`). Las secciones curated nunca se sobrescriben.
|
||||
|
||||
### Como Claude usa los grupos
|
||||
|
||||
Cuando una tarea cae en un dominio conocido:
|
||||
|
||||
1. `Read docs/capabilities/INDEX.md` para localizar grupo.
|
||||
2. `Read docs/capabilities/<grupo>.md` para cargar funciones + ejemplo.
|
||||
3. Solo si el grupo no cubre lo necesario, `mcp__registry__fn_search` para funciones sueltas.
|
||||
4. Si el grupo deberia cubrir pero falta funcion -> `fn-constructor` + tagear con el grupo en el frontmatter.
|
||||
|
||||
### Auditoria
|
||||
|
||||
```bash
|
||||
fn doctor capabilities # lista grupos + drift
|
||||
fn doctor capabilities --json # para agentes
|
||||
```
|
||||
|
||||
Comprueba:
|
||||
- Tag con N >=3 funciones pero sin pagina madre -> "tag huerfano".
|
||||
- Pagina madre sin tag respaldo -> "grupo fantasma".
|
||||
- Funcion con tag de grupo pero la pagina madre no la lista (autogen desfasada) -> "drift".
|
||||
|
||||
### Relacion con dominios
|
||||
|
||||
Los **dominios** del registry (`core`, `infra`, `finance`, `datascience`, `cybersecurity`, `shell`, `tui`, `pipelines`, `browser`) son taxonomia ortogonal — un grupo puede atravesar varios dominios (ej. `deploy` toca `infra` y `shell`). NO renombrar dominio a grupo ni viceversa.
|
||||
@@ -0,0 +1,42 @@
|
||||
## Delegacion: spawn fn-constructor en vez de escribir inline
|
||||
|
||||
**REGLA DURA.** Si vas a escribir logica reutilizable inline en un artefacto (app, analysis, playground) o heredoc, STOP y delega a `fn-constructor`. La misma sesion debe crear + usar la funcion. No acumular huerfanas.
|
||||
|
||||
### Cuando un patron es candidato a funcion
|
||||
|
||||
- Aparece >=2 veces en esta sesion o en heredocs recientes.
|
||||
- Firma generica (no depende de tipos internos del artefacto).
|
||||
- 1 responsabilidad clara (CRUD, parse, transform, http call, formato fijo, etc.).
|
||||
- No es one-liner idiomatico de stdlib (`time.Now().UTC().Format(...)` queda fuera).
|
||||
|
||||
### Flujo obligatorio (mismo turno)
|
||||
|
||||
1. **Detectar**. Si vas a escribir >=5 lineas de logica reutilizable inline -> STOP.
|
||||
2. **Spawn `fn-constructor` inmediato** via `Agent(subagent_type="fn-constructor", ...)`:
|
||||
- **Sin preguntar al usuario** (autorizado por defecto).
|
||||
- Si hay >1 funcion independiente -> una sola llamada al Agent tool con **N tool_use blocks paralelos** en el mismo mensaje. NO serializar.
|
||||
3. **Tagear con grupo de capacidad** al menos UN tag de grupo (`notebook`, `metabase`, `deploy`, etc.). Ver `capability_groups.md`.
|
||||
4. **`fn index`** para registrar.
|
||||
5. **Importar + invocar en el mismo turno** — no dejar funcion huerfana recien creada.
|
||||
6. **Auto-verificar** con `fn doctor uses-functions` y `fn doctor unused` si tocas >=3 funciones nuevas.
|
||||
|
||||
### Anti-patrones auditables
|
||||
|
||||
| Anti-patron | Consecuencia | Sustituir por |
|
||||
|---|---|---|
|
||||
| Escribir helper inline en artefacto en vez de delegar | Reinvento por sesion | Spawn fn-constructor |
|
||||
| Crear N funciones serialmente | Latencia x N | Multiples `Agent()` en mismo mensaje |
|
||||
| Crear funcion y no usarla en el turno | Huerfana desde dia 1 (`calls_90d=0`) | Importar + invocar antes de cerrar turno |
|
||||
| Crear funcion sin tag de grupo | Imposible descubrir en bloque proxima sesion | Anadir tag de grupo (capability group) |
|
||||
| Reescribir en heredoc logica que ya existe | Capitalizacion perdida | `mcp__registry__fn_search` antes de escribir |
|
||||
|
||||
### Excepciones
|
||||
|
||||
- **Logica de dominio especifica del artefacto** (CRUD de tabla concreta, layout de UI, flujo unico de la app) -> queda en el artefacto. Solo lo reutilizable se delega.
|
||||
- **Stub temporal con `not implemented`**: aceptable si la dependencia externa no esta disponible. Documentar en `.md` (ver `stubs.md`).
|
||||
|
||||
### Telemetria
|
||||
|
||||
Cada `code_writes` + `calls` se registra en `call_monitor/operations.db` (issue 0085). Vista `session_capability_growth` mide ratio creadas vs usadas por sesion. Hook `UserPromptSubmit` inyecta `CAPABILITY-GROWTH: created_this_session=X used=Y orphan=Z` en cada turno.
|
||||
|
||||
Si `orphan>0` al cerrar la sesion -> revisar: o la funcion era especulativa (no debio crearse) o falta integrarla en el codigo del artefacto.
|
||||
@@ -0,0 +1,132 @@
|
||||
## 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.
|
||||
|
||||
### 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 / Dagu sin pasar por `fn run`.
|
||||
- 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).
|
||||
Reference in New Issue
Block a user