hbox-docs/DESPLIEGUE.md
Leopoldo Rodríguez e86b0a59fb Pendientes: elegir uno de tus comentarios como el «¿por qué?» al mover
Pedido de Leo (28-oct, 18:00): Michelle comenta antes de mover y luego tenía que escribir lo mismo otra vez. El «¿por
qué?» lista tus últimos comentarios del pendiente; si comentaste después del último cambio de columna, ya viene elegido.
PATCH /api/tarea acepta _nota_de (el «cuando» de un comentario tuyo; 400 si no existe o es de otra persona): la entrada
guarda «ref», no repite el texto en el hilo y lleva un extracto para la Actividad. En el hilo, el comentario elegido lleva
«→ Hecho»; un porqué pegado igual que un comentario anterior también se ve una sola vez. «Comentar» revisa el texto al
hacer clic. Pruebas: tests-comentarios.sh (26) y las demás; caso de Michelle en el navegador. DESPLIEGUE A14; lote
2026-10-28_comentarios-elegir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013SfRSvDd335NPYEbBrDFGE
2026-10-28 18:17:17 -06:00

44 KiB

Despliegue — sitio, backend de actas y usuarios

Servidor: git.helgen.online · dominio público: https://drogheda.helgen.online

Qué vive dónde

Parte Ruta en el servidor Cómo se actualiza
Sitio (docs, formularios, tableros) /srv/projects/drogheda/hbox-docs/site ./scripts/deploy.sh desde la Mac
Backend de actas (app.py, tablero.py) /srv/projects/drogheda/hbox-docs/backend ./scripts/deploy.sh lo copia (rsync, sin git en el servidor) + systemctl restart actas-hbox si cambió app.py
Actas recibidas (JSON + índice CSV) /srv/projects/drogheda/actas las escribe el backend
Fotos de campo /srv/projects/drogheda/adjuntos las escribe el backend
Pendientes del tablero (kanban) /srv/projects/drogheda/tareas las escribe el backend — 13-oct
Inventario (ítems y movimientos) /srv/projects/drogheda/inventario las escribe el backend — 13-oct
Sitios /srv/projects/drogheda/sitios las escribe el backend — 13-oct
Plan de producción /srv/projects/drogheda/plan-produccion.json lo escribe el backend (admin) — 13-oct
Equipo (miembros, bitácora de accesos) /srv/projects/drogheda/equipo las escribe el backend — 14-oct
Enfoque de la semana (página de inicio) /srv/projects/drogheda/enfoque.json lo escriben Leo y Adriana desde el inicio — 21-oct
Lotes del tablero ya aplicados /srv/projects/drogheda/lotes-aplicados.json lo escribe backend/tablero.py al aplicar un lote de Claude — 21-oct
Foto del tablero (para Claude) _estado/ en la Mac, fuera de git ./scripts/tablero.sh foto y cada ./scripts/deploy.sh — 21-oct
Usuarios (HTTP Basic) /etc/nginx/hbox-docs.htpasswd htpasswd

Lo importante: deploy.sh publica el sitio y copia el backend (rsync; en el servidor el backend no es un repositorio de git), pero no reinicia el servicio. Si cambió backend/app.py, hay que hacer además el restart, o el sitio nuevo hablará con un backend viejo (las fotos darían 404 y el diagnóstico se bloquearía como si fuera un acta de banco).


A · Despliegue de esta versión (diagnóstico en campo)

Los pasos 3, 4 y 5 son de una sola vez; los pasos 1 y 2 son el ciclo normal de siempre.

1 · Desde la Mac — publicar el sitio

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
git add -A && git commit -m "diagnóstico en campo: acta, fotos, tablero y procedimiento"
git push
./scripts/deploy.sh                 # mkdocs build --strict + rsync a /srv/.../site

2 · En el servidor — actualizar el backend

cd /srv/projects/drogheda/hbox-docs && git pull
sudo systemctl restart actas-hbox
curl -s http://127.0.0.1:8081/api/salud        # {"ok":true,"servicio":"actas-hbox"}

3 · Carpeta de fotos (una sola vez)

sudo mkdir -p /srv/projects/drogheda/adjuntos
sudo chown -R leo:leo /srv/projects/drogheda/adjuntos

El backend la deduce sola de ACTAS_DIR; si prefieres fijarla, agrega a /etc/systemd/system/actas-hbox.service (y luego daemon-reload + restart):

Environment=ADJUNTOS_DIR=/srv/projects/drogheda/adjuntos
Environment=ADJUNTO_MAX_MB=8
Environment=ARCHIVO_MAX_MB=20
Environment=ACTAS_REPETIBLES=diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion,prueba-bateria-modem,prueba-ethernet-serie

4 · nginx — subida de fotos e identidad del técnico (una sola vez)

En el bloque server { listen 443 … } de drogheda.helgen.online, el location /api/ queda así:

    location /api/ {
        proxy_pass http://127.0.0.1:8081;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Remote-User     $remote_user;   # ← quién es el técnico
        client_max_body_size 25m;                          # ← sin esto, las fotos dan 413
        proxy_read_timeout   120s;                         # ← subidas con señal mala
    }

Las dos líneas marcadas son nuevas y obligatorias:

  • Sin client_max_body_size, nginx corta la subida en 1 MB y el técnico ve "la foto pesa demasiado" aunque no lo pese. (El formulario ya reduce cada foto a ~1600 px / 300-400 kB antes de subirla; el margen es para ráfagas de varias fotos.)
  • Sin X-Remote-User, el backend no sabe quién envió cada acta: el nombre no se precarga y —más serio— cualquier usuario autenticado podría mandar la cabecera a mano y hacerse pasar por leo para borrar actas. Al fijarla en nginx, el valor del cliente se sobrescribe siempre. Revisa si ya la tienes: es fácil que no, porque no venía en el README original.
sudo nginx -t && sudo systemctl reload nginx

5 · Usuarios de los técnicos (una sola vez)

sudo htpasswd /etc/nginx/hbox-docs.htpasswd alan
sudo htpasswd /etc/nginx/hbox-docs.htpasswd daniel
# (sin -c: -c CREA el archivo de cero y borraría a leo y a luz)
sudo cut -d: -f1 /etc/nginx/hbox-docs.htpasswd     # lista de usuarios

Administradores (quién puede borrar o reabrir actas) = variable ACTAS_ADMINS del servicio, hoy leo. Alan y Daniel quedan como técnicos: llenan y envían, no borran.

7 · Permisos para que los técnicos cambien su contraseña (una sola vez)

La página Mi cuenta (/cuenta.html) deja que cada quien cambie su propia contraseña, y que el administrador restablezca la de otro. Para eso el servicio tiene que poder escribir el archivo de usuarios que nginx solo lee:

# quién es el usuario de nginx (normalmente www-data)
grep -E "^\s*user" /etc/nginx/nginx.conf

sudo chown leo:www-data /etc/nginx/hbox-docs.htpasswd
sudo chmod 640          /etc/nginx/hbox-docs.htpasswd
sudo systemctl restart actas-hbox

Así: leo (el servicio) escribe, www-data (nginx) lee, y nadie más puede ni leerlo.

Comprueba que las contraseñas estén en el formato que el servicio sabe verificar:

sudo cut -d: -f2 /etc/nginx/hbox-docs.htpasswd | cut -c1-6

Todas deben decir $apr1$. Si alguna sale distinta ($2y$ es bcrypt), esa persona no va a poder cambiar la suya sola: restablécesela desde la propia página, que la vuelve a guardar en $apr1$.

!!! note "Por qué no se usa el comando htpasswd desde el servicio" Pasar una contraseña como argumento (htpasswd -b … clave) la deja visible en ps para cualquiera con sesión en el servidor. El hash $apr1$ se calcula dentro del proceso; está verificado carácter por carácter contra la salida de htpasswd, incluyendo acentos y espacios.

!!! tip "Al cambiar la contraseña, la sesión no se cae" HTTP Basic se guarda en el navegador: quien cambie la suya sigue navegando con la anterior hasta que cierre sesión o cierre el navegador. La página lo explica y ofrece la liga de Salir para probarla de inmediato. No es un error.


A2 · Despliegue 13-oct — tableros, inventario, sitios e instalación en campo

Esta versión agrega al backend cuatro colecciones nuevas (pendientes del tablero, plan de producción, inventario con movimientos, sitios) y páginas que las usan (tableros/, inventario/, sitios/, formularios/instalacion.html). Igual que siempre: el sitio se publica desde la Mac; el backend se actualiza en el servidor. Si solo publicas el sitio, los tableros dirán «No se pudo cargar» porque hablan con endpoints que el backend viejo no tiene.

1 · Desde la Mac — publicar el sitio

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
git add -A && git commit -m "tableros kanban, inventario, sitios, acta de instalación (13-oct)"
git push
./scripts/deploy.sh

2 · En el servidor — backend y carpetas (las carpetas, una sola vez)

ssh leo@git.helgen.online
cd /srv/projects/drogheda/hbox-docs && git pull

# carpetas de las colecciones nuevas (junto a /actas; el backend las deduce de ACTAS_DIR)
sudo mkdir -p /srv/projects/drogheda/{tareas,inventario/items,inventario/movimientos,sitios}
sudo chown -R leo:leo /srv/projects/drogheda/{tareas,inventario,sitios}
sudo chown leo:leo /srv/projects/drogheda      # (sin -R) el backend escribe plan-produccion.json en esta carpeta

sudo systemctl restart actas-hbox
curl -s http://127.0.0.1:8081/api/salud                       # {"ok":true,"servicio":"actas-hbox"}
curl -s -H 'X-Remote-User: leo' http://127.0.0.1:8081/api/tareas/resumen   # {"ok":true,"total":0,...}

Variables de entorno: no hace falta tocar el .service. Si quieres mover las carpetas, descomenta en /etc/systemd/system/actas-hbox.service las líneas TAREAS_DIR, INVENTARIO_DIR, SITIOS_DIR, PLAN_FILE (vienen comentadas en backend/actas-hbox.service) y luego daemon-reload + restart.

3 · Siembra inicial (una sola vez; repetirla no duplica nada)

cd /srv/projects/drogheda/hbox-docs
./backend/venv/bin/python3 backend/seed_1013.py --usuario leo

Crea: el plan (570 → 113 V5.6 / 327 V5.7 / 130 v6), los 4 sitios (icave, lzc, eit, timsa — ubicación y contactos «por confirmar» donde no se sabe), 18 ítems de inventario con stock 0 (Jesús hace el conteo físico con movimientos «ajuste») y los 12 pendientes del tablero del 13-oct. Lo que ya exista con el mismo id se deja intacto. Para importar los pendientes del Task Tracker de Notion, exporta una lista JSON con id, titulo, pista, estado, responsable y pásala con --extra archivo.json.

4 · Verificar

curl -s -u leo:PASS https://drogheda.helgen.online/api/tareas | head -c 300
curl -s -u leo:PASS https://drogheda.helgen.online/api/plan | head -c 200
curl -s -u leo:PASS https://drogheda.helgen.online/api/inventario | head -c 200
curl -s -u leo:PASS https://drogheda.helgen.online/api/sitios | head -c 200

Y desde el navegador: /tableros/kanban.html (arrastra un pendiente y revisa que el historial anote quién lo movió), /inventario/inventario.html (registra un «ajuste» con el conteo real), /sitios/sitio.html?id=icave, /formularios/instalacion.html con la unidad PRUEBA.

5 · Respaldo

Se suman tres carpetas y un archivo a la rutina de siempre:

rsync -az leo@git.helgen.online:/srv/projects/drogheda/tareas/     ~/respaldo-tareas/
rsync -az leo@git.helgen.online:/srv/projects/drogheda/inventario/ ~/respaldo-inventario/
rsync -az leo@git.helgen.online:/srv/projects/drogheda/sitios/     ~/respaldo-sitios/
rsync -az leo@git.helgen.online:/srv/projects/drogheda/plan-produccion.json ~/respaldo-plan/

A3 · 14-oct: equipo y usuarios

Agrega la colección equipo (quién es quién; los responsables de los pendientes), la administración de accesos desde el sitio (/tableros/equipo.html, solo admin) y hace obligatorio el responsable en cada pendiente. Compatible con lo ya desplegado: los pendientes viejos siguen funcionando; la siembra les calcula responsable_ids y pone leo solo donde el responsable estaba vacío.

# Mac
cd ~/Documents/Claude/Projects/Drogheda/hbox-docs && git add -A && git commit -m "equipo, usuarios y responsables (14-oct)" && git push && ./scripts/deploy.sh

# servidor
ssh leo@git.helgen.online
cd /srv/projects/drogheda/hbox-docs && git pull
sudo systemctl restart actas-hbox
./backend/venv/bin/python3 backend/seed_1013.py --usuario leo     # crea equipo/ (9 miembros, sin accesos) + el pendiente de ferretería; no duplica
curl -s -H 'X-Remote-User: leo' http://127.0.0.1:8081/api/equipo | head -c 200

La carpeta equipo/ la crea la siembra (o el backend al primer alta) junto a /actas; no hay variables nuevas que fijar (EQUIPO_DIR, HTPASSWD_LOCK vienen comentadas en el .service).

Para crear o quitar accesos desde la página, el servicio tiene que poder escribir el htpasswd — es lo mismo que ya pide «Mi cuenta» (§7): sudo chown leo:www-data /etc/nginx/hbox-docs.htpasswd && sudo chmod 640 /etc/nginx/hbox-docs.htpasswd. Si no está hecho, la página de Equipo lo avisa y solo deja editar miembros. A partir de aquí los usuarios se crean en Equipo → Nuevo usuario / miembro (la contraseña inicial se muestra una sola vez), no con htpasswd en el servidor. Las cuentas que ya existen y no son miembro (p. ej. luz) aparecen como «cuentas con acceso que no están en el equipo»: dales de alta como miembro sin marcar «crear acceso».

Verificar: /tableros/equipo.html como leo (botones) y como un técnico (solo lectura); crear un pendiente sin responsable debe rechazarse; backend/tests-1014.sh reproduce todo con un htpasswd temporal.

Síntoma Causa Arreglo
Los tableros dicen «No se pudo cargar (HTTP 404)» backend viejo paso 2 (git pull + restart)
«El servicio no tiene permiso de escribir el archivo de usuarios» al crear un acceso falta el chown/chmod del htpasswd §7
«Falta el responsable del pendiente» al crear regla del 14-oct elige a alguien del equipo (o escríbelo en texto libre)
«No puedo identificarte (falta X-Remote-User…)» al crear o mover nginx no manda la cabecera paso 4 del despliegue anterior
El tablero sale vacío falta la siembra paso 3
«Solo un administrador…» al borrar o editar sitios/plan ese usuario no está en ACTAS_ADMINS así debe ser; pide a Leo
PermissionError al guardar el plan /srv/projects/drogheda no es de leo chown del paso 2

A4 · 21-oct: página de inicio, enfoque de la semana y actividad

El inicio deja de ser una página escrita a mano: lee docs/datos/proyecto.json (fase, ruta a producción, fechas, sitios), el tablero y dos rutas nuevas del backend:

  • GET /api/hoy · PUT /api/hoy: el enfoque de la semana. Lo escriben HOY_EDITORES (por omisión leo,adriana) y los administradores; se guarda en enfoque.json junto a /actas, con sus 20 versiones anteriores.
  • GET /api/actividad?dias=7&limite=30: lo que pasó, del historial de los pendientes y de las actas recibidas (sin las series PRUEBA). La usan el inicio y la Bitácora.

No hay carpetas ni variables nuevas que fijar (las dos vienen comentadas en el .service). Como cambió backend/app.py, después del deploy hay que reiniciar el servicio:

# Mac
cd ~/Documents/Claude/Projects/Drogheda/hbox-docs && ./scripts/deploy.sh
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'

# verificar (en el servidor)
curl -s -H 'X-Remote-User: leo' http://127.0.0.1:8081/api/hoy
curl -s -H 'X-Remote-User: leo' 'http://127.0.0.1:8081/api/actividad?dias=7&limite=3' | head -c 400
Síntoma Causa Arreglo
En el inicio, el enfoque dice «El servidor no respondió» y la actividad, «HTTP 404» backend viejo el restart de arriba
«El enfoque de la semana lo escriben adriana y leo» al guardar ese usuario no está en HOY_EDITORES así debe ser; para agregar a alguien, Environment=HOY_EDITORES=leo,adriana,… en el .service
PermissionError al guardar el enfoque /srv/projects/drogheda no es de leo chown del paso 2 de A2

A5 · 21-oct: el sitio como centro de mando — lotes y foto del tablero

Regla de Leo (21-oct): si no está en el sitio, no pasó; cada tarea termina aquí. Claude no llega al servidor, así que deja sus cambios al tablero en lotes (backend/lotes/AAAA-MM-DD_tema.json, uno por tarea terminada) y lee el tablero en una foto (_estado/tablero.md, en la Mac). backend/tablero.py hace las dos cosas en el servidor a través del mismo backend (127.0.0.1:8081): cada cambio pasa por las mismas validaciones que el sitio y queda en el historial con su autor (claude) y su nota. No cambia app.py ni pide reiniciar nada. Formato y reglas: backend/lotes/README.md.

# Mac — deploy.sh ya copia tablero.py y al final baja la foto
cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
./scripts/deploy.sh                   # termina con «Foto del tablero en _estado/tablero.md» y «Tablero: N cambio(s)…»
./scripts/tablero.sh revisar          # qué haría el lote 2026-10-21_pasada-6 contra el tablero real
./scripts/tablero.sh aplicar          # lo aplica y baja la foto nueva

El primer lote (2026-10-21_pasada-6, cierre de la pasada 6) cierra lo que la pasada terminó (pasadas 2 a 5 y la fuente de la V5.7), pone en curso la revisión y el Gerber del piloto, crea D-21-2 para Leo, el pedido del piloto (si no existe ya uno), la prueba de banco de la V5.7 (si no existe) y el pendiente de D34 de la v6 para Michelle, y comenta los de la v6. Lo que no cuadra con el tablero real (ya está en Hecho, no existe, hay uno parecido, el título coincide con varios) se salta, y revisar dice por qué antes de escribir nada.

Síntoma Causa Arreglo
No contesta el backend en http://127.0.0.1:8081 el servicio está abajo ssh leo@git.helgen.online 'sudo systemctl status actas-hbox'
…/venv/bin/python3: No such file or directory el venv vive en otro lugar BACKEND_PATH=/ruta/al/backend ./scripts/tablero.sh …
PermissionError en lotes-aplicados.json /srv/projects/drogheda no es de leo chown del paso 2 de A2
«los cambios N ya no son los que se aplicaron» alguien editó un lote ya aplicado un lote aplicado no se edita: Claude escribe otro
un cambio «se salta» y no debía el título del pendiente real es distinto pásale a Claude la línea de revisar; escribe un lote nuevo con el id correcto

A6 · 22-oct: modo crisis — semáforo, sitio de cada pendiente y juntas

Qué cambia: la página de inicio muestra el semáforo del proyecto (rojo) y el color de cada sitio (docs/datos/proyecto.json); cada pendiente lleva su sitio (sitios: icave, timsa, eit, lzc, qro; sin sitio = Querétaro), con filtro y vista Por sitio en Pendientes; pista nueva Gestión; página nueva Juntas. backend/tablero.py acepta sitios en los lotes, salta un lote con errores sin frenar los demás y recorta notas de más de 300 caracteres (lo avisa).

Cambió backend/app.py: hay que reiniciar el servicio ANTES de aplicar lotes con sitios. Con el backend viejo, revisar marca esos cambios como inválidos («el backend todavía no conoce «sitios»: reinicia actas-hbox») y no los manda.

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
mkdocs build --strict
git add -A && git commit -m "Modo crisis: semáforo, sitio por pendiente, juntas" && git push
cd .. && git add hbox-docs && git commit -m "hbox-docs: modo crisis" && git push && cd hbox-docs   # sube el submódulo
./scripts/deploy.sh                                   # sitio + backend (rsync); la foto todavía con el backend viejo
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'
curl -s -u leo:PASS 'https://drogheda.helgen.online/api/tareas?sitio=eit' | head -c 300   # «sitios» en la respuesta
./scripts/tablero.sh revisar                          # ya con sitios
./scripts/tablero.sh aplicar
Síntoma Causa Arreglo
En Pendientes no aparecen los botones de sitio navegador con el kanban.js viejo recarga (los .js llevan ?v= desde el 21-oct)
Sitio inválido: … al guardar sitio que no existe icave, timsa, eit, lzc o qro (también acepta Veracruz, Manzanillo, Ensenada, LCT, Querétaro)
revisar dice «el backend todavía no conoce «sitios»» no se reinició el servicio el restart de arriba

A7 · 22-oct: tablero de Instalaciones por sitio y prueba en sitio

Qué cambia: página nueva Instalaciones (/tableros/instalaciones/, pestañas ICAVE · TIMSA · EIT · LCT): una tarjeta por unidad con su vehículo, sus actas y su etapa (enviada → recibida → instalada → probada en sitio → en operación, más «con falla») contra la meta de cada sitio. Acta nueva Prueba en sitio (formularios/prueba-sitio.html, tipo prueba-sitio). El acta de instalación pide además 12/24 V, los tipos Reach Stacker y Empty Handler y el nombre en el monitor (H00xx). Los formularios aceptan ?serie=…&sitio=… para abrir la siguiente acta ya llena (acta.js).

Backend: backend/instalaciones.py (nuevo; lo engancha el final de app.py) da GET /api/instalaciones[?sitio=eit] y vuelve repetible prueba-sitio sin tocar ACTAS_REPETIBLES del servicio. deploy.sh ya copia el archivo nuevo.

Cambió backend/app.py: reinicia el servicio. Sin reiniciar, la página dice «falta reiniciar el servicio actas-hbox» y la segunda prueba en sitio de una misma unidad daría «Ya existe un acta».

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
mkdocs build --strict
git add -A && git commit -m "Tablero de Instalaciones por sitio y acta de prueba en sitio" && git push
cd .. && git add hbox-docs && git commit -m "hbox-docs: tablero de Instalaciones" && git push && cd hbox-docs   # sube el submódulo
./scripts/deploy.sh                                   # sitio + backend (rsync, ya con instalaciones.py)
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'
curl -s -u leo:PASS 'https://drogheda.helgen.online/api/instalaciones?sitio=icave' | head -c 300   # «"ok": true»
Síntoma Causa Arreglo
«No se pudo leer el tablero de instalaciones (HTTP 404)» servicio sin reiniciar, o falta backend/instalaciones.py en el servidor deploy.sh y el restart; si falta el archivo, journalctl -u actas-hbox dice «instalaciones.py no se cargó»
Una unidad instalada no aparece no tiene acta, o su acta no trae el sitio ni el nombre del puerto llenar el acta de instalación con el sitio y la fecha real
La unidad sale en otro sitio cuenta en el sitio de su acta más reciente revisar el sitio de esa acta en el expediente

A8 · 22-oct: Adriana y Gibran pueden borrar pendientes

Qué cambia: borrar un pendiente ya no es solo de los administradores (ACTAS_ADMINS): también pueden los usuarios de TAREAS_BORRAN, que por omisión son adriana y gibranlp (no hace falta tocar el servicio). No cambia ningún otro permiso: actas, usuarios, sitios y plan siguen siendo solo de administradores.

Borrar ya no destruye: el pendiente pasa a /srv/projects/drogheda/tareas/_borrados/<id>__<AAAAMMDDhhmmss>.json con su historial, sigue en la Actividad («Adriana borró …», sin liga) y quien lo esperaba («Espera a que terminen») deja de esperarlo, con una nota en su historial.

Cambió backend/app.py: reinicia el servicio.

./scripts/deploy.sh
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'

Para dar o quitar el permiso: en /etc/systemd/system/actas-hbox.service, la línea Environment=TAREAS_BORRAN=adriana,gibranlp con los usuarios que sí (los administradores siempre pueden); después sudo systemctl daemon-reload && sudo systemctl restart actas-hbox.

Recuperar un pendiente borrado (en el servidor):

cd /srv/projects/drogheda/tareas
ls _borrados/                                   # <id>__AAAAMMDDhhmmss.json
mv _borrados/<id>__<fecha>.json <id>.json       # vuelve al tablero como estaba; las dependencias que se quitaron no regresan solas
Síntoma Causa Arreglo
Adriana o Gibran no ven «Borrar» en el detalle servicio sin reiniciar el restart de arriba
«Solo … pueden borrar pendientes» ese usuario no es administrador ni está en TAREAS_BORRAN así debe ser; si debe poder, agrégalo a TAREAS_BORRAN

A9 · 23-oct: inventario por sitio

Qué cambia: Inventario (/tableros/inventario/) tiene una pestaña por sitio —ICAVE · TIMSA · EIT · LCT · Querétaro— y Todo. Cada movimiento dice en qué sitio pasó. Tipo nuevo Envío: saca material de un sitio (o de «Proveedor o fuera del inventario»: una compra en camino, o material que salió antes de que se contara) y lo deja en camino hasta que el sitio que lo espera le da Recibir (tipo recepcion; si llegó menos, la diferencia baja del total). Cada ítem puede decir cuánto lleva una instalación (Por instalación) y la pestaña de cada puerto calcula qué falta para instalar N unidades. Categoría nueva Instalación (soportes, tornillería, cable de potencia). Instalaciones tiene un botón Material que abre la pestaña del sitio. El CSV trae una columna por sitio y otra de lo que va en camino. Reglas para el equipo: Política de inventario.

Datos: no hay migración. Los movimientos de antes no tienen sitio y cuentan como Querétaro; el total de cada ítem no cambia. El primer paso del equipo es el conteo físico de cada sitio (lote 2026-10-23_inventario-sitios).

Cambió backend/app.py: reinicia el servicio. Sin reiniciar, la página muestra solo Todo y avisa que falta reiniciar actas-hbox.

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
mkdocs build --strict
git add -A && git commit -m "Inventario por sitio: pestañas, envíos en camino y kit por instalación" && git push
cd .. && git add hbox-docs && git commit -m "hbox-docs: inventario por sitio" && git push && cd hbox-docs   # sube el submódulo
./scripts/deploy.sh
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'
curl -s -u leo:PASS https://drogheda.helgen.online/api/inventario | head -c 300   # debe traer «"sitios"» y «"envios"»
./scripts/tablero.sh revisar && ./scripts/tablero.sh aplicar

Pruebas en la Mac (opcional, necesita Flask): bash backend/tests-1023.sh . (35 revisiones: entrada por sitio, envío, recepción completa y con faltante, conteo físico, envío desde fuera, errores y CSV).

Síntoma Causa Arreglo
Solo se ve Todo y un aviso de reiniciar servicio sin reiniciar el restart de arriba
«No hay stock suficiente en EIT: hay 0 pza registrados» ese sitio no tiene su conteo físico ni entradas Conteo físico en ese sitio primero; si es material que ya salió sin contarse, envío desde «Proveedor o fuera del inventario»
Un envío no sale en la pestaña del sitio que lo espera se registró con otro destino, o ya se recibió tocar el código del ítem: sus movimientos dicen a dónde iba y quién lo recibió
El total de un ítem subió sin que llegara nada un envío desde «Proveedor o fuera del inventario» entra al total al registrarse (va en camino) así debe ser; si al recibir llega menos, la diferencia baja

A10 · 23-oct: cuentas nuevas, contraseña inicial para todos y correo de cada quien

Qué cambia:

  • backend/cuentas.py (nuevo; deploy.sh ya lo copia junto con backend/cuentas/): restablece contraseñas en bloque y da de alta a varios. Primera corrida (cuentas/2026-10-23_alta-sitios.json): altas de jesush (Jesús Honorato, TIMSA), miguel (Miguel Ángel Peña, EIT), joel (Joel Orantes, EIT), antonio (Antonio Ventura, LCT) y danielg (Daniel González Cruz, ensamble, Querétaro); el usuario jesus pasa a llamarse Jesús Romero; se llenan los correos que ya conocíamos; y todos menos leo y claude quedan con la misma contraseña inicial.
  • Equipo: columnas Sitio y Correo, filtro por sitio, campos «Sitio» y «Otro correo» (el del otro dominio) y, para el administrador, la marca contraseña inicial.
  • Quien sigue con la contraseña inicial ve una franja arriba en todas las páginas, con la liga a Mi cuenta; se quita sola cuando la cambia.
  • Los pendientes que tenían a alguien como texto libre («Miguel») ya cuentan como suyos en cuanto se da de alta.

Cambió backend/app.py: reinicia el servicio antes de correr cuentas.py y antes de aplicar el lote.

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
mkdocs build --strict
git add -A && git commit -m "Cuentas en bloque, contraseña inicial y sitio/correo en Equipo" && git push
cd .. && git add hbox-docs CLAUDE.md && git commit -m "hbox-docs: cuentas del 23-oct" && git push && cd hbox-docs
./scripts/deploy.sh
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'
ssh -t leo@git.helgen.online 'cd /srv/projects/drogheda/hbox-docs/backend && venv/bin/python3 cuentas.py todo cuentas/2026-10-23_alta-sitios.json'
./scripts/tablero.sh revisar && ./scripts/tablero.sh aplicar      # lote 2026-10-23_usuarios-campo

cuentas.py muestra el plan (altas, a quién restablece, qué datos llena) y pide «si»; luego la contraseña, dos veces y sin eco. No la escribas en ningún archivo ni en un chat de grupo: pásala en persona o por mensaje directo. Para revisar después (sin contraseñas ni hashes): ssh leo@git.helgen.online 'cd /srv/projects/drogheda/hbox-docs/backend && venv/bin/python3 cuentas.py lista'. Pruebas en la Mac (opcional, necesita Flask): bash backend/tests-cuentas.sh . (47 revisiones).

Síntoma Causa Arreglo
cuentas.py: «No encuentro el archivo de usuarios» o PermissionError no se corrió como leo, o el htpasswd no es de leo correr como leo; sudo chown leo:www-data /etc/nginx/hbox-docs.htpasswd && sudo chmod 640 /etc/nginx/hbox-docs.htpasswd
Alguien no puede entrar después escribió otra contraseña, o su cuenta salió como «sin miembro activo» y no se tocó cuentas.py lista; restablécesela en Mi cuenta (admin)
Hay que deshacer el restablecimiento — el archivo anterior quedó en /srv/projects/drogheda/equipo/_respaldos/htpasswd-AAAAMMDD-hhmmss: cópialo encima de /etc/nginx/hbox-docs.htpasswd como leo
No sale la franja de la contraseña ni la columna Sitio servicio sin reiniciar el restart de arriba

A11 · 27-oct: página de entrada, incidencias, standup, vigía y agente diario

Por qué. Adriana reportó el 26-oct que, después de restablecer contraseñas, la gente no podía volver a entrar. Con la ventana gris de HTTP Basic el navegador y el teléfono guardan la contraseña anterior y la vuelven a mandar sola; Mi cuenta no tenía campo de usuario (el gestor de contraseñas nunca se actualizaba); «Adriana» con mayúscula no existe para nginx; no había cómo cerrar sesión ni registro de quién fallaba. Además, a las 9:15 del 27-oct se instaló a mano un actas-hbox.service viejo (sin prueba-ethernet-serie) que el repo no tenía.

Qué cambia.

  • Página de entrada propia (docs/entrar.html, backend/acceso.py): nginx pregunta a /api/_auth en cada petición (auth_request); sesión = cookie firmada de 30 días; se entra con el usuario o con el correo de Equipo, sin importar mayúsculas ni espacios; cambiar la propia contraseña no saca a nadie; restablecer cierra las sesiones de esa persona; cada intento queda en /srv/projects/drogheda/acceso/intentos-AAAA-MM.jsonl (sin la contraseña) y se ve en Incidencias (administradores). Las contraseñas siguen en el mismo htpasswd: Mi cuenta, Equipo y cuentas.py no cambian. curl -u ya no entra (revisión de seguridad: por ahí se podían probar contraseñas sin límite). Los scripts del servidor (vigía, tablero.py, el agente) hablan con 127.0.0.1:8081 directo; para probar desde tu terminal: read -s P; curl -c /tmp/j -H 'Content-Type: application/json' -d "{\"usuario\":\"leo\",\"password\":\"$P\"}" https://drogheda.helgen.online/api/entrar && curl -b /tmp/j https://drogheda.helgen.online/api/whoami. Revisión de seguridad independiente antes de publicar (27-oct): además, POST/PATCH/DELETE solo desde páginas del sitio, bloqueo por usuario + IP (nadie deja fuera a otro), redirección después de entrar solo dentro del sitio, avisos públicos solo con su texto público, y lo tecleado como usuario se guarda enmascarado.
  • nginx (backend/nginx/drogheda.helgen.online.conf, lo instala backend/nginx/cambiar-entrada.sh con respaldo, nginx -t, 14 pruebas —incluida una sesión de prueba firmada con el secreto del servicio, sin contraseña— y regreso automático; revisa antes que todas las contraseñas sean $apr1$): sin sesión → /entrar.html; las API → 401 JSON; públicos solo la entrada, /publico/, las fuentes, helgen.css, tema.js y el ícono; X-Remote-User lo pone nginx desde la sesión.
  • Servicio (backend/actas-hbox.service): ACTAS_ADMINS=leo,gibranlp (como lo dejó Gibran), la lista completa de actas repetibles, gunicorn con hilos (-k gthread -w 2 --threads 8: cada página pasa por /api/_auth) y Restart=always.
  • Incidencias (backend/incidencias.py, docs/js/incidencias.js, docs/tableros/incidencias.md): «Problemas del sitio» al pie de cada página, reporte sin sesión desde la entrada, avisos públicos, errores del navegador y del servidor en /srv/projects/drogheda/incidencias/_errores/.
  • Standup (backend/standup.py, docs/tableros/standup.md, docs/js/standup.js) y vigía (backend/vigia.py, crontab de leo, minuto 7 de cada hora). Reglas: docs/agentes.md; instrucciones del agente: agentes/agente-diario.md.
  • Correos de Equipo: backend/cuentas/2026-10-27_correos.json (12 correos; solo llena vacíos).

Un comando (en la Mac; pide sudo una vez y dos «si»):

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs && ./scripts/despliegue-1027.sh

Lo mismo a mano: sync-docs.sh → commit/push de hbox-docs y del submódulo → ./scripts/deploy.sh → ssh -t leo@git.helgen.online 'sudo bash /srv/projects/drogheda/hbox-docs/backend/nginx/cambiar-entrada.sh servicio && sudo bash /srv/projects/drogheda/hbox-docs/backend/nginx/cambiar-entrada.sh aplicar' → cuentas.py alta cuentas/2026-10-27_correos.json → crontab del vigía + semilla_1027.py → tablero.sh revisar / aplicar.

Regresar a la ventana gris (si algo no te gusta; el servicio nuevo puede quedarse): ssh -t leo@git.helgen.online 'sudo bash /srv/projects/drogheda/hbox-docs/backend/nginx/cambiar-entrada.sh deshacer'.

Confirmar la causa del 26-oct (opcional, lee el log de nginx de antes del cambio): ssh -t leo@git.helgen.online "sudo grep -hE 'password mismatch|was not found' /var/log/nginx/error.log /var/log/nginx/error.log.1 | tail -40" — «password mismatch» = contraseña vieja o mal escrita; «was not found» = usuario mal escrito (p. ej. con mayúscula).

Síntoma Causa Arreglo
Sale «Un momento…» en todas las páginas el backend no contesta (sin él no hay sesión) ssh -t leo@git.helgen.online 'sudo systemctl status actas-hbox'; journalctl -u actas-hbox -n 50
La entrada dice «No encuentro ese usuario» escribió otro usuario; el registro de intentos lo dice Incidencias → «Intentos de entrada»; su usuario está en Equipo
Alguien entra pero las API dicen 401 cookie de otro dominio o navegador sin cookies que entre de nuevo en /entrar.html; si sigue, incidencia con datos técnicos
cambiar-entrada.sh aplicar dice que el backend no tiene la entrada falta el paso servicio (reiniciar con los módulos nuevos) correr servicio primero
Quiero cerrar la sesión de todos — sudo rm /srv/projects/drogheda/.sesion-secreto && sudo systemctl restart actas-hbox

A12 · 28-oct: fotos de la galería y documentos en las actas

Lo pidió el equipo de campo (Leo, 28-oct 12:10): en los formularios solo se podía tomar la foto en vivo, no elegirla de la galería, y no había dónde subir un documento terminado.

  • docs/formularios/acta.js + acta.css: cada foto tiene dos botones, Tomar foto (cámara) y Elegir de la galería (sin capture); una foto HEIC que el navegador no puede convertir lo dice en vez de fallar en el servidor. Sección Documentos al final de toda acta (opcional; cfg.sinDocumentos la quita): PDF, Word, Excel, ODT/ODS, PowerPoint o la foto de una hoja. Los botones de subir se desactivan en un acta ya enviada.
  • backend/app.py: /api/archivos acepta además EXT_DOC (.pdf .docx .xlsx .pptx .odt .ods .doc .xls .ppt) y revisa los primeros bytes (un HTML renombrado a .pdf se rechaza). /api/adjunto manda nosniff, abre en el navegador fotos, PDF y texto, y descarga lo demás con su nombre original.
  • docs/tableros/unidad.md: el expediente dice «Archivos y documentos».
  • Pruebas: bash backend/tests-actas-documentos.sh . (15; se llamaba tests-1028.sh, nombre que pasó a Compras) además de 1027 (55), 1023 (35) y campeón (25); en teléfono, claro y oscuro.

Ya está desplegado (28-oct, 12:17): páginas con rsync después de la comparación con el servidor, y el backend con kill -HUP al proceso maestro de gunicorn (corre como leo: recarga los workers sin sudo y sin cortar). Si algo falla: git revert del commit y ./scripts/deploy.sh, luego ssh -t leo@git.helgen.online 'sudo systemctl restart actas-hbox'.

A13 · 28-oct: comentarios en los pendientes y «¿por qué?» al moverlos

Lo pidió Leo (28-oct, 12:57 y 16:12): que cada tarjeta tenga comentarios y que se pueda justificar por qué se movió, sobre todo cuando algo pasó a Hecho sin estar terminado.

  • backend/app.py (PATCH /api/tarea): _nota hasta 1000 caracteres (antes 300) y además del texto del historial queda aparte en historial[-1].nota. Comentar no necesita ser campeón; mover sí (sin cambio).
  • docs/tableros/kanban.js + tablero.css: sección Comentarios en el detalle (hilo + caja) y contador en la tarjeta; reglaMover() pide «¿por qué?» en el mismo PATCH: a Hecho (evidencia, D-27-6), al reabrir desde Hecho, a Bloqueado y de En curso a Por hacer; «Cancelar» o Esc lo deja donde estaba. Lo demás se mueve sin preguntar. Vale para arrastrar, la franja, las flechas, «Mover a…» y los botones del detalle. En el teléfono el detalle cabe en la pantalla.
  • docs/tableros/pendientes.md («Cómo se usa») y juntas.md (D-27-6) lo explican.
  • Pruebas: bash backend/tests-comentarios.sh . (16) además de 1027 (55), 1028 Compras (58), 1023 (35), campeón (25) y actas-documentos (15); en el navegador: escritorio y teléfono (iPhone 13 y SE), claro y oscuro.

Desplegado el 28-oct: páginas con rsync después de la comparación con el servidor, backend con kill -HUP al maestro de gunicorn. Para deshacer: git revert del commit, ./scripts/deploy.sh y ssh -t leo@git.helgen.online 'sudo systemctl restart actas-hbox'.

A14 · 28-oct: elegir uno de tus comentarios como el porqué

Lo pidió Leo (28-oct, 18:00): Michelle comenta antes de mover y luego el «¿por qué?» la hacía escribir lo mismo otra vez.

  • backend/app.py (PATCH /api/tarea): _nota_de = el cuando de un comentario de la misma persona en ese pendiente (si no existe o es de otra persona: 400). La entrada del movimiento guarda ref (sin nota, así no se repite) y su texto para la Actividad lleva «por su comentario del dd/mm hh:mm: «extracto»». Con _nota además, guarda las dos cosas.
  • docs/tableros/kanban.js + tablero.css: el «¿por qué?» lista tus últimos 5 comentarios del pendiente; si comentaste después del último cambio de columna, ese ya viene elegido (un clic en «Mover»). «Escribir otro motivo» sigue ahí. En el hilo, el comentario elegido lleva la etiqueta «→ Hecho». El porqué pegado igual que un comentario anterior de la misma persona también se muestra una sola vez. El orden se toma del historial, no de la hora (hay horas en UTC y en hora local).
  • Pruebas: bash backend/tests-comentarios.sh . (26) y las demás; en el navegador, el caso de Michelle de punta a punta.

Desplegado igual que A13 (rsync de páginas tras la comparación; kill -HUP al maestro de gunicorn).

B · Verificación después de desplegar

# 1. el backend responde y sabe quién eres
curl -s -H 'X-Remote-User: leo' http://127.0.0.1:8081/api/whoami   # en el servidor (desde el 27-oct curl -u ya no entra por fuera)
curl -s -H 'X-Remote-User: alan' http://127.0.0.1:8081/api/whoami  # {"user":"alan","admin":false}

Si user sale vacío, falta la línea X-Remote-User del paso 4.

# 2. la subida de fotos funciona de punta a punta
curl -s -u alan:PASS -F tipo=diagnostico-campo -F serie=CAMPO-PRUEBA \
     -F foto=@cualquier-foto.jpg \
     https://drogheda.helgen.online/api/adjuntos
# → {"ok":true,"fotos":[{"ruta":"diagnostico-campo/CAMPO-PRUEBA/2026…jpg",…}]}

# 3. el acta de campo NO se bloquea al segundo envío
curl -s -u alan:PASS https://drogheda.helgen.online/api/historial\?tipo=diagnostico-campo\&serie=CAMPO-PRUEBA

Y la prueba que de verdad cuenta, desde un teléfono: abrir https://drogheda.helgen.online/formularios/diagnostico.html, elegir la unidad ⚙ PRUEBA (no cuenta), tomar una foto y enviar. Las series con PRUEBA se guardan aparte, en _pruebas/, y no ensucian el registro.


C · Ciclo normal a partir de aquí

# contenido nuevo desde el repo Drogheda (lista curada dentro del script)
./scripts/sync-docs.sh
git add -A && git commit -m "…" && git push
./scripts/deploy.sh                 # sitio + backend (rsync) + foto del tablero
./scripts/tablero.sh revisar        # si Claude dejó un lote nuevo en backend/lotes/
./scripts/tablero.sh aplicar
# y SOLO si cambió backend/app.py (deploy.sh ya lo copió):
ssh leo@git.helgen.online 'sudo systemctl restart actas-hbox'

D · Respaldo

Todo lo que los técnicos generan son dos carpetas planas — cópialas y ya:

rsync -az leo@git.helgen.online:/srv/projects/drogheda/actas/    ~/respaldo-actas/
rsync -az leo@git.helgen.online:/srv/projects/drogheda/adjuntos/ ~/respaldo-fotos/

D4 · 28-oct: Compras (órdenes de compra del cotizador) y la hora local

backend/app.py cambió (engancha compras.py) y el servicio también, así que hay que reiniciar.

cd ~/Documents/Claude/Projects/Drogheda/hbox-docs
./scripts/deploy.sh                      # sube el sitio y backend/compras.py; revisa enlaces antes de publicar
ssh leo@git.helgen.online
  sudo cp /srv/projects/drogheda/hbox-docs/backend/actas-hbox.service /etc/systemd/system/
  sudo systemctl daemon-reload && sudo systemctl restart actas-hbox
  curl -s localhost:8081/api/compras -H 'X-Remote-User: leo' | head -c 200      # debe traer ok:true

Qué trae el servicio nuevo:

  • TZ=America/Mexico_City — el servidor corre en UTC y por eso el sitio enseñaba todo 6 horas adelantado (lo vio el equipo en la junta del 28-oct). Desde el reinicio, cada fecha que escribe el backend queda en hora de Querétaro. Lo guardado antes del 28-oct sigue en UTC y no se corrige solo.
  • COTIZADOR_SECRETO (comentado) — el secreto compartido con el cotizador. Mientras no esté, las rutas /api/integracion/ contestan 503 con un mensaje claro, la página Compras lo avisa y el resto del sitio funciona igual. Para encenderlo:
openssl rand -hex 32                     # el MISMO valor va en el cotizador como PORTAL_SECRETO
sudo systemctl edit actas-hbox           # o descomenta la línea en el .service y vuelve a copiarlo

La base /srv/projects/drogheda/drogheda.db se crea sola al arrancar (SQLite en modo WAL) y se respalda con el resto de /srv/projects/drogheda. nginx no necesita cambios: /api/integracion/ entra por el location /api/ que ya existe y la firma la revisa el backend, no la sesión.

Pruebas antes de publicar: bash backend/tests-1028.sh ~/Documents/Claude/Projects/Drogheda/hbox-docs (58).

E · Problemas frecuentes

Síntoma Causa Arreglo
"La foto pesa demasiado" con fotos normales falta client_max_body_size paso 4
El nombre del técnico no se precarga falta X-Remote-User paso 4
"Ya existe un acta para esta serie" en diagnóstico backend viejo paso 2 (git pull + restart)
Las fotos se ven rotas en el tablero falta la carpeta adjuntos o su permiso paso 3
mkdocs build --strict falla un enlace del nav apunta a un archivo que no existe revisa mkdocs.yml
El técnico no puede entrar usuario no creado, o se usó htpasswd -c y borró a los demás paso 5
«El servicio no tiene permiso de escribir el archivo de usuarios» falta el chown/chmod del htpasswd paso 7
«Tu contraseña está guardada en un formato que no puedo verificar» esa entrada es bcrypt, no $apr1$ restablecerla desde /cuenta.html como administrador