[Release prod] Enrique Martínez Lozano + carta «Hacia el corazón» — ejecución y findings #182
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Handoff de producción — Enrique Martínez Lozano + carta «Hacia el corazón»
Relacionado con #174 (cierre autónomo de carta) y #181 (derivados/TTS).
Este issue es el registro operativo único para el release y para los findings del operador.
Estado local aprobado
#55041: El tesoro está ya en nosotros.Comentarios al evangelio+Feadulta.wordpress/wp-content/uploads/tts/55041.mp3(2,221,101 bytes, vozNicoFeadulta2026).#55047, FR#55048, IT#55049, PT#55050.#55042: Hacia el corazón.#55051, FR#55052, IT#55053, PT#55054.El render local se verificó, incluido reproductor TTS y la carta FR.
Producción: origen y guardarraíl crítico
#54914, títuloHacia el corazón, estadopublish.fea_phase_a_source_prod_id=54914.#54914, nunca clonar otra carta ES.Prohibido
Su dry-run demostró que clonaría
#55042como una segunda carta ES; no actualiza#54914.Preflight obligatorio (sin escritura)
Estos dry-runs ya pasaron en local:
#55041planificado sin errores.Antes de escribir en producción:
#54914(título, contenido, autor, fecha, categorías, estado y hash);Ejecución solicitada al operador de producción
draft. Registrar los cinco IDs reales.55041). Verificar fichero, URL y metasfea_audio_done,fea_audio_url,fea_audio_voice.#54914con la candidata local#55042; conservar autor, fecha, categorías y estado existentes salvo incompatibilidad documentada.#54914y enlazarlas correctamente en Polylang. No crear una segunda carta ES.publish.Verificación y rollback
wp eval,wp post get, consultas Polylang/metas); no depender de navegador headless/Cloudflare.#54914al snapshot previo.Respuesta obligatoria del operador en ESTE issue
Responder con un comentario usando exactamente esta estructura:
Referencia local
docs/releases/enrique-2026-07-25-prod-dry-run.mdNo subir ni aplicar cambios fuera de este alcance.
Resultado de release
Estado:
bloqueado—rollback aplicadoEjecutado por: operador de producción, 25-jul-2026. Resultado registrado por Hermes porque el operador no pudo autenticarse para comentar en Gitea.
Preflight
#54914:logs/release-issue-182/backups/prod-54914-before-20260725T111148Z.json.logs/release-issue-182/backups/prod-54914-polylang-cluster-before-20260725T111337Z.json.#54914antes: SHA-2565e7eb1b880bfc6311edb78ac8b6e81639f5b4502282a1b182c610b1d01308b69.ok=1 skip=0 error=0.IDs y URLs en producción
#54997— draft.#54998/#54999/#55000/#55001— draft.#54997:/web/wp-content/uploads/tts/54997.mp3https://www.feadulta.com/wp-content/uploads/tts/54997.mp3NicoFeadulta2026.#54914— publish.#54979, FR#54980, IT#54981, PT#54982— publish.Aplicación y rollback
#54914y sus traducciones existentes, sin crear una segunda carta ES.55047–55050a IDs reales54998–55001; evidencia:logs/release-issue-182/card-links-remap-20260725T111700Z.json.read_fulldevolvió HTML de WordPress Database Error para varios posts. No se promovieron los drafts.logs/release-issue-182/rollback-carta-cluster-20260725T112000Z.json.Verificación posterior de Hermes (sólo lectura, server-side)
#54981(post, idioma Polylang, grupo y categorías): correctas, sin error SQL reportado.read_fullpara#54997,#54914,#54979–#54982: 18/18 JSON válidos, 0 fallos. El error original parece transitorio/intermitente, no una corrupción demostrada de#54981.Findings / desviaciones
Database Errorque apareció durante la verificación original. Que ahora las lecturas pasen no autoriza reintentar el release sin revisar los logs de MySQL/PHP/WordPress del intervalo20260725T111700Z–112000Z.Rollback
#54914/#54979/#54980/#54981/#54982.Siguiente paso propuesto
Diagnosticar en sólo lectura los logs de base de datos/PHP/WordPress del intervalo del incidente, documentar causa y prueba de estabilidad, y abrir un nuevo intento únicamente desde los drafts existentes
#54997–#55001(sin clonar ni recrear la carta).Seguimiento de diagnóstico — límite de conexiones MySQL + correcciones obligatorias del verificador
Hallazgo confirmado: límite por usuario de MySQL
Lectura server-side confirmada de la base MySQL de producción:
myfeadulta@localhost;max_connections=500global;Max_used_connections=61;WITH MAX_USER_CONNECTIONS 3;Threads_running=1.Por tanto, el global no estaba saturado, pero tres procesos PHP/WP simultáneos del mismo usuario pueden provocar un error de conexión. Esto es una causa probable fuerte del HTML
Database Error, aunque los logs accesibles ya no conservan el texto literal del incidente.El script estándar
sync_translations_to_prod.pyejecuta sus clones en serie; no hay evidencia en los artefactos de que él mismo lanzase cinco clones paralelos. El riesgo está en cualquier orquestador que paralelice helpers/verificaciones o solape procesos PHP.Hallazgo adicional: falsos negativos en
server-verification-20260725T111550Z.jsonEl rollback fue correcto por principio de seguridad, pero la verificación tiene defectos que deben repararse antes de un nuevo intento:
/?p=<id>, mientras el contenido de producción usa permalinks. El nombre de Enrique se encontraba exactamente una vez en cada carta, pero el check devolviópath_count=0/has_own_link=falsepor esa expectativa errónea.carta_content_matches_local=falseno es una comparación válida.draft; esa ruta no es un test válido para un post que todavía no es público. Debe verificar metas/archivo y render server-side con contexto de post.Gates obligatorios antes de reintentar
myfeadulta@localhost(propuesta inicial: 15) y verificar conSHOW GRANTS; no modificarmax_connectionsglobal. La operación debe hacerla quien administra MySQL y registrar el valor final.flock), worker/concurrencia1, sinxargs -P, procesos en background,Promise.allni verificaciones paralelas.#54997–#55001; no clonar otro grupo ni tocar la carta hasta que los gates 1–4 estén verdes.No se han hecho cambios en producción durante este diagnóstico.
Corrección de seguridad — no cambiar la configuración MySQL
El límite
MAX_USER_CONNECTIONS 3es una restricción operativa preexistente con la que Fe Adulta ya ha publicado correctamente. No está autorizado cambiar el grant,max_user_connectionsni ninguna configuración MySQL como parte de este release.El Gate 1 propuesto en el comentario anterior queda anulado. El reintento debe resolver el problema en la capa de ejecución: una sola operación PHP/WP remota a la vez, sin verificaciones/helpers concurrentes, y con una investigación del runner concreto que produjo el fallo.
No se ha aplicado ningún cambio de MySQL ni de producción durante el diagnóstico. Se documentará aquí el flujo serial mínimo y la causa encontrada antes de reintentar.
Propuesta de reintento de bajo riesgo — sin cambios MySQL
Investigación completada:
ThreadPool,asyncio,xargs -P,parallel, etc.) en los runners versionados.Decisión operativa
No cambiar MySQL.
MAX_USER_CONNECTIONS 3es una restricción histórica compatible con releases previos y no forma parte del alcance de este release.Alternativa de menor riesgo
Preparar un runner específico de #182 que use un lock remoto y una única sesión PHP/WP por fase:
flock -nremoto para impedir dos releases Fe Adulta simultáneos.wp-load.php, leer y guardar el snapshot completo del cluster de carta.Esto reduce al mínimo la presión sobre MySQL: como máximo un proceso PHP/WP controlado del release por fase, sin ráfagas ni solapamientos. Antes de usarlo debe existir dry-run local y dry-run remoto sólo lectura, ambos registrados en este issue.
Instrucción operativa vigente para el reintento
Esta nota sustituye cualquier propuesta anterior de cambiar MySQL: no se modifica MySQL ni su límite de conexiones.
Flujo obligatorio
#54997–#55001; no crear un segundo grupo.#54914y su cluster#54979–#54982; nunca clonar otra carta ES.Gitea: entorno y comentario obligatorio
Antes de cualquier escritura, cargar el entorno correcto y confirmar acceso al Gitea vivo:
FEA_GITEA_TOKENautentica comorafaengitea.feadulta.com; nunca imprimirlo ni copiarlo en logs. El operador debe comentar resultado, findings, IDs/URLs, dry-runs, snapshot, checks y rollback —si lo hubo— en este mismo issue y verificar que el comentario es visible.Resultado de release
Estado:
bloqueado—rollback aplicadoEjecutado por: Hermes / Luigi — 25-jul-2026 12:16 UTC
Preflight
logs/release-issue-182/retry-serial/snapshot.json#54997–#55001confirmado como grupo Polylang completo endraft; carta#54914/#54979–#54982confirmada existente ypublish.IDs y URLs en producción
/web/wp-content/uploads/tts/54997.mp3, 2,221,101 bytes; metas ya asociadas al ID ES real#54997.Verificaciones server-side
Findings / desviaciones
logs/release-issue-182/retry_issue182_serial.py; usa lock remoto atómico y una única sesión PHP/WP por fase.TypeError: Cannot access offset of type string on string) antes de devolver JSON válido. Según el plan vigente, se considera check fallido: no se publicó Enrique.Rollback
#54914,#54979,#54980,#54981,#54982en una sola fase PHP/WP serial.logs/release-issue-182/retry-serial/rollback.json.Corrección del fallo PHP del reintento serial
Causa exacta
El error
TypeError: Cannot access offset of type string on stringera un bug del verificador localretry_issue182_serial.py, no de WordPress/MySQL:expectedenviaba por idioma un string de contenido;['title']y['content'].Corrección y prueba
docs/backups/retry-serial-runner-20260725T161834Z/retry_issue182_serial.py.drafty el cluster de carta siguepublish.expectedahora contiene explícitamentetitleycontent.py_compile: PASS.verifyserver-side sólo lectura: devolvió JSON válido, sin TypeError ni HTML/Database Error.Resultado de la verificación post-rollback:
Estos checks rojos son esperados: la carta fue restaurada al snapshot anterior y por tanto aún no contiene el bloque de Enrique. No se ha ejecutado
applynipublish, ni se ha tocado producción durante esta corrección.El runner ya puede pasar a un nuevo dry-run completo; el siguiente apply sigue requiriendo snapshot fresco y todas las salvaguardas del comentario #430.
Dry-run completo del runner serial corregido
Estado: PASS para todas las fases de preflight/planificación; no se ejecutó
applynipublish.Backup y snapshot
docs/backups/retry-serial-dryrun-20260725T162323Z/.logs/release-issue-182/retry-serial/snapshot.json.publishy Enrique#54997–#55001siguedraft.Pruebas realizadas
py_compiledel runner corregido: PASS.dry-runserial server-side: PASS para los diez posts, con grupos Polylang completos y estados esperados.title+contentválidas.snapshotserial server-side: PASS.verifyserial server-side, sólo lectura: JSON válido y sinTypeError, HTML niDatabase Error.Resultado actual de
verify:Es el resultado esperado antes de aplicar: las cinco cartas conservan el snapshot restaurado y no contienen todavía el bloque de Enrique. No hay otros checks rojos (Polylang, estados de drafts, autor/categorías y audio están verdes en el plan/verificador).
El runner queda preparado para un apply futuro con snapshot fresco, lock remoto y rollback, pero esta ejecución no cambió contenido ni estados en producción.
Próximos pasos — después del dry-run PASS
No ejecutar
applytodavía. Falta un último guardrail: elverifyactual exige que Enrique esté endraft, por lo que sirve para la verificación pre-publicación, pero no para la comprobación final traspublish.Secuencia segura propuesta
verify-publishedde sólo lectura: debe exigirpublishpara Enrique y conservar las comprobaciones de contenido, Polylang, enlaces/permalinks y audio. Backup + dry-run antes de modificar el runner.applyuna sola vez, con el runner batch serial. No publicar todavía.verifypre-publicación. Sólo puede continuar si devuelveok: truesin checks.publishdel grupo #54997–#55001 en su única sesión PHP/WP serial.verify-published; sólo aceptarok: true.apply, restaurar el cluster de carta desde el snapshot fresco; no despublicar ni borrar Enrique sin evidencia de que el publish parcial lo requiera.No modificar MySQL. No clonar posts ni cartas. No usar paralelismo.
Guardrail
verify-publishedañadido y probadodocs/backups/retry-serial-runner-20260725T163016Z/retry_issue182_serial.py.verify-published; exigepublishpara Enrique y conserva los checks de contenido, Polylang, posición y TTS.py_compile: PASS.El resultado rojo es esperado antes de aplicar/publicar: Enrique permanece
drafty la carta continúa restaurada sin su bloque. No se ejecutóapply,publish, rollback ni ningún cambio MySQL.El runner queda preparado para la secuencia del comentario #434: preflight fresco → snapshot → apply → verify → publish → verify-published, con rollback del cluster si falla cualquier fase.
Resultado de release
Estado:
completadoEjecutado por: Hermes / Luigi — 25-jul-2026 16:35 UTC
Preflight
logs/release-issue-182/retry-serial/snapshot.jsonrafa, y runner serial bajo lock remoto.IDs y URLs en producción
/web/wp-content/uploads/tts/54997.mp3, 2,221,101 bytes,https://www.feadulta.com/wp-content/uploads/tts/54997.mp3, vozNicoFeadulta2026.Verificaciones server-side
Findings / desviaciones
verifypre-publicación yverify-publisheddevolvieron ambos{"checks":[],"ok":true}.Rollback