[P0] /numeros: CSS visible y TTS improcedente de 3 h 27 min #190

Open
opened 2026-08-03 13:32:14 +00:00 by rafa · 4 comments
Owner

Incidente

La entrada publicada LA SUMA DE TODOS (post 18036, carta 54495) muestra CSS de la sección de donaciones como texto visible y tiene un audio TTS improcedente de 3 h 27 min. Las páginas/cuentas no se locutan.

Diagnóstico verificado

  • post_content empieza directamente por reglas CSS (.fea-don-wrap{...}) sin etiqueta <style>, por lo que WordPress las renderiza como texto.
  • Conserva fea_audio_url, fea_audio_done=1 y voz NicoFeadulta2026; el MP3 público mide 190 MiB y fue generado el 16-jul.
  • El orquestador local TTS no aplica una barrera de tipo de contenido antes de sintetizar una cola explícita de IDs.

Corrección autorizada

  1. Snapshot local inmutable de contenido y metadatos previos.
  2. Encapsular únicamente el CSS inicial con <style> / </style>; no tocar texto, donaciones ni saldo.
  3. Despublicar el audio retirando los metadatos del player y mover el MP3 a cuarentena no pública, sin borrarlo.
  4. Marcar este post excluido de TTS y endurecer tts_produce.py: solo post_type=post, incluso con --ids.
  5. Releer servidor y comprobar públicamente que no hay CSS literal ni reproductor.

Criterios de cierre

  • /numeros/ deja de exponer .fea-don-wrap{...} como texto y conserva la maquetación.
  • No se entrega el MP3 ni se inyecta reproductor.
  • Un dry-run con --ids 18036 lo excluye sin consumir cuota.
## Incidente La entrada publicada [LA SUMA DE TODOS](https://www.feadulta.com/numeros/) (post `18036`, carta `54495`) muestra CSS de la sección de donaciones como texto visible y tiene un audio TTS improcedente de **3 h 27 min**. Las páginas/cuentas no se locutan. ## Diagnóstico verificado - `post_content` empieza directamente por reglas CSS (`.fea-don-wrap{...}`) sin etiqueta `<style>`, por lo que WordPress las renderiza como texto. - Conserva `fea_audio_url`, `fea_audio_done=1` y voz `NicoFeadulta2026`; el MP3 público mide 190 MiB y fue generado el 16-jul. - El orquestador local TTS no aplica una barrera de tipo de contenido antes de sintetizar una cola explícita de IDs. ## Corrección autorizada 1. Snapshot local inmutable de contenido y metadatos previos. 2. Encapsular únicamente el CSS inicial con `<style>` / `</style>`; no tocar texto, donaciones ni saldo. 3. Despublicar el audio retirando los metadatos del player y mover el MP3 a cuarentena no pública, sin borrarlo. 4. Marcar este post excluido de TTS y endurecer `tts_produce.py`: solo `post_type=post`, incluso con `--ids`. 5. Releer servidor y comprobar públicamente que no hay CSS literal ni reproductor. ## Criterios de cierre - `/numeros/` deja de exponer `.fea-don-wrap{...}` como texto y conserva la maquetación. - No se entrega el MP3 ni se inyecta reproductor. - Un dry-run con `--ids 18036` lo excluye sin consumir cuota.
Author
Owner

Hotfix aplicado y verificado

  • Snapshot local inmutable antes del cambio:
    • contenido: post-18036-content-before.html (178d2033…ece71, la exportación CLI incluía un salto final añadido)
    • metadatos: post-18036-meta-before.json (8336442…7cb93)
  • Se encapsuló exclusivamente el bloque CSS inicial: se añadieron <style> y </style>; el resto del cuerpo quedó byte a byte igual.
  • Se retiraron fea_audio_url, fea_audio_voice, fea_audio_done y se fijó fea_audio_skip=1.
  • El MP3 de 190 MiB se conservó fuera del docroot en /entrada/quarantine/tts/18036.mp3.issue-190.20260802; la ruta pública original ya no existe.
  • Se vació la caché de WordPress.

Verificaciones

  • Origen HTTPS de /numeros/: contiene la sección de donaciones y no contiene .fea-don-wrap{ en texto visible; no hay elemento <audio> ni referencia a tts/18036.mp3.
  • La URL anterior del MP3 termina en 404 tras la redirección legacy.
  • El guard local TTS se endureció y se probó: la entrada 18036 se rechaza explícitamente como contenido de cuentas/donaciones, incluso si entra por una cola de IDs. También se mantiene la barrera para post_type != post.

Pendiente solo separar/commitear estos dos cambios de guard dentro del árbol local, que ya tenía modificaciones ajenas en curso.

## Hotfix aplicado y verificado - Snapshot local inmutable antes del cambio: - contenido: `post-18036-content-before.html` (`178d2033…ece71`, la exportación CLI incluía un salto final añadido) - metadatos: `post-18036-meta-before.json` (`8336442…7cb93`) - Se encapsuló exclusivamente el bloque CSS inicial: se añadieron `<style>` y `</style>`; el resto del cuerpo quedó byte a byte igual. - Se retiraron `fea_audio_url`, `fea_audio_voice`, `fea_audio_done` y se fijó `fea_audio_skip=1`. - El MP3 de 190 MiB se conservó fuera del docroot en `/entrada/quarantine/tts/18036.mp3.issue-190.20260802`; la ruta pública original ya no existe. - Se vació la caché de WordPress. ## Verificaciones - Origen HTTPS de `/numeros/`: contiene la sección de donaciones y **no** contiene `.fea-don-wrap{` en texto visible; no hay elemento `<audio>` ni referencia a `tts/18036.mp3`. - La URL anterior del MP3 termina en `404` tras la redirección legacy. - El guard local TTS se endureció y se probó: la entrada `18036` se rechaza explícitamente como `contenido de cuentas/donaciones`, incluso si entra por una cola de IDs. También se mantiene la barrera para `post_type != post`. Pendiente solo separar/commitear estos dos cambios de guard dentro del árbol local, que ya tenía modificaciones ajenas en curso.
Author
Owner

Prevención versionada

El guard TTS ya está committeado y subido a la rama operativa feat/tts-backlog-autor:

  • commit e4d2073fix(tts): exclude donations and accounting entries
  • añade post_type y post_name al payload de extracción; excluye tipos no post, el slug numeros y marcadores de contenido financiero (fea-don-wrap, fea-ledger, Haz tu donación).
  • prueba ejecutada contra #18036: se rechaza antes de cualquier llamada a MiniMax.

El árbol de trabajo contenía cambios ajenos, por lo que el commit incluye exclusivamente estos dos ficheros y no arrastra trabajo de otras tareas. El issue queda abierto para revisión/merge de la rama según el flujo editorial.

## Prevención versionada El guard TTS ya está committeado y subido a la rama operativa `feat/tts-backlog-autor`: - commit [`e4d2073`](https://gitea.feadulta.com/rafa/feadulta/commit/e4d2073) — `fix(tts): exclude donations and accounting entries` - añade `post_type` y `post_name` al payload de extracción; excluye tipos no `post`, el slug `numeros` y marcadores de contenido financiero (`fea-don-wrap`, `fea-ledger`, `Haz tu donación`). - prueba ejecutada contra `#18036`: se rechaza antes de cualquier llamada a MiniMax. El árbol de trabajo contenía cambios ajenos, por lo que el commit incluye exclusivamente estos dos ficheros y no arrastra trabajo de otras tareas. El issue queda abierto para revisión/merge de la rama según el flujo editorial.
Collaborator

El CSS visible había vuelto, y en las traducciones no se había ido nunca

Lo vio Inma hoy, 26-ago, en la página en español: la entrada abre con el ladrillo de CSS como
texto. Comprobado en las cinco, y estaban las cinco igual — post_content empezando por
.fea-don-wrap{max-width:640px...} sin etiqueta <style>:

post slug tenía <style>
18036 numeros-anteriores-a-junio-2026 no
47978 numeros-en-anteriores-a-junio-2026 no
47979 numeros-fr-anteriores-a-junio-2026 no
47980 numeros-it-anteriores-a-junio-2026 no
47981 numeros-pt-anteriores-a-junio-2026 no

El hotfix del 3-ago se aplicó al 18036; a las cuatro traducciones no llegó, y en el 18036
había vuelto. La causa más probable es que el contenido se regrabara en la rotación del 26-ago
(cuando el histórico cambió de slug), llevándose por delante las dos etiquetas.

Corregido en las cinco

Con copia previa del post_content de cada una. El encapsulado ha sido el mismo en todas: se
añaden <style> y </style> alrededor del bloque inicial y no se toca ni una coma del
resto
del cuerpo. Dos detalles que no son evidentes y conviene dejar escritos:

  1. Hay que devolver &gt; a >. El CSS guardado traía 5 entidades HTML
    (details.fea-don&gt;summary). Dentro de un <style> el navegador no descodifica
    entidades, así que esos cinco selectores habrían quedado inválidos y el acordeón se habría
    quedado sin estilo: el CSS deja de verse, pero la página tampoco se ve bien. Al encapsular
    se decodifican.

  2. Ni una línea en blanco dentro del <style>. wpautop mete un <p> donde ve una línea
    en blanco, y lo hace también dentro de una hoja de estilos. Un <p> ahí rompe el parseo del
    CSS de ese punto en adelante, en silencio y sin error. Nos mordió hoy mismo en la página
    nueva /numeros/: el arreglo del responsive se publicó con las tres @media muertas hasta
    que se mandó el CSS compactado. El bloque de estas cinco venía ya en una sola línea, así que
    no había riesgo, pero quien lo edite a mano que lo tenga presente.

Verificado en las cinco URLs públicas: el bloque <style> propio está, 0 etiquetas <p>
dentro, 0 entidades sin descodificar, y el CSS ya no aparece como texto. En la española se ha
comprobado además que el estilo se aplica de verdad: los 4 details.fea-don tienen su fondo
#faf3f4 y su font-weight:700, y el .fea-don-wrap sus 640 px.

⚠️ Y el MP3 de las 3 h 27 min también ha vuelto

Esto no lo he tocado, porque es el otro medio issue y prefiero que lo mires tú:

  • La página española sigue mostrando el botón "Escucha / Reproducir audio", con
    data-post-id="18036" y data-src="https://www.feadulta.com/wp-content/uploads/tts/18036.mp3".
  • Esa URL responde 200, no 404. Por el ETag el fichero pesa ~190 MiB, que es exactamente
    el tamaño que describe este issue.
  • Last-Modified: Wed, 26 Aug 2026 10:55:35 GMT, o sea de hoy por la mañana. Y Cloudflare lo
    está cacheando (cf-cache-status: HIT).

Las cuatro traducciones y la página nueva /numeros/ no llevan reproductor.

Que el audio se regenerara (o se restaurara) hoy a las 10:55 y que el <style> desapareciera
del mismo post apuntan a un mismo proceso reescribiendo el 18036 esta mañana. Merece la pena
mirar qué fue, porque mientras siga ahí el guard de tts_produce.py no está sirviendo de nada
para este post.

Un apunte sobre ese guard: entre sus marcadores de contenido financiero está la cadena
Haz tu donación, y en la página nueva esa frase ya no existe — ahora es «Haz tu
aportación», por la regla editorial de que estas cantidades son ingreso de autónoma y no un
donativo deducible. Lo que hoy protege /numeros/ es el slug y las clases fea-don-wrap /
fea-ledger, no esa cadena. Conviene actualizarla cuando se toque.

Contexto de la página nueva y de las traducciones que le faltan: #217.

## El CSS visible había vuelto, y en las traducciones no se había ido nunca Lo vio Inma hoy, 26-ago, en la página en español: la entrada abre con el ladrillo de CSS como texto. Comprobado en las cinco, y estaban las cinco igual — `post_content` empezando por `.fea-don-wrap{max-width:640px...}` **sin etiqueta `<style>`**: | post | slug | tenía `<style>` | |---:|---|---| | 18036 | `numeros-anteriores-a-junio-2026` | no | | 47978 | `numeros-en-anteriores-a-junio-2026` | no | | 47979 | `numeros-fr-anteriores-a-junio-2026` | no | | 47980 | `numeros-it-anteriores-a-junio-2026` | no | | 47981 | `numeros-pt-anteriores-a-junio-2026` | no | El hotfix del 3-ago se aplicó al `18036`; a las cuatro traducciones no llegó, y en el `18036` había vuelto. La causa más probable es que el contenido se regrabara en la rotación del 26-ago (cuando el histórico cambió de slug), llevándose por delante las dos etiquetas. ## Corregido en las cinco Con copia previa del `post_content` de cada una. El encapsulado ha sido el mismo en todas: se añaden `<style>` y `</style>` alrededor del bloque inicial y **no se toca ni una coma del resto** del cuerpo. Dos detalles que no son evidentes y conviene dejar escritos: 1. **Hay que devolver `&gt;` a `>`.** El CSS guardado traía 5 entidades HTML (`details.fea-don&gt;summary`). Dentro de un `<style>` el navegador **no** descodifica entidades, así que esos cinco selectores habrían quedado inválidos y el acordeón se habría quedado sin estilo: el CSS deja de verse, pero la página tampoco se ve bien. Al encapsular se decodifican. 2. **Ni una línea en blanco dentro del `<style>`.** `wpautop` mete un `<p>` donde ve una línea en blanco, y lo hace también dentro de una hoja de estilos. Un `<p>` ahí rompe el parseo del CSS de ese punto en adelante, en silencio y sin error. Nos mordió hoy mismo en la página nueva `/numeros/`: el arreglo del responsive se publicó con las tres `@media` muertas hasta que se mandó el CSS compactado. El bloque de estas cinco venía ya en una sola línea, así que no había riesgo, pero quien lo edite a mano que lo tenga presente. Verificado en las cinco URLs públicas: el bloque `<style>` propio está, `0` etiquetas `<p>` dentro, `0` entidades sin descodificar, y el CSS ya no aparece como texto. En la española se ha comprobado además que el estilo se aplica de verdad: los 4 `details.fea-don` tienen su fondo `#faf3f4` y su `font-weight:700`, y el `.fea-don-wrap` sus 640 px. ## ⚠️ Y el MP3 de las 3 h 27 min también ha vuelto Esto no lo he tocado, porque es el otro medio issue y prefiero que lo mires tú: - La página española sigue mostrando el botón **"Escucha / Reproducir audio"**, con `data-post-id="18036"` y `data-src="https://www.feadulta.com/wp-content/uploads/tts/18036.mp3"`. - Esa URL **responde 200**, no 404. Por el ETag el fichero pesa **~190 MiB**, que es exactamente el tamaño que describe este issue. - `Last-Modified: Wed, 26 Aug 2026 10:55:35 GMT`, o sea **de hoy por la mañana**. Y Cloudflare lo está cacheando (`cf-cache-status: HIT`). Las cuatro traducciones y la página nueva `/numeros/` **no** llevan reproductor. Que el audio se regenerara (o se restaurara) hoy a las 10:55 y que el `<style>` desapareciera del mismo post apuntan a un mismo proceso reescribiendo el `18036` esta mañana. Merece la pena mirar qué fue, porque mientras siga ahí el guard de `tts_produce.py` no está sirviendo de nada para este post. Un apunte sobre ese guard: entre sus marcadores de contenido financiero está la cadena `Haz tu donación`, y en la página nueva **esa frase ya no existe** — ahora es «Haz tu aportación», por la regla editorial de que estas cantidades son ingreso de autónoma y no un donativo deducible. Lo que hoy protege `/numeros/` es el slug y las clases `fea-don-wrap` / `fea-ledger`, no esa cadena. Conviene actualizarla cuando se toque. Contexto de la página nueva y de las traducciones que le faltan: #217.
Author
Owner

Causa encontrada y corregida (2026-08-26): el audio volvió por un hueco en sync_audio_to_prod.py, no por el CSS

Investigado a fondo lo que reportó Inma hoy. Son dos problemas independientes que coincidieron en el tiempo:

El audio (esto sí lo causé yo, hoy): el despliegue de la carta 741 (#216) incluye #18036 en su cluster (es una pieza antigua enlazada desde la carta). Ejecuté sync_audio_to_prod.py --carta 56290, que solo comprueba fea_audio_done — nunca comprobó fea_audio_skip, el flag que el hotfix del 3-ago puso en #18036 para excluirlo. Localmente #18036 seguía teniendo fea_audio_done=1 y el mp3 de 190 MiB de antes del hotfix (el hotfix del 3-ago se aplicó solo en prod, nunca se limpió en local), así que el script lo volvió a subir y republicar. Last-Modified que vio Inma (10:55:35 GMT) coincide al segundo con mi ejecución.

Ya revertido:

  • sync_audio_to_prod.py --rollback --ids 18036 (meta borrado, mp3 fuera de prod, verificado 403 en la URL pública).
  • Meta local de #18036 limpiado (unsetaudio) para que no vuelva a parecer "listo para subir".
  • Fix de raíz en el código: PR #218sync_one() ahora comprueba fea_audio_skip antes que nada. Verificado con dry-run contra el cluster real de la 741: #18036: excluido (fea_audio_skip).

El CSS (esto no lo causé yo): revisé cada paso de mi despliegue de hoy — ninguno de mis scripts tocó post_content de #18036 ni de sus 4 traducciones (el clon a prod las excluyó por ya existir; el único paso que las tocó fue save_translations, que solo hace pll_save_post_translations, sin escribir contenido). La hipótesis de Inma sobre la rotación de slugs al crear la página nueva /numeros/ (#217) sigue siendo la pista más plausible para esa parte — no encontré nada en mi despliegue que la explique.

Dejo abierto el issue por si Inma quiere verificar el cierre completo, pero el audio queda resuelto y con guard de código, no solo de estado.

**Causa encontrada y corregida (2026-08-26): el audio volvió por un hueco en `sync_audio_to_prod.py`, no por el CSS** Investigado a fondo lo que reportó Inma hoy. Son dos problemas independientes que coincidieron en el tiempo: **El audio (esto sí lo causé yo, hoy):** el despliegue de la carta 741 (#216) incluye `#18036` en su cluster (es una pieza antigua enlazada desde la carta). Ejecuté `sync_audio_to_prod.py --carta 56290`, que solo comprueba `fea_audio_done` — nunca comprobó `fea_audio_skip`, el flag que el hotfix del 3-ago puso en `#18036` para excluirlo. Localmente `#18036` seguía teniendo `fea_audio_done=1` y el mp3 de 190 MiB de antes del hotfix (el hotfix del 3-ago se aplicó solo en prod, nunca se limpió en local), así que el script lo volvió a subir y republicar. `Last-Modified` que vio Inma (10:55:35 GMT) coincide al segundo con mi ejecución. Ya revertido: - `sync_audio_to_prod.py --rollback --ids 18036` (meta borrado, mp3 fuera de prod, verificado 403 en la URL pública). - Meta local de `#18036` limpiado (`unsetaudio`) para que no vuelva a parecer "listo para subir". - Fix de raíz en el código: PR #218 — `sync_one()` ahora comprueba `fea_audio_skip` antes que nada. Verificado con dry-run contra el cluster real de la 741: `#18036: excluido (fea_audio_skip)`. **El CSS (esto no lo causé yo):** revisé cada paso de mi despliegue de hoy — ninguno de mis scripts tocó `post_content` de `#18036` ni de sus 4 traducciones (el clon a prod las excluyó por ya existir; el único paso que las tocó fue `save_translations`, que solo hace `pll_save_post_translations`, sin escribir contenido). La hipótesis de Inma sobre la rotación de slugs al crear la página nueva `/numeros/` (#217) sigue siendo la pista más plausible para esa parte — no encontré nada en mi despliegue que la explique. Dejo abierto el issue por si Inma quiere verificar el cierre completo, pero el audio queda resuelto y con guard de código, no solo de estado.
Sign in to join this conversation.
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rafa/feadulta#190