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:
@@ -139,3 +139,164 @@ Hooks de salida del monitor:
|
||||
- El monitor empieza pasivo (solo loguea). En una fase posterior puede bloquear violaciones criticas (ej. `sqlite3 registry.db` directo aborta con mensaje + sugerencia).
|
||||
- Datos sensibles: el `args_hash` se guarda pero los valores concretos NO. Para queries SQL que contienen secretos por accidente, mantener allowlist de redaccion.
|
||||
- Compatible con el patron de `task_runs` del issue 0069 — comparten el concepto de "ejecucion trazable".
|
||||
|
||||
## Decisiones 2026-05-13
|
||||
|
||||
- **UI**: tab dentro de `registry_dashboard` (proyecto `fn_monitoring`). Reusa C++/ImGui + `sqlite_api`. Razon: pregunta clave "que deprecar" = JOIN entre `functions` y `function_stats`; una sola app gana.
|
||||
- **Plumbing**: `projects/fn_monitoring/apps/call_monitor/` (Go + hook Bash `PostToolUse`). Repo Gitea propio (`dataforge/call_monitor`).
|
||||
- **Hook scope**: solo proyecto (`.claude/settings.local.json`). Captura solo sesiones de fn_registry.
|
||||
- **Orden de sub-issues**: 0085j (docs) → 0085a (schema) → 0085b (hook) → 0085c (wrapper Python).
|
||||
- **0085j hecho** (2026-05-13): seccion "Como invocar funciones del registry (CANONICO)" en `.claude/CLAUDE.md`, regla `.claude/rules/registry_calls.md` (rule #27), entrada `INDEX.md`, memoria `feedback_canonical_registry_calls.md`.
|
||||
- **MCP ampliado**: tool nuevo `fn_proposal` (read-only, list + show by id) en `apps/registry_mcp/tool_proposal.go`. Cierra el gap de `sqlite3 registry.db "SELECT ... FROM proposals"` inline. Rebuild aplicado.
|
||||
|
||||
## Schema extendido — contadores por funcion
|
||||
|
||||
Event-log tables (append-only):
|
||||
|
||||
| Tabla | Captura |
|
||||
|---|---|
|
||||
| `calls` | function_id, tool_used, session_id, duration_ms, success, error_class, error_snippet, args_hash, ts |
|
||||
| `code_writes` | function_id (derivado del path), session_id, lines_added, lines_removed, ts |
|
||||
| `test_runs` | function_id, test_id, passed, duration_ms, output_snippet, ts |
|
||||
| `e2e_runs_fn` | function_id, app_id, check_id, passed, ts (cruza con `apps.uses_functions`) |
|
||||
| `violations` | rule_id, session_id, command_snippet, severity, ts |
|
||||
| `patterns` | pattern_hash, session_ids[], representative_snippet, occurrences, last_seen |
|
||||
| `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) |
|
||||
|
||||
Migracion: `projects/fn_monitoring/apps/call_monitor/migrations/001_init.sql` con las 7 tablas + indices sobre `function_id`, `session_id`, `ts`. Vista `function_stats` se construye en `002_function_stats_view.sql` (materializar como TABLE si performance bajo, recalcular cada N min).
|
||||
|
||||
## Cobertura por lenguaje — capas de monitorizacion
|
||||
|
||||
Cada lenguaje del registry tiene un "techo" de lo monitorizable runtime. Las 8 capas de cobertura propuestas, ordenadas por ROI:
|
||||
|
||||
| # | Capa | Lenguaje | Cubre | Esfuerzo | Estado |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | Hook PostToolUse Bash | universal | mcp__registry__*, `./fn run`, Edit/Write sobre `functions/`, violations | bajo | **hecho (0085b)** |
|
||||
| 2 | Wrapper Python (`registry_telemetry`) activable con `FN_TELEMETRY=1` | py | heredocs Python + notebooks Jupyter + scripts dentro del registry | bajo | pending 0085c |
|
||||
| 3 | Wrapper Bash `bash/lib/telemetry_prelude.sh` (redefine cada funcion del registry con cronograma + log) | bash | heredocs bash + scripts/apps Bash | bajo | pending 0085c-bash |
|
||||
| 4 | Interceptor en `fn run` (binario Go) | go/py/bash/ts | invocaciones via CLI con duration/error real medido | medio | pending 0085d-go |
|
||||
| 5 | `fn doctor copied-code` — fingerprint match registry vs apps | universal (estatico) | codigo copiado/parafraseado sin import | medio | pending 0085k |
|
||||
| 6 | Tabla `function_versions` + snapshot en Edit-hook | universal (estatico) | drift de versiones, forks silenciosos | bajo | pending 0085l |
|
||||
| 7 | Build-tag `telemetry` Go + codegen wrappers | go | runtime de apps Go (parcial, opt-in por build) | alto | futuro |
|
||||
| 8 | Macro `FN_CALL(name, ...)` C++ opt-in | cpp | C++ apps cooperantes | alto | futuro |
|
||||
|
||||
**Reglas duras:**
|
||||
- **Go y C++ compilados:** sin monkey-patch dinamico. Apps que linkean estaticamente la funcion **no se pueden auditar runtime** salvo capas 7/8 con opt-in.
|
||||
- **Solucion realista Go/C++:** medir 3 caminos legitimos — `./fn run`, `mcp_fn_run`, `go test`/`ctest`. Runtime de app en produccion queda fuera.
|
||||
- **Wrapper Python/Bash:** activacion explicita via `FN_TELEMETRY=1`. Si la app no exporta esa env var, no se mide.
|
||||
|
||||
## Drift detection — funciones copiadas y modificadas
|
||||
|
||||
### 5. Codigo copiado en apps (sin `import`)
|
||||
|
||||
Problema: app reescribe el cuerpo de una funcion del registry en vez de importarla. Invisible runtime — el codigo nunca pasa por el registry. Solo se detecta por analisis estatico.
|
||||
|
||||
Tecnicas (ordenadas por precision/coste):
|
||||
|
||||
| Tecnica | Pilla | Limitacion |
|
||||
|---|---|---|
|
||||
| Hash exacto del cuerpo normalizado (strip whitespace + comments) | copy-paste literal | 0 false-positives, miss si renombran 1 var |
|
||||
| AST fingerprint via Tree-Sitter → token sequence → SimHash | copia con renames | requiere parser por lenguaje |
|
||||
| ssdeep / TLSH fuzzy hash sobre cuerpo normalizado | copia con tweaks pequeños | umbral arbitrario ~85% true match |
|
||||
| Embeddings de codigo (Code2Vec, starcoder, etc.) | parafraseado / refactor parcial | costoso, opaco |
|
||||
|
||||
**MVP propuesto:** `fn doctor copied-code` que cruza fingerprints de cada funcion del registry contra cuerpos de funciones declaradas en `apps/`, `projects/*/apps/`. Salida: `{app_id, app_file, app_func_name, matched_registry_id, similarity, kind}`. Severidad:
|
||||
|
||||
- `exact_copy` → critical → proposal `import_instead`
|
||||
- `near_copy` (>0.85 fuzzy) → warning
|
||||
- `partial_match` (>0.6) → info
|
||||
|
||||
Persistencia: tabla nueva `copied_code` en `call_monitor.operations.db`. Aporta a `function_stats` columna `copies_detected`.
|
||||
|
||||
### 6. Versiones modificadas (`function_versions`)
|
||||
|
||||
`registry.db.functions.content_hash` ya existe — `fn index` lo recalcula. Falta historial.
|
||||
|
||||
Schema propuesto (migracion `003_function_versions.sql` en call_monitor):
|
||||
|
||||
```sql
|
||||
CREATE TABLE function_versions (
|
||||
function_id TEXT NOT NULL,
|
||||
content_hash TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
snapped_at INTEGER NOT NULL,
|
||||
source TEXT NOT NULL, -- 'index' | 'edit_hook' | 'copy_detected'
|
||||
lines_added INTEGER DEFAULT 0,
|
||||
lines_removed INTEGER DEFAULT 0,
|
||||
PRIMARY KEY (function_id, content_hash)
|
||||
);
|
||||
```
|
||||
|
||||
Llenado:
|
||||
- Cada `fn index` inserta snapshot con `source='index'`.
|
||||
- Hook PostToolUse Edit/Write sobre `functions/...` inserta snapshot con `source='edit_hook'` (ya tenemos `code_writes` — extender o cross-reference).
|
||||
- `fn doctor copied-code` registra version observada en la copia con `source='copy_detected'`.
|
||||
|
||||
Consultas utiles:
|
||||
- Forks silenciosos: app copio version X, registry esta en version Y, app sigue en X.
|
||||
- Velocidad de cambio: funciones con muchas versions en poco tiempo → inestables.
|
||||
- Backport candidates: app tiene version vieja, version nueva resuelve bug → flag.
|
||||
|
||||
## Que se escapa del monitor (boundary explicito)
|
||||
|
||||
| Caso | Capturado? | Capa que lo cubriria |
|
||||
|---|---|---|
|
||||
| `mcp__registry__fn_*` | si | hook PostToolUse |
|
||||
| `./fn run X` desde Bash | si | hook PostToolUse |
|
||||
| Edit/Write sobre `functions/*/*.{go,py,sh,ts}` | si | hook PostToolUse |
|
||||
| Heredoc Python importando registry | parcial (solo `heredoc_py`, no funciones internas) | wrapper Python (0085c) |
|
||||
| Heredoc Bash sourcing registry | parcial | wrapper Bash (0085c-bash) |
|
||||
| Notebook Jupyter (MCP jupyter) ejecutando codigo registry | parcial | wrapper Python (0085c) — el kernel hereda env vars |
|
||||
| Sub-agente (`Agent` tool) | NO transitivo | requiere registrar hooks en cada sub-agente |
|
||||
| **Funcion Go llamada por codigo de app en runtime** | **NO** | capa 7 (build-tag) — futuro |
|
||||
| **Funcion Bash sourceada por otro script en runtime** | **NO** | wrapper Bash (0085c-bash) si se sourcea el prelude |
|
||||
| **Funcion C++ compilada y llamada por app** | **NO** | capa 8 (macro `FN_CALL`) — futuro |
|
||||
| Test automatico que ejecuta funciones | NO si corre fuera de Claude | capa 4 (`fn run` instrumentado captura `go test` via fn) |
|
||||
| Service de produccion (`registry_api.service`) recibe HTTP | **NO** | sin cobertura — runtime sistema, no agente |
|
||||
| Cron / Dagu / systemd timer | **NO** | sin cobertura |
|
||||
| Funcion clonada/copiada (sin `import`) | NO runtime | capa 5 (`fn doctor copied-code`) detecta estatico |
|
||||
|
||||
**Verdad operativa:** monitorizamos al **agente** y a las **invocaciones canonicas** del registry. El runtime de cada app en produccion queda fuera. Compensar con: tests, e2e_checks (capa propia por app, issue 0068), y telemetria de invocacion via `fn run`/MCP.
|
||||
|
||||
## Implementacion por pasos (actualizado 2026-05-13)
|
||||
|
||||
| Paso | Tarea | Sub-issue | Estado |
|
||||
|---|---|---|---|
|
||||
| 1 | Migracion `call_monitor.operations.db` schema (7 tablas event-log + vista `function_stats`) | 0085a | **hecho** |
|
||||
| 2 | Hook Bash `PostToolUse` que parsea tools y escribe `calls`/`code_writes`/`violations` | 0085b | **hecho** |
|
||||
| 3a | Wrapper Python `registry_telemetry` (activable con `FN_TELEMETRY=1`) | 0085c | pending |
|
||||
| 3b | Wrapper Bash `bash/lib/telemetry_prelude.sh` | 0085c-bash | pending |
|
||||
| 3c | Interceptor en `fn run` (binario Go) | 0085d-go | **hecho** |
|
||||
| 4 | Tab "Claude Usage" en `registry_dashboard` (datasource `ops:call_monitor`, KPIs + 3 sub-tabs) | 0085d | **hecho** |
|
||||
| 5 | Top usage, huerfanas, sesiones (vistas UI) | 0085e | pending |
|
||||
| 6 | Clusterizacion heredocs + tabla `patterns` populada | 0085f | pending |
|
||||
| 7 | Reglas violation configurables YAML | 0085g | pending |
|
||||
| 8 | Pipeline `call_monitor propose` (funcion `infra.GenerateProposalsFromTelemetry` + `infra.PersistProposalDrafts`) que escribe a `registry.db.proposals` desde `function_stats` + `copied_code` + `violations`. 4 reglas MVP: copy_detected, orphan, bug, wrapper_skip. INSERT OR IGNORE con id determinista | 0085h | **hecho** |
|
||||
| 9 | `e2e_checks` propios de call_monitor (en `app.md`, ya declarados) | 0085i | parcial (declarado) |
|
||||
| 10 | Documentacion CLAUDE.md + rules (registry_calls.md) | 0085j | **hecho** |
|
||||
| 11 | `fn doctor copied-code` + tabla `copied_code` + subcomando `call_monitor copied-code` | 0085k | **hecho** |
|
||||
| 12 | Tabla `function_versions` + subcomando `call_monitor snapshot` + edit-hook con sha256 file | 0085l | **hecho** |
|
||||
| 13 (futuro) | Go build-tag `telemetry` con codegen | 0085m | futuro |
|
||||
| 14 (futuro) | C++ macro `FN_CALL` opt-in | 0085n | futuro |
|
||||
|
||||
Reference in New Issue
Block a user