From fb877ac5b9769b0863850b2f9f7abc89cf042c22 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:31:55 +0200 Subject: [PATCH 1/9] docs: point commit references to the rewritten history - Replace the 176 commit hashes cited in VERIFY, GITCLEAN, DECISIONS, TODO and SESSION with their rewritten equivalents, from the filter-repo commit map - Update the two hashes quoted inside one commit subject in VERIFY Why: the history rewrite changed every commit hash, so the old references point to commits that do not exist in this repository. --- docs/DECISIONS.md | 4 +- docs/GITCLEAN.md | 12 +- docs/SESSION.md | 2 +- docs/TODO.md | 6 +- docs/VERIFY.md | 338 +++++++++++++++++++++++----------------------- 5 files changed, 181 insertions(+), 181 deletions(-) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 244af850..a5258bf9 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -592,8 +592,8 @@ - `claude/compassionate-planck-gh6aof` entra en `main` con un **merge normal** («Create a merge commit»), **no con squash**. Los commits llegan a `main` uno a uno. - - Los **5 commits con autor `Claude `** (`ec8e901`, - `f9d1541`, `2ed0abb`, `8584e93`, `6a67ab3`) **no se rehacen ahora**: su + - Los **5 commits con autor `Claude `** (`c4719b2`, + `8af5641`, `9c583eb`, `1d5b3ca`, `d432c30`) **no se rehacen ahora**: su autor pasa a `bpstack` en la reescritura de la fase 2, con `git-filter-repo --mailmap`, la misma pasada que quita los ficheros privados (`GITCLEAN.md`). diff --git a/docs/GITCLEAN.md b/docs/GITCLEAN.md index 53240cdd..c1043554 100644 --- a/docs/GITCLEAN.md +++ b/docs/GITCLEAN.md @@ -50,7 +50,7 @@ el historial. sustitutos que el árbol actual; - `replacements-names.txt`: nombres del personal. En mayúsculas pasan a `EMP_01`…`EMP_11`, como el corpus del solver; en el resto, a nombres - inventados (los mismos que el árbol desde `4c6a038`). `Salvador Pérez` + inventados (los mismos que el árbol desde `42f6ac0`). `Salvador Pérez` se queda como autor; - `mailmap.txt`: `Claude`, `Azyd` y `bp` pasan a `bpstack` (los tres son el propietario); @@ -61,7 +61,7 @@ el historial. - `strip-blobs.txt`: los 2 blobs del informe Opera real de ingresos (`OperaPrint-sample.pdf`, su copia en `backend/test/data/` y el `.txt` que genera `pdf-parse`), hallado en el barrido de OpenCode y sustituido - en el árbol por una muestra sintética (`a2cfbec`) + en el árbol por una muestra sintética (`c50b09a`) (`--strip-blobs-with-ids`). - ✅ Barrido de OpenCode (2026-10-05): no añade secretos ni rutas nuevas; sí el informe Opera. Su informe escribió en claro la contraseña y el host @@ -80,7 +80,7 @@ el historial. `bpstack` y `GitHub`), pero las puntas no son idénticas: `main` aún no tenía los arreglos de la rama (nombres, informe Opera) y una regla tocaba una línea de este documento. Se repite tras fusionar la rama en `main`. -- ✅ Rama fusionada en `main` (PR #9, `289c737`). +- ✅ Rama fusionada en `main` (PR #9, `2d1005a`). - ✅ Ejecución definitiva en `work.git` (clon nuevo de GitHub, 2026-10-05), auditada: - 0 secretos, 0 dominios y nombres reales, 0 restos del informe Opera en @@ -160,7 +160,7 @@ propietario. - **Volcados de BD**: `backend/db-mysql/backup/backup_hotel_db_*.sql` (cuatro; versionados hasta el 2026-10-04, aunque `.gitignore` ya los ignoraba) y los ya borrados `backup_hotel_db-aiven.sql` y `backup_hotel_db-local.sql` - (commits `dad3cdc`, `f5d47d6` y `7ac45e7`). + (commits `f3d9898`, `d04504f` y `8109e5e`). - **Scripts con la contraseña de Aiven**, quitados del árbol el 2026-10-04: `backend/db-mysql/scripts/basics/` (10), `add-libre-number.ts`, `backfill-libre-numbers.ts`, `set-holidays-2026.ts` y `backup-aiven.sh`. @@ -229,8 +229,8 @@ Decidido en ADR-031 (2026-10-04). Se hace en la misma pasada de `git-filter-repo`, con `--mailmap`; no cambia el contenido de ningún commit. - **Obligatorio**: los 5 commits con autor `Claude `, - todos de la rama `claude/compassionate-planck-gh6aof` (`ec8e901`, `f9d1541`, - `2ed0abb`, `8584e93`, `6a67ab3`), pasan a `bpstack `. + todos de la rama `claude/compassionate-planck-gh6aof` (`c4719b2`, `8af5641`, + `9c583eb`, `1d5b3ca`, `d432c30`), pasan a `bpstack `. Ninguna otra rama tiene commits de ese autor (_comprobado el 2026-10-04_). - **A confirmar en el paso 2 (Propuesta)**: unificar también `Azyd` y `bp`, los dos con el mismo correo, en `bpstack`. diff --git a/docs/SESSION.md b/docs/SESSION.md index 9387c0c6..0bf11f52 100644 --- a/docs/SESSION.md +++ b/docs/SESSION.md @@ -55,7 +55,7 @@ Los cuatro `backend/db-mysql/backup/backup_hotel_db_{aiven,local}_2026*.sql` salieron del árbol el 2026-09-28 (`git rm --cached`; siguen en el disco del PC `dz`, ignorados por `.gitignore`). En el historial siguen estos cuatro y además `backup_hotel_db-aiven.sql` y `backup_hotel_db-local.sql`; entraron en -`dad3cdc`, `f5d47d6` y `7ac45e7`. **No se han abierto.** Se eliminan del +`f3d9898`, `d04504f` y `8109e5e`. **No se han abierto.** Se eliminan del historial en la fase 2, en todas las ramas: `main`, `chore/audit-prep-sprint-0`, `feature/ai-schedule-generator`, `feature/auth-hardening` y `feature/observability-pino` (sin tags). diff --git a/docs/TODO.md b/docs/TODO.md index 018de87a..c1ec6acd 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -18,7 +18,7 @@ hace que alguien reimplemente lo que ya existe. ## Rama `claude/compassionate-planck-gh6aof` -Fusionada en `main` el 2026-10-04 (PR #7, commit de merge `942fdfa`). La rama +Fusionada en `main` el 2026-10-04 (PR #7, commit de merge `5ccb64f`). La rama sigue siendo donde se arregla este fichero y se prueba en el entorno preview. ## Antes de publicar @@ -34,7 +34,7 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. 2026-10-04: se quitaron los 14 ficheros con la de Aiven (`basics/`, `add-libre-number.ts`, `backfill-libre-numbers.ts`, `set-holidays-2026.ts`, `backup-aiven.sh`) y los 3 scripts locales - leen `backend/.env`. Siguen en el historial desde `f5d47d6`, junto con + leen `backend/.env`. Siguen en el historial desde `d04504f`, junto con los 4 volcados de `backup/` (versionados hasta el 2026-10-04) y `backend/.env.example` hasta el 2026-10-02. Se limpian en la fase 2 (`GITCLEAN.md`) y después se rotan. `user-repository-login.test.ts` @@ -58,7 +58,7 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. nombres), los 18 `.http` de `backend/API REST/` (contraseñas, tokens y usuarios) y los nombres de los comentarios de `aiven/19_scheduling.sql` y `20251224_add_scheduling.sql`; los fixtures del corpus del solver - usan `EMP_01`…`EMP_07` desde `8af5a8b`. Quedan los usuarios de horarios + usan `EMP_01`…`EMP_07` desde `7328f13`. Quedan los usuarios de horarios en la BD (9 de 10 con nombre de pila). El autor de las guías del checklist y de los README es el propietario y se queda. Todo sigue en el historial (fase 2, `GITCLEAN.md`). diff --git a/docs/VERIFY.md b/docs/VERIFY.md index 229ee0b5..6ac996f3 100644 --- a/docs/VERIFY.md +++ b/docs/VERIFY.md @@ -23,10 +23,10 @@ CI). **Fuera de la tabla:** -- `f9d1541` y `2ed0abb` (fnb) y sus reverts `8584e93` y `6a67ab3`: se anulan +- `8af5641` y `9c583eb` (fnb) y sus reverts `1d5b3ca` y `d432c30`: se anulan entre sí y no cambian código. Sus dos puntos siguen abiertos en `TODO.md`. -- `baa8ed3`: documentación del entorno preview. -- Las filas marcadas «(otra IA)» las probó un segundo modelo con el mismo entorno y las mismas reglas; se revisaron sus peticiones. Las que había probado contra la ruta equivocada (`7f8ea41`, `fe4e006`, `80c540e` y las cuatro de logbook) se repitieron. +- `5236a84`: documentación del entorno preview. +- Las filas marcadas «(otra IA)» las probó un segundo modelo con el mismo entorno y las mismas reglas; se revisaron sus peticiones. Las que había probado contra la ruta equivocada (`997b80d`, `31c99c7`, `3d8dd70` y las cuatro de logbook) se repitieron. ## Estado previo @@ -44,169 +44,169 @@ CI). | Commit | Qué cambia | Punto de `TODO.md` que cierra | Prueba | Resultado | | ------ | ---------- | ----------------------------- | ------ | --------- | -| `ec8e901` | fix(logbook): validate messages, dates, route params and list queries | Logbook: validación incompleta | offset -1, limit 501, fecha 2026-02-31, mensaje vacío, importancia inválida, id no numérico → 400 | ✅ 2026-10-04 | -| `0a47a7f` | fix(logbook): take the entry author from the session, not the body | Logbook: cualquiera puede crear entradas en nombre de otro | qa_recep crea entrada con `author_id` de admin → se guarda con qa_recep | ✅ 2026-10-04 | -| `dba54de` | chore(githooks): mark commit-msg and pre-push as executable | — | no aplica | — | -| `7f8ea41` | fix(conciliation): validate dates, form entries, status and route params | Conciliation: sin validación Zod, a diferencia del resto del proyecto
Conciliation: un `reason` que no sea uno de los válidos falla en… | PUT /conciliations/:id/form con motivo inventado, importe sin número o líneas que faltan → 400 Zod | ✅ 2026-10-04 | -| `a592aae` | fix(search): limit global search to the modules the role can open | `mantenimiento` lee la lista negra por la búsqueda global | /api/search?q=ab|an|er: admin y recep ven parking y blacklist; mant solo maintenance y groups | ✅ 2026-10-04 | -| `cdb871a` | fix(activity): show only activity from modules the role can open | `mantenimiento` lee actividad de módulos vetados por `/api/activity` | /api/activity/recent: mant solo ve maintenance y groups | ✅ 2026-10-04 | -| `df41041` | fix(api): stop sending caught error messages in 500 responses | Maintenance: los errores 500 devuelven el mensaje de MySQL
El controlador de notificaciones devuelve `error.message` de MySQL
Parking: `listAvailableSpots` devuelve el mensaje de error de MySQL | `tests/security/no-raw-error-messages.test.ts` | 🧪 2026-10-04 | -| `a621139` | docs(todo): record how to merge the working branch without its extra history | — | no aplica | — | -| `3993e35` | fix(cashier): answer known shift, payment and count errors with 4xx | Cashier: los 500 de turnos, pagos y recuentos devuelven… | `tests/cashier/cashier-errors.test.ts` | 🧪 2026-10-04 | -| `96b9393` | fix(checklist): cap image uploads in multer instead of after buffering | Checklist: subida de imágenes sin límite de tamaño en multer | Avatar de 3 MB (límite 2 MB) → 413; checklist usa el mismo middleware | ✅ 2026-10-04 | -| `f2d19b0` | fix(uploads): check image uploads by their bytes, not the declared type | Blacklist: el tipo de imagen solo se valida por el mimetype que declara… | Avatar y blacklist con bytes no-imagen declarados `image/png` → 400 | ✅ 2026-10-04 | -| `33a0f00` | fix(uploads): use the checked image upload for maintenance and avatars | — | Avatar con tipo `text/plain` → 400; PNG válido → 200 | ✅ 2026-10-04 | -| `f6dc0ff` | fix(checklist): reject unknown checklists, foreign steps and bad record ids | Checklist: `:id` sin validar
Checklist: comentarios e imágenes no comprueban que el paso exista | Historial de un checklist inexistente → 404 (otra IA) | ✅ 2026-10-04 | -| `4051153` | fix(checklist): validate the history query instead of failing with 500 | Checklist: fechas de `/history` sin validar | /checklists/:id/history con fecha imposible, limit 0 y 101 → 400 (otra IA) | ✅ 2026-10-04 | -| `acc3f3d` | fix(maintenance): reject blank and oversized texts and invalid route ids | Maintenance: validación incompleta | Parte con título en blanco o descripción corta → 400 (otra IA) | ✅ 2026-10-04 | -| `d26cca9` | fix(blacklist): reject blank texts and impossible dates | — | Blacklist con nombre en blanco o fecha imposible → 400 (otra IA) | ✅ 2026-10-04 | -| `fe4e006` | fix(api): reject impossible calendar dates in every date param and field | — | /conciliations/day/2026-02-31 → 400 `INVALID_DATE_FORMAT`; /activity/recent?date_from=2026-02-31 → 400 | ✅ 2026-10-04 | -| `bd6cfe4` | fix(notifications): keep direct links inside the app and validate notices | `direct_link` de las notificaciones admite cualquier URL
Notificaciones manuales: destinatarios y textos sin validar | `direct_link` `//…`, `/…`, `https://…`, `javascript:` → 400; `userIds` no UUID y título vacío → 400 | ✅ 2026-10-04 | -| `42e827f` | docs(todo): restore the blank line between the groups and scheduling blocks | — | no aplica | — | -| `42fc46f` | fix(messages): validate participants and query params, escape LIKE search | Mensajería: sin validación de entrada ni transacción al crear
Mensajería: `limit` sin validar, `LIKE` sin escapar y fallo de la… | Conversación con participantes repetidos o UUID inválido → 400 (otra IA) | ✅ 2026-10-04 | -| `8948e37` | docs(todo): detail why the parking booking source is lost and what to decide | — | no aplica | — | -| `1949da7` | fix(logbook): answer 403 when editing someone else's entry | Logbook: editar una entrada ajena devuelve 500 | qa_gadmin edita la entrada de qa_recep → 403 `LOGBOOK_ONLY_AUTHOR_UPDATE` | ✅ 2026-10-04 | -| `9715966` | chore(backend): remove the unused email service and EJS view engine | — | Backend de preview arranca y sirve sin el servicio de email ni EJS | ✅ 2026-10-04 | -| `415b767` | fix(api): send Cache-Control: no-store on every API response | El API no envía `Cache-Control: no-store` | GET /api/auth/me → `Cache-Control: no-store` | ✅ 2026-10-04 | -| `1720bdd` | fix(backoffice): allow-list the columns supplier and invoice edits may set | Backoffice: inyección SQL por los nombres de campo | PATCH proveedor QA con clave `"notes = (SELECT 1), name"` → claves ignoradas, solo cambia `name` | ✅ 2026-10-04 | -| `5640950` | fix(backoffice): only fetch invoice PDFs from our own Cloudinary cloud | Backoffice: SSRF en la descarga de PDF | Factura con PDF en 169.254.169.254, dominio externo u otra cuenta de Cloudinary → 400 | ✅ 2026-10-04 | -| `25c0fc5` | fix(cloudinary): only delete files from the folder of the module asking | Blacklist: `DELETE /upload/:publicId` borra cualquier recurso de…
Backoffice: borrado arbitrario en Cloudinary | Factura con `public_id` fuera de `invoices/` (crear y validar) → 400 | ✅ 2026-10-04 | -| `e4a0254` | fix(backoffice): accept only integer year and month in batch payments | Backoffice: el pago en lote paga todos los meses con un objeto | Batch payment con year 2026.5 o "2026" → 400 (otra IA) | ✅ 2026-10-04 | -| `c5ee754` | fix(scheduling): stop constraint edits from setting their own status | — | PUT /scheduling/constraints/:id con `status: approved` → la clave se descarta (400 «al menos un campo»); PUT …/approve como recep → 403 | ✅ 2026-10-04 | -| `b94c5d3` | docs(todo): close the scheduling self-approval entry, note the open decision | Scheduling: un recepcionista puede aprobarse sus propias solicitudes | no aplica | — | -| `2569c05` | fix(parking): validate the booking update body against its columns | Parking: rutas de edición sin Zod | Reserva de parking con importe negativo o método de pago desconocido → 400 (otra IA) | ✅ 2026-10-04 | -| `59f8450` | fix(scheduling): bound bulk edits and validate employee id lists | Scheduling: validación incompleta | Edición masiva de 32 días → 400 (otra IA) | ✅ 2026-10-04 | -| `b931244` | fix(scheduling): stop sending solver internals in generate errors | Scheduling: errores del solver y del arranque devueltos al cliente. | `tests/security/no-raw-error-messages.test.ts` | 🧪 2026-10-04 | -| `7284910` | fix(blacklist): accept only images uploaded through the blacklist upload | Blacklist: el array `images` acepta cualquier URL | POST /api/blacklist con imagen de otro dominio o de otra carpeta de Cloudinary → 400 «Cada imagen debe haberse subido desde la lista negra» | ✅ 2026-10-04 | -| `91c8063` | fix(cloudinary): sanitise new public ids, accept legacy ones when deleting | Maintenance: `public_id` con el nombre original sin sanear. | Avatar subido como `mi foto ñ (1)#x.png` → public_id `avatar_…_mi_foto____1__x` (borrado después) | ✅ 2026-10-04 | -| `c95514d` | fix(cloudinary): check control characters without a control-character regex | — | `tests/uploads/cloudinary-url.test.ts` | 🧪 2026-10-04 | -| `65308c1` | chore(conciliation): drop the commented-out admin check in the monthly status | Conciliation: comentario de código muerto en el controlador mensual | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `527e6ad` | docs(tests): replace the outdated backend tests README | `backend/tests/README.md` está desfasado | no aplica | — | -| `84d79a0` | chore(db): keep a single copy of the Aiven CA certificate | Certificado de Aiven duplicado | El backend de preview conecta a Aiven con TLS verificado y `apply-migration.sh` usa la misma CA | ✅ 2026-10-04 | -| `852a4b9` | chore(env): bring the .env.example files in line with the code | Actualizar los `.env.example` | El backend de preview arranca con las variables de producción; `.env.example` sin valores | ✅ 2026-10-04 | -| `63c6bd6` | docs(agents): note that the unused email service is gone | Corregir el `AGENTS.md` raíz sobre next-intl | no aplica | — | -| `1b0619b` | docs(cashier): stop presenting the daily totals trigger as reliable | Corregir el `AGENTS.md` de cashier | no aplica | — | -| `009dd3c` | docs(logbook): document the input checks and close the stale entry | Corregir `backend/services/logbook/AGENTS.md` | no aplica | — | -| `738807c` | docs(todo): close the module and parking AGENTS.md entries, already fixed | Corregir los `AGENTS.md` de módulo
Corregir los `AGENTS.md` de parking | no aplica | — | -| `69c3d0d` | docs(blacklist): describe the real blacklist_entries columns and soft delete | Corregir el `AGENTS.md` de blacklist | no aplica | — | -| `9a2258c` | docs(backoffice): describe assets, both PDFs and the unenforced lifecycle | Corregir el `AGENTS.md` de backoffice | no aplica | — | -| `f3b6175` | docs(checklist): describe the param validation and the real table names | Corregir el `AGENTS.md` de checklist | no aplica | — | -| `e3f8c11` | refactor(api): drop param checks the route guards already make | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `ab2700b` | refactor(maintenance): drop the per-handler id checks the route guard makes | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `79f5831` | refactor(parking): call isCalendarDate directly instead of a one-line wrapper | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `3cd0e52` | chore(backend): remove the parking PDF placeholder and dead config code | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `34888a8` | chore(backend): remove the unused conciliation logic and payment method repo | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `229d9f7` | refactor(backend): remove exports nothing uses | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `196cb20` | docs(scheduling): fix PI and FO, the weights claim and the rotation emitter | Corregir los `AGENTS.md` de scheduling | no aplica | — | -| `dcfae5b` | chore(backend): remove the unused nodemailer dependency | — | Backend de preview arranca sin `nodemailer` | ✅ 2026-10-04 | -| `9be16b1` | fix(messages): store message notifications under the messages module | Notificaciones de mensajes con `module: 'system'` | Mensaje urgente QA → aviso con `module: messages`. Fallaba («Data truncated»): faltaba la migración, aplicada con `ff47ab7` | ✅ 2026-10-04 | -| `acf6feb` | fix(logbook): stop returning author and editor emails nobody displays | Emails de más en las respuestas de logbook | Listas de logbook: `author_name` sin campos de email | ✅ 2026-10-04 | -| `7090b47` | fix(checklist): keep attachment public_id on the server | Checklist: respuestas con campos internos | `tests/checklist/attachment-response.test.ts` | 🧪 2026-10-04 | -| `10064b1` | fix(checklist): reject control characters in comments, escape them in reports | Checklist: `checklist-report.ts` imprime los comentarios sin filtrar… | Comentario de checklist con ESC y BEL → 400 «caracteres no válidos»; con salto de línea → 201 (borrado después) | ✅ 2026-10-04 | -| `683b618` | refactor(blacklist): remove the unused image delete endpoint and API methods | Blacklist: `DELETE /upload/:publicId` borra cualquier imagen de la…
Ids sin codificar en las URL de `blacklistApi.ts` | DELETE /api/blacklist/upload/:id → 404 «Ruta no encontrada» | ✅ 2026-10-04 | -| `7804790` | refactor(parking): remove the analytics API and occupancy query nobody calls | Parking: código sin uso | GET /api/parking/stats/analytics/{trends,comparison,performance} → 404 | ✅ 2026-10-04 | -| `2f19ca8` | chore(frontend): remove the unused auth routes and dependencies | Quitar código muerto | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `4cbfbf7` | chore(frontend): remove the old backoffice tabs and their mock data | Backoffice: notas `IMPORTANT-PRODUCTION.MD` obsoletas | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `5df5e86` | chore(frontend): remove 19 unimported files and 3 unused dependencies | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `a67fe04` | refactor(frontend): remove unused exports and fix the real lint errors | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `eb216e4` | fix(reports): filter maintenance by the statuses the backend uses | Informes: la sección de mantenimiento filtra por estados que no… | Los 7 estados del filtro de informes → 200 en /api/maintenance; los antiguos `pending` y `resolved` → 400 | ✅ 2026-10-04 | -| `1de7d8b` | fix(blacklist): render search highlights as React nodes, not injected HTML | Blacklist: resaltado de búsqueda con `dangerouslySetInnerHTML` | Búsqueda "Mar" en blacklist → 6 `` en nombre y documento, solo con nodos de texto | ✅ 2026-10-04 | -| `1498c48` | fix(backoffice): neutralise spreadsheet formulas in the paid invoices CSV | Backoffice: inyección de fórmulas en la exportación CSV | CSV de pagadas con proveedor QA `=1+1 QA-Supplier-csv` → celda `"'=1+1 QA-Supplier-csv"` (blob interceptado, sin descarga) | ✅ 2026-10-04 | -| `af883d4` | style(frontend): apply Prettier to the TypeScript and JavaScript files | — | no aplica | — | -| `1d5fa18` | refactor(logbook): drop the req.user.isAdmin check that never holds | Logbook: comprobación de `isAdmin` que nunca se cumple
`backend/scheduling-solver/AGENTS.md` cita `backend/debug-*.js` | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `1c56b24` | fix(parking): read every occupying booking on the status map and wizard | Parking: el mapa de estado deja de ver reservas a partir de 50 | Mapa de estado de parking carga (20 plazas); el caso de más de 50 reservas lo cubre `tests/frontend/fetch-all-pages.test.ts` | 🧪 2026-10-04 | -| `60265d7` | refactor(blacklist): drop the created_by list filter and two unused actions | Blacklist: filtro `created_by` sin restricción | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `a459dbe` | docs(blacklist): note that no screen restores a deleted entry | — | no aplica | — | -| `60189a3` | chore(fnb): log uploads through the logger and fix the cache docs | Corregir el `AGENTS.md` de `fnb`
Fnb: `console.log` sin pasar por el logger del proyecto
Corregir el comentario de `canAccessFnb` | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | -| `dc6565c` | ci: lint, format, typecheck and DB-free tests on every push | CI mínimo | no aplica | — | -| `8138448` | fix(cashier): export the PDF when a text has an emoji or a non-Latin-1 letter | Cashier: la exportación a PDF puede romperse con emojis | `tests/frontend/pdf-text.test.ts`; mismo helper que el PDF de facturas probado en vivo con emoji (`d4ad8e1`) | 🧪 2026-10-04 | -| `d4ad8e1` | fix(pdf): keep free text drawable in the scheduling, invoice and editor PDFs | — | PDF de facturas pendientes con proveedor QA `🚀 Łódź ✓` → PDF de 2,9 KB sin error. Scheduling y editor: texto libre pasa por `encodableFor` (revisado) y `tests/frontend/pdf-text.test.ts` | ✅ 2026-10-04 | -| `809dcd8` | fix(logbook): stop solving, reopening, reading or editing deleted rows | Logbook: se pueden resolver, reabrir y marcar como leídas entradas…
Logbook: comentarios borrados que se pueden editar y volver a borrar | Entrada y comentario QA borrados: editar, borrar, resolver, reabrir y leer → 404 | ✅ 2026-10-04 | -| `24e7d24` | fix(messages): a removed participant can no longer edit or delete messages | Mensajería: un expulsado sigue editando y borrando sus mensajes | Participante expulsado de un grupo QA: editar y borrar su mensaje → 403 | ✅ 2026-10-04 | -| `79417b5` | fix(maintenance): keep deleted reports out of non-admin lists and writes | Maintenance: partes borrados visibles y modificables | Parte QA borrado: fuera de las listas no admin; escrituras → 400 (status, priority y assign daban 500, arreglado en `76e9c9f`) | ✅ 2026-10-04 | -| `7d8b419` | fix(parking): keep the floor or number a booking edit leaves unchanged | Parking: cambiar solo la planta puede mover la reserva a otra plaza | Reserva QA en -3 #2: solo número 3 → -3 #3; solo planta -2 → -2 #3; ambos → -3 #4 | ✅ 2026-10-04 | -| `03a6b4e` | fix(maintenance): only admins read a deleted report | — | Parte borrado: recep → 404, admin → 200 con `is_deleted` (otra IA y mía) | ✅ 2026-10-04 | -| `26ba020` | fix(notifications): only admin and group-admin run the pending check | — | POST /api/notifications/check-pending como recep → 403 | ✅ 2026-10-04 | -| `fc55127` | docs(logbook): record that any user may re-route an entry by commenting | Logbook: un comentario cambia la prioridad o el departamento de una…
Cualquier rol lanza a mano la generación de notificaciones
Maintenance: el detalle, las fotos y el historial de un parte borrado… | no aplica | — | -| `1f4bd5f` | fix(auth): leave the disabled demo-admin role out of the new rules | — | `tests/maintenance/deleted-reports.test.ts` (`isAdminRole` excluye `demo-admin`, desactivado) | 🧪 2026-10-04 | -| `1c6886c` | docs(todo): the demo user being disabled was not checked in the database | — | no aplica | — | -| `1b7c3f1` | fix(parking): free a deleted booking's days and validate list paging | Parking: borrar una reserva no libera sus días | Paginación inválida → 400 (otra IA). Reserva QA borrada: su plaza vuelve a salir libre en esas fechas (antes no) | ✅ 2026-10-04 | -| `80c540e` | fix(search): escape LIKE wildcards in every text search | — | /api/search?q=%% y q=__ → 0 resultados (q=ab → 5) | ✅ 2026-10-04 | -| `98ce180` | docs(release): plan the merge without squash and track the branch's fixes | — | no aplica | — | -| `0d97dac` | docs(deploy): record the QA users created for the preview tests | — | no aplica | — | -| `76e9c9f` | fix(maintenance): answer 400, not 500, when status, priority or assign hit a deleted report | — (hallado al probar `79417b5`) | Parte QA borrado: PATCH /status, /priority y /assign → 400; el parte no cambia | ✅ 2026-10-04 | -| `ff47ab7` | fix(db): add 'messages' to the notifications module ENUM | — (hallado al probar `9be16b1`) | Migración aplicada en Aiven; mensaje urgente QA → aviso con `module: messages` | ✅ 2026-10-04 | -| `e3b2667` | docs(db): add a migration runner and record the notifications ENUM as applied | — | `apply-migration.sh --dry-run` contra local y Aiven; segunda ejecución salta (idempotente) | ✅ 2026-10-04 | -| `ee2cb48` | docs(verify): close every row of the branch's production checks | — | no aplica | — | -| `ad9d14b` | fix(cashier): correct daily cash total inflated by trigger LEFT JOIN | Cashier: el total diario de efectivo se infla | Trigger en Aiven consultado: creado el 2026-09-29, sin el `LEFT JOIN` | ✅ 2026-10-04 | -| `b376ab7` | fix(cashier): expected cash is fund + cash income - vouchers still out of the drawer | Cashier: el descuadre calculado en el servidor siempre es 0 | Día QA 2026-12-01: noche con vale 20 €, income 100 y recuento 270 → esperado 280, descuadre −10; mañana (vale arrastrado, uno de 5 € cancelado) y tarde → esperado 180; cierre que justifica el vale → 200. Editar income recalcula. Vale pendiente pasa al día siguiente (noche 2026-12-02: 15 €). Datos QA borrados después | ✅ 2026-10-04 | -| `68f62b1` | docs(deploy): record the merge of PR #7 and how the branch continues | — | no aplica | — | -| `2c6f01b` | docs(decisions): keep simple permissions in blacklist, F&B, maintenance and cashier | Blacklist, F&B (dos), mantenimiento y caja: permisos por acción (ADR-032, intencionado) | no aplica | — | -| `0dd1610` | fix(fnb): reject negative, out-of-range and impossible-date manual entries | Fnb: `manualEntry` acepta cualquier número, incluido negativo o absurdamente grande | POST /api/fnb/entries con −50, 100.000.000, "12" (texto) o fecha 2026-02-31 → 400; no se escribió ninguna cifra | ✅ 2026-10-04 | -| `3cacb9e` | fix(fnb): reject non-PDF uploads and bound PDF parsing time | Fnb: el PDF subido no se valida por contenido, solo por extensión o mimetype declarado | Subida de un texto como `.pdf` → 422 «El fichero no es un PDF»; PDF con cabecera y cuerpo roto → 422 «No se pudo leer el PDF». Límite de páginas y tiempo: `tests/fnb/pdf-upload-guard.test.ts` | ✅ 2026-10-04 | -| `37c5031` | fix(backoffice): enforce invoice status transitions and add single payment revert | Backoffice: sin máquina de estados | Factura QA 36: pagar pendiente → 409; validar → 200; con validada, editar, validar, rechazar y subir PDF → 409; pagar → 200; recep en `/unpay` → 403; `/unpay` → 200 (`validated`, `paid_date` null), repetido → 409; id inexistente → 404; rechazar la pagada 35 → 409. Historial con el estado anterior real. Navegador: validadas sin Editar ni Subir PDF; botón «Revertir pago» en Pagadas revierte la 35 con confirmación. Datos QA restaurados | ✅ 2026-10-04 | -| `ab20f75` | docs(decisions): keep parking payments editable in any booking state | Parking: importes cobrados editables siempre (ADR-032, intencionado; queda abierto el historial del cobro) | no aplica | — | -| `90e61f2` | fix(scheduling): make constraint writes admin-only and keep locked cells consistent | Scheduling: cualquier rol edita o borra restricciones ajenas o ya aprobadas | recep: crear, editar y borrar restricción → 403; leer → 200. Admin, mes 114, restricción QA 54 (vacaciones de una empleada 9–10 de mayo, antes `L`): aprobar → celdas V bloqueadas; editar a 16–17 → 9–10 vuelven a `L` sin bloqueo y 16–17 quedan V bloqueadas; borrar → 16–17 vuelven a `L`. Historial: `manual_edit` «Restricción editada» y «Restricción eliminada». Celdas como estaban | ✅ 2026-10-04 | -| `1a2eab8` | docs(decisions): keep the messaging user directory open to every role | Mensajería: directorio de emails y roles (ADR-032, intencionado) | no aplica | — | -| `e0f2b1d` | fix(parking): prevent double bookings and keep the availability calendar extended | Parking: doble reserva al cambiar fechas; carrera al crear reserva y en la entrada; el calendario se acaba el 2026-12-26 | Aiven tras el despliegue: calendario ampliado al arrancar hasta 2027-10-04. Plaza -3/10, marzo 2027: crear encima de otra → 400; mover solo fechas encima de otra → 400; mover a días libres → 200 y el calendario se recoloca; cancelar libera solo sus días (trigger migrado). Concurrencia: falló (una 201 y una 500), arreglado en `b6e5f64` | ✅ 2026-10-04 (con `b6e5f64`) | -| `b6e5f64` | fix(parking): count overlapping bookings with a locking read | Parking: carrera al crear reserva | Dos rondas de 3 altas simultáneas en la misma plaza y fechas → una 201 y dos 400, sin 500. Reservas QA borradas y calendario limpio | ✅ 2026-10-04 | -| `7e5b675` | docs(todo): fold the backoffice pdf-download entry into retiring demo-admin | Backoffice: `/pdf-download` no exige `isRealAdmin` (se integra en «Retirar el rol demo-admin»; `demo` inactivo en Aiven) | no aplica | — | -| `7bf40f1` | docs(todo): drop the cashier entry about writes to missing columns | Cashier: operaciones que fallan siempre en producción (ya resuelto por `c4b79b6` y la migración 20260929; columnas comprobadas en Aiven) | no aplica | — | -| `b9072af` | fix(logbook): write each change and its history in one transaction | Logbook: el historial no es atómico | Aiven, entrada QA 141 con qa_recep: crear, editar, comentar, editar y borrar comentario, leído, no leído, resolver, reabrir y borrar → todo 200/201 y 11 filas en `logbook_history` en orden (la de borrado, después del borrado). Commit y rollback: `tests/logbook/atomic-history.test.ts` | ✅ 2026-10-04 | -| `d1cbd79` | refactor(notifications): type query params and catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `5554002` | refactor(messages): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `30884bf` | refactor(checklist): type catch clause instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `4a9f557` | refactor(conciliation): type catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `9df2b9a` | refactor(blacklist): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `86e4b16` | refactor(group): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `69eee7f` | refactor(maintenance): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `42460c3` | refactor(backoffice): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `e7fa474` | refactor(cashier): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `e9ac38f` | refactor(scheduling): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `f5e7312` | refactor(fnb): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `b0e04fc` | refactor(activity): type catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `4664e1f`) | ✅ 2026-10-04 | -| `4664e1f` | Merge branch 'chore/backend-types' into claude/compassionate-planck-gh6aof | Backend: 341 usos de `any` | Preview tras desplegar: listados de mantenimiento, blacklist, facturas, caja, conciliación, grupos, F&B, notificaciones, mensajes, checklist, scheduling y parking → 200; logbook crear y borrar → 201/200 | ✅ 2026-10-04 | -| `1e52f97` | fix(maintenance): keep NULL in the change history after the any cleanup | Regresión de la fusión anterior (historial con '' en vez de NULL; 5 tests) | Parte QA 041026-003 sin asignar → asignar a qa_mant: `maintenance_history` guarda `old_value` NULL en `assigned_to` y `assigned_type`. Parte borrado | ✅ 2026-10-04 | -| `0eaf236` | docs(todo): update the remaining any count after the backend typing | Backend: usos de `any` (quedan 55) | no aplica | — | -| `a341cd3` | docs(decisions): limit phase 1c to what must be fixed before publishing | Alcance de la fase 1c (ADR-033): TODO en «Antes de publicar» y «Después de publicar» | no aplica | — | -| `4e8b0c6` | fix(frontend): serve the design-system and fonts-test pages only in development | Proteger o quitar `/design-system` y `/fonts-test` | Navegador del dueño en preview: `/design-system` y `/fonts-test` → «HTTP ERROR 404» (la extensión no tiene permiso en ese dominio y curl solo ve el SSO de Vercel) | ✅ 2026-10-04 | -| `48a1c28` | fix(auth): fail closed if Render's proxy chain changes | `trust proxy 3` depende de la red de Render | Preview: login fallido con `X-Forwarded-For` falso distinto cada vez → `ratelimit-remaining` 4, 3, 2 (la IP falsa se ignora y la cadena se reconoce como la esperada). El fallo cerrado ante una cadena distinta: `tests/auth/client-ip.test.ts` | ✅ 2026-10-04 | -| `330ff9c` | fix(scheduling): keep health data to admin and out of the logs | Scheduling: datos de salud expuestos | Preview: recep en `/months`, `/constraints/by-period` y `/shift-stats` → 403; admin → 200. Log de INFEASIBLE: `tests/scheduling/constraint-permissions.test.ts` | ✅ 2026-10-04 | -| `0742693` | docs(verify): record the proxy chain and scheduling checks, move solver run retention to later | Retención de `solver_runs` (a «Después de publicar») | no aplica | — | -| `6dbfd3e` | docs(roadmap): plan a public demo environment as phase 5 | Demo pública (fase 5) | no aplica | — | -| `d164234` | feat(uploads): keep invoice PDFs, stamps and signatures private behind the API | Backoffice: PDFs de facturas, sello y firma públicos en Cloudinary | Preview tras migrar (`39b1661`): admin → PDF original y validado de las facturas 28, 29 y 34, sello 8 y firma 9 → 200 con su tipo; ZIP de 29 y 34 → 2 PDF; sin login → 401; recep en el sello → 403. Ninguna respuesta de facturas ni de sellos lleva `cloudinary.com`; `/pdf-url` → 404 | ✅ 2026-10-04 | -| `0f3f39c` | feat(blacklist): serve guest photos as private files through the API | Fotos de huéspedes de blacklist públicas (mismo problema que backoffice) | Preview: ficha 11 guarda `/api/blacklist/images/…`; la foto → 200 `image/jpeg` para admin y recep; un nombre con `../` → 404; el detalle de la ficha no lleva `cloudinary.com` | ✅ 2026-10-04 | -| `c55e87f` | feat(uploads): serve maintenance photos and checklist attachments through the API | Maintenance: fotos públicas (la parte que queda pasa a «Después de publicar») | Preview: fotos 6 y 7 de los partes 060326-002 y 180526-002 → 200 `image/png` y `image/jpeg`; el detalle del parte no lleva `cloudinary.com`. Checklist sin adjuntos en Aiven: solo `tests/checklist/attachment-response.test.ts` | ✅ 2026-10-04 | -| `cf066e8` | feat(frontend): load Google and Vercel Analytics only when configured | Cambio del dueño (analítica por variables de entorno) | Preview sin `NEXT_PUBLIC_GA_ID`: la CSP servida no incluye `googletagmanager.com` y el detector de `securitypolicyviolation` no registró ningún intento de cargarlo en todo el recorrido del 2026-10-04 (comprobación indirecta). Producción necesita `NEXT_PUBLIC_GA_ID` y `NEXT_PUBLIC_VERCEL_ANALYTICS` en Vercel para seguir igual que hoy | ✅ 2026-10-05 | -| `ef8a285` | chore: license the project under MIT | Licencia del proyecto | no aplica | — | -| `39b1661` | chore(uploads): script to make the already uploaded files private | Ficheros ya subidos siguen públicos | Aiven 2026-10-04: dry-run y `--apply`, 17 ficheros a `authenticated` (4 públicos: avatares y `main-sample`), filas de facturas, sellos, fotos y blacklist 11 reescritas; segunda pasada → 0 por migrar. Las 8 URL públicas de antes → 404 | ✅ 2026-10-04 | -| `5a45248` | docs(verify): record the private files migration and its checks in preview | Registro de la migración de ficheros privados | no aplica | — | -| `0e5a096` | chore(db): drop private scripts and dumps, read local credentials from .env | Contraseñas de la BD escritas en ficheros versionados (parte del árbol; el historial, en la fase 2) | `git grep` de la contraseña de Aiven en el árbol → 0 ficheros; los 4 volcados siguen en disco y fuera del índice; `bash -n` de los 3 scripts locales; hash de `mock-data.sql` comprobado con bcrypt. Los scripts locales no se han ejecutado | ✅ 2026-10-04 | -| `eb70761` | feat(frontend): add a Content-Security-Policy and COOP, drop X-Powered-By | Cabeceras del frontend en producción incompletas para L3 | Navegador en preview: la página trae CSP (origen de la API de preview y `vercel.live`), COOP `same-origin`, `X-Frame-Options` y sin `X-Powered-By`. Con un detector de `securitypolicyviolation`: panel, mantenimiento, logbook, parking, horarios, checklist, perfil, backoffice (visor y editor de PDF) y blacklist → 0 bloqueos. El editor dibuja el PDF sin `eval`. Sin hacer: HSTS `preload` (decisión del dueño; no recomendado) y SRI de Google Analytics (Google cambia el script) | ✅ 2026-10-04 | -| `65f8dc1` | fix(uploads): let the frontend show private images from the API | Regresión de `d164234`: el navegador bloqueaba las `` de ficheros privados (CORP `same-origin` de helmet) | Navegador en preview: foto de la ficha 11 de blacklist (800 px) y su descarga por `fetch` (200, 109 824 B); foto 7 del parte 180526-002; sello y firma en el editor de PDF y en la configuración de backoffice → cargan; 0 bloqueos de CSP. Editor cerrado sin guardar (facturas 28 y 36 siguen pendientes) | ✅ 2026-10-04 | -| `8af5a8b` | test(scheduling): anonymize employee names in solver corpus fixtures (OpenCode) | Nombres reales del personal en el repo (fixtures del solver) | Revisado: el diff solo cambia nombres por `EMP_01`…`EMP_07`; ningún nombre del personal en `tests/scheduling-corpus/` ni `scheduling-solver/`. Tras la fusión: vitest de scheduling 132 passed, pytest del solver 105 passed, 1 skipped | ✅ 2026-10-04 | -| `22d7d4d` | Merge branch 'chore/anonymize-solver-fixtures' into claude/compassionate-planck-gh6aof | (fusión) | Los mismos tests | ✅ 2026-10-04 | -| `ede875f` | chore: remove test requests and the planning importer with real staff data | Nombres reales del personal en el repo (parte del árbol) | `git grep` en el árbol: sin los 18 `.http` ni el importador; los comentarios de los dos SQL usan `uuid-employee-N`. Sin cambio de comportamiento | ✅ 2026-10-05 | -| `e97b9b8` | fix(auth): give every failed login the same minimum response time | Login: el tiempo de respuesta puede delatar si un usuario existe | Preview, 18 intentos fallidos por tipo, antes del arreglo: mediana 364 ms real frente a 340 ms inexistente (Mann-Whitney p ≈ 0,048). Después: 666 frente a 663 ms (p ≈ 0,27; un intento de 63,8 s fue el arranque en frío de Render). `tests/auth/login-timing.test.ts` | ✅ 2026-10-05 | -| `b708524` | docs(todo): fix the login timing entry and drop the done CSP entry | (documentación) | no aplica | — | -| `634343a` | chore(checklist): replace the real email distribution list with example contacts | Correos reales de personas en el checklist (hallado en el barrido del 2026-10-04) | `git grep` en el árbol de los dominios reales → 0; tests del checklist 28 passed. No probado en la interfaz | ✅ 2026-10-05 | -| `3eb77a0` | docs(gitclean): add what the history analysis found | Análisis de la fase 2 | no aplica | — | -| `dbb04fe` | docs(gitclean): note that the old Anthropic key is revoked | (documentación) | no aplica | — | -| `09c7679` | Merge pull request #8 from bpstack/claude/compassionate-planck-gh6aof | Fase 1c en producción | Producción tras el despliegue (Vercel y Render): cabeceras con CSP (con Google Analytics), COOP y HSTS, sin `X-Powered-By`; la API sirve las rutas nuevas de ficheros; en el navegador, foto de la ficha 11 de blacklist (800 px), sello y firma en el editor de PDF (cerrado sin guardar), PDF de la factura 34 por la API (200, 266 575 B), Google Analytics y Vercel Analytics cargan; recorrido por panel, mantenimiento, logbook, parking y perfil. Única pega: la CSP bloquea 2 píxeles de Google Analytics a `www.google.es` (audiencias de Google Signals), sin efecto en la medición | ✅ 2026-10-05 | -| `4c6a038` | chore: replace the remaining staff names with invented ones | Nombres reales del personal en el árbol (hallado al preparar las listas de la fase 2) | `git grep` en el árbol de los nombres del personal → solo el autor; ninguna fila del checklist en Aiven usa el id antiguo de la guía; tests de restricciones 37 passed. No probado en la interfaz | ✅ 2026-10-05 | -| `141661a` | docs(gitclean): record the approved phase 2 lists | (documentación) | no aplica | — | -| `a2cfbec` | test(fnb): replace the real Opera revenue report with a synthetic sample | Informe Opera real de ingresos en los fixtures (hallado en el barrido de OpenCode) | El texto extraído del PDF sintético tiene las mismas 344 líneas y estructura que el real (solo cambian cifras y nombre del hotel); backend 909 passed. No probado subiendo el PDF en la interfaz | ✅ 2026-10-05 | -| `b3dcc62` | docs(gitclean): record the OpenCode sweep and the dry run | (documentación) | no aplica | — | -| `5351fc5` | docs(gitclean): record the first real run and reword a line the rules touched | (documentación) | no aplica | — | -| `23a9d9c` | docs(verify): add the rows for b3dcc62 and 5351fc5 | (documentación) | no aplica | — | -| `289c737` | Merge pull request #9 from bpstack/claude/compassionate-planck-gh6aof | `main` con los arreglos previos a la limpieza | CI de la PR en verde (backend y frontend); árbol de `main` igual al de la rama. Despliegue de producción no revisado: solo cambian tests, documentación, un placeholder y el id de una guía | ✅ 2026-10-05 | -| `a2da823` | docs(gitclean): record the final rewrite and its audit | (documentación) | no aplica | — | -| `21dfe77` | feat(checklist): load the hotel's real content from a private repository at build time | (revertido en `b5e9c8e`) | no aplica | — | -| `21beff1` | chore(checklist): replace the hotel's procedures with example content | (revertido en `b5e9c8e`) | no aplica | — | -| `bce10ac` | chore: parametrize the hotel name and the local paths | Nombre del hotel y rutas personales en el código | El pie impreso lee `NEXT_PUBLIC_HOTEL_NAME` con «Hotel PMS» por defecto; CI en verde. Pie impreso no revisado en el preview | ✅ 2026-10-05 | -| `a2e1338` | chore(frontend): replace the screenshots that showed a staff user | Capturas con un usuario del personal | Revisado por el propietario en el preview | ✅ 2026-10-05 | -| `b6f7ef9` | test(fnb): remove chain and partner names from the synthetic Opera sample | Nombres de la cadena y del partner en la muestra | CI en verde (backend) | ✅ 2026-10-05 | -| `b18057c` | docs: describe the hotel's files without their names | (documentación) | no aplica | — | -| `b5e9c8e` | revert(checklist): keep the procedures in the repository instead of a private one | Contenido del checklist | CI en verde; ver `6b6aa0f` | ✅ 2026-10-05 | -| `6b6aa0f` | chore(checklist): generalize the hotel's procedures | Nombres de la cadena, sus sistemas y partners en el checklist | Las 22 referencias `guide:`/`reference:` de las tareas apuntan a ficheros existentes; tareas del backend idénticas a las del frontend; el propietario abrió los enlaces en el preview | ✅ 2026-10-05 | -| `3a0fdd0` | style(checklist): format the night audit tasks | (formato) | no aplica | — | +| `c4719b2` | fix(logbook): validate messages, dates, route params and list queries | Logbook: validación incompleta | offset -1, limit 501, fecha 2026-02-31, mensaje vacío, importancia inválida, id no numérico → 400 | ✅ 2026-10-04 | +| `ee00b32` | fix(logbook): take the entry author from the session, not the body | Logbook: cualquiera puede crear entradas en nombre de otro | qa_recep crea entrada con `author_id` de admin → se guarda con qa_recep | ✅ 2026-10-04 | +| `9342a3b` | chore(githooks): mark commit-msg and pre-push as executable | — | no aplica | — | +| `997b80d` | fix(conciliation): validate dates, form entries, status and route params | Conciliation: sin validación Zod, a diferencia del resto del proyecto
Conciliation: un `reason` que no sea uno de los válidos falla en… | PUT /conciliations/:id/form con motivo inventado, importe sin número o líneas que faltan → 400 Zod | ✅ 2026-10-04 | +| `dc134dc` | fix(search): limit global search to the modules the role can open | `mantenimiento` lee la lista negra por la búsqueda global | /api/search?q=ab|an|er: admin y recep ven parking y blacklist; mant solo maintenance y groups | ✅ 2026-10-04 | +| `095a09a` | fix(activity): show only activity from modules the role can open | `mantenimiento` lee actividad de módulos vetados por `/api/activity` | /api/activity/recent: mant solo ve maintenance y groups | ✅ 2026-10-04 | +| `6b1e523` | fix(api): stop sending caught error messages in 500 responses | Maintenance: los errores 500 devuelven el mensaje de MySQL
El controlador de notificaciones devuelve `error.message` de MySQL
Parking: `listAvailableSpots` devuelve el mensaje de error de MySQL | `tests/security/no-raw-error-messages.test.ts` | 🧪 2026-10-04 | +| `812f64f` | docs(todo): record how to merge the working branch without its extra history | — | no aplica | — | +| `990bc72` | fix(cashier): answer known shift, payment and count errors with 4xx | Cashier: los 500 de turnos, pagos y recuentos devuelven… | `tests/cashier/cashier-errors.test.ts` | 🧪 2026-10-04 | +| `6c5d33b` | fix(checklist): cap image uploads in multer instead of after buffering | Checklist: subida de imágenes sin límite de tamaño en multer | Avatar de 3 MB (límite 2 MB) → 413; checklist usa el mismo middleware | ✅ 2026-10-04 | +| `17b496b` | fix(uploads): check image uploads by their bytes, not the declared type | Blacklist: el tipo de imagen solo se valida por el mimetype que declara… | Avatar y blacklist con bytes no-imagen declarados `image/png` → 400 | ✅ 2026-10-04 | +| `b122d5e` | fix(uploads): use the checked image upload for maintenance and avatars | — | Avatar con tipo `text/plain` → 400; PNG válido → 200 | ✅ 2026-10-04 | +| `7e28102` | fix(checklist): reject unknown checklists, foreign steps and bad record ids | Checklist: `:id` sin validar
Checklist: comentarios e imágenes no comprueban que el paso exista | Historial de un checklist inexistente → 404 (otra IA) | ✅ 2026-10-04 | +| `3295769` | fix(checklist): validate the history query instead of failing with 500 | Checklist: fechas de `/history` sin validar | /checklists/:id/history con fecha imposible, limit 0 y 101 → 400 (otra IA) | ✅ 2026-10-04 | +| `50c0c85` | fix(maintenance): reject blank and oversized texts and invalid route ids | Maintenance: validación incompleta | Parte con título en blanco o descripción corta → 400 (otra IA) | ✅ 2026-10-04 | +| `a628c55` | fix(blacklist): reject blank texts and impossible dates | — | Blacklist con nombre en blanco o fecha imposible → 400 (otra IA) | ✅ 2026-10-04 | +| `31c99c7` | fix(api): reject impossible calendar dates in every date param and field | — | /conciliations/day/2026-02-31 → 400 `INVALID_DATE_FORMAT`; /activity/recent?date_from=2026-02-31 → 400 | ✅ 2026-10-04 | +| `f06d8d5` | fix(notifications): keep direct links inside the app and validate notices | `direct_link` de las notificaciones admite cualquier URL
Notificaciones manuales: destinatarios y textos sin validar | `direct_link` `//…`, `/…`, `https://…`, `javascript:` → 400; `userIds` no UUID y título vacío → 400 | ✅ 2026-10-04 | +| `2ca2847` | docs(todo): restore the blank line between the groups and scheduling blocks | — | no aplica | — | +| `6377b61` | fix(messages): validate participants and query params, escape LIKE search | Mensajería: sin validación de entrada ni transacción al crear
Mensajería: `limit` sin validar, `LIKE` sin escapar y fallo de la… | Conversación con participantes repetidos o UUID inválido → 400 (otra IA) | ✅ 2026-10-04 | +| `9783e4c` | docs(todo): detail why the parking booking source is lost and what to decide | — | no aplica | — | +| `17f13b4` | fix(logbook): answer 403 when editing someone else's entry | Logbook: editar una entrada ajena devuelve 500 | qa_gadmin edita la entrada de qa_recep → 403 `LOGBOOK_ONLY_AUTHOR_UPDATE` | ✅ 2026-10-04 | +| `88dfd58` | chore(backend): remove the unused email service and EJS view engine | — | Backend de preview arranca y sirve sin el servicio de email ni EJS | ✅ 2026-10-04 | +| `420a534` | fix(api): send Cache-Control: no-store on every API response | El API no envía `Cache-Control: no-store` | GET /api/auth/me → `Cache-Control: no-store` | ✅ 2026-10-04 | +| `9045b8e` | fix(backoffice): allow-list the columns supplier and invoice edits may set | Backoffice: inyección SQL por los nombres de campo | PATCH proveedor QA con clave `"notes = (SELECT 1), name"` → claves ignoradas, solo cambia `name` | ✅ 2026-10-04 | +| `99bd9f7` | fix(backoffice): only fetch invoice PDFs from our own Cloudinary cloud | Backoffice: SSRF en la descarga de PDF | Factura con PDF en 169.254.169.254, dominio externo u otra cuenta de Cloudinary → 400 | ✅ 2026-10-04 | +| `5127df8` | fix(cloudinary): only delete files from the folder of the module asking | Blacklist: `DELETE /upload/:publicId` borra cualquier recurso de…
Backoffice: borrado arbitrario en Cloudinary | Factura con `public_id` fuera de `invoices/` (crear y validar) → 400 | ✅ 2026-10-04 | +| `60dba0a` | fix(backoffice): accept only integer year and month in batch payments | Backoffice: el pago en lote paga todos los meses con un objeto | Batch payment con year 2026.5 o "2026" → 400 (otra IA) | ✅ 2026-10-04 | +| `c043ff8` | fix(scheduling): stop constraint edits from setting their own status | — | PUT /scheduling/constraints/:id con `status: approved` → la clave se descarta (400 «al menos un campo»); PUT …/approve como recep → 403 | ✅ 2026-10-04 | +| `1c48a16` | docs(todo): close the scheduling self-approval entry, note the open decision | Scheduling: un recepcionista puede aprobarse sus propias solicitudes | no aplica | — | +| `9c42b09` | fix(parking): validate the booking update body against its columns | Parking: rutas de edición sin Zod | Reserva de parking con importe negativo o método de pago desconocido → 400 (otra IA) | ✅ 2026-10-04 | +| `f591165` | fix(scheduling): bound bulk edits and validate employee id lists | Scheduling: validación incompleta | Edición masiva de 32 días → 400 (otra IA) | ✅ 2026-10-04 | +| `765e271` | fix(scheduling): stop sending solver internals in generate errors | Scheduling: errores del solver y del arranque devueltos al cliente. | `tests/security/no-raw-error-messages.test.ts` | 🧪 2026-10-04 | +| `1c3878d` | fix(blacklist): accept only images uploaded through the blacklist upload | Blacklist: el array `images` acepta cualquier URL | POST /api/blacklist con imagen de otro dominio o de otra carpeta de Cloudinary → 400 «Cada imagen debe haberse subido desde la lista negra» | ✅ 2026-10-04 | +| `1282e33` | fix(cloudinary): sanitise new public ids, accept legacy ones when deleting | Maintenance: `public_id` con el nombre original sin sanear. | Avatar subido como `mi foto ñ (1)#x.png` → public_id `avatar_…_mi_foto____1__x` (borrado después) | ✅ 2026-10-04 | +| `8fe1514` | fix(cloudinary): check control characters without a control-character regex | — | `tests/uploads/cloudinary-url.test.ts` | 🧪 2026-10-04 | +| `54a5210` | chore(conciliation): drop the commented-out admin check in the monthly status | Conciliation: comentario de código muerto en el controlador mensual | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `758883b` | docs(tests): replace the outdated backend tests README | `backend/tests/README.md` está desfasado | no aplica | — | +| `e8157bf` | chore(db): keep a single copy of the Aiven CA certificate | Certificado de Aiven duplicado | El backend de preview conecta a Aiven con TLS verificado y `apply-migration.sh` usa la misma CA | ✅ 2026-10-04 | +| `4d65223` | chore(env): bring the .env.example files in line with the code | Actualizar los `.env.example` | El backend de preview arranca con las variables de producción; `.env.example` sin valores | ✅ 2026-10-04 | +| `6bf04e9` | docs(agents): note that the unused email service is gone | Corregir el `AGENTS.md` raíz sobre next-intl | no aplica | — | +| `9b83a28` | docs(cashier): stop presenting the daily totals trigger as reliable | Corregir el `AGENTS.md` de cashier | no aplica | — | +| `2551afb` | docs(logbook): document the input checks and close the stale entry | Corregir `backend/services/logbook/AGENTS.md` | no aplica | — | +| `8ba44f0` | docs(todo): close the module and parking AGENTS.md entries, already fixed | Corregir los `AGENTS.md` de módulo
Corregir los `AGENTS.md` de parking | no aplica | — | +| `4fea090` | docs(blacklist): describe the real blacklist_entries columns and soft delete | Corregir el `AGENTS.md` de blacklist | no aplica | — | +| `4f64a36` | docs(backoffice): describe assets, both PDFs and the unenforced lifecycle | Corregir el `AGENTS.md` de backoffice | no aplica | — | +| `f6d15f8` | docs(checklist): describe the param validation and the real table names | Corregir el `AGENTS.md` de checklist | no aplica | — | +| `d7b7c83` | refactor(api): drop param checks the route guards already make | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `404f99e` | refactor(maintenance): drop the per-handler id checks the route guard makes | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `31371bd` | refactor(parking): call isCalendarDate directly instead of a one-line wrapper | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `9a8d9e8` | chore(backend): remove the parking PDF placeholder and dead config code | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `55c5a18` | chore(backend): remove the unused conciliation logic and payment method repo | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `d3873b1` | refactor(backend): remove exports nothing uses | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `ac0c937` | docs(scheduling): fix PI and FO, the weights claim and the rotation emitter | Corregir los `AGENTS.md` de scheduling | no aplica | — | +| `a82c284` | chore(backend): remove the unused nodemailer dependency | — | Backend de preview arranca sin `nodemailer` | ✅ 2026-10-04 | +| `25f3334` | fix(messages): store message notifications under the messages module | Notificaciones de mensajes con `module: 'system'` | Mensaje urgente QA → aviso con `module: messages`. Fallaba («Data truncated»): faltaba la migración, aplicada con `5365d5c` | ✅ 2026-10-04 | +| `e2fdf9c` | fix(logbook): stop returning author and editor emails nobody displays | Emails de más en las respuestas de logbook | Listas de logbook: `author_name` sin campos de email | ✅ 2026-10-04 | +| `77d8f22` | fix(checklist): keep attachment public_id on the server | Checklist: respuestas con campos internos | `tests/checklist/attachment-response.test.ts` | 🧪 2026-10-04 | +| `967d65f` | fix(checklist): reject control characters in comments, escape them in reports | Checklist: `checklist-report.ts` imprime los comentarios sin filtrar… | Comentario de checklist con ESC y BEL → 400 «caracteres no válidos»; con salto de línea → 201 (borrado después) | ✅ 2026-10-04 | +| `7574d8c` | refactor(blacklist): remove the unused image delete endpoint and API methods | Blacklist: `DELETE /upload/:publicId` borra cualquier imagen de la…
Ids sin codificar en las URL de `blacklistApi.ts` | DELETE /api/blacklist/upload/:id → 404 «Ruta no encontrada» | ✅ 2026-10-04 | +| `c7a2703` | refactor(parking): remove the analytics API and occupancy query nobody calls | Parking: código sin uso | GET /api/parking/stats/analytics/{trends,comparison,performance} → 404 | ✅ 2026-10-04 | +| `b293e28` | chore(frontend): remove the unused auth routes and dependencies | Quitar código muerto | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `364ce12` | chore(frontend): remove the old backoffice tabs and their mock data | Backoffice: notas `IMPORTANT-PRODUCTION.MD` obsoletas | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `e276f80` | chore(frontend): remove 19 unimported files and 3 unused dependencies | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `0014b3a` | refactor(frontend): remove unused exports and fix the real lint errors | — | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `da5822d` | fix(reports): filter maintenance by the statuses the backend uses | Informes: la sección de mantenimiento filtra por estados que no… | Los 7 estados del filtro de informes → 200 en /api/maintenance; los antiguos `pending` y `resolved` → 400 | ✅ 2026-10-04 | +| `eca5507` | fix(blacklist): render search highlights as React nodes, not injected HTML | Blacklist: resaltado de búsqueda con `dangerouslySetInnerHTML` | Búsqueda "Mar" en blacklist → 6 `` en nombre y documento, solo con nodos de texto | ✅ 2026-10-04 | +| `ee73ff7` | fix(backoffice): neutralise spreadsheet formulas in the paid invoices CSV | Backoffice: inyección de fórmulas en la exportación CSV | CSV de pagadas con proveedor QA `=1+1 QA-Supplier-csv` → celda `"'=1+1 QA-Supplier-csv"` (blob interceptado, sin descarga) | ✅ 2026-10-04 | +| `425a194` | style(frontend): apply Prettier to the TypeScript and JavaScript files | — | no aplica | — | +| `5f04740` | refactor(logbook): drop the req.user.isAdmin check that never holds | Logbook: comprobación de `isAdmin` que nunca se cumple
`backend/scheduling-solver/AGENTS.md` cita `backend/debug-*.js` | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `032fb13` | fix(parking): read every occupying booking on the status map and wizard | Parking: el mapa de estado deja de ver reservas a partir de 50 | Mapa de estado de parking carga (20 plazas); el caso de más de 50 reservas lo cubre `tests/frontend/fetch-all-pages.test.ts` | 🧪 2026-10-04 | +| `79f8913` | refactor(blacklist): drop the created_by list filter and two unused actions | Blacklist: filtro `created_by` sin restricción | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `a64fc0d` | docs(blacklist): note that no screen restores a deleted entry | — | no aplica | — | +| `9f269c7` | chore(fnb): log uploads through the logger and fix the cache docs | Corregir el `AGENTS.md` de `fnb`
Fnb: `console.log` sin pasar por el logger del proyecto
Corregir el comentario de `canAccessFnb` | Recorrido de los 17 módulos como admin en preview: cargan y pintan sin peticiones fallidas (fuera de la barra de Vercel); CI verde | ✅ 2026-10-04 | +| `af9811b` | ci: lint, format, typecheck and DB-free tests on every push | CI mínimo | no aplica | — | +| `861d972` | fix(cashier): export the PDF when a text has an emoji or a non-Latin-1 letter | Cashier: la exportación a PDF puede romperse con emojis | `tests/frontend/pdf-text.test.ts`; mismo helper que el PDF de facturas probado en vivo con emoji (`a9f44fb`) | 🧪 2026-10-04 | +| `a9f44fb` | fix(pdf): keep free text drawable in the scheduling, invoice and editor PDFs | — | PDF de facturas pendientes con proveedor QA `🚀 Łódź ✓` → PDF de 2,9 KB sin error. Scheduling y editor: texto libre pasa por `encodableFor` (revisado) y `tests/frontend/pdf-text.test.ts` | ✅ 2026-10-04 | +| `7bebc39` | fix(logbook): stop solving, reopening, reading or editing deleted rows | Logbook: se pueden resolver, reabrir y marcar como leídas entradas…
Logbook: comentarios borrados que se pueden editar y volver a borrar | Entrada y comentario QA borrados: editar, borrar, resolver, reabrir y leer → 404 | ✅ 2026-10-04 | +| `acb24fa` | fix(messages): a removed participant can no longer edit or delete messages | Mensajería: un expulsado sigue editando y borrando sus mensajes | Participante expulsado de un grupo QA: editar y borrar su mensaje → 403 | ✅ 2026-10-04 | +| `0d2b9cf` | fix(maintenance): keep deleted reports out of non-admin lists and writes | Maintenance: partes borrados visibles y modificables | Parte QA borrado: fuera de las listas no admin; escrituras → 400 (status, priority y assign daban 500, arreglado en `25a3df3`) | ✅ 2026-10-04 | +| `66e5c4e` | fix(parking): keep the floor or number a booking edit leaves unchanged | Parking: cambiar solo la planta puede mover la reserva a otra plaza | Reserva QA en -3 #2: solo número 3 → -3 #3; solo planta -2 → -2 #3; ambos → -3 #4 | ✅ 2026-10-04 | +| `a77f94f` | fix(maintenance): only admins read a deleted report | — | Parte borrado: recep → 404, admin → 200 con `is_deleted` (otra IA y mía) | ✅ 2026-10-04 | +| `dbfd651` | fix(notifications): only admin and group-admin run the pending check | — | POST /api/notifications/check-pending como recep → 403 | ✅ 2026-10-04 | +| `ae68010` | docs(logbook): record that any user may re-route an entry by commenting | Logbook: un comentario cambia la prioridad o el departamento de una…
Cualquier rol lanza a mano la generación de notificaciones
Maintenance: el detalle, las fotos y el historial de un parte borrado… | no aplica | — | +| `7ef4e77` | fix(auth): leave the disabled demo-admin role out of the new rules | — | `tests/maintenance/deleted-reports.test.ts` (`isAdminRole` excluye `demo-admin`, desactivado) | 🧪 2026-10-04 | +| `05d58ff` | docs(todo): the demo user being disabled was not checked in the database | — | no aplica | — | +| `6dc2ba1` | fix(parking): free a deleted booking's days and validate list paging | Parking: borrar una reserva no libera sus días | Paginación inválida → 400 (otra IA). Reserva QA borrada: su plaza vuelve a salir libre en esas fechas (antes no) | ✅ 2026-10-04 | +| `3d8dd70` | fix(search): escape LIKE wildcards in every text search | — | /api/search?q=%% y q=__ → 0 resultados (q=ab → 5) | ✅ 2026-10-04 | +| `00cc932` | docs(release): plan the merge without squash and track the branch's fixes | — | no aplica | — | +| `e249dea` | docs(deploy): record the QA users created for the preview tests | — | no aplica | — | +| `25a3df3` | fix(maintenance): answer 400, not 500, when status, priority or assign hit a deleted report | — (hallado al probar `0d2b9cf`) | Parte QA borrado: PATCH /status, /priority y /assign → 400; el parte no cambia | ✅ 2026-10-04 | +| `5365d5c` | fix(db): add 'messages' to the notifications module ENUM | — (hallado al probar `25f3334`) | Migración aplicada en Aiven; mensaje urgente QA → aviso con `module: messages` | ✅ 2026-10-04 | +| `c0f61c1` | docs(db): add a migration runner and record the notifications ENUM as applied | — | `apply-migration.sh --dry-run` contra local y Aiven; segunda ejecución salta (idempotente) | ✅ 2026-10-04 | +| `0d923c1` | docs(verify): close every row of the branch's production checks | — | no aplica | — | +| `0b8b008` | fix(cashier): correct daily cash total inflated by trigger LEFT JOIN | Cashier: el total diario de efectivo se infla | Trigger en Aiven consultado: creado el 2026-09-29, sin el `LEFT JOIN` | ✅ 2026-10-04 | +| `f22d799` | fix(cashier): expected cash is fund + cash income - vouchers still out of the drawer | Cashier: el descuadre calculado en el servidor siempre es 0 | Día QA 2026-12-01: noche con vale 20 €, income 100 y recuento 270 → esperado 280, descuadre −10; mañana (vale arrastrado, uno de 5 € cancelado) y tarde → esperado 180; cierre que justifica el vale → 200. Editar income recalcula. Vale pendiente pasa al día siguiente (noche 2026-12-02: 15 €). Datos QA borrados después | ✅ 2026-10-04 | +| `56a2743` | docs(deploy): record the merge of PR #7 and how the branch continues | — | no aplica | — | +| `cbd338f` | docs(decisions): keep simple permissions in blacklist, F&B, maintenance and cashier | Blacklist, F&B (dos), mantenimiento y caja: permisos por acción (ADR-032, intencionado) | no aplica | — | +| `938fed1` | fix(fnb): reject negative, out-of-range and impossible-date manual entries | Fnb: `manualEntry` acepta cualquier número, incluido negativo o absurdamente grande | POST /api/fnb/entries con −50, 100.000.000, "12" (texto) o fecha 2026-02-31 → 400; no se escribió ninguna cifra | ✅ 2026-10-04 | +| `b578dbf` | fix(fnb): reject non-PDF uploads and bound PDF parsing time | Fnb: el PDF subido no se valida por contenido, solo por extensión o mimetype declarado | Subida de un texto como `.pdf` → 422 «El fichero no es un PDF»; PDF con cabecera y cuerpo roto → 422 «No se pudo leer el PDF». Límite de páginas y tiempo: `tests/fnb/pdf-upload-guard.test.ts` | ✅ 2026-10-04 | +| `9847ef7` | fix(backoffice): enforce invoice status transitions and add single payment revert | Backoffice: sin máquina de estados | Factura QA 36: pagar pendiente → 409; validar → 200; con validada, editar, validar, rechazar y subir PDF → 409; pagar → 200; recep en `/unpay` → 403; `/unpay` → 200 (`validated`, `paid_date` null), repetido → 409; id inexistente → 404; rechazar la pagada 35 → 409. Historial con el estado anterior real. Navegador: validadas sin Editar ni Subir PDF; botón «Revertir pago» en Pagadas revierte la 35 con confirmación. Datos QA restaurados | ✅ 2026-10-04 | +| `3de0012` | docs(decisions): keep parking payments editable in any booking state | Parking: importes cobrados editables siempre (ADR-032, intencionado; queda abierto el historial del cobro) | no aplica | — | +| `ebee351` | fix(scheduling): make constraint writes admin-only and keep locked cells consistent | Scheduling: cualquier rol edita o borra restricciones ajenas o ya aprobadas | recep: crear, editar y borrar restricción → 403; leer → 200. Admin, mes 114, restricción QA 54 (vacaciones de una empleada 9–10 de mayo, antes `L`): aprobar → celdas V bloqueadas; editar a 16–17 → 9–10 vuelven a `L` sin bloqueo y 16–17 quedan V bloqueadas; borrar → 16–17 vuelven a `L`. Historial: `manual_edit` «Restricción editada» y «Restricción eliminada». Celdas como estaban | ✅ 2026-10-04 | +| `005211e` | docs(decisions): keep the messaging user directory open to every role | Mensajería: directorio de emails y roles (ADR-032, intencionado) | no aplica | — | +| `c845875` | fix(parking): prevent double bookings and keep the availability calendar extended | Parking: doble reserva al cambiar fechas; carrera al crear reserva y en la entrada; el calendario se acaba el 2026-12-26 | Aiven tras el despliegue: calendario ampliado al arrancar hasta 2027-10-04. Plaza -3/10, marzo 2027: crear encima de otra → 400; mover solo fechas encima de otra → 400; mover a días libres → 200 y el calendario se recoloca; cancelar libera solo sus días (trigger migrado). Concurrencia: falló (una 201 y una 500), arreglado en `8db94f1` | ✅ 2026-10-04 (con `8db94f1`) | +| `8db94f1` | fix(parking): count overlapping bookings with a locking read | Parking: carrera al crear reserva | Dos rondas de 3 altas simultáneas en la misma plaza y fechas → una 201 y dos 400, sin 500. Reservas QA borradas y calendario limpio | ✅ 2026-10-04 | +| `567d9c4` | docs(todo): fold the backoffice pdf-download entry into retiring demo-admin | Backoffice: `/pdf-download` no exige `isRealAdmin` (se integra en «Retirar el rol demo-admin»; `demo` inactivo en Aiven) | no aplica | — | +| `93bb20d` | docs(todo): drop the cashier entry about writes to missing columns | Cashier: operaciones que fallan siempre en producción (ya resuelto por `210f445` y la migración 20260929; columnas comprobadas en Aiven) | no aplica | — | +| `16fc3bf` | fix(logbook): write each change and its history in one transaction | Logbook: el historial no es atómico | Aiven, entrada QA 141 con qa_recep: crear, editar, comentar, editar y borrar comentario, leído, no leído, resolver, reabrir y borrar → todo 200/201 y 11 filas en `logbook_history` en orden (la de borrado, después del borrado). Commit y rollback: `tests/logbook/atomic-history.test.ts` | ✅ 2026-10-04 | +| `4e2fabc` | refactor(notifications): type query params and catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `47e1c0c` | refactor(messages): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `5f8670e` | refactor(checklist): type catch clause instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `e9cb75d` | refactor(conciliation): type catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `bceb353` | refactor(blacklist): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `8ab6209` | refactor(group): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `916c378` | refactor(maintenance): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `2ab1bbc` | refactor(backoffice): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `af2f731` | refactor(cashier): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `5c282b3` | refactor(scheduling): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `2809745` | refactor(fnb): type catch clauses and query params instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `528c08a` | refactor(activity): type catch clauses instead of any | Backend: 341 usos de `any` (otra IA) | Sin cambio de comportamiento: typecheck, lint y 883 tests tras la fusión; humo en preview (abajo, `c1510e2`) | ✅ 2026-10-04 | +| `c1510e2` | Merge branch 'chore/backend-types' into claude/compassionate-planck-gh6aof | Backend: 341 usos de `any` | Preview tras desplegar: listados de mantenimiento, blacklist, facturas, caja, conciliación, grupos, F&B, notificaciones, mensajes, checklist, scheduling y parking → 200; logbook crear y borrar → 201/200 | ✅ 2026-10-04 | +| `c14ddcd` | fix(maintenance): keep NULL in the change history after the any cleanup | Regresión de la fusión anterior (historial con '' en vez de NULL; 5 tests) | Parte QA 041026-003 sin asignar → asignar a qa_mant: `maintenance_history` guarda `old_value` NULL en `assigned_to` y `assigned_type`. Parte borrado | ✅ 2026-10-04 | +| `26eedc1` | docs(todo): update the remaining any count after the backend typing | Backend: usos de `any` (quedan 55) | no aplica | — | +| `e72ac44` | docs(decisions): limit phase 1c to what must be fixed before publishing | Alcance de la fase 1c (ADR-033): TODO en «Antes de publicar» y «Después de publicar» | no aplica | — | +| `4748168` | fix(frontend): serve the design-system and fonts-test pages only in development | Proteger o quitar `/design-system` y `/fonts-test` | Navegador del dueño en preview: `/design-system` y `/fonts-test` → «HTTP ERROR 404» (la extensión no tiene permiso en ese dominio y curl solo ve el SSO de Vercel) | ✅ 2026-10-04 | +| `5b13897` | fix(auth): fail closed if Render's proxy chain changes | `trust proxy 3` depende de la red de Render | Preview: login fallido con `X-Forwarded-For` falso distinto cada vez → `ratelimit-remaining` 4, 3, 2 (la IP falsa se ignora y la cadena se reconoce como la esperada). El fallo cerrado ante una cadena distinta: `tests/auth/client-ip.test.ts` | ✅ 2026-10-04 | +| `1057c6d` | fix(scheduling): keep health data to admin and out of the logs | Scheduling: datos de salud expuestos | Preview: recep en `/months`, `/constraints/by-period` y `/shift-stats` → 403; admin → 200. Log de INFEASIBLE: `tests/scheduling/constraint-permissions.test.ts` | ✅ 2026-10-04 | +| `fd4ad23` | docs(verify): record the proxy chain and scheduling checks, move solver run retention to later | Retención de `solver_runs` (a «Después de publicar») | no aplica | — | +| `72a8224` | docs(roadmap): plan a public demo environment as phase 5 | Demo pública (fase 5) | no aplica | — | +| `40371d8` | feat(uploads): keep invoice PDFs, stamps and signatures private behind the API | Backoffice: PDFs de facturas, sello y firma públicos en Cloudinary | Preview tras migrar (`02b063d`): admin → PDF original y validado de las facturas 28, 29 y 34, sello 8 y firma 9 → 200 con su tipo; ZIP de 29 y 34 → 2 PDF; sin login → 401; recep en el sello → 403. Ninguna respuesta de facturas ni de sellos lleva `cloudinary.com`; `/pdf-url` → 404 | ✅ 2026-10-04 | +| `4d2bd3b` | feat(blacklist): serve guest photos as private files through the API | Fotos de huéspedes de blacklist públicas (mismo problema que backoffice) | Preview: ficha 11 guarda `/api/blacklist/images/…`; la foto → 200 `image/jpeg` para admin y recep; un nombre con `../` → 404; el detalle de la ficha no lleva `cloudinary.com` | ✅ 2026-10-04 | +| `86e76bc` | feat(uploads): serve maintenance photos and checklist attachments through the API | Maintenance: fotos públicas (la parte que queda pasa a «Después de publicar») | Preview: fotos 6 y 7 de los partes 060326-002 y 180526-002 → 200 `image/png` y `image/jpeg`; el detalle del parte no lleva `cloudinary.com`. Checklist sin adjuntos en Aiven: solo `tests/checklist/attachment-response.test.ts` | ✅ 2026-10-04 | +| `b67c3b2` | feat(frontend): load Google and Vercel Analytics only when configured | Cambio del dueño (analítica por variables de entorno) | Preview sin `NEXT_PUBLIC_GA_ID`: la CSP servida no incluye `googletagmanager.com` y el detector de `securitypolicyviolation` no registró ningún intento de cargarlo en todo el recorrido del 2026-10-04 (comprobación indirecta). Producción necesita `NEXT_PUBLIC_GA_ID` y `NEXT_PUBLIC_VERCEL_ANALYTICS` en Vercel para seguir igual que hoy | ✅ 2026-10-05 | +| `5c2b3a8` | chore: license the project under MIT | Licencia del proyecto | no aplica | — | +| `02b063d` | chore(uploads): script to make the already uploaded files private | Ficheros ya subidos siguen públicos | Aiven 2026-10-04: dry-run y `--apply`, 17 ficheros a `authenticated` (4 públicos: avatares y `main-sample`), filas de facturas, sellos, fotos y blacklist 11 reescritas; segunda pasada → 0 por migrar. Las 8 URL públicas de antes → 404 | ✅ 2026-10-04 | +| `db32a70` | docs(verify): record the private files migration and its checks in preview | Registro de la migración de ficheros privados | no aplica | — | +| `c1d01a6` | chore(db): drop private scripts and dumps, read local credentials from .env | Contraseñas de la BD escritas en ficheros versionados (parte del árbol; el historial, en la fase 2) | `git grep` de la contraseña de Aiven en el árbol → 0 ficheros; los 4 volcados siguen en disco y fuera del índice; `bash -n` de los 3 scripts locales; hash de `mock-data.sql` comprobado con bcrypt. Los scripts locales no se han ejecutado | ✅ 2026-10-04 | +| `76f9da1` | feat(frontend): add a Content-Security-Policy and COOP, drop X-Powered-By | Cabeceras del frontend en producción incompletas para L3 | Navegador en preview: la página trae CSP (origen de la API de preview y `vercel.live`), COOP `same-origin`, `X-Frame-Options` y sin `X-Powered-By`. Con un detector de `securitypolicyviolation`: panel, mantenimiento, logbook, parking, horarios, checklist, perfil, backoffice (visor y editor de PDF) y blacklist → 0 bloqueos. El editor dibuja el PDF sin `eval`. Sin hacer: HSTS `preload` (decisión del dueño; no recomendado) y SRI de Google Analytics (Google cambia el script) | ✅ 2026-10-04 | +| `1bc177c` | fix(uploads): let the frontend show private images from the API | Regresión de `40371d8`: el navegador bloqueaba las `` de ficheros privados (CORP `same-origin` de helmet) | Navegador en preview: foto de la ficha 11 de blacklist (800 px) y su descarga por `fetch` (200, 109 824 B); foto 7 del parte 180526-002; sello y firma en el editor de PDF y en la configuración de backoffice → cargan; 0 bloqueos de CSP. Editor cerrado sin guardar (facturas 28 y 36 siguen pendientes) | ✅ 2026-10-04 | +| `7328f13` | test(scheduling): anonymize employee names in solver corpus fixtures (OpenCode) | Nombres reales del personal en el repo (fixtures del solver) | Revisado: el diff solo cambia nombres por `EMP_01`…`EMP_07`; ningún nombre del personal en `tests/scheduling-corpus/` ni `scheduling-solver/`. Tras la fusión: vitest de scheduling 132 passed, pytest del solver 105 passed, 1 skipped | ✅ 2026-10-04 | +| `f2eabdc` | Merge branch 'chore/anonymize-solver-fixtures' into claude/compassionate-planck-gh6aof | (fusión) | Los mismos tests | ✅ 2026-10-04 | +| `4829a52` | chore: remove test requests and the planning importer with real staff data | Nombres reales del personal en el repo (parte del árbol) | `git grep` en el árbol: sin los 18 `.http` ni el importador; los comentarios de los dos SQL usan `uuid-employee-N`. Sin cambio de comportamiento | ✅ 2026-10-05 | +| `223e847` | fix(auth): give every failed login the same minimum response time | Login: el tiempo de respuesta puede delatar si un usuario existe | Preview, 18 intentos fallidos por tipo, antes del arreglo: mediana 364 ms real frente a 340 ms inexistente (Mann-Whitney p ≈ 0,048). Después: 666 frente a 663 ms (p ≈ 0,27; un intento de 63,8 s fue el arranque en frío de Render). `tests/auth/login-timing.test.ts` | ✅ 2026-10-05 | +| `a227edc` | docs(todo): fix the login timing entry and drop the done CSP entry | (documentación) | no aplica | — | +| `c1e213d` | chore(checklist): replace the real email distribution list with example contacts | Correos reales de personas en el checklist (hallado en el barrido del 2026-10-04) | `git grep` en el árbol de los dominios reales → 0; tests del checklist 28 passed. No probado en la interfaz | ✅ 2026-10-05 | +| `c337c2d` | docs(gitclean): add what the history analysis found | Análisis de la fase 2 | no aplica | — | +| `55efaeb` | docs(gitclean): note that the old Anthropic key is revoked | (documentación) | no aplica | — | +| `5794432` | Merge pull request #8 from bpstack/claude/compassionate-planck-gh6aof | Fase 1c en producción | Producción tras el despliegue (Vercel y Render): cabeceras con CSP (con Google Analytics), COOP y HSTS, sin `X-Powered-By`; la API sirve las rutas nuevas de ficheros; en el navegador, foto de la ficha 11 de blacklist (800 px), sello y firma en el editor de PDF (cerrado sin guardar), PDF de la factura 34 por la API (200, 266 575 B), Google Analytics y Vercel Analytics cargan; recorrido por panel, mantenimiento, logbook, parking y perfil. Única pega: la CSP bloquea 2 píxeles de Google Analytics a `www.google.es` (audiencias de Google Signals), sin efecto en la medición | ✅ 2026-10-05 | +| `42f6ac0` | chore: replace the remaining staff names with invented ones | Nombres reales del personal en el árbol (hallado al preparar las listas de la fase 2) | `git grep` en el árbol de los nombres del personal → solo el autor; ninguna fila del checklist en Aiven usa el id antiguo de la guía; tests de restricciones 37 passed. No probado en la interfaz | ✅ 2026-10-05 | +| `2fe93a0` | docs(gitclean): record the approved phase 2 lists | (documentación) | no aplica | — | +| `c50b09a` | test(fnb): replace the real Opera revenue report with a synthetic sample | Informe Opera real de ingresos en los fixtures (hallado en el barrido de OpenCode) | El texto extraído del PDF sintético tiene las mismas 344 líneas y estructura que el real (solo cambian cifras y nombre del hotel); backend 909 passed. No probado subiendo el PDF en la interfaz | ✅ 2026-10-05 | +| `0ad5967` | docs(gitclean): record the OpenCode sweep and the dry run | (documentación) | no aplica | — | +| `d12e117` | docs(gitclean): record the first real run and reword a line the rules touched | (documentación) | no aplica | — | +| `98e6fb9` | docs(verify): add the rows for 0ad5967 and d12e117 | (documentación) | no aplica | — | +| `2d1005a` | Merge pull request #9 from bpstack/claude/compassionate-planck-gh6aof | `main` con los arreglos previos a la limpieza | CI de la PR en verde (backend y frontend); árbol de `main` igual al de la rama. Despliegue de producción no revisado: solo cambian tests, documentación, un placeholder y el id de una guía | ✅ 2026-10-05 | +| `cd3b7fa` | docs(gitclean): record the final rewrite and its audit | (documentación) | no aplica | — | +| `b6f37ff` | feat(checklist): load the hotel's real content from a private repository at build time | (revertido en `a9e8842`) | no aplica | — | +| `ee07328` | chore(checklist): replace the hotel's procedures with example content | (revertido en `a9e8842`) | no aplica | — | +| `47ceb9b` | chore: parametrize the hotel name and the local paths | Nombre del hotel y rutas personales en el código | El pie impreso lee `NEXT_PUBLIC_HOTEL_NAME` con «Hotel PMS» por defecto; CI en verde. Pie impreso no revisado en el preview | ✅ 2026-10-05 | +| `27b6761` | chore(frontend): replace the screenshots that showed a staff user | Capturas con un usuario del personal | Revisado por el propietario en el preview | ✅ 2026-10-05 | +| `34630b8` | test(fnb): remove chain and partner names from the synthetic Opera sample | Nombres de la cadena y del partner en la muestra | CI en verde (backend) | ✅ 2026-10-05 | +| `c9b4323` | docs: describe the hotel's files without their names | (documentación) | no aplica | — | +| `a9e8842` | revert(checklist): keep the procedures in the repository instead of a private one | Contenido del checklist | CI en verde; ver `fa85339` | ✅ 2026-10-05 | +| `fa85339` | chore(checklist): generalize the hotel's procedures | Nombres de la cadena, sus sistemas y partners en el checklist | Las 22 referencias `guide:`/`reference:` de las tareas apuntan a ficheros existentes; tareas del backend idénticas a las del frontend; el propietario abrió los enlaces en el preview | ✅ 2026-10-05 | +| `563b5b1` | style(checklist): format the night audit tasks | (formato) | no aplica | — | From 2b84b0a7bbca02a146891fb770dabb6cc237b83c Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:35:31 +0200 Subject: [PATCH 2/9] build: pin the Node major version in .nvmrc - Add .nvmrc with 22, matching engines >=22.16.0 in both packages Why: fnm and nvm pick the right Node version on every machine without remembering to run fnm use 22. --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 From aab1848836aa247df39fde6a4e437a7dd1bd8415 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:37:48 +0200 Subject: [PATCH 3/9] docs(todo): close the pre-publication section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove "Antes de publicar": the history cleanup resolved the database passwords and staff names, and the rest moves below - Add the credential rotation, the Aiven test data cleanup and the Render Node version check to "Después de publicar" - Point the branch note to the PR #10 merge Why: the clean history is published; what remains no longer blocks publication and the rotation procedure stays outside the repository. --- docs/TODO.md | 61 +++++++++++++++------------------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index c1ec6acd..cd344fd4 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -18,52 +18,9 @@ hace que alguien reimplemente lo que ya existe. ## Rama `claude/compassionate-planck-gh6aof` -Fusionada en `main` el 2026-10-04 (PR #7, commit de merge `5ccb64f`). La rama +Fusionada en `main` el 2026-10-05 (PR #10, commit de merge `0953e94`). La rama sigue siendo donde se arregla este fichero y se prueba en el entorno preview. -## Antes de publicar - -> Lo que hay que resolver antes de la limpieza del historial y la publicación -> (ADR-033): brechas que alguien de fuera o un rol bajo puede aprovechar, los -> secretos y datos privados del repo (se resuelven en la fase 2) y las -> decisiones para publicar. **Resolverlo o quitarlo de aquí antes de publicar.** - -### 🔴 Alta - -- [ ] **Contraseñas de la BD en el historial** — fuera del árbol desde el - 2026-10-04: se quitaron los 14 ficheros con la de Aiven (`basics/`, - `add-libre-number.ts`, `backfill-libre-numbers.ts`, - `set-holidays-2026.ts`, `backup-aiven.sh`) y los 3 scripts locales - leen `backend/.env`. Siguen en el historial desde `d04504f`, junto con - los 4 volcados de `backup/` (versionados hasta el 2026-10-04) y - `backend/.env.example` hasta el 2026-10-02. Se limpian en la fase 2 - (`GITCLEAN.md`) y después se rotan. `user-repository-login.test.ts` - solo crea un usuario de prueba con su contraseña (no existe en Aiven) y - no hace falta tocarlo. _Comprobado por mí el 2026-10-04._ -- [ ] **Rotar todas las credenciales al terminar la preparación** — - `SECRET_JWT_KEY`, contraseñas de MySQL (local y Aiven), claves de - Cloudinary, SMTP si se usa, y las claves de IA que siguen en - `backend/.env` aunque ningún código las lea (`ANTHROPIC_API_KEY`, - `CLAUDE_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `MINIMAX_API_KEY`, - `OPENAI_COMPAT_API_KEY`). El agente tuvo acceso a `.env`, BD y volcados - desde el 2026-09-28 (ADR-014), y el repositorio ha contenido volcados de - la BD (ADR-007). **Dejar escrito el procedimiento** (qué variable, en qué - panel, cómo comprobar que todo sigue funcionando): no existe ninguno. - `SECRET_JWT_KEY` ya se cambió el 2026-09-29 en Render y en local (el - backend exige ahora 32 caracteres o más); el propietario lo generó sin - pasarlo por el agente. -- [ ] **Nombres reales del personal en el repo** — el 2026-10-04 se quitaron - del árbol el alta de una empleada (`20260520_insert_user_*.sql`), - el importador del Excel de horarios (`import-planning-2026.ts`, 13 - nombres), los 18 `.http` de `backend/API REST/` (contraseñas, tokens y - usuarios) y los nombres de los comentarios de `aiven/19_scheduling.sql` - y `20251224_add_scheduling.sql`; los fixtures del corpus del solver - usan `EMP_01`…`EMP_07` desde `7328f13`. Quedan los usuarios de horarios - en la BD (9 de 10 con nombre de pila). El autor de las guías del - checklist y de los README es el propietario y se queda. Todo sigue en el - historial (fase 2, `GITCLEAN.md`). - _Comprobado por mí el 2026-10-04._ - ## Después de publicar > Mejoras sin brecha de seguridad: lógica de negocio, rastro de cambios, @@ -72,6 +29,11 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. ### 🔴 Alta +- [ ] **Rotar las credenciales de producción** — contraseña de la BD y claves + de Cloudinary, y revocar las claves de servicios que ningún código usa. + El historial publicado no las contiene, pero siguen en copias privadas + (ADR-007). El procedimiento está fuera del repositorio. + `SECRET_JWT_KEY` ya se cambió el 2026-09-29. - [ ] **Fnb: ninguna mutación de ingresos deja rastro de quién la hizo** — `upsertMany` sobrescribe el importe anterior (`ON DUPLICATE KEY UPDATE`) y `deleteDay` borra filas sin dejar ningún registro de usuario, valor @@ -94,6 +56,17 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. ### 🟡 Media +- [ ] **Borrar los datos de prueba de Aiven** — la BD de producción solo + tiene datos de prueba (incluidos usuarios de horarios con nombre de + pila). Si el borrado se retrasa o se da acceso a gente de fuera, antes + renombrarlos (`Empleado 01`…). +- [ ] **Render: comprobar qué versión de Node usa** — `engines` pide + `>=22.16.0` sin tope y, según la documentación de Render, un rango + abierto se resuelve a la última versión; el log de producción muestra el + aviso `DEP0205`, que podría ser de una versión posterior. Mirarlo en el + log de build y, si no es la 22, acotar `engines` (`>=22.16.0 <23`) o + fijar `NODE_VERSION`. + _No comprobado._ - [ ] **Maintenance: las fotos no se borran con el parte** — ya son privadas (solo la API las sirve), pero el borrado lógico no las toca, `auto_delete_on_close` se guarda y nadie lo usa, y subir o borrar fotos From 2fd2f5629360d527a8551cc2fc21b159e8d33684 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:44:04 +0200 Subject: [PATCH 4/9] docs(gitclean): record the third pass, its audit and the private publication - Record the third rewrite after PR #10, the commits it pruned and the identical tip trees - Record the two independent audits and the empty blob dump lesson - Record the private repository, the archive rename and the Vercel and Render reconnection, and list what is left before going public - Replace the local working paths with a generic description Why: the document still ended at the second pass and named local paths that do not belong in a public repository. --- docs/GITCLEAN.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/GITCLEAN.md b/docs/GITCLEAN.md index c1043554..f0a2ed9c 100644 --- a/docs/GITCLEAN.md +++ b/docs/GITCLEAN.md @@ -32,9 +32,8 @@ el historial. `claude/compassionate-planck-gh6aof`. Las otras cuatro no tienen ningún commit fuera de `main` (comprobado el 2026-10-05). Sin tags; las referencias `pull/*` de GitHub no se publican. -- **Dónde**: `C:\Users\dz\projects\four-points-phase2\`, fuera del - repositorio. `git-filter-repo` 2.47.0 está en - `C:\Users\dz\tools\git-filter-repo` (venv propio, 2026-10-05). +- **Dónde**: una carpeta de trabajo fuera del repositorio, con + `git-filter-repo` 2.47.0 en un entorno virtual propio (2026-10-05). ### Estado (2026-10-05) @@ -92,8 +91,26 @@ el historial. - **árbol de la punta idéntico** al de GitHub en las dos ramas (`main` 624 → 621 commits; la rama 622 → 619), así que los tests son los mismos que pasaron en CI con la PR #9. -- ⏳ Publicación - final y publicación (ADR-035). +- ✅ Tercera pasada en `work.git` (2026-10-05), tras fusionar la PR #10, que + generalizó las guías del checklist: las mismas listas, ampliadas con rutas, + reemplazos y mensajes nuevos. Además quita del historial los 2 blobs del + informe Opera, 120 versiones antiguas de las guías del checklist y una + imagen confidencial, y mantiene el renombrado de la guía del F&B. 12 + commits quedan vacíos (solo tocaban lo borrado) y se podan: `main` queda en + 624 commits y la rama en 621, con el árbol de las dos puntas idéntico al del + repositorio original. +- ✅ Auditoría de la tercera pasada, hecha dos veces por separado (los 4.960 + blobs, mensajes y rutas): 0 secretos, 0 datos personales, 0 referencias al + hotel, la cadena o sus socios y 0 rastros de IA en los commits; autoría + `bpstack` y `GitHub`. Comprobar siempre que el volcado de blobs no sale + vacío antes de dar una búsqueda por limpia. +- ✅ Publicación en privado (ADR-035, 2026-10-05): `bpstack/four-points` con + las dos ramas; el original pasa a `four-points-archive`, privado. Vercel y + Render despliegan ya desde el nuevo, en producción y en preview. Las + referencias a commits de los documentos apuntan a los hashes nuevos. +- ⏳ Archivar `four-points-archive`, pasar `four-points` a público y activar + secret scanning y push protection (GitHub no los deja activar en un + repositorio privado gratuito). ## Reglas From d197ad6c90d420d852592839c04132693d1ac057 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:44:04 +0200 Subject: [PATCH 5/9] docs(todo): drop the local path of the harness repository - Describe the harness repository without its path on the main machine Why: the path names a local user folder and adds nothing for readers of a public repository. --- docs/TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TODO.md b/docs/TODO.md index cd344fd4..93161940 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -121,7 +121,7 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. `event_scheduler=ON` y el evento está `ENABLED` (última ejecución en Aiven: 2026-09-27)._ - [ ] **Adaptar el harness a repos con varios proyectos e implantarlo** — en el - repo `harness` (`C:\Users\dz\projects\harness` en el PC principal): + repo `harness`, privado y local: soportar repos sin `package.json` ni lockfile en la raíz (aquí `frontend/` y `backend/` son dos proyectos pnpm independientes) y `AGENTS.md` anidados. Después, implantar la capa 1 en este repo. _Comprobado el From 5b216a006c80b6d9f307f4512c1bab914389c7c2 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:45:46 +0200 Subject: [PATCH 6/9] docs(roadmap): close phases 1c and 2 and make publication current - Close phases 1c and 2 with a one-line result each; the detail moves to the local archive - Make phase 4 current: the clean history is in the new private repository and deploys from it, with the steps left to go public - Say two branches instead of all branches, as ADR-035 decided Why: the roadmap still showed phase 1c as current and the publication of every branch. --- docs/ROADMAP.md | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index ffbeb6e8..c6a45bd7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -19,13 +19,14 @@ cosa va a `TODO.md`. | 1 | `docs/` nueva por módulos, escrita desde el código; lo antiguo archivado | — | | 1b | Lo trascendental de `docs/_archive/` rescatado a `docs/` o a `TODO.md` | 1 | | 1c | «Antes de publicar» de `TODO.md` resuelto y probado en producción | 1b | -| 2 | Clon con el historial limpio (todas las ramas) + `docs/GITCLEAN.md` | 1c | -| 4 | Repositorio público nuevo con el historial limpio (todas las ramas) | 2 | +| 2 | Clon con el historial limpio (dos ramas) + `docs/GITCLEAN.md` | 1c | +| 4 | Repositorio público nuevo con el historial limpio (dos ramas) | 2 | | 5 | Demo pública en un entorno aparte, con datos ficticios y reinicio diario | 4 | El orden lo fija ADR-001, revisado por ADR-022 (fase 1b) y ADR-027 (fase 1c, que además absorbe la antigua fase 3 de tareas pendientes). ADR-033 limita la 1c a lo -que hay que resolver antes de publicar. La fase 4 sigue ADR-006, ya aceptada. +que hay que resolver antes de publicar. La fase 4 sigue ADR-006, ya aceptada, y +ADR-035 deja en dos las ramas que se publican. --- @@ -40,34 +41,23 @@ Lo trascendental de `docs/_archive/` rescatado a `docs/` y `TODO.md`, comprobado contra el código; `TODO-old.md` vaciado y borrado. Detalle en `docs/_archive/roadmap-history.md` (local). -## Fase 1c — Resolver «Antes de publicar» y probarlo en producción ← **actual** +## Fase 1c — Resolver «Antes de publicar» ✅ cerrada (2026-10-05) -La sección «Antes de publicar» de `TODO.md`, de 🔴 a 🟢, arreglada y probada en -producción (Render, Vercel y Aiven) antes de tocar el historial (ADR-027, -alcance limitado por ADR-033). Cada punto se borra de `TODO.md` cuando está -hecho y probado. «Después de publicar» queda para el repositorio público. - -- **Orden:** primero los fallos transversales de autenticación (hechos), después - las brechas de cada módulo, y al final las decisiones de publicación (licencia - y analítica). Los secretos y datos privados del repo se resuelven en la fase 2. -- **Flujo:** en la rama `claude/compassionate-planck-gh6aof` y su entorno - preview (ADR-030). El agente arregla, verifica y commitea; el propietario hace - push, el agente lo prueba en preview y lo anota en `VERIFY.md`. A `main` entra - por PR con merge commit (ADR-031). -- Hecho al empezar: los 4 volcados de BD fuera del árbol (`git rm --cached` + - `.gitignore`); del historial se quitan en la fase 2. +Las brechas de seguridad arregladas y probadas en producción antes de tocar el +historial (ADR-027, ADR-033), PR #7 a #10. Detalle en +`docs/_archive/roadmap-history.md` (local). -## Fase 2 — Limpieza del historial +## Fase 2 — Limpieza del historial ✅ cerrada (2026-10-05) -Análisis, propuesta, limpieza con `git-filter-repo` y auditoría, sobre un clon y -sin push, **en todas las ramas** (ADR-026). Reglas, procedimiento y candidatos -conocidos en `GITCLEAN.md`; la lista de privados la aprueba el propietario -(ADR-005). +Historial de `main` y `claude/compassionate-planck-gh6aof` reescrito y auditado +(ADR-035). Resultado en `GITCLEAN.md`. -## Fase 4 — Publicación +## Fase 4 — Publicación ← **actual** -Crear el repositorio público y subir el historial limpio de todas las ramas -(ADR-026). Rotar las credenciales que aparezcan en lo eliminado (ADR-007). +El historial limpio está en `bpstack/four-points`, todavía privado, y Vercel y +Render ya despliegan desde él. Falta archivar el repositorio original, pasar el +nuevo a público y activar secret scanning y push protection. La rotación de +credenciales (ADR-007) no bloquea: va en «Después de publicar» de `TODO.md`. ## Fase 5 — Demo pública From 0f237d0473c7449900f7c92667f77887648bdce3 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:47:57 +0200 Subject: [PATCH 7/9] docs(decisions): record the publication order and when to rotate - Add ADR-036: archive the original last, after Vercel and Render deploy from the new repository; rotate credentials after publishing, with the procedure kept outside the repository - Link ADR-035 and ADR-007 to the decision that revises them - Replace the local path of the harness templates in ADR-008 Why: the order and the rotation timing changed during the publication and were not written down; the path names a local user folder. --- docs/DECISIONS.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index a5258bf9..d35537ad 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -89,7 +89,8 @@ ## ADR-007 — Toda credencial que aparezca en lo limpiado se rota -- **Estado:** ✅ aceptada (2026-09-28) +- **Estado:** ✅ aceptada (2026-09-28) · 📌 momento revisado por + [ADR-036](#adr-036--el-original-se-archiva-al-final-y-la-rotación-va-después-de-publicar) - **Fecha:** 2026-09-28 - **Decisión:** si alguna ruta eliminada del historial contenía credenciales o hashes de contraseñas, se rotan aunque la limpieza haya salido bien. @@ -106,8 +107,8 @@ - **Decisión:** - En la raíz queda un `README.md` breve que enlaza a `docs/`. - `README.md`, `ROADMAP.md`, `TODO.md`, `DECISIONS.md` y `SESSION.md` viven en - `docs/`, partiendo de las plantillas de - `c:\Users\dz\projects\harness\templates\`, y se escriben en español. + `docs/`, partiendo de las plantillas del repo `harness` (privado), y se + escriben en español. - Lo que se va terminando sale de `docs/` y se registra en `docs/_archive/roadmap-history.md`. - **Motivo:** mantener `docs/` limpio. GitHub muestra el `README.md` de la raíz, @@ -705,7 +706,8 @@ ## ADR-035 — Se publican dos ramas y el repositorio actual se archiva con otro nombre -- **Estado:** ✅ aceptada (2026-10-05) +- **Estado:** ✅ aceptada (2026-10-05) · 📌 orden revisado por + [ADR-036](#adr-036--el-original-se-archiva-al-final-y-la-rotación-va-después-de-publicar) - **Fecha:** 2026-10-05 - **Decisión:** - Se limpian y se publican **solo `main` y @@ -729,3 +731,25 @@ nuevo repositorio, GitHub deja de redirigir `bpstack/four-points` al archivado: un clon viejo sin el `origin` cambiado subiría al público. - **Revisa:** ADR-026 en las ramas que se publican. + +## ADR-036 — El original se archiva al final y la rotación va después de publicar + +- **Estado:** ✅ aceptada (2026-10-05) +- **Fecha:** 2026-10-05 +- **Decisión:** + - **Orden de la publicación:** renombrar el original a + `four-points-archive`; crear `four-points` privado con el historial + limpio; sustituir los clones locales por clones nuevos (no se cambia el + `origin`); reconectar Vercel y Render, producción y preview; y solo + entonces archivar el original y pasar el nuevo a público. + - **La rotación de credenciales no bloquea la publicación:** se hace + después, en «Después de publicar» de `TODO.md`. + - **El procedimiento de rotación queda fuera del repositorio**, con el + propietario. +- **Motivo:** decisión del propietario. Un repositorio archivado no admite + push: mientras producción despliegue desde el original, tiene que poder + recibir un arreglo urgente. El historial + publicado no contiene credenciales y la BD de producción solo tiene datos de + prueba. El procedimiento describe paneles, equipos y claves concretas que no + sirven a quien lea el repositorio. +- **Revisa:** ADR-035 en el orden; ADR-007 en el momento de la rotación. From 2009f394c4f1fcf8f8573a6bf6f8d20b99d8087a Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:49:53 +0200 Subject: [PATCH 8/9] docs(verify): record the switch to the new repository - Record the manual deploys from bpstack/four-points in production and preview, and that the deploy on push is still unchecked - Add the rows for fb877ac to 0f237d0 Why: the deploys now come from the new repository and the table lists every commit of the branch. --- docs/VERIFY.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/VERIFY.md b/docs/VERIFY.md index 6ac996f3..e9401dba 100644 --- a/docs/VERIFY.md +++ b/docs/VERIFY.md @@ -40,6 +40,20 @@ CI). `OPTIONS /…` con 400, `/.well-known/vercel/jwe` con 503 y prefetch `?_rsc=` con 503. En producción la misma navegación no da ninguna petición fallida. +## Cambio al repositorio nuevo (2026-10-05) + +Vercel y Render se reconectaron de `four-points-archive` a `bpstack/four-points` +(ADR-036) y se desplegó desde él en los cuatro entornos: + +- **Producción:** `main` en `0953e94`. Vercel _Ready_ y `four-points.stackbp.es` + responde; Render _live_, conecta con la BD, arranca los cron y el solver, y + `api.four-points.stackbp.es` responde 200. +- **Preview:** la rama en `d9e7230`. Vercel _Ready_; el dominio está tras la + protección de Vercel y lo abrió el propietario. Render _live_ con la misma + comprobación y `api-preview` responde 200. +- Los cuatro despliegues se lanzaron a mano; el despliegue automático con un + push al repositorio nuevo **queda sin comprobar** hasta el primer push. + ## Arreglos | Commit | Qué cambia | Punto de `TODO.md` que cierra | Prueba | Resultado | @@ -210,3 +224,10 @@ CI). | `a9e8842` | revert(checklist): keep the procedures in the repository instead of a private one | Contenido del checklist | CI en verde; ver `fa85339` | ✅ 2026-10-05 | | `fa85339` | chore(checklist): generalize the hotel's procedures | Nombres de la cadena, sus sistemas y partners en el checklist | Las 22 referencias `guide:`/`reference:` de las tareas apuntan a ficheros existentes; tareas del backend idénticas a las del frontend; el propietario abrió los enlaces en el preview | ✅ 2026-10-05 | | `563b5b1` | style(checklist): format the night audit tasks | (formato) | no aplica | — | +| `fb877ac` | docs: point commit references to the rewritten history | (documentación) | Los 176 hashes citados existen en el repositorio nuevo, con el mismo asunto que en el original (uno solo cambia en los hashes que cita) | ✅ 2026-10-05 | +| `2b84b0a` | build: pin the Node major version in .nvmrc | — | no aplica | — | +| `aab1848` | docs(todo): close the pre-publication section | (documentación) | no aplica | — | +| `2fd2f56` | docs(gitclean): record the third pass, its audit and the private publication | (documentación) | no aplica | — | +| `d197ad6` | docs(todo): drop the local path of the harness repository | (documentación) | no aplica | — | +| `5b216a0` | docs(roadmap): close phases 1c and 2 and make publication current | (documentación) | no aplica | — | +| `0f237d0` | docs(decisions): record the publication order and when to rotate | (documentación) | no aplica | — | From 819403151c8f66335669ce1d156b7847ee1e2e60 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 5 Oct 2026 12:53:56 +0200 Subject: [PATCH 9/9] docs(session): move the session to the publication phase - Rewrite the state: phases 1c and 2 closed, clean history in the new private repository, deploys already switched - List the steps left to go public and the hooks setup for new clones - Drop the database dump and private candidate notes, resolved in phase 2, and the local machine name Why: the session still described phase 1c and the old repository. --- docs/SESSION.md | 69 ++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/docs/SESSION.md b/docs/SESSION.md index 0bf11f52..b7cf4489 100644 --- a/docs/SESSION.md +++ b/docs/SESSION.md @@ -7,58 +7,48 @@ Lo que no encaje aquí es una decisión (a `DECISIONS.md`), un hallazgo (a su propio fichero) o ruido (se borra). -**Última actualización:** 2026-09-28 · PC principal (`dz`) +**Última actualización:** 2026-10-05 --- ## Estado -Preparación del repositorio para publicarlo como open source. Orden de fases en -`ROADMAP.md`; decisiones en `DECISIONS.md` (ADR-001 a ADR-028); procedimiento de -la limpieza del historial en `GITCLEAN.md`. El plan original (`OPEN-SOURCE.md`) -está en `docs/_archive/`. +Fase actual: **4 — Publicación** (`ROADMAP.md`). Las fases 1c y 2 quedaron +cerradas el 2026-10-05: las brechas de «Antes de publicar» arregladas y +probadas, y el historial de `main` y `claude/compassionate-planck-gh6aof` limpio +y auditado (`GITCLEAN.md`). -Fase actual: **1c — Resolver `TODO.md` y probarlo en producción** (ADR-027). Las -fases 1 y 1b quedaron cerradas el 2026-09-28; detalle en -`docs/_archive/roadmap-history.md` (local). +El historial limpio está en `bpstack/four-points`, **todavía privado**. El +original sigue privado como `four-points-archive`. Vercel y Render despliegan ya +desde el nuevo, en producción y en preview (`VERIFY.md`, «Cambio al repositorio +nuevo»). Decisiones en `DECISIONS.md`, hasta ADR-036. ## ⚠️ Empieza por aquí -1. **Fase 1c**: se hace todo `TODO.md`, de 🔴 a 🟢, y se prueba en producción - antes de limpiar el historial. El clon limpio y el repo público nuevo - (ADR-006, ya aceptada) vienen después. -2. **Qué se publica** (ADR-026): los ficheros de trabajo, todas las ramas y los - `AGENTS.md`/`CLAUDE.md`. No se publica `docs/_archive/`. +1. **Este repositorio va a ser público**, también su historial: lo que se + commitee no se puede borrar después sin reescribirlo. Nada de secretos, hosts + o usuarios de la BD, IDs de servicios, rutas locales ni datos del personal o + de la empresa. +2. **Clones nuevos:** activar los hooks una vez con + `git config core.hooksPath .githooks` (no viene con el clon; ver + `AGENTS.md`). 3. **Idiomas:** se trabaja en español; la documentación pública de `docs/` va en - inglés y no cita ADR ni `TODO.md`. `DECISIONS.md`, `ROADMAP.md`, `SESSION.md` - y `TODO.md` siguen en español. -4. **Material de consulta:** `docs/_archive/` refleja las rutas originales. + inglés y no cita ADR ni `TODO.md`. `DECISIONS.md`, `ROADMAP.md`, + `SESSION.md`, `TODO.md`, `VERIFY.md` y `GITCLEAN.md` siguen en español. -## Hallazgos +## Siguiente -**Método por módulo** (ADR-018): documentar → revisión `security` L3 → comprobar -lo grave → `TODO.md`. Aplicado el 2026-09-28 a los 12 módulos (general con -mensajería y base de datos incluidas). - -Los hallazgos de la lectura del código (seguridad, docs desalineadas, código -muerto, decisiones previas a publicar) están en `TODO.md`, y **se resuelven -todos antes de publicar** (ADR-027). +1. Push de la rama y comprobar que Vercel y Render despliegan solos desde el + repositorio nuevo (queda sin comprobar en `VERIFY.md`). +2. Fusionar la rama en `main` por PR con merge commit (ADR-031). +3. Archivar `four-points-archive` (ADR-036). +4. Pasar `four-points` a público y activar secret scanning y push protection. +5. Después, «Después de publicar» de `TODO.md`, empezando por la rotación de + credenciales. ## Esperando decisión -Nada ahora mismo. Cómo se trabaja en la 1c (orden y flujo de push) está en -`ROADMAP.md`. - -## Volcados de BD - -Los cuatro `backend/db-mysql/backup/backup_hotel_db_{aiven,local}_2026*.sql` -salieron del árbol el 2026-09-28 (`git rm --cached`; siguen en el disco del PC -`dz`, ignorados por `.gitignore`). En el historial siguen estos cuatro y además -`backup_hotel_db-aiven.sql` y `backup_hotel_db-local.sql`; entraron en -`f3d9898`, `d04504f` y `8109e5e`. **No se han abierto.** Se eliminan del -historial en la fase 2, en todas las ramas: `main`, `chore/audit-prep-sprint-0`, -`feature/ai-schedule-generator`, `feature/auth-hardening` y -`feature/observability-pino` (sin tags). +Nada ahora mismo. ## Cuidado con esto @@ -75,7 +65,4 @@ historial en la fase 2, en todas las ramas: `main`, `chore/audit-prep-sprint-0`, - **Enlaces rotos conocidos** tras archivar: `backend/README.md` (enlaces a `docs/backend/…`) y comentarios que citan `Global-Plan.md` o `SCHEDULING-SOLVER-PLAN.md`. Se arreglan al escribir cada módulo (ADR-010). -- **Candidatos a privados para la fase 2**, además de los volcados: los dos - Excel archivados (`PLANNING 2026.xlsx`, el Excel de presencias), - que siguen en el historial, y los 18 `.http` de `backend/API REST/` - (peticiones de prueba; suelen llevar tokens o contraseñas). Ninguno abierto. + _Sin repasar desde el 2026-09-28._