VIGÍA · Auditoría de Repositorio · rama claude/module-archaeology-60-days-7kt1i6

Arqueología completa de módulos sin tocar hace 60–90 días

Barrido completo de .py trackeados en git con 60+ días sin commit, cruzados contra los 5 entry points de producción, los workflows de CI, y duplicados por nombre de archivo en todo el repo.

Fecha del corte
2026-07-14
Umbral
≥ 60 días sin commit
Archivos evaluados
402 .py trackeados
Archivos en el corte
112
Método
grafo de imports (AST) + texto de CI + duplicados por basename

Resumen ejecutivo

El clon estaba superficial (52 commits, todo dentro de la última semana) — se hizo git fetch --unshallow antes de fechar nada, porque con historia truncada cada archivo aparece falsamente "reciente". Con historia completa (hasta abril), el corte real de 60+ días dio 112 archivos, no los ~30 esperados. El hallazgo central no es un fósil aislado como el de ayer: es un patrón sistémico. 31 archivos implementan lógica que, por nombre, docstring o cita directa en CLAUDE.md, debería estar en el camino de decisión — y no lo está.

31
Fósil de alto riesgo
44
Conectado y vivo
16
Dead weight real
21
Fósil de bajo riesgo

Por qué esto es más grande que el caso de signal_quality_gate.py de ayer

Ese caso no era una excepción — es la firma de un patrón que se repite en al menos ocho subsistemas: el gate de Causal Closure Score completo (4 archivos, con test unitario propio que nunca se conecta al scorer), el trío Ockham/dissent/planner-acotado, todo vigia/abduction/ (93KB, el núcleo teórico peirceano), el pilar geopolítico de falsa bandera (dos versiones, ambas muertas), y el propio vigia_core_semiotic_detector.py — un stub de 4 líneas que siempre retorna "NORMAL" si algo lo llegara a cablear por error.

Prioridad — lo que Anna necesita ver primero

Cuatro hallazgos individuales antes que los clusters temáticos, porque no son "código sin usar" — son riesgos activos o casos con evidencia de daño ya ocurrido.

Crítico — semántica de veredicto contradictoria

vigia/governance/risk_bounded_layer_v2.py

pre_release_check.py afirma que v2 es la versión activa en producción. No lo es: vigia/pipeline/pipeline.py importa vigia.core.risk_bounded_layer (sin _v2) directamente. Peor — las dos definen posterior con semántica opuesta: la versión viva usa P = P(fabricación | evidencia); la huérfana v2 tiene un fix P0-001 documentado que usa P = P(autenticidad | evidencia), con una advertencia explícita en el propio código: "si algún desarrollador futuro interpreta P como P(fabricación), TODOS los veredictos se invertirán." El archivo vivo no tiene ese guard.

Esto es la capa de governance del veredicto en sí misma. Necesita revisión de ingeniería antes que cualquier otro ítem de esta lista.

Incidente de producción confirmado — BUG-EML-001

vigia/core/signal_contract.py

No es hipotético: tests/test_eml_import_regression.py documenta que este archivo —un re-export de una línea que colisiona de nombre con el SignalBuilder real en vigia/tools/signal_contract.py— ya causó que tres módulos vivos (eml_symbolic.py, eml_gci.py, signal_adapter.py) importaran desde el lugar equivocado, recibieran ImportError, y quedaran silenciosamente al 0% de cobertura hasta que alguien lo notó.

Prueba directa de que un huérfano por colisión de nombre no es "inerte" — ya rompió producción una vez.

Stub fail-open — riesgo silencioso

vigia/core/vigia_core_semiotic_detector.py

Cuatro líneas: analyze() retorna incondicionalmente {"alert_level": "NORMAL", ...} — un no-op hardcodeado. Está deprecado según pre_release_check.py, pero si alguna vez se cablea por accidente en lugar del detector real, no falla con error: deshabilita la detección semiótica en completo silencio.

El peor tipo de fósil — uno que falla cerrado hacia "todo normal" en vez de fallar ruidosamente.

Verificador legacy peligroso

vigia/cli.py

Implementa su propio verificador de cadena de hash (verify_manifest, verify_ledger) con un esquema simple, sin HMAC — exactamente el patrón "legacy v1" que CLAUDE.md dice que no debe usarse para investigaciones nuevas. No es forensics/verify_ebs_v1.py ni verify_tool_log.py (los canónicos). Alguien podría correrlo esperando protección HMAC equivalente y obtener un falso "verificado" sobre un bundle alterado.

Los 31 fósiles de alto riesgo, agrupados por subsistema

Ninguno es un archivo aislado — cada cluster representa una capacidad completa, con frecuencia testeada de forma aislada, que nunca llegó a conectarse al scorer o al bridge vivos.

Causal Closure Score — gate Daubert completo

4 archivos · tiene test unitario propio (tests/test_audit_gates.py) que nunca toca vigia_scorer.py
  • vigia/core/causal_closure.py
  • vigia/patterns/adversarial_silence.py
  • vigia/temporal/coherence_validator.py
  • vigia/core/explainable_governance.py

El gate matemático que CLAUDE.md describe como "cap en ABSTAIN por debajo del 50% de cierre causal" — construido, testeado, nunca invocado por el scorer real.

Verdict / governance sin cablear

6 archivos
  • vigia/core/config_sentinel.py
  • vigia/core/dissent_report.py
  • vigia/core/ockham_adversarial.py
  • vigia/core/peirceplanner_bounded.py
  • vigia/core/advanced_signal_router.py
  • vigia/core/narrative_auditor.py

Penalidad Ockham anti-SolarWinds, escalación por disenso minoritario, límite de Miller para abducción, detección de tampering de config — todos invariantes que CLAUDE.md describe como parte de la arquitectura viva.

Gates de calidad sin cablear

5 archivos · mismo patrón que el caso conocido de ayer
  • vigia/signal_quality_gate.py
  • vigia/core/signal_quality_gate.py
  • vigia/core/normalization_layer.py
  • vigia/scripts/ci_gate.py
  • vigia/tools/picerl_mapping.py

normalization_layer.py se autodocumenta como "el único checkpoint obligatorio" — calco textual del patrón signal_quality_gate.

Capacidades forenses construidas y no cableadas

4 archivos
  • vigia/forensics/temporal_forensics.py
  • vigia/forensics/pdf_dual_parser.py
  • vigia/forensics/rfc3161_chain.py
  • vigia/tools/entropy_locality.py

Detección de anacronismo/falsificación de fecha, comparación dual fitz/pypdf para evasión de PDF, timestamping RFC 3161 independiente, entropía espacial para esteganografía. Ninguno es un duplicado — cada uno es una capacidad ausente del pipeline vivo.

Pilar geopolítico de falsa bandera

2 archivos · ambas versiones muertas
  • vigia/tools/geopolitical.py
  • vigia/core/geopolitical_v2.py

Ambas cuelgan de vigia/vigia_core.py, un monolito legacy con cero importadores en todo el repo — el "Quinto Pilar" de atribución APT/falsa-bandera está completamente fuera de producción.

Detección semiótica duplicada / en duda

2 archivos
  • vigia/tools/pattern_detector.py
  • vigia/core/semiotic_detector.py

semiotic_detector.py es duplicado de semiotic_detector_v2.py, pero el estado "vivo" de v2 mismo depende solo de dev tooling y tests, no confirmado alcanzable desde vigia_scorer.py — vale una verificación adicional de ingeniería.

Pipeline de calibración huérfano

2 archivos
  • scripts/fit_calibration.py
  • vigia/tools/generate_calibration.py

La cadena generate_calibration.py → fit_calibration.py → calibrated_lr_models.pkl no es alcanzable desde ningún entry point ni CI. Vale confirmar si los .pkl que carga el LikelihoodEngine en producción realmente salieron de este código.

Núcleo teórico peirceano sin cablear

1 entrada de paquete → 93KB de contenido huérfano
  • vigia/abduction/__init__.py
  • ↳ hypothesis_lineage.py
  • ↳ vigia_artifact_graph.py
  • ↳ vigia_counter_fact.py

Cero importadores del namespace vigia.abduction en todo el repo. Es la maquinaria de linaje de hipótesis y contrafactuales — el corazón teórico peirceano del proyecto — completamente aislado.

Primitivas de seguridad sin cablear

1 archivo
  • vigia/security/vigia_seguridad.py

Su propio docstring ordena "importar desde vigia_sift_bridge.py; no dispersar lógica de seguridad" — el bridge nunca lo hace. Implementa una whitelist de subprocess sin equivalente confirmado en el sandbox vivo.

Tabla completa — los 112 archivos

Filtrable por categoría y buscable por ruta. Ordenada por prioridad de categoría y luego por antigüedad.

Archivo Última modificación Días Categoría Cluster Justificación

Metodología

1. Corrección del clon superficial

El repo llegó con git rev-parse --is-shallow-repository → true (52 commits, todos de la última semana). Con esa historia, git log -1 sobre cualquier archivo sin cambios desde antes del límite del shallow-clone devuelve la fecha del commit límite, no la fecha real — ocultando exactamente los fósiles que este barrido busca. Se corrió git fetch --unshallow antes de fechar nada; la historia completa retrocede hasta el 21 de abril.

2. Grafo de alcanzabilidad por imports

Se parseó con AST cada uno de los 402 .py trackeados, se construyó un grafo dirigido de imports (incluyendo imports relativos resueltos por paquete), y se calculó BFS desde los 5 entry points reales: vigia_agent.py, vigia/core/vigia_scorer.py, vigia/vigia_sift_bridge.py, vigia/tools/vigia_planner.py, vigia/pipeline/pipeline.py. Se corrigió un falso negativo: un __init__.py es cargado implícitamente por Python en cuanto cualquier submódulo del paquete se importa, así que se propagó alcanzabilidad a los __init__.py ancestros de todo módulo alcanzado.

3. CI y pytest

Se leyeron los 3 workflows (pytest.yml, vigia-forensic-ci.yml, docker-publish.yml). pytest.yml corre pytest tests/ vigia/tests/ sin --ignore, así que todo test_*.py bajo esos árboles se considera "conectado" por auto-discovery, incluso sin importador directo. Se detectó una referencia rota: vigia-forensic-ci.yml invoca tests/test_integration_end_to_end.py (ruta que no existe — el archivo real vive en tests/e2e/), con el fallo silenciado por || true.

4. Duplicados por nombre de archivo

Se agrupó cada basename no-__init__.py del repo; donde ≥2 rutas comparten nombre y al menos una está en el corte de 60 días, se verificó con diff -q si eran idénticas byte a byte, y se leyó el contenido de cada una para distinguir shim deliberado (p. ej. la capa engine/·forensics/· governance/·models/ en la raíz, confirmada como re-exports de una línea usados por el verificador EBS v1 independiente y aislados a propósito de vigia/) de duplicado muerto por deriva de código.

5. Clasificación de contenido (4 agentes en paralelo)

Los 68 archivos sin señal mecánica clara de "vivo" se repartieron en lotes de 17 para lectura de contenido — docstrings, imports internos, y cruce contra pre_release_check.py / BANNED_MODULES / docs/AUDITORIA_FUGA_INDIRECTA.md donde el propio repo ya documentaba el estado de un archivo. Un caso se reconciliyó entre lotes: vigia/memory/__init__.py se había marcado ALTO RIESGO independientemente hasta cruzar con el hallazgo de que su único contenido, case_pattern_library.py, es un duplicado superado confirmado — se reclasificó a BAJO RIESGO.

Limitaciones

  • El grafo de imports es estático — no ejecuta el código. Imports dinámicos vía importlib.import_module con nombre construido en runtime no se detectan (se buscó ese patrón específicamente en los entry points y no apareció).
  • "Fósil de alto riesgo" es un juicio de contenido, no solo de grafo — se basa en si el nombre/docstring/cita en CLAUDE.md sugiere un rol de decisión. Vale una segunda lectura de ingeniería antes de borrar cualquier ítem de esa categoría.
  • No se modificó, movió, ni eliminó ningún archivo — este es un mapa, no una limpieza.