# Reglas de equipo — correcciones y cambios en mbinv

> ⚠️ **Estas reglas SOLO las pueden reescribir Andrea o Manuel.** El resto —
> programadores y agentes de Claude— las **leen y las siguen**. Cualquier cambio a
> este archivo requiere la aprobación de Andrea o Manuel (ver `CODEOWNERS`).
>
> Este archivo es la **fuente única** de las reglas del equipo, compartida por todas
> las cuentas de Claude (grupo y personales) porque vive en el repo. No copiar las
> reglas a la memoria personal de nadie: se leen de acá.

---

## 1. LEY: nada sin tiquete

**Nunca** se trabaja una corrección, modificación o cambio **sin un tiquete**. Si ya
hay algo en curso sin tiquete, **crear uno** antes de seguir. Pasa muy seguido que
soporte reporta por chat, se arranca sin tiquete, se olvida y queda en el aire — sin
bitácora, sin dueño. El tiquete es lo que hace que el trabajo exista.

## 2. Nada se da por probado sin evidencia

- **Antes/después**, probado **en pruebas** (nunca en producción). `php -l` es solo
  sintaxis, **no** es prueba.
- Los **comentarios del bot (Copilot/codex)** los revisa y resuelve **quien hace el
  fix**, antes de pedir review humano — no dejarlos colgando.
- El tiquete lleva una **guía de prueba completa**: qué sitio/URL (el ambiente aislado
  de la rama), paso a paso qué hacer, qué se debe ver (antes vs después), **y qué hacer
  según el resultado** (ver sección 3).
- **Se prueba por el camino que usa la gente, no por el más cómodo.** Si el cambio se
  usa desde una pantalla, la prueba es **clic real en el botón real**, con sesión real.
  Un `curl` que devuelve 200 **no** prueba que el botón funcione: el navegador manda
  cabeceras, preflight de CORS y cookies que `curl` no manda. Lo mismo aplica a probar
  una consulta suelta en vez de la pantalla, o el endpoint en vez del flujo completo.
- **Si solo se pudo probar por el camino cómodo, hay que decirlo.** "Probado por API,
  falta probarlo desde la pantalla" es una entrega honesta; reportarlo como verificado
  a secas es lo que deja bugs vivos con el tiquete cerrado.
- **Sin insumos para reproducir, no se arregla a ciegas.** Cuando el error depende de un
  dato que trae el usuario —**carga masiva / importación**, ante todo— el reporte debe
  **adjuntar el archivo exacto que falla**. Es parte del "antes": sin él no se reproduce
  ni se prueba, y adivinar el fix es cómo se cierran tiquetes con el bug vivo. Si el
  tiquete llega sin el archivo, se pide antes de arrancar (no después de "corregir").

> **De dónde salió esta regla** (ago-2026): el panel de huellas rompió **dos veces** el
> mismo botón, y las dos veces la verificación previa estaba en verde. Primero, CORS sin
> `methods` explícito bloqueaba PUT/DELETE solo en el navegador. Después, el cliente HTTP
> mandaba `Content-Type: application/json` en peticiones sin cuerpo, y el servidor
> respondía **400 "Bad Request"** — con eso, los tres botones de Eliminar del panel nunca
> funcionaron desde el navegador. En los dos casos `curl` pasaba limpio, porque `curl` no
> manda esas cabeceras. No fue falta de pruebas: fue probar por donde no era.

## 3. Flujo de estados

`En Desarrollo` → `Pruebas de QA` → (el dueño **mergea**) → **`Resuelto`** → **vuelve a
Soporte** → Soporte actualiza al cliente **en el horario acordado** → **visto bueno del
cliente** → `Cerrado`.

- **Nunca `Cerrado` sin el OK del cliente.**
- Si en QA **no funciona**: no cerrarlo; documentar qué se vio y en qué paso (captura +
  status), estado a `En Desarrollo`, y reasignar al dueño con esa evidencia.

## 4. Marcar EN PROGRESO

Al crear un tiquete y empezar a trabajarlo de una, **indicarlo** (estado + asignación)
para que nadie duplique el esfuerzo.

## 5. Asignación y escalación

- **PT (POSTouch) → Andrea** (única que lo ve). **BO → el programador del módulo** (ver
  el mapa de dominios; se regenera del `git blame` reciente).
- **Escala a Manuel** si es **urgente** (cliente esperando) y no hay respuesta en un
  tiempo razonable. **Trivial** (detectado proactivamente, sin cliente detrás) → espera
  al **día hábil**.
- Programadores de BO y otros sistemas: **lunes a viernes, horario variable (HO)**. Para
  lo no-urgente, el "tiempo razonable" cuenta **solo horas hábiles**.
- **Guía (no ley):** no desplegar/actualizar **viernes ni fin de semana**, salvo
  corrección urgente — para no hacer trabajar a programación en horario inhábil.

## 6. Auto-corrección: cuándo el agente consulta

- **Confianza alta** (reproduce el error, señala la línea exacta, y el fix sigue un
  patrón que el propio código ya usa) → **arregla y presenta el PR**.
- **Confianza baja** → **consulta el diagnóstico al programador ANTES de escribir
  código** (validar el disparador).
- Si el agente **se estanca**, para y consulta con *"esto descarté (A, B, C), esto
  necesito (X)"* — no seguir cavando.

## 7. Dominio: las ventas se hacen solo desde el PT

Las ventas se crean **únicamente desde el PT (POSTouch)**, no desde el Back Office. Al
sugerir pruebas de documentos en el BO, **no usar una venta** de ejemplo — usar un
**traslado** o un **ajuste** (que sí se hacen en el BO y no llevan proveedor).

## 8. Ambiente de prueba aislado por rama

`probar-rama.sh <rama>` (en el repo `oci-dr`) monta un ambiente aislado en pruebas con
URL clicable `<slug>.pruebas.sistemasmb.com` (wildcard DNS ya creado). **Cada rama en
su caja** — dos cambios nunca se pisan. Para diagnosticar, `app_dev.php` muestra la
excepción y el status reales. Borrar con `probar-rama.sh <rama> --borrar`.

## 9. Fechas y horas: siempre Guatemala, anclada explícita

Todo lo que **compare, guarde o dispare por fecha/hora** (agendadores, cron, sellos de
tiempo, "vence el…", "actualizar a las…", reportes con hora) usa **hora de Guatemala
anclada de forma explícita** (UTC−6; Guatemala no tiene horario de verano) — **nunca** la
hora "local" del sistema operativo ni UTC crudo.

El "ahora" sin zona (`now()` / `datetime.now()` / `new Date()`) depende de cómo esté
configurado **ese** servidor. El default de casi todo servidor cloud es **UTC**, así que un
`now()` naive se corre **6 horas** y el reloj dispara a la hora equivocada **sin dar ningún
error** — nadie se entera hasta que un despliegue o un aviso sale a deshora.

- **Al revisar o escribir cualquier cambio que toque tiempo**, verificá de dónde sale el
  "ahora", en qué zona se guarda y en qué zona se compara. Si no está anclado a GT de forma
  explícita, es un **bug latente** aunque hoy funcione.
- En **reportes y mensajes al equipo**, mostrar la hora en **GT** (las fuentes —OCI,
  Freshdesk, logs— suelen devolver UTC; convertir antes de mostrar).
- **Ejemplo real (6-ago-2026, #23102):** el agendador del panel de despliegues comparaba
  con `datetime.now()` naive. Funcionaba solo porque el servidor estaba en
  `America/Guatemala`; pasarlo a UTC habría corrido cada despliegue programado 6 h. Se
  ancló con un offset fijo GT (`datetime.now(GT)`), correcto aunque el servidor esté en UTC.

## 10. Prioridad de tiquetes

- **Un error de programación es lo más urgente.** Todo tiquete que sea una **corrección de un
  error** (bug) va con la **prioridad más alta (Urgente)**. La señal para detectarlo es el `type`
  **Incidente / Problema**; si un bug quedó mal tipeado, corregir el tipo para que la regla lo tome.
- **Si el tiquete está esperando la revisión de Manuel** (`cf_pendiente_de_mb = "Sí"`), **subirle la
  prioridad** — su revisión está **frenando al compañero** que quedó bloqueado esperándola, así que
  no puede quedar dormida.

## 11. La evidencia del "después" la entrega el programador

Amplía la §2. El reparto de la evidencia es claro:

- El **"antes"** (el error) lo manda **quien reporta** (cliente/soporte) al abrir el tiquete —
  captura, texto exacto del mensaje, datos para reproducir.
- **Antes de corregir, validar que el reporte trae lo suficiente.** Si ya alcanza (p. ej. la captura
  muestra el error), **no pedir más: trabajarlo.** Si de verdad falta algo, pedir lo puntual **y mover
  el tiquete** (enviar al cliente + pasar a `Pendiente`). Nunca dejar una nota de "falta información"
  sin cambiar el estado — el tiquete queda **muerto** en la cola.
- El **"después"** (que se probó y quedó corregido) lo entrega **el programador**: imágenes o video
  del BO mostrando que ya no sale el error / que la funcionalidad queda como se necesita. **QA no
  valida sin esa evidencia.**

## 12. Traza al mover un tiquete

Toda nota que cambie **asignación o estado** cierra con una línea de **traza**: el **antes → después**
(a quién quedó asignado, en qué estado, o si se le regresa a alguien), con **nombre** del agente y
**nombre** del estado (no ids ni números). Que quien la lea sepa de un vistazo qué pasó, sin adivinar.
Verificar siempre que el cambio **realmente se aplicó** releyendo el tiquete: asignar a un agente
fuera del grupo del tiquete **se ignora en silencio** (el PUT da 200 y no aplica).

---

## Mapa de dominios (quién toca qué)

Por actividad reciente (últimos 12 meses), excluyendo a Manuel (backstop):

- **Christian** — la mayor parte del BO activo: Maedoc, Maecli, Fisico, Producto, Dic,
  BackOrder, Procesos, Reparaciones, Tiquetes…
- **Saraí** — Traslado, TrasladoEnTransito, Documento, Consultas, Parametros…
- **Andrea** — PT (POSTouch) siempre + lo que tocó del BO.
- **Manuel** — backstop / módulos maestros dormidos sin autor reciente.

**Ex-empleados — no asignarles nada:** JC (JC04mb), jmorales17.

> El mapa es **vivo**: se regenera del `git blame` reciente porque la actividad se
> mueve. No tomarlo como fijo.

## Coordinación entre ventanas — registro de trabajo en curso

Trabajamos con varias ventanas de Claude en paralelo que **no se ven entre sí**. Para no
competir por lo mismo (el incidente de oci-dr), antes de empezar una modificación se **avisa** en
un registro central que todas las ventanas consultan.

**Registro:** tabla `claude_en_curso` en `mbinvmacrobase` (la BD de pruebas por el bastión, puerto
3310 — credenciales en `app/config/parameters.yml`) — la misma BD
que todas las ventanas ya alcanzan. Una fila por **recurso** (repo, servidor o módulo):
`recurso, alcance, ventana, tiquete, inicio, ultimo_latido, estado`. Latido viejo (>30 min) =
candado abandonado (se puede tomar).

**Protocolo — toda ventana lo sigue:**
1. **Antes de tocar** un proyecto/servidor: `claim check <recurso>`. Si sale **TOMADO** por otra
   ventana con latido reciente → **no competir**, avisar a MB.
2. **Al empezar:** `claim take <recurso> <ventana> <tiquete> "<alcance>"`.
3. **Mientras trabajás:** `claim beat <recurso> <ventana>` de vez en cuando.
4. **Al terminar:** `claim release <recurso> <ventana>`.
5. `claim list` = todo lo activo.

- **`recurso`**: nombre estable — el repo (`mbinv`, `oci-dr`, `mb-deploy-panel`), el servidor
  (`app-4`, `oci-mbinv`) o `repo:modulo`.
- **`ventana`**: un id estable de tu sesión (p.ej. el UUID de la sesión).
- **Helper (Mac de MB):** `~/bin/claim` (envuelve `~/bin/claim.php`; lee la clave de
  `parameters.yml`). Cuentas sin ese helper usan el mismo SQL sobre la tabla.

**Complementa, no reemplaza:** sigue vigente el aislamiento por **worktree** (§8) y marcar el
tiquete **En Progreso** (§4). El registro es el aviso ENTRE ventanas a nivel *recurso*; el tiquete
es la señal para el equipo humano.

> Ejemplo real (8-ago-2026): dos ventanas iban a tocar "disco" — una en `mb-deploy-panel` (#23090,
> aviso de disco con proyección) y otra en `oci-dr` (#23221, alarmas OCI). El check detectó el
> solape y se paró #23221 antes de duplicar trabajo.
