# 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=<es_id>`
- 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.
