--- name: call_monitor lang: go domain: infra description: "Telemetria de invocaciones del agente al fn_registry. Persiste eventos (calls, code_writes, test_runs, e2e_runs_fn, violations, patterns, sessions) en su propia operations.db. Vista agregada function_stats por function_id alimenta el bucle reactivo (proposals automaticas). Issue 0085." tags: [service, telemetry, monitoring, registry, sqlite] uses_functions: - sqlite_open_go_infra - sqlite_apply_migrations_go_infra - audit_copied_code_go_infra - generate_proposals_from_telemetry_go_infra uses_types: [] framework: "stdlib" entry_point: "main.go" dir_path: "projects/fn_monitoring/apps/call_monitor" repo_url: "" e2e_checks: - id: build cmd: "CGO_ENABLED=1 go build -tags fts5 -o call_monitor ." timeout_s: 60 - id: init cmd: "./call_monitor init --db /tmp/call_monitor_e2e.db" expect_stdout_contains: "ready" - id: status_empty cmd: "./call_monitor status --db /tmp/call_monitor_e2e.db" expect_stdout_contains: "no calls recorded yet" - id: schema_view cmd: "sqlite3 /tmp/call_monitor_e2e.db 'SELECT COUNT(*) FROM function_stats;'" expect_stdout_contains: "0" --- ## Arquitectura App Go bajo modulo `fn-registry` (sin `go.mod` propio). Estructura: ``` projects/fn_monitoring/apps/call_monitor/ app.md main.go # CLI: init, status db.go # openDB + apply migrations + queries migrations/ 001_init.sql # 7 tablas event-log 002_function_stats_view.sql # vista agregada por function_id operations.db # creada al primer init (gitignored) ``` Reusa `infra.SQLiteOpen` + `infra.ApplyMigrations` del registry (mismo patron que `apps/kanban/backend/db.go`). ## Tablas event-log (append-only) | Tabla | Captura | |---|---| | `sessions` | Sesion Claude Code: session_id, cwd, started_at, ended_at, health_score, mcp_ratio | | `calls` | Cada invocacion al registry (heredoc/mcp/fn_run): function_id, tool_used, duration_ms, success, error_class, args_hash | | `code_writes` | Edit/Write sobre archivo del registry: function_id, file_path, lines_added/removed | | `test_runs` | Unit tests: function_id, test_id, passed, duration_ms, output_snippet | | `e2e_runs_fn` | E2E checks de apps que dependen: function_id, app_id, check_id, passed | | `violations` | Antipatrones (sqlite3 inline, import *, heredoc reinvento): rule_id, function_id, severity | | `patterns` | Heredocs clusterizados por similitud: pattern_hash, occurrences, session_ids[] | | `function_versions` | Historial de versiones por function_id. source = `index` (poblado por `call_monitor snapshot` tras `fn index`), `edit_hook` (poblado por hook PostToolUse), `copy_detected` (futura fase 0085k) | Datos sensibles: solo `args_hash`, NUNCA argumentos concretos. ## Vista `function_stats` Rollup por `function_id` con: - **Uso**: calls_total, calls_24h/7d/30d/90d, last_used_at - **Errores**: errors_total, error_rate, last_error_ts - **Performance**: mean_duration_ms (p95 pendiente — requires window functions o sub-query) - **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 Vista O(N) sobre tablas event-log. Si performance degrada en >100k filas, materializar como TABLE refrescada por cron. ## Uso ```bash # Build cd projects/fn_monitoring/apps/call_monitor CGO_ENABLED=1 go build -tags fts5 -o call_monitor . # Crear/abrir BD (aplica migraciones idempotentemente) ./call_monitor init # Resumen actual ./call_monitor status --top 20 # Snapshot versions desde registry.db (idempotente, ejecutar tras cada fn index) ./call_monitor snapshot ./call_monitor snapshot --registry /home/lucas/fn_registry/registry.db # BD personalizada ./call_monitor init --db /tmp/test.db ./call_monitor status --db /tmp/test.db ``` ## Integracion con el resto | Componente | Como interactua | |---|---| | Hook `PostToolUse` (0085b) | Parsea cada Bash + cada mcp__registry__* y hace `INSERT INTO calls/...` directo sobre operations.db | | Wrapper Python `registry_telemetry` (0085c) | Patcha imports al activar `FN_TELEMETRY=1`, registra calls del heredoc | | `registry_dashboard` (UI) | Lee via `sqlite_api`: nuevo datasource `ops:call_monitor`. Tab "Claude usage" con top funciones, huerfanas, patrones | | `fn-mejorador` (fase 5 bucle reactivo) | Consulta `function_stats` + `patterns` + `violations` para generar proposals con evidencia trazable | | `fn-orquestador` (issue 0069) | Usa `sessions.health_score` como criterio de exito adicional | ## Roadmap - 0085a (este paso): schema + skeleton ✓ - 0085b: hook PostToolUse Bash que insert en `calls`/`code_writes`/`violations` - 0085c: wrapper Python con `FN_TELEMETRY=1` - 0085d: anadir datasource a `sqlite_api` + tabs en `registry_dashboard` - 0085e..h: clusterizacion, proposals automaticas, gating - p95 en mean_duration_ms via percentile calc o ext. ## Notas - BD vive **junto al binario** (`/operations.db`) por defecto, no en el cwd del agente. Hook puede pasar `--db` explicito si conviene. - `operations.db` gitignored — telemetria es local por PC, no se sincroniza. - Sin `repo_url`: aun no se ha hecho `gitea_create_repo`. Se inicializara con `/full-git-push` cuando este la fase 0085b lista para evitar repos vacios.