Commit Graph
2 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Opus 5 b7c52f174a fix(umind): auditoría de las cinco features — cinco defectos y un guard permanente
Revisión de coherencia de todo lo que entró. Cinco cosas mal, ninguna
detectable compilando:

1. LA PUERTA DE TOOLS DECIDÍA AL REVÉS. executeUmindTool encolaba antes de
   clasificar, así que en un canal público CUALQUIER nombre de herramienta
   terminaba como pendiente de aprobación — incluidos los que el modelo
   inventa y los internos, cuyos guards ("solo el dueño") quedaban
   inalcanzables porque vivían más abajo en el flujo. Un visitante del widget
   podía llenarle al dueño la bandeja de solicitudes para ejecutar cosas que
   no existen.

   Ahora se clasifica primero: lectura ejecuta, interna se rechaza si el
   pedido es público, acción se encola, desconocida se rechaza. Verificado
   inyectando la falla: el test falla y vuelve a pasar al arreglarlo.

2. El enlace "Ver el documento" de la bandeja estaba muerto: archivo_id no lo
   escribía nadie. Ahora la acción aprobada engancha el PDF que produjo.

3. Los PDF generados esquivaban la cuota de almacenamiento. El tope del plan
   se evitaba pidiéndole documentos al agente en vez de subiendo archivos.

4. La vista de Consumo no conocía el tipo "documento" — el rubro nuevo salía
   sin nombre ni color. Y el icono que le puse no existía en el set, así que
   habría quedado invisible: el mismo bug de los iconos de hace unas semanas,
   que compila y se ve vacío.

5. Las seis lecturas nuevas se registraron con w() en vez de r(), exigiendo
   SoloAdmin para leer. No era una fuga —era más estricto— pero un usuario
   staff sin admin veía la lista de agentes y 403 en sus avisos.

Y el guard que evita el próximo: TestTodoHandlerUmindValidaAlcance recorre los
65 handlers de uMind y exige que cada uno valide el alcance del tenant, o esté
en una lista de exenciones con el motivo escrito. Los mismos handlers se montan
para staff y para clientes; el middleware de scope deja los tenants permitidos
en el contexto pero no mira el :id de la URL — eso lo hace cada handler, y el
que se olvide compila igual. Al escribirlo el propio test encontró que mi lista
de exenciones sobraba en dos, y verifiqué con un handler con fuga deliberada
que lo detecta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 22:04:03 -05:00
Lizandro GuarnizoandClaude Opus 5 544cba6d34 feat(umind): repositorio de archivos del espacio y bandeja de aprobación
Dos piezas que se necesitan mutuamente.

ARCHIVOS — a nivel espacio y no de agente, porque los papeles son del negocio:
si mañana crea un segundo agente, sus contratos no se mudan. Subir, descargar,
borrar, cuota por plan, y un botón "usar como conocimiento" que lo manda por la
ingesta que ya existe — un clic, no volver a subir el mismo PDF.

Nunca se sirven por el estático: salen por su endpoint, que valida el tenant.
La cuota se mira antes de escribir, porque rechazar después de copiar 25 MB al
disco es cobrarle el espacio igual.

PENDIENTES — no es un motor de workflows, es una tabla y una regla: lo que sale
hacia afuera pedido por alguien que no es el dueño no se ejecuta, se encola.

El eje es quién está del otro lado, no qué tan peligrosa suena la acción. En el
widget público escribe cualquiera: ahí toda acción espera. En un canal interno
el dueño ya autorizó al escribirlo, y mandarlo a aprobar su propio pedido sería
fricción sin ninguna seguridad a cambio. (El plan decía aprobar siempre el
envío de correo; esto es más flojo a propósito y la propiedad que importa se
mantiene entera: desde un canal público no sale nada sin una persona.)

Cuatro decisiones que valen más que el esquema:
- Se guarda el payload EXACTO y se ejecuta eso. Aprobar no vuelve a llamar al
  modelo: si regenerara, aprobarías una cosa y saldría otra, y la diferencia
  aparecería recién en la mano del cliente. Hay un test que lo vigila.
- Dos personas mirando la misma bandeja pueden aprobar a la vez; el reclamo es
  un UPDATE condicional, así la cotización no sale dos veces.
- Vencen a los 7 días. Una cotización aprobada tres semanas tarde llega con
  precios de otro mes: es peor que ninguna.
- Al cliente nunca se le dice "rechazado" ni se le menciona una aprobación
  interna — se le dice que le responde alguien del equipo. También con test.

El correo de aviso lleva un enlace al panel CON login: un enlace que ejecuta
algo irreversible sin autenticar es un enlace que reenviado por error firma.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 21:37:37 -05:00