# Handoff técnico para Rafa — issues #174 y #175 ## Alcance Preparación técnica solamente. **No aplicar en producción desde aquí.** --- ## Issue #174 — cierre autónomo de la carta (publicar + rotar) ### Regla funcional confirmada por Inma/Mixbot La rotación que ya funcionó hoy manualmente por WP REST API para la carta 735 es esta, y **solo para el post ES**: 1. **nueva carta** → añadir cat **6** (`carta actual`) 2. **la que estaba en cat 6** → quitar cat 6, añadir cat **22** (`semana pasada`), manteniendo cat **21** (`archivo acumulativo`) 3. **la que estaba en cat 22** → quitar cat 22, dejar solo cat **21** 4. **no tocar traducciones** en este paso ### Decisión pendiente para Rafa Hay dos caminos válidos: #### Opción A — adaptar `rotate_cartas.php` a ES-only Pros: - reutiliza lógica ya existente y conocida en el repo - más coherente con el toolkit actual de scripts - fácil dejar `DRY-RUN` + `APPLY=1` Contras: - hay que modificar el script actual porque hoy rota todos los idiomas - si Mixbot publica por REST y la rotación la hace otro script aparte, el flujo queda repartido en dos piezas #### Opción B — que Mixbot haga la rotación por WP REST API Pros: - ya está validado en vivo hoy - mantiene toda la operación editorial en el mismo sitio donde ya se publica la carta - evita acoplar la decisión editorial a un script server-side adicional Contras: - la lógica queda fuera del repo de scripts PHP si no se documenta bien - conviene dejarla muy explícita para no divergirse del comportamiento esperado ### Recomendación técnica Mi lectura: **cualquiera vale**, pero elegiría según quién vaya a mantenerlo: - si Rafa quiere la lógica **versionada y centralizada en el repo**, mejor **A: `rotate_cartas.php` ES-only** - si Inma/Mixbot ya tienen el flujo sólido y prefieren autonomía total desde su lado, mejor **B: REST API desde Mixbot**, con la lógica documentada en el issue/runbook ### Si Rafa elige A (script ES-only), propuesta concreta Crear una variante mínima o adaptar `rotate_cartas.php` con estas reglas: - entrada: `CARTA=` - lookup solo en idioma `es` - localizar: - `actual_es` = post con cat 6 - `pasada_es` = post con cat 22 - `nueva_es` = `CARTA` - operaciones: - `pasada_es` pierde 22, conserva 21 - `actual_es` pierde 6, gana 22, conserva 21 - `nueva_es` gana 6 y conserva/gana 21 - **nunca tocar** posts EN/FR/IT/PT - mantener modo `DRY-RUN` ### Dry-run deseable para Rafa Salida ideal del dry-run: - `nueva_es=#54495 -> +6 (+21 si faltaba)` - `actual_es=#54254 -> -6 +22 (mantiene 21)` - `pasada_es=#53984 -> -22 (mantiene 21)` - `translations untouched` ### Decisión funcional que conviene dejar escrita Para evitar ambigüedad futura, dejar explícito en el issue o en el script: - `21` es acumulativa y la conservan todas las cartas archivables - `6` y `22` son mutuamente excluyentes - la rotación editorial inicial afecta **solo al ES** - las traducciones rotan/publican en un paso posterior independiente --- ## Issue #175 — endpoint `subir-avatar` ### Base ya existente El sistema actual ya tiene casi todo: - frontend lee `user_meta('foto_perfil')` - `face_crop_avatar.py` ya resuelve recorte/centrado de cara - `regen_avatars.php` ya existe para regeneración/ajustes - caso real listo para probar: **Silvia Martínez Cano**, `user_id=1112`, `slug=silvia-mtnz` ### Contrato recomendado Yo le propondría a Rafa este contrato por simplicidad operativa: #### Endpoint `POST /wp-json/fea/v1/subir-avatar` #### Auth La misma Application Password que ya usa `crear-autor` (`#166`), con el mismo criterio de permisos. #### Identificador **`user_id`** mejor que `slug`. Motivo: - Mixbot ya lo tiene en el roster - evita ambigüedades por slug cambiado / transliteraciones / colisiones - simplifica el lookup #### Formato de entrada **multipart/form-data** mejor que base64. Motivo: - más natural para subir imágenes - menos overhead - encaja mejor con `media_handle_sideload` / manejo típico WP - más fácil de depurar que base64 #### Campo esperado - `user_id` (obligatorio) - `file` (obligatorio) Opcionalmente: - `crop=server|client` si Rafa quiere dejar ambas puertas abiertas, aunque probablemente es overkill de entrada ### Recomendación de procesamiento Mi recomendación clara: **que el endpoint NO haga face-crop complejo** en la primera versión. Mejor v1: - Mixbot/Inma mandan la imagen **ya cuadrada** - estándar objetivo: **1254x1254** (el que ya usáis) - el endpoint solo: 1. valida usuario 2. guarda attachment 3. actualiza `foto_perfil` 4. devuelve `attachment_id`, `user_id`, `url` Pros: - mucho menos riesgo y menos dependencias server-side - evita meter OpenCV/Pillow/lógica pesada dentro del endpoint - más fácil de testear e idempotente - si mañana queréis face-crop automático, se añade en v2 ### Comportamiento recomendado del endpoint - requiere login + permiso tipo `edit_others_posts` (igual filosofía que `crear-autor`) - valida que `user_id` exista - acepta solo imagen (`jpg`, `jpeg`, `png`, `webp` si WP lo admite ahí) - crea attachment en Media Library - hace `update_user_meta($user_id, 'foto_perfil', $attachment_id)` - respuesta JSON: - `user_id` - `display_name` - `attachment_id` - `url` - `updated: true` ### Idempotencia mínima útil No hace falta obsesionarse en v1, pero sí conviene: - si se vuelve a subir otra foto para el mismo `user_id`, simplemente reemplazar el `foto_perfil` al nuevo attachment - opcional: si el hash del fichero coincide con el ya asignado, devolver `updated:false` ### Caso piloto sugerido Primera prueba con: - `user_id=1112` - `slug=silvia-mtnz` - imagen ya recortada por Inma/Mixbot a **1254x1254** Así la prueba valida solo el endpoint, no el pipeline de crop. --- ## Cierres administrativos Por estado funcional, salvo sorpresa: - `#166` → cerrable - `#168` → cerrable - `#170` → cerrable --- ## Recomendación final para Rafa Si quiere minimizar trabajo y riesgo: - **#174**: usar la lógica que ya validó Mixbot hoy, y solo portar al repo si él prefiere centralizar - **#175**: endpoint mínimo con `user_id` + multipart + imagen ya cuadrada a 1254x1254 Eso deja la autonomía mucho más cerca sin meterse en una refactor rara.