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.