Skip to content

El pipeline de datos ​

Futuros no consulta APIs en vivo cuando alguien abre una página. Todo el corpus se ingiere, se precalcula y se congela como JSON estático antes del despliegue; la SPA solo lee archivos. Eso hace la plataforma rápida, cacheable, offline-capaz, y, sobre todo, auditable: cada cifra que ves fue verificada en tiempo de build contra su registro de fuente. Este pipeline sirve directamente al Pilar 1 — Fideicomiso de Datos: un corpus con procedencia y licencia limpia es el único activo que un modelo regional no puede copiar.

Transportes meta: una API, muchas agencias ​

Dos conectores no traen una fuente sino decenas, y por eso rinden más por línea de código que cualquier adaptador individual:

  • DBnomics (makeDbnomicsAdapter), 93 proveedores tras una sola API abierta; es el transporte de FMI WEO, UNCTAD y OMC.
  • World Bank Data360 (scripts/sources/gap/data360.ts), ~35 bases estadísticas del Banco y de agencias asociadas (UIT, WIPO, WEF, Yale) tras una API sin clave con código ISO3 en cada fila. El motor descarga primero el CSV masivo de cada base (data360files.worldbank.org, sin límite de tasa) y lo filtra en streaming; solo cae a la API paginada cuando el archivo supera el tope de tamaño. La API responde con HTTP 417 cuando limita el tráfico, así que el motor lleva su propio backoff de 3 a 60 segundos.

Dos disciplinas que estos transportes imponen y que valen para cualquier fuente nueva:

  • Fijar los miembros de cada dimensión. Una misma serie puede traer el presupuesto aprobado y el ejecutado, en moneda local y como porcentaje del gasto, bajo un único código. Si no se fija la dimensión, la serie mezcla dos cosas distintas sin avisar. Los codelists se leen del endpoint /portal/v1/dimensions, no se adivinan.
  • Cortar las series de flujo en el último año completo. Los agregados anuales de aprobaciones (Banco Mundial, CFI, MIGA) tendrían un año parcial al final, y la celda lo leería como un desplome. Se corta en el último año calendario cerrado. GCF: solo años calendario completos; el año en curso no se publica como dato comparable. La cuota renovable SDG 7.2.1 vive en eg_fec_rnew_zs vía UN SDG (EG_FEC_RNEW); el gemelo CEPALSTAT 3883 (cepal_energia_renovable) se retira en la ingesta. Intensidad energética SDG 7.3.1 (EG_EGY_PRIM) alimenta se4all_energy_intensity (MJ/US$ PPA 2021).

El registro de fuentes ​

Cada fuente externa es un SourceRecord declarado en scripts/sources/registry/. Define de dónde sale el dato y bajo qué reglas puede publicarse:

  • AdapterEngine: el tipo de conector: series, bulk, sdmx, ckan, ocds, event, entity, microdata, provider o meta. Cada API externa tiene un solo adaptador en adapters.ts (WHO GHO, UNESCO UIS, OpenAlex, Climate Watch, UN SDG, UNHCR, OWID, IMF DataMapper, SDMX/DBnomics genérico…). Las fuentes añadidas por olas posteriores viven en scripts/sources/gap/, un archivo por fuente, y entran a ALL_ADAPTERS como un solo ...GAP_ADAPTERS: añadir una fuente es un archivo nuevo más una línea de import, nunca una edición dentro del adapters.ts de 4.400 líneas. La ola E lleva ese mismo principio un nivel más abajo: sus adaptadores viven en scripts/sources/gap/wave-e/<fuente>.ts con su propio índice, porque la ola D había concentrado sus 19 fuentes en un solo gap/index.ts y eso recreaba, un nivel más adentro, el punto de colisión que este diseño existe para evitar, cada persona que añade una fuente quiere la última línea del mismo archivo. La ola F (granularidad plena, wave-f/ + src/lib/gap-sources-f/) usa el mismo corte: un archivo por fuente para emaciación JME, matrícula terciaria UIS, nutrición DHS, espacio cívico CIVICUS, relleno Ember/IRENA, y sondas de hueco honesto (RICYT, ILIA/GIRAI/GDB, INE.Stat salud/innovación de Chile, ENDI Ecuador) que re-verifican y devuelven [] en vez de inventar un número. scripts/refresh-wave-f.ts refresca solo las series que producen valores (mensual). Con el corte por archivo, dos personas que añaden dos fuentes tocan dos archivos distintos. scripts/sources/gap/wave-e/probe-file.ts completa el patrón: ejecuta los adaptadores de un módulo sin escribir nada, de modo que una fuente se construye y se verifica entera antes de que su línea entre en ningún índice compartido. La ola H (wave-h/ + src/lib/gap-sources-h/) sigue el mismo corte. Su primera fuente es el registro de préstamos soberanos del China Development Bank y del Export-Import Bank of China compilado por Myers y Gallagher (Boston University Global Development Policy Center e Inter-American Dialogue): acervo de compromisos, no deuda pendiente, con dirección neutral, así que no entra al índice compuesto. La segunda fuente es NOAA NCEI IBTrACS v04r01: ciclones tropicales únicos por temporada completa desde 1980, asignados a los 25 países con las costas de Natural Earth. Es un conteo de amenaza y queda neutral en el índice compuesto. La tercera fuente son las estadísticas de turismo receptor de ONU Turismo (llegadas y recepciones, socio Mundo): niveles absolutos que crecen con el tamaño del país, así que quedan neutrales en el índice compuesto, y el país cuyo dato más reciente queda fuera del umbral de vigencia se omite en lugar de imputarse. La cuarta fuente son las estadísticas del BIS (precios reales de vivienda residencial, crédito total y a hogares como % del PIB, brecha crédito/PIB): solo publican seis de los 25 países (Argentina, Brasil, Chile, Colombia, México y Perú; Argentina sin precios de vivienda, Perú sin crédito), el resto se omite y no se imputa, y quedan neutrales en el índice compuesto. La quinta fuente es IATI vía d-portal (Secretaría de IATI): desembolsos y compromisos en USD corrientes por país receptor y año calendario completo, ponderados por el porcentaje de país declarado en cada actividad, más una serie de desembolsos que excluye nueve bancos multilaterales de desarrollo; los flujos intermediados pueden contarse dos veces y la cobertura de publicadores creció con el tiempo; el año en curso se omite, un año faltante es un hueco y no un cero; quedan neutrales en el índice compuesto. GIRAI 2026 (2.ª edición): el overall (girai_rai_index) se lee del snapshot publisher scripts/sources/gap/wave-e/data/girai-2026-rankings.json (extraído de GIRAI_rankings_and_scores.xlsx en la biblioteca enlazada desde global-index.ai); las dimensiones 2024 de gobernanza/DDHH se conservan sin reescalar porque la 2.ª edición no tiene homólogo 1:1 y las ediciones no son comparables. GDELT protest intensity post-2019: bake-gdelt-protest-annual.ts fusiona años nuevos en gdelt-protest-intensity-2020-2024.json (sin pisar filas previas) y solo publica un año calendario con ≥330 zips diarios.
  • LicensePosture: kind / redistribute / aggregates_only: qué se puede republicar y qué solo se puede citar.
  • Verificación por sonda: sources.ts es SOURCE_REGISTRY_V2, con endpoints probados; regulation-sources.ts es el registro hermano para el rastreo de leyes de IA. Los nso/*.json son 27 listas de fuentes por oficina nacional de estadística.
  • Anatomía de verificación: cada registro declara además su deep_link (la URL por-registro que se estampa en citations.json), su cadencia (cadence_days), un spec verify de re-verificación por muestreo (refetch-compare: volver a pedir una muestra y comparar contra lo guardado) y un expected_volume dimensionado para un centinela de volumen de ±50%. Ojo: el centinela y el re-verificador son hoy contrato declarado del esquema, ningún ingestor los ejecuta todavía; el guardián que sí corre en cada refresco es el guardián de frescura.

El esquema completo de un SourceRecord (scripts/sources/registry/schema.ts), cada campo con la regla que validateRegistry() le aplica:

CampoTipo / valoresRegla en validateRegistry()
keyUPPER_SNAKE estableregex ^[A-Z0-9_]+$; duplicado o inválido = error
tier · priority · statusprimary/secondary/curated · P0/P1/P2 · wired/ready/blocked/researchel tick ingest:due del daemon drena filas ready
engineuno de los 10 AdapterEnginemotor desconocido = error
licensekind (open/cc-by/non-commercial/share-alike/cite-only) + redistribute + aggregates_only?la compuerta de licencia (abajo); aggregates_only + motor microdata = warn recordatorio: el adaptador agrega, nunca vuelca filas
authnone/env_key/registration/approval + envenv = variable de entorno que lee el adaptador
endpointpatrón URL con placeholders {iso3}/{code}faltante = error
deep_linkpatrón URL por-registroes lo que se estampa en citations.json
cadence_daysnúmeroel ledger de frescura compara contra esto
geos · geo_levelISO3[] o "ALL" · country/adm1/adm2/point/supranationalgeo fuera del set de 26 (25 países + LATAM) = warn
pillarsslugs de dimensiónvacío con motor ≠ provider = warn
expected_volumenúmero ≥ 0negativo o no-numérico = error; dimensiona el centinela ±50%
verifysample_n + method (refetch-compare/liveness-only/checksum)bloque faltante o sample_n < 0 = error

Los adaptadores están aislados por fallo: un endpoint muerto devuelve [] en vez de romper la ingesta. La cadencia (cada cuántos meses se refresca cada indicador) vive junto a la fuente y se materializa en el ledger de frescura.

La compuerta de licencia (en compilación) ​

La regla más importante del registro no es de runtime: es de compilación. validateRegistry() rechaza cualquier fuente cuya licencia diga redistribute: false pero esté cableada a un motor que no sea provider. La condición exacta es triple: redistribute === false y motor ≠ provider y status === "wired": una fuente cite-only puede quedar declarada como research o blocked sin romper el build; lo que no puede es estar cableada a un motor que produce valores. Republicar valores de una fuente que solo permite citar es un error de compilación (assertRegistryValid() lanza con la lista de claves ofensoras), no un aviso que alguien podría ignorar. La postura de licencia de una fuente decide si sus datos aparecen como indicador republicado, solo como agregado, o solo como cita, y lo hace antes de que el build produzca un solo archivo.

Esta misma compuerta aplica a los datos aportados por terceros vía /contribuir: nada se redistribuye más allá de lo que su licencia permite. Es el mecanismo que mantiene honesto al Fideicomiso de Datos.

Las etapas de ingesta ​

scripts/ingest-all.ts orquesta tres etapas, cada una aislada por fallo (una etapa que falla no arrastra a las otras):

  1. runMultisource() → celdas de parameter-cache/ + citas. Es el grueso: el valor de cada indicador por país, con su citation_id. Cuando dos adaptadores emiten el mismo id, mergeSeriesByYear une por año: el overlay curado gana en cada año compartido y se conserva el historial que el overlay no cubre. FORCE_REPLACE_IDS no entra en esa unión: esos ids se sustituyen enteros cuando cambia la metodología o el tip debe retroceder.
  2. runDocuments() → documents/ y news/: documentos y noticias por eje y país.
  3. runSignals() → señales citadas, extraídas por LLM, en signals/.

Al terminar, escribe refresh-meta.json. Hay ingestores de dominio adicionales para presupuesto abierto, libertad económica, libertad digital, deriva autoritaria, OGP, libertad humana, libertad de prensa, regulación, observatorio, el nivel subnacional ADM1 y el grano ciudad (ingest:city: determinista y sin red, valores verificados dos veces contra los endpoints oficiales citados y fijados como constantes con su fórmula declarada, el mismo patrón que el ingestor de homicidios ADM1).

Existe además una cuarta vía cadenciada: el pulso social. ingest-social.ts (eventos GDELT + GKG + RSS social) y fetch-trends.ts alimentan las familias social/, social-trends/, attention/ y civic/, con sus propios precálculos (bake-trends, bake-civic, bake-attention). Aparece en refresh-meta.json como la fuente social:gdelt, con conteo de posts.

Y una vía determinista de señales: observación de la Tierra. bake-earthobs-signals.ts (sin LLM y sin claves) consulta dos fuentes. Climate TRACE v7: emisiones mensuales de GEI por país para las 25 geografías, en un único GET por mes cuyo URL codifica el mes consultado; licencia CC BY 4.0, con el inventario de registro en Zenodo (DOI conceptual 10.5281/zenodo.15014790). Y TerraBrasilis del INPE (CC-BY-SA 4.0): DETER (avisos mensuales agregados de deforestación y minería en la Amazonía, updated_date como fecha del último aviso; se señala el último mes completo porque el mes corriente siempre es parcial) y PRODES (tasa anual consolidada de la Amazonía Legal). Emite señales de tipo earth_obs en signals/medio-ambiente-clima__<ISO3>.json, junto a las señales LLM de la celda pero con otro contrato de procedencia (ver Procedencia). Reglas de honestidad: cada señal cita el GET exacto que produjo el valor; un país ausente o en cero en la respuesta se omite, nunca se inventa; una fuente caída deja sus celdas intactas (el upsert reemplaza por fuente, no por celda); solo estadísticas agregadas, sin imágenes, sin geometrías, sin tasking satelital ni reventa de imaginería. Corre en el tier docs después de la poda de señales, que le aplica además un tope de antigüedad por fuente para que un upstream caído no deje su última lectura en pantalla indefinidamente.

Y una quinta vía: alta frecuencia (HF). ingest-hf.ts corre NASA POWER diario (un punto capital por ISO3) y, en la misma corrida, adapters de FIRMS, ClimateSERV CHIRPS, Ember mensual, Wikimedia, GHS, FAO ASIS, GFW y Meta Movement. Desde el 30 de septiembre de 2026 también están registrados, y siguen sin publicarse, siete feeds de comportamiento: Banxico SIE (remesas mensuales y SPEI diario, más el CSV de FIX, tasa objetivo, TIIE 28 y reservas), precios de alimentos del PMA vía HDX, demanda eléctrica mensual de CAMMESA (espejo SSPM), órdenes de compra mensuales de ChileCompra, mezcla mensual de generación de Ember (las acciones renovables, fósil y carbón; la generación total sigue en EMBER_MONTHLY) y el archivo diario de OpenAQ (promedio país de PM2.5 y PM10). El 15 de octubre de 2026 se sumaron, también sin publicar: ONS Brasil (carga y mezcla del SIN, más EAR/ENA por subsistema; CC BY 4.0, diario, BRA), conteos diarios de Mercado Público Chile (API ChileCompra, CHL; complementa el ZIP mensual CHILECOMPRA_OC), dengue semanal de la OPS/PAHO (YTD por país al cierre de la semana epidemiológica; CC BY-NC 3.0 IGO), remesas mensuales de bancos centrales de GTM/SLV/HND/DOM, y columnas diarias de CO y NO2 de Sentinel-5P TROPOMI (espejo AWS meeo-s5p, 25 geos). La regla de publicación es estricta: solo nasa-power.json se commitea y se sirve. Un almacén vacío (observations: []) o un feed sin filas publicables no se escribe bajo public/data/hf-observations/: un 200 hueco es un defecto, no un “próximamente”. check-hf-publish.ts bloquea el build si aparece cualquier JSON extra o un POWER vacío. POWER no entra en parameter-cache ni en observations.csv (ese CSV ya rebasa el tope de 100 MB de GitHub). El lane hf no mueve generated_at. No se listan en la superficie pública de Fuentes: el mismo criterio que las olas E/F. No se adaptan Advan, Veraset, Spectus, rasters de luz nocturna, Open-Meteo, rastros de dispositivos, GDB, GIRAI ni el lakehouse B0.5.

Cadencia recurrente ​

Desde agosto de 2026 el refresco ya no depende de corridas manuales. La cadencia vivió primero en GitHub Actions; desde octubre de 2026 corre en la VPS (ops/vps/, timers systemd --user de build) con los mismos scripts, la misma compuerta verde (prebuild completo → PR rodante bot/<…> → squash-merge automático; nunca un push directo a main). Un fallo dispara ops/vps/autofix.sh (worker vía orch dispatch). Detalle operativo: ops/vps/README.md § «Scheduled jobs».

Job / tier (VPS; antes workflow)Cadencia (UTC)Qué refresca
social-pulse-ingestcada 2 horas (:17)pulso social, cívico y de atención (GDELT + Wikimedia + RSS, sin clave)
data-refresh · docslun/mié/vie 08:41corpus de noticias y documentos (GDELT + RSS + gacetas sin clave; Exa si su clave existe) + extracción de señales citadas (si ANTHROPIC_API_KEY está configurada; si no, ingesta de docs sin señales LLM) + poda de señales huérfanas + señales earth-obs deterministas (Climate TRACE + INPE DETER/PRODES, sin LLM ni clave) + snapshot Polymarket
data-refresh · derivedmartes 08:41sonda de frontera WB (recheck-wb-freshness) + precálculos derivados que rezagan de sus insumos: puntajes compuestos, resiliencia democrática, screener de mercados, brief de pulso, manifiesto _index.json
data-refresh · corpusdía 1 09:41corpus numérico completo: Banco Mundial/WGI (refresh-from-live-apis) + series multisource (ingest-all --skip-docs --skip-signals) + shards de citas + todos los derivados + ingesta y precálculo del observatorio regulatorio (ingest-regulation → bake-regulation)
data-refresh-hf · dailycada día 06:17NASA POWER (punto capital) + FIRMS (bbox; FIRMS_MAP_KEY solo si la clave existe) + CHIRPS (aislado; HF_SKIP_CHIRPS=1 lo sostiene) + GFW alertas integradas (GFW_API_KEY opcional; sin clave usa download/csv)
data-refresh-hf · emberdías 1 y 15 07:17Ember mensual, PASS-PARTIAL (12 ISO3 en el CSV; no se inventan celdas)
data-refresh-hf · wikidías 3–5 08:17Wikimedia pageviews top-by-country (ISO2→ISO3; CU/HN/NI/VE ausentes en 2026-07 se omiten)
data-refresh-hf · ghsdía 1 09:17JRC GHS-COUNTRY-STATS (superficie construida km², épocas 1975–2030; el ZIP no se commitea)
data-refresh-hf · asisdía 1 10:17FAO ASIS anual nacional (PROVINCE=ALL; 403 a UA de Python, 200 con UA de navegador)
data-refresh-hf · metadías 1 y 15 11:17Meta Movement Distribution (HDX; rollup país×día; CSV crudo 48–195 MB se descarta)
ingest-hf.ts · banxico / wfp / cammesa / chilecompra / openaq / ember (mix) / ons / procurement / paho / remittances / s5pmanual, hasta que el job VPS los adopteBanxico SIE, precios PMA-HDX, demanda CAMMESA, OC ChileCompra, archivo OpenAQ, mezcla Ember, ONS (carga + embalses), Mercado Público CL (conteos diarios), dengue PAHO semanal, remesas CA y Sentinel-5P CO/NO2. Se ingieren y no se escriben: la compuerta de publicación sigue siendo solo NASA POWER.

Las corridas del tier corpus van envueltas en el guardián de frescura (snapshot → repair) y en INGEST_GUARD=1, de modo que un adapter estrangulado no puede borrar celdas en silencio. Los lanes con clave de pago (Exa, Voyage, señales LLM vía ANTHROPIC_API_KEY) y los índices anuales que requieren un bump de URL por edición (V-Dem, RSF, HFI, WEO oct/abr) siguen siendo corridas manuales deliberadas cuando la clave falta, el detalle vive en la auditoría DATA_AUDIT_2026-08-22.md del repo.

Ola de tecnologías de frontera (2026-08, re-horneada sobre main en 2026-10). Ciento diecisiete registros verificados por sonda viven en scripts/sources/registry/frontier-sources.ts (unidos a SOURCE_REGISTRY_V2) y en src/lib/gap-sources-frontier/ (metadatos y prefijos de cita, sin hinchar source-registry.ts). Treinta y nueve adaptadores en scripts/sources/frontier/ se dispersan en ALL_ADAPTERS; la ingesta bun run scripts/ingest-multisource.ts --only=frontier los corre y bake-frontier-indicators.ts lee solo celdas citadas del parameter-cache. El flag snapshot?: true en IndicatorSpec marca stocks contados una vez (reactores IAEA, estaciones SatNOGS, ROR, DOAJ…): sin él, el guardia de «≥2 puntos» de flows anuales borraba el valor y el run seguía reportando éxito. Un cero que la fuente reporta se publica como sourced-zero citado; un país ausente del payload se omite; un fetch fallido nunca se escribe como cero. El detalle y las siete ausencias medidas están en Inteligencia de Frontera y en state/FRONTIER_CORPUS_SHIPPED.md.

Ampliaciones de corpus (2026-08-24). Sobre los adaptadores de las olas E/F ya cableados, se precalcularon series nuevas y verificadas en vivo: expansión CEPALSTAT (femicidios en número, maternidad adolescente, participación laboral, ocupación carcelaria, primaria completa 15–19); la tasa de política monetaria del BPI (BIS_CBPOL / WS_CBPOL) para los seis bancos centrales de ALC que publican la serie; profundidad WHO GHO (MCV2, hipertensión, cobertura ART) y, en el adaptador GHO cableado, densidad de médicos who_physicians_density (GHO HWF_0001, distinta de las series WB/OWID); UNESCO UIS (finalización secundaria, alfabetización adulta); RSF y ND-GAIN llevados a parameter-cache (libertad de prensa 2013–2026 e índice/vulnerabilidad/preparación climática 1995–2024); DesInventar/Sendai (DESINVENTAR) con el conteo anual de eventos de desastre reportados para 22 geografías (BRA/CUB/SUR sin perfil publicado, omitidos); Freedom House FIW (FIW) con Total/PR/CL 2013-2024 para las 25 geografías; lactancia materna exclusiva UNICEF (NT_BF_EXBF) sobre el adaptador SDMX ya cableado; inseguridad alimentaria severa OWID/FAO; anomalía térmica CMIP6 SSP2-4.5 vía CCKP (cuatro ventanas 2020-2099); e iniciativas STI iniciadas (OCDE STIP) para siete geografías del panel. Cada cifra lleva cita con deep-link al registro, CSV o ZIP concreto; los países sin serie se omiten, nunca se imputan.

El precálculo (bake) ​

scripts/bake-all.ts transforma el cache crudo en los datasets derivados que consumen las páginas: narrativas → indicadores de frontera → subnacional → briefs y contradicciones de pulso → pronósticos → shadows → grafo de personas → puntajes compuestos → índice de mercados → _index.json. Son 44 scripts bake-*.ts más 10 generadores build-*: 54 scripts de precálculo y generación: puntajes, mercados, V-Dem, exploración, insights, salud del dato, señales, coaliciones, ontología, tendencias, cívica, atención, feeds, contribuciones, soberanía de cómputo, convergencia regional, nowcast de inestabilidad, triangulación, narrativas y grafo de personas, más generadores como build-democracy-index y build-search-index.

explore/, insights/ y ontology/ se vuelven a hornear en cada refresco programado de datos (tier corpus de data-refresh.yml, junto a split-citations.ts).

Dos generadores de dominio indexan familias que ya viste en la lista de datasets: build-cases-index.ts regenera el _index.json de cases/ (los precedentes y aprendizajes por país, autorados archivo a archivo) y gen-elections-index.mjs el de elections/ (el calendario electoral regional).

Ningún hecho queda incrustado dentro de un modelo: los hechos viven en los datos con su cita determinista, y el precálculo solo calcula lo que se desprende de ellos.

La API pública precalculada ​

El pipeline tiene un cuarto producto además de los datos de la SPA: una API estática de solo lectura en public/api/v1, regenerada en cada despliegue por bake-api.ts: citations.csv/.json, freshness.json, contributions.json, futuros-data.xlsx y registros por país y por indicador con interoperabilidad M49/SDMX. Tiene su propia compuerta: check-api.ts corre después del precálculo y verifica que la API espeja los datos actuales (por ejemplo, que el conteo de citas de su índice coincide con citations.json). Por construcción, la API nunca queda desfasada del corpus. Referencia completa en Public API.

Las familias de datasets (public/data/**) ​

El resultado son 69 familias de datos estáticos. Entre las principales:

  • parameters.json (los 10 ejes), geographies.json (25 países + LATAM, con ISO3; los códigos M49 no viven aquí, se unen desde un mapeo ISO3→M49 propio de bake-api.ts al precalcular la API pública).

  • parameter-cache/ (celdas indicador×país) y las tres capas subnacionales: parameter-cache-adm1/ (miles de celdas ADM1), parameter-cache-adm1-series/ (sus series temporales) y parameter-cache-adm1-universal/ (la superposición universal GDL (esperanza de vida, INB per cápita, escolaridad esperada) que cubre unidades donde los ejes no llegan).

  • Vintages de seguridad ADM1 (2025, lane subnacional): INEGI defunciones por homicidio 2025 preliminar (Tabla 2, tasa nacional 21,4 /100k) en seguridad__MEX__*; FBSP 20º Anuário 2026 MVI 2025 (nacional 19,11) en seguridad__BRA__*; Policía Nacional homicidio intencional 2025 ÷ proyecciones DANE 2025 en seguridad__COL__*; SNIC tasa de víctimas 2025 en seguridad__ARG__* (reemplaza celdas sintéticas); AECA Anuario 2025 Tabla 2.3 conteos departamentales ÷ población ADM1 en seguridad__URY__* (nacional 10,3; homicidios carcelarios no se imputan a departamentos).

  • Infraestructura ADM1 MEX (ENDUTIH 2025): personas usuarias de internet (%) por entidad desde la presentación INEGI ENDUTIH 2025 (nacional 86,1 %) en infraestructura__MEX__*, vía scripts/ingest-adm1-homicide.ts --only=ENDUTIH-MEX (reemplaza sintéticos anclados a WB IT.NET.USER.ZS).

  • Series ADM1 de comportamiento (2026-10-15, lote 03): Anatel (accesos BLF/SMP y densidad por UF, mensual/anual, BRA, datos abiertos CC BY); BCB Pix (transacciones por pagador PF al mes, mensual agregado a año completo, BRA, ODbL); SII Chile (empresas y participación femenina por región, anual, CHL, abierto); Conab (rendimiento/producción/área de granos por UF, mensual en temporada, BRA, cite-only con atribución); CNJ Justiça em Números (congestión/IAD por tribunal estatal, anual, BRA, LAI; complementa CNJ DataJud); INEGI ENOE microdatos (TIL1 de informalidad laboral por entidad, trimestral, MEX, Términos de Libre Uso; preferido sobre ENOE BIE). Cada celda cita el ZIP o registro concreto; sin imputación.

  • Series ADM1 y país de comportamiento (2026-10-15, lote 04): ICFES Saber 11 (media departamental de punt_global, calendario A 2014-2022, COL, CC BY-SA 4.0 vía datos.gov.co; familia parameter-cache-adm1-series-icfes/); IMSS asegurados (puestos, asegurados, SBC y eventual por entidad, mensual, MEX, LIBRE USO MX; familia hermana parameter-cache-adm1-series-icfes/); PNCP Pregão Eletrônico (conteos anuales por UF, BRA, datos abiertos); MapBiomas LULC (vegetación nativa por ADM1, anual, BRA/ARG/CHL, CC BY 4.0); INPE PRODES (deforestación anual Amazonia Legal y Cerrado por UF, km², BRA, CC BY-SA TerraBrasilis; celdas *__inpe_prodes.json en parameter-cache-adm1-series-icfes/ para no colisionar con MapBiomas ni con orphan_series); OpenStreetMap amenities vía Geofabrik taginfo (hospital/clínica/escuela/banco por 100.000 hab., corte de fin de año, 22 países, ODbL). Cada celda cita el registro concreto; sin imputación.

  • SESNSP serie estatal 2015-2025 + RNID 2026: snapshot sesnsp-delitos-estatal.json (listing gob.mx via r.jina.ai); scripts/sources/adm1/bake-sesnsp-delitos.ts escribe parameter-cache-adm1-series/seguridad__MEX__* (homicidio doloso en la cabecera, cinco delitos en detail[], bloque monthly_rnid aparte). Ruptura de serie en 2026 (RNID): no se empalma la metodología nueva.

  • Series ADM1 de comportamiento (2026-10-15, lote 05): SECOP II (participación de contratación directa por departamento, anual 2022-2025, COL, CC BY-SA 4.0 vía datos.gov.co); SIAP cierre agrícola (siniestralidad, valor, superficie y maíz grano por entidad, anual, MEX, CC BY 4.0 SADER); TSE comparecimiento electoral (1ª vuelta presidencial por UF, bienal, BRA, CC BY; familia parameter-cache-adm1-series-icfes/ para no colisionar con PNCP); IBGE PNAD Contínua (desocupación, informalidad y rendimento real por UF, trimestral, BRA, abierto SIDRA; familia parameter-cache-adm1-series-icfes/). Cada celda cita el registro concreto; sin imputación.

  • Series país de comportamiento (2026-10-15, lote 06): CEPALSTAT uso del tiempo y cuidado no remunerado (OIG; inactividad por cuidado, país, anual, CC BY vía CEPALSTAT; citas ECLAC); UNESCO LLECE ERCE 2019 / TERCE 2013 vía UIS (competencia mínima lectura/matemáticas en 6º, país y quintil pobre, 17 países, share-alike UIS); GBIF ocurrencias (conteos anuales de registros y publicación doméstica, 25 países, CC BY agregados); Global Forest Watch / UMD Hansen pérdida de cobertura arbórea (ha, bosque primario, % sobre extensión 2000 y emisiones brutas, anual, 25 países, CC BY 4.0; se mantiene el adaptador de la API GFW frente al espejo OWID de la ola I); GitHub Innovation Graph ampliado (organizaciones, repositorios, colaboración LATAM/EE.UU., pushers Python y licencia MIT, trimestral, 25 países, CC0). Cada celda cita el registro concreto; sin imputación.

  • Series país de comportamiento (2026-10-15, lote 07): Glottolog CLDF v5.3 (lenguas vivas/en peligro/extintas y % en peligro, país, por edición, CC BY 4.0 Max Planck); UN Comtrade composición de exportaciones por capítulos SA (extractivas, agroalimentos, maquinaria, país, anual, cite-only agregados); Digital Policy Alert / SGEPT (eventos de política digital por país-año, 16 países, CC BY-NC 4.0); EIA International Energy Statistics (uso per cápita, intensidad PPP y pérdidas de red, país, anual, dominio público EE.UU.); Ember emisiones del sector eléctrico (intensidad gCO2/kWh, MtCO2 y % gas en generación, país, anual, CC BY 4.0; deep links corregidos); EM-DAT perfiles de país vía HDX (eventos, climáticos, muertes y afectados, país, anual, no comercial CRED/UCLouvain); GEM Adult Population Survey (ocho indicadores clave de emprendimiento, 10 países con dato fresco ≥2024, cite-only agregados). Cada celda cita el registro concreto; sin imputación.

  • Series país de comportamiento (2026-10-15, lote 08): HRMI Rights Tracker ESR (puntajes SERF educación/alimentación/salud/vivienda/trabajo, 23 países, edición 2022/dato 2019, CC BY-NC 4.0); UIT DataHub v3 (posesión de móvil, espectro IMT y banda ancha móvil por 100 hab., país, anual, CC BY-NC-SA 3.0 IGO); Índice de la Lista Roja UICN / ODS 15.5.1 vía ONU (25 países, anual, no comercial); LAPOP AmericasBarometer (apoyo a democracia, confianza, victimización y cohesión, 24 países, rondas 2025/26, agregados ponderados, no comercial); Latinobarómetro (apoyo y satisfacción con la democracia, 17 países, oleada 2024, cite-only agregados); Media Cloud vía Internet Archive Wayback (atención mediática a la IA por 10.000 historias en ccTLD nacional, 20 países, anual, cite-only agregados). GFW TCL ya iba en el lote 06 (API WRI); se omitió el espejo OWID de la ola I. Cada celda cita el registro concreto; sin imputación.

  • Series país de comportamiento (2026-10-15, lote 09): ReliefWeb lista de desastres vía HDX (conteos anuales de desastres y de desastres climáticos, 25 países, exportación diaria / publicación anual, CC BY 4.0 OCHA); UCDP GED por tipo de violencia (muertes no estatales, unilaterales y civiles, 25 países, anual, CC BY 4.0; reutiliza la clave UCDP); WDPA áreas protegidas vía WDI (terrestres y marinas, 25 países, anual, CC BY 4.0 UNEP-WCMC/IUCN); OpenAlex facetas (participación de acceso abierto y trabajos etiquetados ODS 3/4/7/10/13/16, 25 países, mensual, CC0; clave OPENALEX_FACETS distinta de OPENALEX); Overture Maps Places (densidad de lugares por 1.000 hab. y por categoría, 25 países, por release, CC0 1.0). IATI e IBTrACS ya estaban en main (#1244, #1234) y se omitieron. Cada celda cita el registro concreto; sin imputación.

  • SEMP / cuenta propia en vivo (2026-10-15): wbinf_self_employment deja el XLSX congelado de Informal Economy Database (≤2020) y se sirve desde WDI SL.EMP.SELF.ZS (API v2, tip 2025 en 25 países; prefijo de cita wbinfl-semp conservado). DGE/MIMIC siguen en el libro XLSX histórico.

  • Educación / capital humano en vivo (2026-10-15): HCI+ LAYS/HLOS desde Data360 WB_HCIP (HD_HCIP_LAYS / HD_HCIP_HLOS, tip 2025; reemplaza el espejo WDI HD.HCI.* congelado en 2020); esperanza de vida escolar desde UNESCO UIS SLE.1T8 (reemplaza WB_EDSTATS_SE_SCH_LIFE); gasto COFOG educación/salud/protección/orden desde api.imf.org SDMX 3.0 IMF.STA:GFS_COFOG (reemplaza el espejo DBnomics IMF/GFSCOFOG indexado 2025-08). BOOST ejecutado sigue fijado a WB_BOOST_BUDGET_PARAM_2 (el tip 2023 de _PARAM_1 es presupuesto aprobado, no ejecución).

  • INEI pobreza monetaria 2025 (2026-10-16): inei_per_pobreza tip=2025 (25,7%) desde el informe oficial gob.pe «Perú: Evolución de la pobreza monetaria 2016-2025» (https://www.gob.pe/institucion/inei/informes-publicaciones/8088591-peru-evolucion-de-la-pobreza-monetaria-2016-2025). El overlay en scripts/sources/adapters.ts emite solo 2016-2025; 2014-2015 siguen en el cubo cd1_74.xlsx y la fusión por año los conserva con la cita inei-per-pobreza-per-2024.

  • Honduras trabajo 2025 (2026-10-16): ine_hnd_desempleo tip=2025 (4,9 %) desde el PDF del INE «Cifras del mercado laboral» (EPHPM julio 2025) y bch_hnd_pib tip=2025 (3,8 %) desde el Informe del PIB IV trimestre 2025 del BCH. El xlsx EPHPM sigue en 2024; el overlay en scripts/sources/adapters.ts fusiona por año. OCDE Revenue Statistics LAC (T_1000/T_2000/T_5000) pasa a una sola descarga comodín por código para no cortar la lista de países. ILOSTAT anual de encuesta descarta el año calendario en curso. WID marca data_quality 0 como imputación regional.

  • Series país de comportamiento (2026-10-15, lote 10): OCDE PISA 2022 (medias de matemáticas, lectura y ciencias, 14 países, trienal, CC BY-NC-SA 3.0 IGO); PSMSL nivel medio del mar RLR (anomalía anual, 9 países, anual, cita Holgate et al. / PSMSL); FMI IFS vía DBnomics (depreciación FX y reservas sin oro, 23 países, anual, agregados citados); OMPI IP Statistics (patentes y marcas por origen, 23 países, anual, CC BY 4.0). ONU Turismo ya estaba en main (#1237) y se omitió. Cada celda cita el registro concreto; sin imputación.

  • Infraestructura digital · frescura 2026-10: eia_electricity_net_generation es serie Ember-only por país (Yearly Total Generation, TWh; sin mezclar años EIA); Yale EPI componentes vía zip directo epi2026_indicators_na (edición 2026 en source_name) con años de observación reales del publisher y espejo Data360 solo para códigos ausentes del zip.

  • Crisis Colombia (feeds vivos): scripts/colombia/ingest-all.ts + ingest-seismic-deep.ts refrescan IFRC GO MDRCO036, USGS ShakeMap/PAGER/DYFI y el resto de public/data/colombia/real/; ReliefWeb v2 exige RELIEFWEB_APPNAME o degrada a 403.

  • parameter-cache-city/: el grano ciudad, primera cohorte de cinco ciudades ancla (CABA, CDMX, São Paulo municipio, Bogotá D.C., Santiago/RM) con datos solo medidos de sus oficinas estadísticas nacionales vía rutas keyless machine-readable (SIDRA N6, GAIA/ITER, Series de Tiempo, SODA de datos.gov.co, SDMX de INE). Es un almacén hermano de ADM1, no una fila más: tres de las cinco ciudades coinciden con una unidad ADM1 y el municipio de São Paulo colisionaría con el slug del estado homónimo. El registro canónico vive en src/data/city-units.ts; el grano real de cada indicador se declara por celda (p. ej. los indicadores laborales de Santiago solo existen a grano región). Lo que la fuente no sirve, como el Gini municipal de IBGE, la ENOE de INEGI (con token) o los anexos de DANE tras su WAF, se omite y se documenta, nunca se imputa.

  • hf-observations/: el único JSON publicado es nasa-power.json (NASA POWER, punto capital, no vacío). El resto de feeds HF, incluidos Banxico SIE, precios PMA, CAMMESA, ChileCompra, mezcla Ember, archivo OpenAQ, ONS, Mercado Público CL, dengue PAHO, remesas CA y Sentinel-5P, se ingiere pero no se commitea ni se sirve; un archivo hueco es un defecto.

  • citations.json: el registro central de citas (22.291 registros, varios MB) y citations/: sus shards, para que un consumidor no tenga que traer el registro entero.

  • documents/, news/, signals/, social/, social-trends/, attention/, shadows/.

  • personas/ (perfiles + _index + _graph), state-history/, country-history/.

  • scores.json (compuesto), data-health.json, frontier-indicators/, compute-sovereignty/, agi-economy/, dependency/, climate-action/.

  • pulse/, regional-convergence/, subnational-convergence/, instability-nowcast/, triangulation/, uncertainty/, insights/.

  • pilots/, pilot-archetypes/, financing-instruments/, coalitions/, positions/, cases/.

  • regulation/ (índice precalculado + _instruments.json), regulation-raw/ (lo ingerido antes de precalcular), legislation/, policies/, constitutional/, elections/, democracy/, transparency/, governance/, ai-governance/.

  • markets/, metrics/, ontology/, civic/, gender-gap/, trends/, explore/ (el banco de 760 métricas de /explorar), polymarket/ (snapshot de mercados de predicción), biotech/.

  • colombia/: el corpus de la respuesta de Colombia, cuyo nivel de persona y operación es sintético y va con banner de demostración (ver Colombia).

  • search-index.json (índice Voyage para el asistente), catalog.json, sdg-crosswalk.json, falsifications.json, ai-runs.json, personas-images.json.

Frescura ​

La regla de plataforma: ninguna observación publicada puede tener más de dos años. El umbral vive una sola vez, en STALE_AFTER_YEARS de src/lib/vintage.ts, y de ahí lo derivan las tres superficies que lo aplican: la insignia de vintage que el visitante ve (ámbar pasada la regla), check-vintage a nivel de cifra individual y check-freshness a nivel de familia de datasets (730 días). Cuando la frontera del propio publicador es más vieja que la regla, no se maquilla ni se inventa un número: se refresca desde una fuente actual que cubra el mismo concepto, o se declara esa frontera con su evidencia y su fecha de re-verificación. Al 9 de septiembre de 2026 hay seis frentes así, todos comprobados en vivo ese día: las cinco familias derivadas del Subnational HDI v10.2 de Global Data Lab (13 de mayo de 2026, cobertura hasta 2023, el release más nuevo que GDL publica) y el reporte anual de Cuba en UN Comtrade, que no registra declaración posterior a 2022. Los indicadores del Programa Estado de la Nación en Costa Rica son el mismo caso una escala más abajo, a nivel de cifra: los ocho más viejos —inversión en salud por persona (2015), red vial total y gasto público en salud y en educación (2019), cobertura forestal, emisiones totales de CO₂ y deserción escolar primaria y secundaria (2020)— son exactamente la última observación que el PEN publica para Costa Rica, verificada contra su API el 9 de septiembre de 2026. Se sirven con su año a la vista y en ámbar, nunca como si fueran actuales.

public/data/refresh-meta.json es el ledger de frescura, con dos granularidades: el mapa indicators rastrea por-indicador el subconjunto Banco Mundial / WGI (49 códigos); todo lo demás (884 series en el ledger de fuentes precalculado) se rastrea a nivel de feed en el mapa sources (unas 200 entradas como social:gdelt o doc:exa, con kind, last_refreshed, next_refresh_due y conteos como posts; el conteo vigente lo lleva el propio mapa, que crece con cada fuente nueva). Las filas series:* llevan además cadencia: cadence_months con cadence_basis: "observed" (mediana del espaciado entre observaciones de las series que tenemos) o cadence_basis: "stated" cuando la cadencia viene del calendario oficial de publicación del instituto. Hoy son siete las fuentes auditadas contra su calendario (IBGE, INEGI, DANE, INDEC y BCB mensual; Banxico y CEPALSTAT anual, porque de ellas solo tomamos cierres anuales), que conservan el espaciado observado en cadence_observed_months y enlazan el calendario en cadence_calendar_url. El campo de nivel superior generated_at significa el último ingest no-social (series / documentos / pull del Banco Mundial): el pulso social de cada 2 h estampa sources.social:* y lanes.social, y no mueve generated_at: si lo hiciera, un corpus de noticias rancio de 25 días se leería como actual. El mapa lanes (docs / social / hf / derived) es la lectura honesta por tier. Junto a ellos, el nivel superior lleva total_indicators, scope, vintage_note, generated_at_note, wave_f_refreshed_at y los tres sellos que estampan las compuertas y los refrescos (source_ledger_baked_at, source_ledger_series, wb_freshness_checked_at). La forma de una entrada por-indicador:

CampoEjemplo (SI.POV.GINI)Qué responde
source / source_urlWorld Bank Open Data¿de dónde salió?
cadence_months / next_refresh_due12 / 2027-03-02¿cuándo toca refrescar?
last_refreshed2026-07-28¿cuándo lo pedimos por última vez?
last_observation_year2025¿qué tan nuevo es el dato en sí?
countries_with_data / total_countries22 / 25¿cuánta cobertura?
source_frontier_year / source_frontier_checked_at2025 / 2026-07-23¿existe algo más nuevo en el upstream?
freshness_status / freshness_notecurrentveredicto: current, source_not_updated o no_data

La distinción que carga el peso es last_refreshed vs last_observation_year: un pull de ayer con dato de 2023 no es un fallo del pipeline sino rezago del proveedor, y source_frontier_year lo demuestra, porque registra el año más nuevo que el upstream publica. Es lo que hace posible mostrar el vintage de una cifra en vez de fingir que todo está al día. La superficie /confianza lee este ledger; /incertidumbre es otra cosa, expone bandas de confianza de V-Dem desde uncertainty/vdem-ci.json, no este archivo.

El ledger tiene además un guardián activo, no solo una pantalla. El runner multisource poda y reescribe celdas al ingerir, así que una corrida con un adaptador caído o limitado podría borrar en silencio celdas que existían, el defecto "mi fetch falló, escrito a disco como no hay nada". freshness-guard.py lo impide con tres subcomandos sobre parameter-cache/:

snapshot <out.json>          # antes de la corrida
  por cada parameter-cache/<pilar>__<ISO3>.json, por cada indicador:
    snap[id][celda] = { vintage: vintage_year, value, obj: <indicador completo> }

compare <before.json>        # después de la corrida
  por indicador:  gone       (celdas que desaparecieron)
                  regressed  (vintage retrocedió: after < before)
                  advanced   (vintage avanzó)  ·  added (celdas nuevas)
  reporta el total de celdas perdidas + retrocedidas

repair <before.json> --apply
  restaura SOLO gone ∪ regressed, desde el obj guardado en el snapshot
  advanced y added se dejan intactos — un avance genuino nunca se revierte
  sin --apply es dry run: lista qué restauraría, no toca disco

Invariante sutil del filtro --only=<substr>: el guardián filtra la línea base con los mismos términos que el snapshot vivo. Sin eso, todo indicador fuera del filtro parecería gone y repair restauraría la plataforma entera a la línea base, incluidas correcciones deliberadas posteriores. Las corridas de refresco son no destructivas por construcción.

En el prebuild, bake-source-ledger.ts precalcula el ledger de fuentes que alimenta /confianza y estampa en refresh-meta.json los campos de nivel superior source_ledger_baked_at y source_ledger_series; en los refrescos de datos, recheck-wb-freshness.ts re-verifica los vintages del Banco Mundial contra el upstream y estampa wb_freshness_checked_at.

Las compuertas del prebuild ​

El prebuild es una cadena de compuertas encadenadas con && en package.json; si cualquiera falla, el build falla y nada se despliega:

* = consultiva (--warn): reporta y sale 0.

Primero el terreno: dos typechecks (tsc -p tsconfig.scripts.json para los scripts de ingesta y precálculo, tsc -p tsconfig.server.json para el servidor del asistente y las funciones), la suite completa de tests (bun test --timeout 15000: 2.443 tests en 186 archivos, que incluyen el servidor del asistente, el handler MCP y los generadores de precálculo) y build-positions-index --check. Después, veinticinco comprobaciones check-* con nombre propio, más dos gates en modo --check sobre scripts que normalmente escriben (build-positions-index, bake-trends-index). Dos son consultivas (--warn: reportan y salen 0, check-search-index, check-adm1-coverage); las demás hacen fallar el build, incluida check-vintage, que dejó de ser consultiva el 9 de septiembre de 2026, y la nueva check-freshness.

Procedencia y coherencia del corpus:

  1. check-traced: toda cifra tiene su rastro a fuente.
  2. check-signals: las señales están bien formadas y citadas. Las señales LLM deben citar un documento del corpus de la celda con cifra verbatim de su extracto; las señales earth_obs (deterministas, sin documento hermano) pasan una compuerta estructural: host https en lista blanca (solo api.climatetrace.org y terrabrasilis.dpi.inpe.br), celda y fuente fijas, fecha ISO completa y cifra verbatim dentro de su propio claim_es (con los mismos dígitos en claim_en/claim_pt).
  3. check-source-links: los enlaces de cita apuntan al registro específico de país + indicador. La única excepción es la lista LANDING_OK dentro de la propia compuerta: un opt-out documentado por fuente (capas curadas e internas que resuelven a /metodologia, capas documentales EXA/GDELT/RSS/gacetas/señales y unas pocas fuentes sin URL estable por registro cuyo destino honesto es su propia página de dataset), cada uno con su justificación en el código. Todo lo demás lleva deep link o el build falla.
  4. check-pilots: coherencia de los pilotos.
  5. check-persona-scope: ningún perfil puede quedar inalcanzable. El <select> de país de /personas se construye desde los 25 canónicos, así que un perfil archivado bajo un iso3 fuera de ese conjunto era una ficha completa y con fuentes que ningún visitante podía filtrar (la auditoría D-5 encontró 50 de 1.384 así: Barbados, Bahamas y un columnista radicado en Miami bajo USA). No se borraron, se escalonaron por tier; esta compuerta hace vinculante el escalonamiento.
  6. check-citations: integridad del registro de citas.
  7. check-parameter-cache-citations: cada citation_id de parameter-cache/ resuelve en citations.json, sin recurso a una cita sintética de respaldo. La API pública ya estaba limpia; esta compuerta mantiene limpia la caché desde la que se precalcula.
  8. check-scores: coherencia de los puntajes compuestos (ver El Índice Futuros).
  9. check-provenance: rechaza hashes o digests fabricados (stubs de sha256 truncados) en cualquier parte de public/data; nació de la auditoría de precisión de datos de julio de 2026. 9b. check-frontier-provenance: falla el build si cualquier fila de public/data/frontier-indicators/** lleva provenance: "synthesised", simulated: true, un citation_id que no resuelve, o un valor bajo una clave marcada redistribute: false en el catálogo de frontera. Es la compuerta que retiró el corpus fabricado de ocho indicadores y mantiene el corpus citado (97 indicadores × 25 países) honesto.
  10. colombia/check-real-data: separa lo real de lo sintético en el corpus de Colombia, para que ninguna cifra de demostración se sirva sin su banner.

Estilo, taxonomía y cobertura:

  1. check-emdash-drift: la deriva de estilo (guiones largos) no reaparece en el contenido ya barrido.

  2. check-landing-stats: cada número impreso en public/landing.html sigue siendo el número que dicen los datos. La landing es un artefacto editado a mano, la página de más tráfico y la única sin componente, diccionario ni test detrás; sus estadísticas eran libres de pudrirse y lo hicieron (la auditoría del 13 de agosto de 2026 la encontró anunciando 6.726 citas contra un corpus de 6.855, y "300+ pilotos" contra un índice de 337). La compuerta recomputa cada cifra desde public/data/** del mismo modo que la superficie correspondiente, e imprime el literal que hay que pegar. Hoy verifica seis: fuentes primarias distintas, países, ejes, citas, personas e instrumentos.

  3. check-docs-citations: cada cifra del corpus de citas escrita a mano en docs/ sigue siendo la que dice citations.json. Es la gemela de check-landing-stats para el sitio de documentación: los conteos de citas y de fuentes primarias viven a mano en el índice ES/EN/PT, en Procedencia y citas, en esta misma página y en los ejemplos de /api/v1/meta de dev/public-api, sin componente, diccionario ni test detrás, y se pudrieron igual: hasta agosto de 2026 los docs anunciaban ~6.850 citas (la cifra de julio) mientras el corpus vivo ya iba por 15.661, una subestimación de 2,2x que ninguna auditoría automática detectaba porque nada conectaba las dos. Las cifras se corrigieron el 4 de septiembre de 2026; esta compuerta es lo que impide que vuelvan a separarse. Además de los slots declarados, barre todo docs/ en busca de afirmaciones con forma de conteo, así que una página nueva con una cifra rancia también falla. Las cifras históricas están exentas si dicen de qué auditoría y de cuándo hablan: narrar el pasado es honesto, repetirlo como presente no.

  4. check-basedosdados: la tienda brasileña de grano municipal se vigila con el mismo contrato que parameter-cache: cada citation_id resuelve a la vez en las citas propias de la tienda y en citations.json global; cada cita apunta a una tabla específica de Base dos Dados (?table=<uuid>) y además al registro de la agencia que produjo la cifra, porque BD es un espejo y no el productor; cada indicador declara su propio vintage, de modo que no puede publicarse un «último dato» mezclado; y ninguna columna declarada puede venir vacía en todas las filas. Esta última regla existe porque ocurrió: el ingreso estatal de SICONFI se seleccionaba por etiqueta (Receitas Orçamentárias), rubro que no existe en esa etapa contable, y produjo 27 nulos que parecían un hueco de datos y eran un bug del selector. La tienda cubre solo Brasil (grano municipio/UF), así que la compuerta no exige cobertura de otros países; si la tienda no se ha ingerido nunca, pasa en verde: un checkout limpio sigue compilando.

  5. check-catalog: el catálogo de datasets está completo y consistente.

  6. check-search-index (consultiva), el índice semántico cubre el corpus.

  7. check-law-term-index: el índice de términos legales cubre el 100% del corpus regulatorio (instrument_count de regulation/_index.json). Índice local: Voyage sigue siendo opt-in y falla cerrado.

  8. bake-trends-index --check: el índice de tendencias precalculado coincide con sus insumos.

  9. check-vintage: cuántas cifras publicadas rompen la regla de vintage de dos años. El umbral y el orden de resolución de año salen de src/lib/vintage.ts, el mismo módulo desde el que renderizan las tiras de titular, así que la compuerta mide lo que el visitante ve. Existe porque bake-data-health puntúa una celda por la edad mediana de sus indicadores: la plataforma reportaba 2 celdas rancias de 260 mientras el 27% de las cifras individuales violaba la regla. Fue consultiva hasta el 9 de septiembre de 2026 —medía la regla sin poder hacerla cumplir— y hoy hace fallar el build por encima de su trinquete de 8.978 cifras rancias, que solo puede bajar. La única salvedad es el cambio de año: oldestAllowed rueda con el calendario, así que el 1 de enero toda una cohorte anual se vuelve rancia de golpe y el conteo salta miles por encima de un techo medido en el año anterior. Por eso el trinquete registra el año en que se midió y, fuera de ese año, la compuerta reporta en voz alta y sale 0 hasta que alguien vuelva a medir y confirme el nuevo techo. Un build roto que nadie causó erosiona la compuerta más rápido que la deriva que vigila.

  10. check-freshness: ningún dataset vivo de la plataforma tiene más de dos años. Donde check-vintage cuenta cifras sueltas dentro de parameter-cache/, esta compuerta pregunta lo que aquella no puede: si hay un feed entero que nadie logró avanzar. Recorre las quince familias de datasets cuantitativos declaradas en scripts/lib/freshness.ts y compara la observación más reciente de cada una contra el límite de 730 días derivado del mismo STALE_AFTER_YEARS. Las familias con un archivo por país se juzgan registro por registro, el único grano que detecta el defecto que la originó: el precálculo de UN Comtrade del 23 de julio de 2026 murió tras seis países y dejó 19 de 25 en el año comercial 2023, mientras los seis ya refrescados mantenían la cara de la familia en 2025. Nunca cuenta un generated_at ni un last_refreshed —eso es cuándo corrimos nosotros, no cuándo se midió el mundo— y descarta las proyecciones (el WEO del FMI llega a 2031, los escenarios SSP del CCKP a 2080): un pronóstico no es una observación. Un año se fecha al cierre de ese año, nunca al 1 de enero, porque redondear en la dirección que halaga es exactamente el error que la compuerta busca. Cuando la frontera es del publicador y no nuestra, se declara como una exención con fecha de caducidad: quién publica, qué release, qué año, la URL que lo prueba, cuándo se verificó en vivo y para cuándo hay que volver a verificarlo. La compuerta falla si la exención vence, si la frontera declarada deja de coincidir con lo que miden los archivos, o si cubre datos que ya volvieron a estar frescos — es lo que impide que una exención honesta de hoy se vuelva una lista de excepciones permanente.

  11. check-adm1-coverage (consultiva), la compuerta de honestidad de la capa subnacional. "432 de 456 unidades ADM1 con dato real" era cierto solo de la superposición universal GDL; a nivel de eje ninguna unidad llevaba más de 5 de los 10 ejes medidos, cinco ejes estaban 100% modelados y 26 unidades tenían sus celdas escritas bajo slugs que la app nunca puede pedir. Nada lo detectaba, así que derivó en silencio durante meses.

  12. check-city-cells: la compuerta de honestidad del grano ciudad (parameter-cache-city/), estricta desde el nacimiento (sin trinquete: el almacén nació limpio). El contrato es solo medido: los niveles de cobertura modelados son estructuralmente imposibles en este almacén; cada indicador exige valor finito, año vigente, citation_id terminado en -<año> que resuelve en citations.json y cuya fila lleva exactamente la misma url que el source_url de la celda (la celda y su cita no pueden apuntar a lugares distintos), source_url con deep link (nunca la landing de la fuente) y un slug que resuelve en src/data/city-units.ts. El registro es la afirmación de cobertura: ciudad sin dato, ciudad sin registrar.

  13. check-i18n: la cobertura ES/EN/PT no retrocede.

  14. check-taxonomy: la copia de interfaz no vuelve a los sustantivos retirados para las diez medidas sectoriales (diez parámetros, ten dimensions, dez dimensões) ni apunta la navegación a /governance. Los diez son ejes / axes / eixos.

  15. check-hf-publish: bajo public/data/hf-observations/ solo puede vivir nasa-power.json, y debe ser un almacén NASA POWER no vacío. Cascarones vacíos (observations: []) y feeds no publicados (CHIRPS, FIRMS, Ember…) son un defecto: sirven un 200 que parece vivo.

  16. check-api: corre al final, después de los precálculos de prebuild (bake-data-health, bake-source-ledger, bake-api, bake-feeds, bake-research-library), y verifica con siete grupos de aserciones que la API pública estática espeja los datos actuales. bake-research-library no alimenta la API: escribe public/data/research-library.json y public/research/<slug>.json para la ruta /biblioteca.

Las compuertas de procedencia en sentido estricto son el subconjunto check-traced, check-signals, check-source-links, check-citations, check-parameter-cache-citations, check-provenance y check-frontier-provenance: pero todas comparten la misma consecuencia: son la razón por la que "toda cifra con su fuente" no es un eslogan sino una invariante de compilación. Cómo se resuelve una cita en pantalla se explica en Procedencia y citas.

Un patrón se repite en las compuertas más nuevas (check-landing-stats, check-docs-citations, check-vintage, check-freshness, check-adm1-coverage, check-persona-scope): cada una nació de una auditoría que encontró una afirmación cierta cuando se escribió y falsa cuando se leyó, sin que nada la conectara con el dato. La compuerta es esa conexión. Es la misma razón por la que existe esta documentación y por la que se audita contra el código.

Cada cifra con su fuente — la trazabilidad es el contrato.