--- id: "0085" title: "Estandarizar llamadas a funciones del registry desde Claude + app de monitorizacion de uso" status: completado type: feature domain: - meta scope: multi-app priority: alta depends: [] blocks: [] related: - "0068" - "0069" created: 2026-05-13 updated: 2026-05-17 tags: [] --- ## Cierre 2026-05-15 Todas las piezas del plan implementadas: - Schema event-log + vistas (0085a/0085l) — 7 tablas + `function_stats` view + `function_versions`. - Hook Bash PostToolUse (0085b) capturando mcp/heredoc/sqlite_direct/edit_registry/violations. - Wrappers opt-in (0085c py + 0085c-bash) activables via `FN_TELEMETRY=1`, smoke verificado. - Interceptor en `fn run` (0085d-go) con duration real medida. - UI tab "Claude Usage" en `registry_dashboard` (0085d/0085e) con KPIs + sub-tabs. - Clusterizacion de patrones inline (0085f) — `call_monitor cluster-patterns [--persist]`, 11 clusters detectados, upsert idempotente. - Reglas violation declarativas (0085g, parcial) — `dev/violation_rules.yaml` source-of-truth con 4 activas + 4 propuestas inactivas; runtime YAML reader TBD. - Pipeline `call_monitor propose` (0085h) genera proposals con evidencia desde `function_stats`+`copied_code`+`violations`. - Auditoria estatica de copia (0085k) `fn doctor copied-code`. - Documentacion (0085j) — CLAUDE.md + `.claude/rules/registry_calls.md`. Piezas futuras documentadas pero fuera del MVP: build-tag Go telemetry (0085m), macro C++ `FN_CALL` (0085n), runtime YAML reader del hook, vistas adicionales del dashboard (drill-down por sesion + diff entre sesiones). --- ## Contexto Claude actualmente invoca funciones del registry de formas heterogeneas y sin trazabilidad: | Patron de invocacion | Frecuencia esta sesion (meta_bigq) | Trazabilidad | |---|---|---| | Heredoc Python inline (`python/.venv/bin/python3 - <<'PYEOF' ... PYEOF`) | ~15 veces | ninguna (queda en transcript) | | Bash inline con `sqlite3 registry.db "..."` | 1 (violando regla MCP-first) | ninguna | | `./fn run ` CLI | 0 | log en stdout, no persistido | | `mcp__registry__fn_run` MCP tool | 0 | mensaje tool-result | | `mcp__registry__fn_search/show/code` | 0 (deberia haber sido obligatorio) | ninguna | | Imports directos `from metabase import ...` en heredoc | en cada heredoc | ninguna | | `client._http.request(...)` directo saltando funciones del registry | varias veces (para PUT custom como result_metadata) | ninguna | Consecuencias: - **No sabemos que funciones del registry usa Claude realmente**. Hay ~1200 funciones indexadas pero solo unas pocas se usan en cada sesion. Imposible decidir cuales deprecar, cuales mejorar, cuales son criticas. - **Cada sesion reinventa boilerplate**. Patrones repetitivos (refresh `result_metadata`, dispatch `dimension` vs `variable` mapping, batch param config) se reescriben inline. Si se extraen como funciones del registry nadie lo nota porque no hay metricas de "esto se repite mucho". - **CLAUDE.md tiene reglas (MCP-first, registry-first) que se violan silenciosamente**. Sin telemetria, la regla es aspiracional. - **No hay datos para que el bucle reactivo mejore el registry**. El analizador/mejorador deberian saber "esta funcion se uso 50 veces, esta 0 veces, esta fallo 8 veces" — info que ahora mismo no existe. ## Objetivo 1. **Estandarizar** como Claude invoca funciones del registry (un patron canonico por caso de uso). 2. **Instrumentar** todas las invocaciones para que dejen rastro en una BD local. 3. **App `claude_call_monitor`** (TUI o web) que muestra uso por funcion, latencia, errores, patrones repetidos, violaciones de reglas. 4. **Feedback al registry**: marcar funciones "huerfanas", proponer extracciones cuando un mismo bloque inline se repite N veces, sugerir helpers nuevos. ## Diseno ### Fase 1 — Estandarizacion de invocaciones Tres patrones canonicos. Cada uno con su tool de entrada y formato de log. | Caso de uso | Patron canonico | Cuando usar | |---|---|---| | **Inspeccion del registry** (buscar, leer, ver dependencias) | `mcp__registry__fn_search/show/code/uses` | SIEMPRE. Reemplaza `sqlite3 registry.db "..."` inline. CLAUDE.md ya lo exige; ahora se hace cumplir via lint del log | | **Ejecucion de pipeline/funcion 1-shot** | `mcp__registry__fn_run [args]` o `./fn run [args]` | Cuando hay UNA funcion/pipeline a lanzar con sus args. Salida estructurada | | **Composicion ad-hoc multi-funcion** | Heredoc Python via Bash, importando del registry | Cuando hay logica intermedia (loops, conditionals, dispatch). Esta sesion casi todo el trabajo cae aqui | Para composiciones que se repiten: extraer a `python/functions/pipelines/` o a una funcion del registry. Decision basada en datos del monitor (ver fase 3). ### Fase 2 — Instrumentacion Hook + libreria que captura cada invocacion. Stack propuesto: **2a. Hook en Bash tool**: parsea cada comando Bash. Si contiene heredoc Python `python/.venv/bin/python3` o invoca `./fn run` o `mcp__registry__fn_*`, captura: - timestamp_start, timestamp_end, duration_ms - session_id (del log de Claude Code) - tool_used (Bash heredoc / fn_run / mcp_fn_X / mcp_fn_search / sqlite_direct / etc.) - functions_imported (parse `from import `) - functions_called (mejor esfuerzo: regex sobre el codigo del heredoc + para fn_run el id explicito) - success / exit_code / error_snippet - patron_detected (refresh_metadata, build_mappings, etc — clasificadores configurables) Implementacion: hook `PostToolUse` en `~/.claude/settings.json` que llama a un binario Go que escribe en `~/.claude/projects//call_monitor.db` (SQLite). **2b. Wrapper Python**: `from registry_telemetry import wrap` que parchea las funciones del paquete `metabase`/`bigquery`/etc al importarse en heredoc. Cada llamada se loguea (function_id, args_hash, duration, success). Solo se activa si env var `FN_TELEMETRY=1` (no romper otros usos). **2c. Hook directo en MCP `registry`**: si el server MCP esta bajo nuestro control, anadir logging en cada tool call (mas confiable que parsear bash). Las 3 fuentes se cruzan: si una sesion tiene Bash heredoc usando `metabase_get_dashboard` pero no aparece en MCP logs, es violacion (deberia haber usado `mcp__registry__fn_show` para inspeccionar antes). ### Fase 3 — App `claude_call_monitor` App standalone en `apps/claude_call_monitor/` o `projects/fn_monitoring/apps/claude_call_monitor/`. Stack: - Backend Go (sirve datos de `call_monitor.db` + agregados) - Frontend React + Mantine (consume `@fn_library`) o TUI con `cpp/framework` ImGui — segun preferencia Vistas minimas: 1. **Top funciones por uso** — tabla rankada: `function_id, calls_24h, calls_7d, mean_duration_ms, error_rate, last_used_at`. Filtros por dominio/lang/purity. 2. **Funciones huerfanas** — listado de funciones del registry con `calls_30d = 0`. Cruzado con `fn doctor unused` para distinguir "nunca usada" vs "no usada por Claude pero si por humanos". 3. **Patrones repetidos** — clusterizacion de heredocs Python por similitud. Detecta cuando un bloque inline se repite N veces → proposal automatico para extraer a funcion. 4. **Violaciones de regla** — usos de `sqlite3 registry.db` directo, uso de `Centros_ISO_Limpio` cuando deberia ser via card snippet, etc. Reglas configurables en YAML. 5. **Sesion view** — timeline de una sesion (Claude Code session_id) con todas las llamadas, errores, duraciones. Util para post-mortem. 6. **Health score** — score 0-100 por sesion: ratio de invocaciones canonicas vs ad-hoc, errores, repeticiones. Telemetria para mejorar prompts del agente. ### Fase 4 — Feedback al registry Hooks de salida del monitor: - **Proposals automaticas**: cuando un patron inline se repite >5 veces en distintas sesiones, se crea proposal `new_function` en `registry.db` con evidencia (lista de session_ids + snippet representativo). El humano (o `fn-mejorador`) decide si aprobar. - **Deprecation candidates**: funciones con `calls_90d = 0` y sin `uses_functions` upstream → proposal `deprecate_function`. - **Performance regressions**: funciones cuyo `mean_duration_ms` crece >50% entre semanas → flag al humano. ## Implementacion por pasos | Paso | Tarea | Sub-issue | |---|---|---| | 1 | Migracion `call_monitor.db` schema (`calls`, `sessions`, `patterns`, `violations`) | 0085a | | 2 | Hook Bash `PostToolUse` que parsea comandos y escribe a `calls` | 0085b | | 3 | Wrapper Python opcional con `FN_TELEMETRY=1` | 0085c | | 4 | App `claude_call_monitor` skeleton (Go API + frontend) | 0085d | | 5 | Vistas: top usage, huerfanas, sesiones | 0085e | | 6 | Clusterizacion de heredocs + deteccion de patrones | 0085f | | 7 | Reglas de violacion configurables (YAML) | 0085g | | 8 | Pipeline `fn-monitor proposal` que crea proposals automaticas en `registry.db` desde patrones detectados | 0085h | | 9 | `e2e_checks` para la propia app del monitor | 0085i | | 10 | Documentacion en CLAUDE.md: patrones canonicos + como leer el monitor | 0085j | ## Criterios de exito - Todas las invocaciones de funciones del registry desde Claude quedan registradas (>95% cobertura medida cruzando 3 fuentes). - App `claude_call_monitor` muestra top-20 funciones usadas por Claude en los ultimos 7 dias con metricas reales. - Se detectan al menos 5 patrones repetidos como candidatos a extraccion (con evidencia trazable). - Se identifican >50 funciones huerfanas para decision (deprecar/promover/dejar). - CLAUDE.md tiene seccion "Como llamar a funciones del registry" con los 3 patrones canonicos + tabla "cuando usar cual". - El bucle reactivo (0068) tiene un nuevo input: assertions sobre uso de funciones → proposals. ## Anti-patrones a prohibir explicitamente | Patron | Por que | Alternativa | |---|---|---| | `sqlite3 registry.db "SELECT ..."` para inspeccionar funciones | Salta MCP, no hay logging, FTS5 gotchas | `mcp__registry__fn_search "..."` | | `python -c "import metabase; print(dir(metabase))"` para descubrir helpers | Salta el registry como fuente de verdad | `mcp__registry__fn_search "metabase"` + `mcp__registry__fn_show ` | | Heredoc que reescribe logica que ya existe como funcion | Reinvento + perdida de capitalizacion | Primero `fn_search`, luego importar | | `client._http.request(...)` directo cuando hay un wrapper del registry | Salta validacion y telemetria del wrapper | Usar la funcion del registry; si falta una, delegar a `fn-constructor` | | Crear scripts en `temp/` o paths sueltos cuando es composicion repetida | Codigo se pierde, no se monitoriza | Si patron se repite → pipeline en `python/functions/pipelines/` | ## Stakeholders - **Usuario humano (Lucas / Emanuel)**: revisa proposals automaticas, prioriza extracciones, decide deprecaciones. - **Claude (agente principal)**: lee CLAUDE.md actualizado, usa patrones canonicos, recibe feedback de monitor en CLAUDE.md (top huerfanas, top errores). - **fn-mejorador (fase 5 bucle reactivo)**: consume `call_monitor.db` para generar proposals con evidencia real de uso. - **fn-orquestador (issue 0069)**: usa health score del monitor como criterio adicional de exito. ## Notas - Esta issue no requiere refactorizar las 1200 funciones existentes — solo capturar como se invocan. - 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 | **hecho** — `python/functions/infra/registry_telemetry.py`, sys.meta_path importer + `wrap_namespace`. Smoke verificado 2026-05-15: `filter_list_py_core` logged con `tool_used=python_wrapper`. | | 3b | Wrapper Bash `telemetry_prelude` | 0085c-bash | **hecho** — `bash/functions/infra/telemetry_prelude.sh`, autowrap idempotente via declare -f + eval rename. Smoke verificado 2026-05-15: `wait_for_http_bash_infra` logged con `tool_used=bash_wrapper`. | | 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 | **hecho** — implementadas en `registry_dashboard` tab "Claude Usage" (`projects/fn_monitoring/apps/registry_dashboard/views.cpp`, `data.h`, `data_http.h`). KPIs Reg%, MCP, Errors, Violations + sub-tabs top/huerfanas/sesiones. | | 6 | Clusterizacion heredocs + tabla `patterns` populada | 0085f | **hecho** 2026-05-15 — `call_monitor cluster-patterns [--persist]` (`cluster.go`). Normaliza snippets (quoted strings -> STR, paths -> /PATH, hex 8+ -> HEX, numbers -> N), hashea sha256-truncado, agrega ocurrencias + session_ids. 11 clusters detectados de 286 calls inline; persistencia con UPSERT idempotente. 3 unit tests (TestNormalizeSnippet/TestHashSnippetStable/TestSplitCSV) pass. | | 7 | Reglas violation configurables YAML | 0085g | **parcial** 2026-05-15 — `dev/violation_rules.yaml` cataloga 4 reglas activas (sqlite3_registry_select, python_dir_inspect, import_star_in_heredoc, client_http_request_direct) + 4 propuestas inactivas (mcp_ratio_low, heredoc_repetition, edit_registry_without_fn_index, protected_path_modified). YAML es source-of-truth declarativo. **Runtime reader TBD**: el hook PostToolUse sigue hardcoded; futura iteracion requiere jq/yq + refactor para leer reglas dinamicamente. | | 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 |