Descripción
La herramienta interna bib_impact_query (CORE de análisis de impacto de cambios en la Biblioteca de Conocimiento) ahora enriquece sus resultados con una nueva señal: co-cambios históricos.
Hasta s119, bib_impact_query solo consultaba el grafo estático de dependencias (imports explícitos, llamadas de función, enlaces en documentos declarados). Capturaba el acoplamiento formal pero perdía el acoplamiento empírico: archivos que siempre cambian juntos en la práctica aunque no haya un import explícito entre ellos.
Ejemplo real: monitoring/api/signage.py (backend) y static/js/pages/signage/SignageContentManager.js (frontend) casi nunca cambian por separado, pero no hay dependencia formal. Un dev que toca uno y olvida el otro incurre en desincronización sutil.
Con esta feature, bib_impact_query ahora devuelve un campo cochanged_files que enumera qué archivos han cambiado históricamente juntos (según git). La señal se alimenta:
- En caliente (post-merge): cada PR que llega a
mainregistra sus pares de código en la tabla D1bib_cochangesvía el nuevo handlerrecord_cochanges(). - Backfill inicial: el script
bib_cochange_backfill.pyrellena toda la tabla desde el historial git de una vez (one-shot, no iterar).
Implementación
Hook en el ingest: record_cochanges()
Ubicado en .github/scripts/bib_ingest.py (función nueva, líneas ~1597–1655):
def record_cochanges(changed_files: list[str], mcp_token: str, repo_name: str) -> dict
Contrato:
- Corre incondicional en cada merge (como
trigger_tripwires). - Filtra solo archivos de código (
.py,.ts,.tsx,.js,.jsx,.vue,.html,.css,.sql, etc.) — excluye.md,.wiki/, scripts internos (.github/scripts/bib_*), harness, supercontext public. - Excluye especialmente archivos de “altísima rotación” (e.g.
config/settings/base.py, que cambia en cada release) para no ahogar la señal. - Si hay <2 archivos de código, retorna
{"recorded_pairs": 0, "skipped": "..."}. - Llama al MCP
bib_record_cochanges(handler vive en el Workspace) con la lista de paths. - Idempotente, tolerante a fallos — nunca rompe el ingest aunque el MCP falle.
Impacto en el ingest main loop:
- Se invoca después de
trigger_tripwires, independientemente de si el commit se procesa o se salta (fase pre-LLM). - Logs informativos:
"[Co-change] Registrados N pares de co-cambio."o"[Co-change] ERROR: ...".
Backfill: bib_cochange_backfill.py
Script standalone (nuevo, scripts/bib_cochange_backfill.py, 202 líneas):
Propósito: Agregación histórica de pares desde el inicio del repo. Se corre UNA sola vez (one-shot) tras desplegar la migración D1 0043 (que crea la tabla bib_cochanges).
Algoritmo:
git log --no-merges→ itera cada commit no-merge.- Para cada commit, extrae la lista de archivos tocados.
- Filtra a código (mismas reglas que
record_cochanges()). - Cuenta pares ordenados (a<b) en cuántos commits aparecen juntos.
- Ignora commits con >25 archivos (refactores masivos, ruido).
- Solo publica pares con
count >= 2(1 = coincidencia casual). - Envía lotes de ≤500 pares al MCP
bib_record_cochangesen modopairs.
Uso (local o CI):
# Dry-run (reporta pares sin publicar)
BIB_MCP_TOKEN="..." python scripts/bib_cochange_backfill.py --dry-run
# Publicar de verdad
BIB_MCP_TOKEN="..." python scripts/bib_cochange_backfill.py
# Limitar historial (ej: últimos 6 meses)
python scripts/bib_cochange_backfill.py --since "2025-12-09"
# Repo del Workspace
BIB_MCP_TOKEN="..." python scripts/bib_cochange_backfill.py --repo CreaRackSL-workspace
Secretos (env):
BIB_MCP_TOKEN(obligatorio, salvo--dry-run): Bearer token del MCP Workspace.CF_ACCESS_CLIENT_ID,CF_ACCESS_CLIENT_SECRET(opcionales): si el MCP está tras Cloudflare Access.
Idempotencia: Re-lanzar el backfill SUMA contadores de nuevo (no es limpio). Para reload, vaciar bib_cochanges antes.
Configuración y umbral
Parámetro anti-ruido (línea 51 en backfill):
MAX_FILES_PER_COMMIT = 25
Un commit que toca >25 archivos se salta (refactor masivo, ruido estadístico). Mismo valor en record_cochanges() (bib_ingest.py).
Umbral de señal en impact_query:
Mientras el backfill siga granularidad “por commit” (método clásico de logical coupling) y el ingest en caliente cuente “por merge” (lista completa del PR), ambas alimentan la misma tabla. Un umbral de count >= 2 en bib_impact_query suaviza la diferencia.
Motivación: Cairn cherry-pick #2
Esta feature es la tercera y última idea de la evaluación Cairn (auditoría externa de la herramienta Biblioteca de s116–s118). Las primeras dos ya se implementaron:
- Fase 5: Lint de tablas D1 y sintaxis de pages (
fase-5-lint). - Fase 6: Métricas de utilidad en páginas y discovery automático (
fase-6-metricas-utility-score). - Aquí: Enriquecimiento de impact_query con señal empírica (Cairn #2, s119).
Cierra el círculo: la herramienta ahora aprende del historial.
Impacto visible
Para el usuario desarrollador:
- Sin cambios en la CLI de
bib_ask,bib_search_nodes,bib_context_query. bib_impact_querydevuelve un campo extra (cochanged_files): lista de archivos que históricamente cambian juntos.- En la descripción de la sesión s119: “análisis de impacto ahora aprende del historial”.
Para la Biblioteca:
- La tabla D1
bib_cochangesse alimenta en caliente (cada merge) + backfill inicial (one-shot). - El grafo de conocimiento se enriquece con un nuevo tipo de relación: co-cambio empírico (arista
cochanged_with).
Véase también
- [[feature—biblioteca—fase-5-lint]]
- [[feature—biblioteca—fase-6-metricas-utility-score]]
- [[entity—supercontext—tool—bib-impact-query]]
- [[concept—biblioteca—grafo-conocimiento]]
- [[runbook—biblioteca—bib-cochange-backfill]]