Si audio.play() fallaba tras verificar el hash (p.ej. política de autoplay
del navegador tras el await de fetch+digest), el aviso "No se pudo verificar
el audio" quedaba fijo en pantalla aunque el usuario reprodujera el audio a
mano desde los controles nativos que ya estaban visibles. El evento 'play'
real del <audio> ahora limpia ese aviso y oculta el botón, sea cual sea el
camino por el que arrancó la reproducción.
Reportado por Rafa en el canal feadulta, viendo el estado atascado en
"Más allá de la trinchera..." (post 56196) tras el bloqueo 403 de Cloudflare.
Mismo patrón que carta_article_ids() (commit anterior): si el docker exec
contra el contenedor WP local fallaba (contenedor caído/inexistente),
local_meta() devolvía stdout vacío en vez de comprobar returncode.
sync_one() interpretaba la cadena vacía como "sin-audio-local" (skip), no
como error, así que un lote entero podía acabar "ok=0/skip=N" tras perder
Docker en vez de abortar.
Ahora lanza RuntimeError con el stderr si docker exec falla; se propaga
por sync_one() hasta el try/except de main(), que ya cuenta err y devuelve
1 — el wrapper aborta correctamente con exit=1 y 0 sincronizaciones.
Probado: FEA_WP_CONTAINER inexistente + --ids → aborta en el primer ID
(exit=1, ok=0). Camino normal (--ids con contenedor real, dry-run) sigue
dando el mismo resultado que antes.
Revisión de Codix en el PR #215.
- carta_article_ids() en sync_audio_to_prod.py ahora comprueba returncode
del docker exec y lanza RuntimeError si falla, en vez de devolver una
lista vacía silenciosa (bug preexistente: un fallo de Docker/consulta se
confundía con "la carta no tiene artículos").
- El wrapper resuelve --carta a fichero temporal en vez de mapfile con
sustitución de procesos (mapfile < <(cmd) no deja comprobar el exit
code real de cmd), captura el rc de python explícitamente y aborta con
exit 1 sin sincronizar nada si la consulta falla.
- sys.path ya no depende del cwd ("scripts" relativo): se pasa la ruta
absoluta de SCRIPT_DIR al script inline.
Probado: --carta real sin cambios de comportamiento (28 candidatos,
ok=21/skip=7); consulta rota a propósito (FEA_DB_CONTAINER inexistente)
aborta limpio con exit=1 y 0 sincronizaciones; invocación desde otro
directorio de trabajo da el mismo resultado que desde la raíz del repo.
Revisión de Codix en el PR #215.
Envoltorio de sync_audio_to_prod.py para lotes de posts: carga los IDs con
un array (mapfile/--ids/--file) antes de cualquier ssh y redirige el stdin
del proceso hijo a /dev/null, evitando el bug de un while-read compartiendo
stdin con ssh (ssh se come el resto del fichero de IDs y el bucle termina
sin error tras procesar solo el primero — visto en real el 2026-08-24,
lote de 184 que solo sincronizó 1). Para en el primer fallo real, sin
reintentos ciegos, y da un resumen ok/skip al cierre.
Documentado en README con ejemplo de uso y variables de entorno.
Los 30 .doc tal cual llegan por correo, de la carta 713 a la 740, para poder
reproducir el borrado silencioso por <w:smartTag> y diffear contra lo publicado.
Pedidos por Rafa en el #209, donde el barrido quedó bloqueado sin ellos.
Mu-plugin fea-accesibilidad.php:
- Atkinson Hyperlegible (OFL) self-hosted, subset latin regular+bold,
sustituye la tipografia global del cuerpo sin selector.
- Toggle claro/oscuro persistente (localStorage), anti-flash en wp_head,
boton inyectado junto al selector de idioma en el header.
- color-scheme: dark para controles nativos (audio, inputs) + overrides
puntuales donde el sitio fija colores claros a mano (hero, tarjetas,
selector de idioma, buscador movil, beta feedback, cookie consent).
- Acento mas claro (#e8899e) solo en modo oscuro: el carmesi de marca
(#8b1a2e) no llega a 2:1 de contraste sobre el fondo casi negro.
QA local (docker wordpress-web, capturas Playwright en tools/e2e/out/):
portada, tarjetas y single verificados en claro y oscuro.
main local y origin/main son dos historias sin ancestro comun (ver #194).
Analisis del agente: de las 46 commits solo-en-local, el unico contenido que
no esta ya reflejado en origin/main via el snapshot de julio (PR #179) son
estos 10 ficheros de docs/ (guias, handoffs, planes, benchmark, revisiones de
issues). Copiados tal cual desde main con "git show main:docs/X > docs/X".
Redactada una password SSH y una password cPanel/FTP en texto plano que
tenia handoff-carta-46956-2026-06-17.md (servidor CDMON antiguo) antes de
subirlo -- ver .env, nunca en docs versionados.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Los tres scripts asumian el CDMON viejo (134.0.10.170, sin Docker): host y
password hardcodeados, y el helper PHP ejecutandose directamente en el host.
Desde el cutover a Hetzner (Coolify/Docker, 2026-08-03) eso ya no aplica.
- FEA_PROD_HOST/FEA_PROD_PASS pasan a FEA_PROD_SSH_HOST/FEA_PROD_SSH_PASS
(mismo nombre en los tres scripts; sync_carta_from_prod.py ya los usaba).
- Nueva FEA_PROD_DOCKER_CONTAINER: si esta definida, el helper se sube y se
ejecuta con "docker exec -i" dentro del contenedor en vez de en el host.
- Auth por clave (ed25519 claude-code@feadulta): sshpass solo se usa si
FEA_PROD_SSH_PASS no esta vacia.
- Fix del gotcha de "docker exec CONTENEDOR cmd < ruta": esa redireccion la
resuelve el HOST, no el contenedor. El wrapping va dentro de "sh -c" para
que la redireccion se resuelva en el sitio correcto (afecta a la subida
del helper y a la verificacion de tamano del mp3 en audio).
Verificado: dry-run de los tres contra el WP local, y una lectura real de
prod (sync_carta_from_prod.py --carta 55487 --dry-run) que recupero
correctamente el cluster de la carta 738 y el contenido de un post concreto.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Este repo llevaba desde el 28-jun sin actualizarse (salvo sync_carta_from_prod.py).
Se pone al dia con 3 semanas de trabajo que solo vivian en el checkout local
completo del proyecto:
- Fix del buscador (issue #178): AND obligatorio en FULLTEXT (antes devolvia
practicamente todo el sitio con cualquier busqueda), exclusion de paginas
indice residuales de la migracion K2, soporte de frase exacta entre comillas.
- Nuevos mu-plugins ya desplegados a prod: fea-carta-id-api, fea-cloudflare-realip,
fea-crear-autor-api, fea-gsc-verification, fea-legacy-redirect (cutover
Joomla->WP, ver issue #162).
- TTS multi-voz por autor, scripts de traduccion, sync de audio a prod,
homenajes Mardones/Galarreta.
Se mantiene la estructura plana del repo (mu-plugins/ + scripts/, sin el
wordpress/wp-content/ del checkout completo) tal y como documenta el README:
es la convencion establecida para este repo, mas limpia para quien solo
necesita leer el codigo. Anadido .gitignore (cache de Python que no debia
commitearse).
Fuente: checkout local completo del proyecto, rama main (commit 69e849d).