Clock de radio calidad Bloomberg — aplicado 2026-09-07
Objetivo declarado: calidad de radio Bloomberg, pero del otro espectro político. La lectura operativa: el rigor ES la marca, no la ideología. Bloomberg suena a Bloomberg por disciplina de formato (frase corta, atribución en cada claim, consecuencia concreta, reloj consistente, opinión cuarentenada), no por su línea editorial. Las fuentes ya son las otras (Astillero, Ríodoce, AJ English, Democracy Now!, The Intercept, Pie de Página, Mongabay, Brasil de Fato, Doctorow, EFF, Rest of World). Faltaba el oficio.
Aplicado sobre surtido/scripts/radio_parrilla.py + tanda_radio.sh + radio_push.sh +
sandbox-sep-6/parrilla/arma_parrilla.sh + asgi.py + proponer.py.
Auditoría previa: [CREDENCIAL].md.
1. Se mató el bug que mandó contenido viejo al aire
tanda_radio.sh corría radio_parrilla.py y después arma_parrilla.sh; el segundo
hardcodeaba 13 inputs, varios nunca regenerados (b1-mx3, todas las gemelas -c). La
edición del 2026-09-06 salió con el bloque tech de las 14:35 a las 19:33.
tanda_radio.shya no llama aarma_parrilla.sh. Un solo ensamblador.arma_parrilla.shreescrito: ensambla lo que digaplaylist.m3u, sin lista fija._verificar_frescura()aborta si un segmento noticioso tiene mtime anterior al inicio de la corrida. Probado con 4 casos (corrida real pasa, segmento de 5 h aborta, archivo ausente aborta, cortinilla evergreen exenta).radio_push.shya no empuja los globsb1-*/b2-*. Legacy a_viejos-2026-09-06/.- Salida renombrada
parrilla-7min.mp3→parrilla.mp3(el nombre mentía: eran 5:29).asgi.pyresuelve ambos y muestra la duración real.
2. Reglas de guion nuevas (todas verificadas contra el preview real)
| problema audible | fix |
|---|---|
| GPT-6/Alibaba y SpaceX leídos en “Internacional” | ruteo por prioridad, TECH antes que MUNDO; sin substring suelto |
| NFL y US Open en bloques de noticias duras | detector de deportes por contenido → cápsula propia |
| Zenwalk leído dos veces (ES y EN, mismo enlace) | dedup por URL canónica; x.com/<h>/status/<id> → el id |
| ”GPT-6 con bucle caliente…” repetido dos veces seguidas | anti_eco() recorta del qué pasó las cláusulas que solo parafrasean el titular; si no queda aporte, el bloque pasa a brief |
| ”crucial para la economía digital mexicana” | limpiar_boiler(): fuera la relevancia forzada sin dato ni nombre del titular |
titular en inglés leído con voz es-US | se traduce desde reporte/traducciones.json; si no está, se descarta y se informa |
| 5 bullets diciendo ”, en x.com” | atribución = nombre del medio o handle real; fórmula rota (según / de acuerdo con / reporta / informa) |
| la 3ª noticia de México nunca se oyó | presupuesto por bloque: entran las notas que caben, lo demás pasa al siguiente segmento. Nunca se trunca a mitad de frase |
| ”Más de México.” y silencio | guarda de bloque vacío: no se emite |
| ”abajo por menos cinco” | _marcadores(): “2-1” → “2 a 1”, solo con contexto deportivo (no rompe fechas ni versiones) |
| emojis al TTS, posts cortados a media palabra | _limpiar() + corte en frontera de frase |
| el pulso eran 9 s de puros conteos | ahora cita 2 posts reales, descartando saludos y el eco lead/cuerpo |
3. Se recuperaron dos bloques que ya existían y se tiraban
parse_preview() solo capturaba viñetas en negrita. Las otras dos secciones del preview
se parseaban a 0 items:
En 60 segundos→ bloque01-titulares(hard open, estilo Bloomberg: titulares secos antes que nada)PARA SEGUIR→ bloque11-tomorrow
Y el campo Por qué importa, que bullet() descartaba, ahora alimenta los bloques
focus. Es análisis ya escrito que la cadena tiraba a la basura.
4. El clock (14 segmentos + cortinilla + debate)
00 ID + hora al aire · 01 titulares · 02 NACIONAL brief · 03 NACIONAL focus
04 MUNDO brief · 05 MUNDO focus · 06 TECH headlines · 07 TECH focus
08 TECH radar (condicional) · 08b DEPORTES · [cortinilla 8-bit]
09 time check · 10 señales/pulso · 11 mañana · 12 resumen · 13 ID cierre · debate (4 voces)
Separación brief ≠ focus en todo el reloj: el brief da titular + fuente, el focus agrega contexto y consecuencia. Es lo que distingue radio de un RSS leído en voz alta.
5. Audio
loudnorm=I=-20:TP=-2.0:LRA=7 + aresample=48000:async=1 por entrada. Los MP3 de
gcloud vienen a 24 kHz y las cortinillas a 48; sin el resample el concat tiraba
non monotonically increasing dts y Liquidsoap/Icecast calculan mal la duración.
Salida 48 kHz mono 96 kb/s. Medido: parrilla -20.5 LUFS, fallback -20.3 LUFS.
El spread entre segmentos ya era bueno antes (-19.2 a -20.7 LUFS, 1.5 LU). No era un problema; se fijó de todos modos para que no se mueva cuando entre música.
6. fallback-loop.mp3 dejó de ser cp de la parrilla
Era copia idéntica: si caía la fuente en vivo, el oyente escuchaba las mismas 14 noticias en ciclo infinito. Ahora rota la parte atemporal del reloj (focus + señales + mañana + resumen) entre cortinillas. Verificado por md5: distintos.
7. Fix aguas arriba
proponer.py PROMPT_SISTEMA (el de español) no tenía la regla anti-boilerplate que el
PROMPT_SISTEMA_TECH sí tenía. Agregada: consecuencia concreta y verificable, prohibido
cerrar con relevancia genérica, y que_paso no debe repetir el titular. Así el próximo
preview ya viene limpio y limpiar_boiler() pasa a ser red de seguridad, no el filtro
principal.
Verificación
radio_parrilla.py --dryimprime el guion completo con tiempos y lo descartado, sin gastar TTS.- Corrida real: 14 segmentos,
parrilla.mp3334.13 s, decodifica sin errores. arma_parrilla.shpor separado: 16 segmentos, 334 s — ambas rutas coinciden.asgi.pycon TestClient:/radio200 con secciones Nacional / Internacional / Tech / Reloj, player “Al aire — parrilla 5.6 min”,/radio/en200, los 4/radio/audio/*200, cero referencias residuales aparrilla-7min.bash -nen los 4 scripts,py_compileen los 4 módulos.
Lo que NO se hizo (deliberadamente)
- Orpheus local. La cadena es Google Cloud TTS con voces ya calibradas, y la estación es en español (Orpheus es inglés-first). Es otro proyecto.
- Los 60 minutos. Una ronda da 5.6 min. La unidad es el clock, no la hora.
- The Big Question / Closing Commentary. Requieren generación editorial (LLM), no retrieval. Sigue sin cablearse.
- Quitar la cortinilla 8-bit. Choca con el acabado Bloomberg, pero es identidad existente (“donde la nostalgia debate con el futuro”). Decisión de gusto, no técnica.
Siguiente palanca real
El cuello no es el retrieval (278 items por ronda) sino la destilación: el preview entrega
~14 bullets por las cuotas de auditoría (México ≥40%, EEUU ≥40%, independientes ≥40%,
grandes ≤35%, máx 2 por medio). Subir a 30–40 bullets llevaría el clock a ~15–20 min sin
repetir nada. Eso es un cambio en proponer.py, no en la cadena de radio.
Pendiente conocido (aguas arriba, no es de radio)
pulso.json trae “creador de chetos de Call of Duty” — mistranslation de cheats
(trampas). En México “cheto” es delator. Se oyó al aire en el bloque de señales. La
traducción de posts del pulso no pasa por el mismo control que los titulares.