CreaRack-SL

Señal de co-cambio en bib_impact_query (Cairn #2)

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 main registra sus pares de código en la tabla D1 bib_cochanges vía el nuevo handler record_cochanges().
  • Backfill inicial: el script bib_cochange_backfill.py rellena 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:

  1. git log --no-merges → itera cada commit no-merge.
  2. Para cada commit, extrae la lista de archivos tocados.
  3. Filtra a código (mismas reglas que record_cochanges()).
  4. Cuenta pares ordenados (a<b) en cuántos commits aparecen juntos.
  5. Ignora commits con >25 archivos (refactores masivos, ruido).
  6. Solo publica pares con count >= 2 (1 = coincidencia casual).
  7. Envía lotes de ≤500 pares al MCP bib_record_cochanges en modo pairs.

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:

  1. Fase 5: Lint de tablas D1 y sintaxis de pages (fase-5-lint).
  2. Fase 6: Métricas de utilidad en páginas y discovery automático (fase-6-metricas-utility-score).
  3. 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_query devuelve 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_cochanges se 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]]