Cómo funciona DocTracker
DocTracker es un radar que vigila la documentación de los servicios cloud que tu equipo ha homologado, y avisa cuando algo cambia. Esta página explica el proyecto desde cero — por qué existe, qué hace cada día, y cómo está construido.
El problema que resuelve
Tu equipo de plataforma homologa servicios cloud — Azure Foundry, Bedrock, AgentCore — produciendo dos cosas: una guía de servicio que explica cómo usarlo, y un módulo IaC (Terraform/Bicep) que lo despliega de forma aprobada. Eso queda congelado en una versión.
Mientras tanto, el proveedor cloud no para de cambiar la documentación: añade features, deprecia endpoints, modifica límites, introduce breaking changes silenciosos. Tu módulo homologado se desincroniza. Nadie se entera hasta que algo se rompe en producción.
Fig. 1 — el gap entre lo que tu equipo homologó y lo que el proveedor publica hoy
DocTracker cierra ese hueco. Cada día descarga las páginas relevantes del proveedor, las compara con la última versión vista, y si algo cambió, un LLM clasifica si es un breaking change, una nueva feature, o una corrección cosmética. Recibes un email con lo importante y un dashboard navegable con todo el historial.
Un día en la vida de DocTracker
Concretamente: esto es lo que pasa cada mañana sin que nadie tenga que hacer nada.
Fig. 2 — 5 minutos cada mañana, sin intervención humana
<new-feature> con relevancia high, y el equipo lo supo antes de las 09:10 — con un link directo al diff de GitHub.
El diagrama grande
Si solo te quedas con un diagrama, que sea este. Muestra los actores principales, qué dato cada uno produce, y dónde acaba almacenado.
Fig. 3 — el pipeline completo · azul = procesa · amarillo = coordina · verde = produce salida visible
Conceptos clave
Antes de entrar en el detalle del pipeline, estos son los términos que vas a ver en todas partes. Los he agrupado por familia.
Lo que se vigila
Lo que se persiste
relevance, category y un summary de 1-2 frases.Lo que se entrega
Lo que pasa después (homologación)
foundry-agents, bedrock). Es el "consumer" de DocTracker.El pipeline paso a paso
Mismo flujo que la Fig. 3, pero contado desde el dato. Sigue la pista a un solo cambio desde el momento en que el proveedor edita su web hasta que tu equipo lo lee.
Fig. 4 — el dato se transforma en 4 etapas: HTML → Markdown → Diff → PageAnalysis · y se entrega por 2 canales
Etapa 1 · Crawl (HTML → Markdown)
El Crawler arranca en la URL raíz del Watch Target y descubre páginas con BFS, siguiendo enlaces que comparten el mismo prefijo. Cada página descargada se convierte a markdown fiel — preserva código, tablas, listas. No se le pide al LLM que reorganice nada: la fidelidad es la garantía de que el diff de mañana sea reproducible (ver ADR-0004).
Etapa 2 · Snapshot (markdown → git)
El markdown se guarda como un fichero en snapshots/<target>/<hash>.txt y se commitea. Git es la base de datos — no hay otra. Esto da auditoría gratis: cada cambio tiene un commit, cada commit tiene un diff visible en GitHub, y los links del dashboard apuntan ahí directamente (ver ADR-0002).
Etapa 3 · Diff (snapshot N vs N-1)
El Differ compara la versión recién escrita contra la anterior en git. Produce un objeto Change por página afectada — New Page si no existía antes, Modified Page si su contenido difiere. Las páginas que desaparecen del crawl no se reportan como borradas: es ruido (un timeout temporal del proveedor no debe disparar un email).
Etapa 4 · Análisis (diff → PageAnalysis)
Por cada Change, una llamada al LLM con todos los SectionDiffs de esa página en un solo prompt. El modelo devuelve tres campos cerrados: relevance, category, summary. El resultado se persiste en analyses/YYYY-MM-DD.json.
Si el LLM falla, el pipeline continúa sin análisis y el email sale igual (con los cambios pero sin clasificar). Esto se llama "silent degradation" y es intencional — la detección de cambios nunca debe depender de un servicio externo de IA.
Etapa 5 · Salida (sitio + email)
El Site Generator regenera site/index.html y lo commitea — GitHub Pages lo publica automáticamente. El Notifier envía el Change Report por email solo si hay cambios. Cero spam los días tranquilos.
Cómo se clasifica un cambio
El Analyzer asigna dos ejes a cada Change: severidad (qué tan urgente es) y categoría (qué tipo de cambio es). Los enums son cerrados — el LLM no puede inventarse categorías nuevas.
Fig. 5 — matriz de clasificación · severidad × categoría (8 categorías cerradas)
Esta matriz no se hardcodea — es el LLM el que decide. Lo que está fijado es el conjunto de valores posibles. Esto permite que el Analyzer "razone" sobre cada cambio sin que se invente etiquetas, y que el dashboard pueda agrupar/filtrar de forma estable.
Framework + consumer repos
DocTracker no vive en el mismo repo que tus servicios homologados. Es un framework: el motor (crawler, differ, analyzer, generador de sitio) vive en este repositorio, y se inicializa en un consumer repo dedicado por cada servicio.
Fig. 6 — un motor · N consumer repos, cada uno con su servicio y sus artefactos
¿Por qué esta separación? Porque cada servicio homologado tiene su propio ciclo de vida, su propio equipo dueño, y su propio módulo IaC versionado. Mezclarlos en un monorepo acopla cosas que no deberían estar acopladas (ver ADR-0005).
Dónde corre todo esto
Cero infra propia. Todo en GitHub.
- Cron diario en
.github/workflows/crawl.yml— dispara el Orchestrator - Cron semanal en
.github/workflows/digest.yml— dispara el Synthesizer (resumen weekly) - Snapshots y analyses los commitea el runner de Actions de vuelta al repo — git como log append-only
- GitHub Pages sirve
site/index.htmldesdemaster - LLM endpoint es OpenAI-compatible — puede ser DeepSeek, ollama local, o cualquier provider
El coste de operar DocTracker es esencialmente la factura del LLM, y solo de las páginas que cambiaron — no de todas las que se descargaron.
Tres decisiones clave
Si quieres entender el "por qué" detrás del diseño, lee estas tres. Las demás están en el apéndice.
Snapshots se guardan como markdown fiel al HTML original. El LLM corre después — analiza los diffs, no los produce. Si dejáramos que el LLM "limpiara" o "reorganizara" el snapshot, mañana el mismo HTML produciría un snapshot distinto (no-determinismo), y el diff sería falso.
Esta separación es la garantía de que un Change en el report = un cambio real en el origen.
Cero servidor, cero base de datos, cero infra. Cada snapshot es un commit; cada diff es una URL permanente de GitHub; el dashboard es HTML estático regenerado en cada run. Coste de hosting: 0€.
El precio es que no hay búsqueda full-text ni alertas en tiempo real — pero el caso de uso primario es "qué cambió", no "qué dice el doc", así que está bien.
El motor vive aquí; los servicios homologados viven en repos separados que se inicializan con docstracker init. Cada equipo es dueño de su consumer repo y pinea la versión del framework que quiere usar.
La alternativa (todo en un monorepo) acoplaría ciclos de vida que en realidad son independientes.
Preguntas que te estarás haciendo
¿Por qué no usar webhooks del proveedor?
Porque los proveedores cloud no los ofrecen para cambios en documentación. Sus docs son un sitio web; no hay API para subscribirse a cambios. Crawl diario es lo que hay.
¿Por qué un LLM y no regex / heurísticas?
Porque clasificar "este cambio es un breaking change" requiere entender semántica. Una regex puede detectar que cambió la palabra "deprecated", pero no puede distinguir entre una deprecación seria y una nota informativa sobre algo deprecado hace años. El LLM razona; la regex no.
Eso sí: la detección del cambio (el diff) es determinista. El LLM solo entra a etiquetarlo.
¿Por qué cron diario y no en tiempo real?
Porque (a) los cambios en docs no son urgentes en escala de minutos — un breaking change en docs hoy se traduce en código mañana en el mejor caso, y (b) crawlear continuamente sería tirarse piedras al tejado del proveedor y a la factura del LLM. Una vez al día es el sweet spot.
¿Por qué snapshots en git y no en una BD?
Tres razones: (1) git ya hace diff e historial gratis, (2) cada cambio es auditable con un commit y un autor, (3) cero infra — sin BD que mantener, escalar o backupear. La pega es que git no escala a millones de snapshots, pero para el orden de magnitud de DocTracker (cientos de páginas por target, decenas de targets) sobra.
¿Por qué dos modelos (analyzer + synthesizer)?
Tienen perfiles de coste/calidad opuestos. El analyzer corre cada día, una vez por página cambiada → muchas llamadas pequeñas, sensible a latencia y coste → modelo barato y rápido. El synthesizer corre cada semana, una vez → pocas llamadas con contexto grande, sensible a calidad → modelo top.
Forzar un solo modelo te hace pagar de más a diario o quedarte corto los lunes (ver ADR-0006).
¿Qué pasa si el LLM se cae?
Silent degradation: el pipeline sigue, el email se envía con los cambios pero sin clasificación, el dashboard muestra los cambios sin etiqueta. Detectar cambios es la función crítica; clasificarlos es opcional.
Apéndice A · Módulos en src/docstracker/
| Módulo | Fichero | Responsabilidad |
|---|---|---|
| Orchestrator | orchestrator.py | Entry point — secuencia crawl → diff → analyze → site → notify. |
| Crawler | crawler.py | BFS desde la raíz del Watch Target; descarga; extrae secciones markdown. |
| Differ | differ.py | Compara snapshot actual vs anterior; produce objetos Change. |
| Analyzer | analyzer.py | Llamada al LLM por cada Change; produce PageAnalysis. |
| Synthesizer | synthesizer.py | Resumen semanal/on-demand; Target Overviews; Weekly Digests. |
| Site Generator | site_generator.py | Genera site/index.html estático. |
| Notifier | notifier.py | Envía Change Report email (solo si hay cambios). |
| Snapshot Store | snapshot_store.py | Lectura/escritura de snapshots en git. |
| Analysis Store | analysis_store.py | Lectura/escritura de analyses/YYYY-MM-DD.json. |
| Config Loader | config_loader.py | Parser de config.yaml (Watch Targets + bloques LLM). |
| Watch Targets | watch_targets.py | Lógica de selección y filtrado de Watch Targets activos. |
| Change Report | change_report.py | Composición del email a partir de Changes + PageAnalysis. |
| Models | models.py | Tipos de dominio (Page, Snapshot, Change, PageAnalysis…). |
| Replay | replay.py | Re-ejecuta análisis sobre crawls históricos sin re-crawlear. |
| CLI | cli.py | Argumentos y subcomandos (docstracker init, run, replay…). |
| MCP Server | mcp_server.py | Expone snapshots y analyses vía Model Context Protocol para agentes IA. |
Apéndice B · Todos los ADRs
Los 3 más importantes están explicados arriba (decisiones clave). Aquí están los 6 completos para referencia.
Contenido extraído como markdown (prose, código, tablas, listas) agrupado por sección. Preserva código y tablas que el extractor anterior (solo <p>) descartaba silenciosamente. La granularidad por sección mantiene el Change Report legible: diffs por heading, no por línea de la página entera.
Sitio estático servido por GitHub Pages desde site/ en master, estilo S3 Terminal Mono (monospace, denso, links directos al diff de GitHub). Decisión guiada por la query primaria: "qué cambió recientemente", no "qué dice el doc sobre X".
Cómo gestionar Watch Targets y entrega de notificaciones cuando crezca la base de usuarios. Decisión diferida — depende de ADR-0002 y de si la base de lectores supera al usuario configurador único.
Snapshots permanecen deterministas y fieles. La capa IA corre post-crawl, downstream de snapshot writes, y nunca realimenta change detection. Granularidad: por Page. Output: relevance + category (enum cerrado) + summary. Persistido en analyses/YYYY-MM-DD.json. Failure: silent degradation.
DocTracker como framework, inicializado en consumer repos vía docstracker init. El framework provee el motor; cada consumer es dueño de config, Homologated Module, Service Guide, Verification Suite y Snapshots. Versión pineada en cada consumer.
Dos bloques en config.yaml — analyzer (rápido y barato, corre cada día por Page) y synthesizer (calidad, corre semanalmente para overviews y digests). Forzar un solo modelo encarece el análisis diario o degrada la síntesis semanal.