# 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.

## 1b. Un hallazgo NO es un tiquete

La §1 dice que no se trabaja sin tiquete. **No dice que cada cosa que uno encuentra
de paso merezca uno.** Son cosas distintas y confundirlas tiene un costo medible.

**Medido el 11-ago-2026:** la cola visible de Manuel tenía **45 tiquetes**. De esos,
**32 los habían abierto sesiones de Claude esa misma semana** como hallazgos, y solo
**12 eran trabajo de un cliente** (el que falta no quedó clasificado en el conteo).
Ese mismo día, ya con el acuerdo tomado, cuatro ventanas distintas abrieron cuatro
tiquetes más en una tarde.

**La regla:**

- **Trabajo que alguien pidió** → tiquete, siempre. Eso es la §1.
- **Hallazgo encontrado de paso** → **no se abre tiquete solo**. Se junta con los
  demás y se le proponen al final: *"encontré esto, esto y esto; ¿cuál amerita
  tiquete?"*. "Ninguno" es una respuesta válida.
- **Excepción:** si es urgente y le está pegando a un cliente ahora, tiquete de una,
  sin preguntar.
- Los hallazgos internos que igual se abran **nacen en `Parqueado`**, no en la cola de
  trabajo de nadie. Siguen buscables y con su detalle; si ameritan trabajarse, se
  sacan de ahí.

**Por qué va acá y no en la memoria de una sesión:** cada ventana de Claude carga su
memoria **al arrancar**. Un acuerdo tomado a media mañana no llega a las ventanas que
ya estaban corriendo — por eso ese día se siguieron abriendo tiquetes después de
acordar que no. Este archivo, en cambio, lo lee cualquier sesión que trabaje el repo.
Las reglas de coordinación viven acá; la memoria sirve para lo de cada quien.

## 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.

## 3b. El GRUPO es el área. La ETAPA va en el estado

**El grupo dice de qué área es el tiquete. El estado dice en qué momento va.** Son dos cosas
distintas y hay que dejarlas separadas:

- **El grupo no se mueve para avanzar el flujo.** Un tiquete de Programación sigue siendo de
  Programación mientras está en QA, mientras espera autorización y mientras se publica.
- **Para avanzar, se cambia el ESTADO** (§3), nunca el grupo.

**Por qué, y esto es lo importante:** en Freshdesk el grupo además decide **quién puede
recibir el tiquete**. Asignar a alguien que no está en el grupo **se ignora en silencio** —
el PUT contesta 200, aplica el estado y la fecha, y **deja el responsable vacío sin avisar**
(§12). Así que cada vez que el grupo se usa como etapa, mover el tiquete puede dejar fuera a
la persona que lo va a trabajar. El problema no es la asignación: es usar el grupo para dos
cosas a la vez.

**Grupos que eran etapa disfrazada, y a qué estado corresponden:**

| Grupo (etapa) | Estado que le corresponde |
|---|---|
| Correcciones | `En Desarrollo` |
| Pruebas/QA | `Pruebas de QA` |
| Programación Autorización | `Autorización de versión` |
| Publicación de versión | `Publicación de versión` |
| Cierre | `Resuelto` → `Cerrado` |
| Seguimiento | `Esperando respuesta nuestra` |
| Reportería Nuevos / Análisis | `En Análisis` |
| Reportería Cambios / Correcciones | `En Desarrollo` |
| Reportería Revisión | `Pruebas de QA` |

Los cinco de Reportería son una sola área partida en cinco etapas: quedan como **un solo
grupo Reportería**. Los estados `Autorización de versión` y `Publicación de versión` se
crearon el 14-ago-2026 para esto, así que las once etapas tienen su equivalente.

**Medido el 14-ago-2026** sobre los 500 tiquetes tocados desde el 1-may, para que se vea que
esto no rompe nada en uso:

```
grupos con tiquetes abiertos ...........  9 de 27
grupos sin NI UN tiquete en 3½ meses ... 14 de 27   (los 5 de Reportería entre ellos)
tiquetes SIN grupo ..................... 215        <- "ninguno" es el grupo más usado
Programación ........................... 144 tiquetes (106 abiertos)
```

Los cinco grupos de Reportería son el ejemplo exacto del problema —una sola área partida en
cinco etapas— y **ninguno tiene un tiquete desde mayo**: ya se había abandonado en la práctica.

⚠️ **Los grupos que salen NO se borran.** "Sin actividad desde mayo" no es "sin uso": los 27
tienen historia, y los 11 que eran etapa cargan **~1,980 tiquetes** entre todos (Seguimiento
933, Cierre 438, Reportería Nuevos 281, Pruebas/QA 149...). Borrar uno deja sus tiquetes sin
grupo y sin forma de filtrarlos. Se renombran con un aviso —`ZZ — no usar (ver §3b)`— para que
nadie los elija por error, y el historial queda intacto.

**Al mover un tiquete, entonces:** cambiar el estado, dejar el grupo quieto, y asignar a la
persona. Si por algo hay que cambiar de grupo, **mandar grupo y responsable en el mismo
movimiento** y releer el tiquete después para confirmar que la asignación se aplicó.

## 3c. Cuando el trabajo cruza de área: padre en Soporte, hijo en Programación

Si el grupo no se mueve (§3b), falta contestar qué pasa cuando **un caso de Soporte necesita
programación** — que es el cruce más frecuente que tenemos. La respuesta **no** es meter a
todos los agentes en todos los grupos: eso vacía de sentido al grupo y no resuelve lo de
fondo.

**Se abre un tiquete hijo.** El caso del cliente se queda donde está y el trabajo técnico nace
en su propia área:

| | Padre | Hijo |
|---|---|---|
| Vive en | **Soporte** | **Programación** |
| Dueño | Quien le habla al cliente | Quien programa |
| Plazo | El del cliente (horas) | El del desarrollo (semanas) |
| Cierra cuando | El cliente confirma | Se entrega el código |

Así **nadie necesita pertenecer a dos grupos**, y se arregla algo que un solo tiquete no puede
hacer: sostener **dos relojes distintos**. Hoy, cuando el caso se traspasa entero a
Programación, el tiquete arrastra el plazo del cliente a un trabajo que tarda semanas, y se
pierde de vista quién le responde.

**Tres reglas del modelo:**

- **El padre nunca cierra antes que el hijo.**
- **Al cliente le responde siempre el dueño del padre**, aunque el trabajo lo haga otro.
- **Nada de padre/hijo dentro del mismo equipo.** Si los dos tiquetes son del mismo grupo, no
  es padre/hijo: es un tiquete mal partido, o basta con la miga de "Sale de #NNNNN".

En la API va como `parent_id` en el `POST /tickets`; el padre queda con `association_type: 1` y
el hijo con `2`. El hijo lleva **solicitante interno**, así que el cliente no recibe nada de él.

⚠️ **El costo, que hay que aceptar de entrada:** esto **duplica el conteo de tiquetes**. Queda
acordado que **los hijos no cuentan como carga de la mesa de soporte** sino como trabajo de
programación. Si no se dice antes, en tres meses se discute por qué "subieron los tiquetes".

> **Estado al 14-ago-2026:** el modelo se definió el 30-jul y **no se está usando para esto**.
> De 500 tiquetes tocados desde mayo, sólo 16 tienen asociación — y **los 16 están en
> Programación**, padre e hijo en el mismo grupo, que es justo lo que el modelo prohíbe. Se usa
> para agrupar proyectos internos, no para el cruce con Soporte. Mientras tanto Soporte tiene
> 18 tiquetes abiertos y Programación 106: los casos entran por Soporte y terminan viviendo en
> Programación.

## 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 — qué se atiende primero

Orden para elegir qué trabajar, de más a menos urgente. Cada nivel tiene una **señal
detectable** (estado o `type`), no es criterio de cada quien:

1. **Errores de programación que rompen o frenan la operación** (bug). Señal: `type`
   **Incidente / Problema** → prioridad **Urgente**. Si un bug quedó mal tipeado, corregir el
   tipo para que la regla lo tome. Un detalle cosmético que no frena nada **no** es Urgente:
   baja de nivel.
2. **Tiquetes que esperan respuesta NUESTRA** — la pelota está de nuestro lado y el cliente (o
   un compañero) está **bloqueado esperándonos**. Señal: estado **«Esperando respuesta
   nuestra»** (id 13). Incluye los que esperan la revisión de Manuel (`cf_pendiente_de_mb =
   "Sí"`). No pueden quedar dormidos.
3. **Tiquetes de clientes** activos (que no son bug ni esperan respuesta nuestra): estados
   Open / En Desarrollo con un cliente detrás.
4. **Proyectos** internos y funcionalidades nuevas. Aquí entra también la **seguridad y la
   deuda técnica** que no está rompiendo nada (p.ej. endurecer una consulta, sacar una
   credencial del código).
5. **Parqueados** (estado **Parqueado**): lo último.

**Override:** si un cliente **no puede vender, facturar o inventariar**, salta al frente esté
en el nivel que esté.

**Los que esperan respuesta del CLIENTE** (le pedimos un dato, la pelota es de él) van a
estado **Pending «Esperando su respuesta»** y **salen de esta cola** hasta que conteste — no se
priorizan porque no dependen de nosotros. Es el simétrico del estado 13: uno es «esperamos al
cliente», el otro «el cliente nos espera a nosotros».

## 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). La forma de no
toparse con eso es no usar el grupo como etapa (§3b); si aun así hay que cambiar de grupo, mandar
**grupo y responsable juntos** en el mismo cambio.

## 13. La cuota de Claude no se desperdicia

**`/clear` al cambiar de tarea**, y siempre antes de que la ventana pase de **250k** de
contexto. **Sonnet por defecto** (`/model`); Opus sólo para diseñar, depurar lo difícil o
revisar un PR. **Una ventana, un tiquete**: al cerrar el tiquete, se cierra la ventana.

El porqué, medido sobre 49 sesiones reales: el **76%** del gasto es Claude releyendo el
contexto acumulado —no lo que escribe—, y la misma petición cuesta **ocho veces más** a
900k que a 100k. Limpiar antes de los 250k ahorra el **39%**.

**Y toda máquina del equipo lleva instalado el aviso.** Es parte de la puesta a punto,
como el acceso al repo — no es opcional ni queda a criterio de cada quien:

    python3 herramientas/claude-costo/instalar.py

Después, **`/hooks` una vez** en cada ventana ya abierta (se abre y se sale con Esc), o
cerrarlas y abrirlas. Las que ya estaban corriendo no lo cargan solas.

Sin el aviso, las tres reglas de arriba dependen de que cada quien se acuerde — y así es
como se rompieron las reglas anteriores: no por mala fe, sino porque **cada ventana carga
lo que sabe al arrancar** y nadie se entera de lo que se acordó después.

**Si no programás, leé [`GUIA-CLAUDE-PARA-TODOS.md`](GUIA-CLAUDE-PARA-TODOS.md)**: es una
página, sin nada técnico, y trae un texto que pegás en tu chat para que Claude te instale
el aviso solo — sin abrir una terminal ni saber qué es Python.

Las reglas completas, la evidencia y el detalle del aviso:
[`REGLAS-CLAUDE-COSTO.md`](REGLAS-CLAUDE-COSTO.md).

---

## 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.

---

## UN proyecto activo, UN frente por proyecto

La regla de una ventana por proyecto (arriba) evita que dos sesiones se pisen el mismo
archivo, y **eso sí se está cumpliendo**. Lo que nadie limitaba es cuántos frentes abre
esa ventana adentro, que es donde se pierde el tiempo de verdad.

**Medido el 14-ago-2026**, leyendo los repos en disco:

```
ramas vivas de una sola ventana        51        59 directorios de trabajo
   mbinv                               28        mbinv            34
   Panel de rescate (DR)               17        mb-deploy-panel  19
   Panel de despliegues                 6        oci-dr            6

ramas que YA están en la base          56        su trabajo está mergeado: sobran
```

**Las dos reglas:**

1. **Un proyecto activo a la vez, hasta cerrarlo.** Que existan las otras ventanas está
   bien; lo que no debe pasar es que tres proyectos tengan trabajo el mismo día. El
   trabajo en curso es capital inmovilizado: nada rinde hasta que sale.
2. **⚠️ ORDENANZA: una rama se abre y se cierra el MISMO DÍA. Al segundo día es deuda.**

   No es una recomendación. Una rama que envejece contra la base **vuelve convertida en
   conflictos**: de las 51 vivas el 14-ago-2026, varias estaban **265 commits atrás**. Eso
   no es un bug nuevo cada día — es la misma rama desincronizándose, y se siente
   exactamente como "arreglo uno y sale otro".

   **Si el trabajo no cabe en un día, el tiquete es demasiado grande: se parte.** No se
   deja la rama esperando.

   **Cómo se verifica** — nadie tiene que declarar nada:

   ```bash
   que-me-espera.py            lista las ramas vivas con su edad y cuánto están atrás
   limpiar-lo-terminado.py     borra las que ya están mergeadas, con su worktree
   ```

   El informe de las 7:00 marca toda rama de **más de un día**. Una rama de tres días es
   una excepción que hay que poder explicar, no el estado normal.

**La excepción es la cola de tiquetes de clientes.** Va en su propia ventana, siempre
activa: son ciclos cortos, interrumpibles, y es lo que el cliente ve. Esa no cuenta como
"proyecto activo".

### Y por qué esto no es otra regla más

⚠️ **El 14-ago-2026 se encontraron TRES mecanismos de coordinación ya definidos y sin
usar:**

- el **modelo padre/hijo** (§3c): 16 asociaciones en 500 tiquetes, y las 16 mal —
  padre e hijo en el mismo grupo, que es lo que el modelo prohíbe;
- los archivos **`EN-USO-*.md`**: uno quedó colgado desde el 13-ago sin borrarse;
- la tabla **`claude_en_curso`** del `claim`, arriba: **vacía**, el mismo día en que
  cinco ventanas trabajaban cinco proyectos.

**Todo lo que hay que llenar a mano queda vacío.** Por eso esta regla viene con algo que
la mide sola y no le pide nada a nadie: `~/bin/que-me-espera.py` (repo `mb-herramientas`,
corre a las 7:00 y a las 14:00) lee los repos en disco y contesta qué se puede cerrar ya,
qué está en riesgo, cuántos frentes hay de verdad y qué tiquetes llevan una semana
quietos. Deja el informe en `~/Documents/que-me-espera.txt`.

Si una regla no se puede medir sin que alguien la declare, no se va a cumplir.

---

## Consultar las bases de PRUEBAS sin pegar la clave en el chat

Cuando le pidas a Claude que revise datos de una base de pruebas, **no le pegues la
contraseña en la conversación**: queda en el historial del chat para siempre. Usá el guion
del repo, que lee la clave de un archivo tuyo y nunca la imprime.

**Una sola vez, en tu máquina:**

```bash
mkdir -p ~/.config/mb
printf "BD_USUARIO=tu_usuario\nBD_CLAVE='tu_clave'\n" > ~/.config/mb/pruebas.env
chmod 600 ~/.config/mb/pruebas.env
```

Ese archivo no va a ningún repo. Si no sabés tu usuario o tu clave, pedíselos a Manuel —
**nunca los pongas en el chat ni en un tiquete**.

**Después, desde el repo:**

```bash
scripts/consultar-pruebas.sh --quien-soy              comprobar el acceso
scripts/consultar-pruebas.sh --bases                  qué bases hay
scripts/consultar-pruebas.sh --tablas <base>          qué tablas tiene
scripts/consultar-pruebas.sh --columnas <base> <tabla>
scripts/consultar-pruebas.sh "SELECT ..."             una consulta
```

**Deja hacer todo lo que necesites** — consultar, insertar, actualizar, borrar filas, crear
y alterar tablas. Es el servidor de pruebas y ahí se trabaja.

⚠️ **Lo único que pide confirmación son las tres cosas que no se deshacen:** borrar una base
entera (`DROP DATABASE`), y un `DELETE` o un `UPDATE` **sin `WHERE`**, que se llevan la tabla
completa. No es que no se pueda — hay que agregar `--se-lo-que-hago`:

```bash
scripts/consultar-pruebas.sh --se-lo-que-hago "DELETE FROM ..."
```

El motivo es que en el servidor de pruebas viven **copias de bases de clientes reales**, y las
cuentas del equipo tienen permisos de administrador sobre todo el servidor. El freno no está
para limitarte: está para que un descuido no borre la base de un cliente. Para todo lo demás
el guion no se mete.

**Y comprobá siempre contra qué servidor estás:** `--quien-soy` imprime la huella. La de
PRUEBAS es `wemuedtigeloq3nx`. Si sale otra, pará y avisá — el puerto 3309 es PRODUCCIÓN y
está a un dígito de distancia.

## Al crear un usuario de MySQL, nombrale el plugin viejo

**Todo usuario de base de datos que crees —a mano, en un guion o en código nuevo— lleva el
plugin escrito:**

```sql
CREATE USER 'nombre'@'%' IDENTIFIED WITH mysql_native_password BY 'la clave';
```

**No lo dejes en `IDENTIFIED BY` a secas.** Sin nombrarlo, MySQL usa el que el servidor tenga
por omisión, y eso deja de servirnos: en **MySQL 8.4 esa variable se ignora** y el servidor
siempre usa `caching_sha2_password`, **que el PHP 5.6 de los sitios no sabe hablar**. El
usuario nace y el `CREATE USER` no falla; lo que falla es el sitio del cliente cuando intenta
conectarse, semanas después y sin nada que lo relacione con el alta.

Medido el 15-ago-2026 con el PHP 5.6 real de `oci-mbinv`: un usuario con
`caching_sha2_password` responde *"The server requested authentication method unknown to the
client"*. Uno con `mysql_native_password` entra sin problema.

**Comprobá siempre con qué plugin quedó**, que es una consulta:

```sql
SELECT user, plugin FROM mysql.user WHERE user = 'nombre';
```

**Ya está cubierto** el alta por las dos vías normales —el panel de altas y la pantalla de
administración de mbinv—, así que esta regla es para todo lo demás: los usuarios que se crean
a mano en el bastión, los guiones sueltos y cualquier código nuevo que dé de alta cuentas.

⚠️ **Y esto es una prórroga, no la solución.** En **MySQL 9.x el plugin viejo desaparece del
todo**: ahí no hay parámetro que valga y los usuarios que sigan en `mysql_native_password`
quedan afuera, sin vuelta atrás. La salida de fondo es que los sitios corran PHP 7.2 o más;
recién ahí un usuario puede nacer con `caching_sha2_password`. Mientras tanto, el plugin viejo
es lo único que funciona en todo el parque. Ver el tiquete #23486.
