From 8272c0bb7b8a45b8b1046878874d5ca7ca46cc9e Mon Sep 17 00:00:00 2001 From: borja Date: Mon, 29 Sep 2025 10:15:44 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20a=C3=B1ade=20plan=20detallado=20por=20e?= =?UTF-8?q?tapas=20para=20multicomunidades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aider (openrouter/openai/gpt-5) --- docs/plan-multicomunidades.md | 160 ++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/docs/plan-multicomunidades.md b/docs/plan-multicomunidades.md index 5f45d9a..0615d1d 100644 --- a/docs/plan-multicomunidades.md +++ b/docs/plan-multicomunidades.md @@ -217,3 +217,163 @@ Notas de implementación - Mantener compatibilidad hacia atrás: en creación de tareas, si el group_id no está allowed, persistir sin group_id (null). - No bloquear DMs por diseño (gating aplica solo a grupos). - Reutilizar ResponseQueue para todas las notificaciones (incluyendo admins). + +Plan detallado etapa por etapa (con impacto en tests y archivos a tocar) +Convenciones transversales para no romper la suite: +- Nueva variable GROUP_GATING_MODE = 'off' | 'discover' | 'enforce'. Valor por defecto: 'off' (especialmente en tests). Solo se activará en tests específicos. +- Helper de tests tests/helpers/db.ts: + - makeMemDb(), injectAllServices(db), seedAllowed(db, groupIds[]), resetServices(). +- Opcional tests/setup.ts (o equivalente): fija GROUP_GATING_MODE='off' por defecto para toda la suite. +- AllowedGroups con dbInstance inyectable, caché con clearCache/resetForTests y métodos idempotentes. + +Etapa 0 — Preparación y criterios (tests-first) +Objetivo +- Asegurar un “harness” de tests estable y predecible para DB y servicios estáticos. + +Cambios de código +- Ninguno funcional. Solo infraestructura de tests (helpers y, si procede, setup global). + +Archivos a tocar (código) +- tests/helpers/db.ts (nuevo) +- tests/setup.ts (opcional, si existe configuración global de tests) + +Tests nuevos a añadir +- tests/unit/db/migrations.smoke.test.ts: comprueba que initializeDatabase en :memory: no lanza y crea tablas base (sin allowed_groups aún). + +Tests existentes a actualizar +- Ninguno obligatorio. Opcional: migrar suites que crean DB manualmente a usar tests/helpers/db.ts. + +Etapa 1 — Esquema y servicio AllowedGroups (sin gating todavía) +Objetivo +- Crear tabla allowed_groups y servicio con caché. No bloquear nada aún. + +Cambios de código +- src/db/migrations/index.ts: añadir migración v9_allowed_groups (CREATE TABLE IF NOT EXISTS ...). +- src/services/allowed-groups.ts (nuevo): métodos upsertPending, isAllowed, setStatus, listByStatus, seedFromEnv, clearCache/resetForTests. +- src/services/metrics.ts: gauges allowed_groups_total{status} (best-effort). + +Tests nuevos a añadir +- tests/unit/services/allowed-groups.test.ts +- tests/unit/db/migrations.allowed-groups.test.ts + +Tests existentes a actualizar +- Ninguno. La migración se ejecuta en initializeDatabase de suites que ya lo llaman. + +Etapa 2 — Descubrimiento seguro de grupos (registrar, no operar) +Objetivo +- Registrar grupos desconocidos como pending en modo 'discover', sin cambiar comportamiento por defecto de la suite. + +Cambios de código +- src/server.ts: + - handleMessageUpsert: si GROUP_GATING_MODE='discover' y es group_id desconocido → upsertPending y return temprano sin procesar comandos/sync. + - Métrica unknown_groups_discovered_total (src/services/metrics.ts). +- src/services/contacts.ts (opcional): obtener label/subject si viene en el webhook. + +Tests nuevos a añadir +- tests/unit/server/unknown-group-discovery.test.ts + +Tests existentes a actualizar +- tests/unit/server.test.ts: asegurar que: + - Setea GROUP_GATING_MODE='off' en beforeEach, o + - Si prueba mensajes de grupo, usar seedAllowed(db, ['']). +- Resto de suites: sin cambios esperados. + +Etapa 3 — Gating mínimo en superficies críticas +Objetivo +- Bloquear comandos y sync en grupos no allowed cuando GROUP_GATING_MODE='enforce'. + +Cambios de código +- src/services/command.ts: + - Al inicio, si message viene de grupo y GROUP_GATING_MODE='enforce', llamar AllowedGroups.isAllowed(groupId). Si no allowed: política por defecto “silencio” o respuesta breve configurable. +- src/services/group-sync.ts: + - Filtrar operaciones a solo grupos AllowedGroups.isAllowed. +- src/server.ts: + - Antes de encolar/ejecutar comandos, si es grupo y 'enforce', exigir allowed. +- src/services/metrics.ts: + - commands_blocked_total + - sync_skipped_group_total (opcional) + +Tests nuevos a añadir +- tests/unit/services/command.gating.test.ts +- tests/unit/services/group-sync.gating.test.ts + +Tests existentes a actualizar +- tests/unit/services/command.claim-unassign.test.ts: sembrar grupo allowed en beforeAll/beforeEach o fijar GROUP_GATING_MODE='off'. +- tests/unit/services/command.reminders-config.test.ts: idem si el test ejecuta comandos en contexto de grupo. +- tests/unit/services/command.date-parsing.test.ts: normalmente DM; no tocar salvo que simule grupo. +- tests/unit/services/group-sync.members.test.ts: sembrar grupo allowed para '123@g.us'. +- tests/unit/services/group-sync.scheduler.test.ts: sembrar grupos allowed en el scheduler o setear mode='off'. +- tests/unit/tasks/claim-unassign.test.ts: si hay contexto de grupo, seedAllowed; si es DM, sin cambios. +- tests/unit/services/cleanup-inactive.test.ts, tests/unit/services/metrics-health.test.ts, tests/unit/services/response-queue*.test.ts, tests/unit/services/reminders.test.ts: no deberían requerir cambios; si alguno falla por simulación de grupo, aplicar seedAllowed o mode='off'. + +Etapa 4 — Flujo de aprobación administrativa y notificaciones +Objetivo +- Permitir a ADMIN_USERS aprobar/bloquear grupos y consultar pendientes; notificar a admins al descubrir pending. + +Cambios de código +- src/services/admin.ts (nuevo): /admin habilitar-aquí, deshabilitar-aquí, pendientes, allow-group , block-group . +- src/server.ts: router para /admin y validación de ADMIN_USERS; usar ResponseQueue para notificaciones. +- src/services/allowed-groups.ts: asegurar setStatus, listByStatus, get(groupId). +- src/services/response-queue.ts (o webhook-manager): enviar DM a ADMIN_USERS en descubrimiento pending (best-effort y detrás de flag para no romper tests existentes). + +Tests nuevos a añadir +- tests/unit/services/admin.test.ts +- tests/unit/services/command.admin-approval.test.ts + +Tests existentes a actualizar +- tests/unit/server.test.ts: si parsea /admin, agregar casos explícitos o mantener aislado con mode='off' para suites no relacionadas. +- No tocar otras suites. + +Etapa 5 — Integración con recordatorios y tareas +Objetivo +- Recordatorios y operaciones por grupo respetan AllowedGroups cuando hay contexto de grupo. DMs no se ven afectados. + +Cambios de código +- src/services/reminders.ts: filtrar por AllowedGroups.isAllowed(gid) cuando el recordatorio/consulta use contexto de grupo. +- src/tasks/service.ts: sin cambios funcionales (seguir permitiendo group_id=null). +- src/services/rate-limit.ts (opcional): clave compuesta por grupo. + +Tests nuevos a añadir +- tests/unit/services/reminders.gating.test.ts +- tests/unit/tasks/service.gating.test.ts + +Tests existentes a actualizar +- tests/unit/services/reminders.test.ts: si usa contexto de grupo, seedAllowed o mode='off' para mantener expectativas. +- Resto: sin cambios. + +Etapa 6 — Observabilidad y operaciones +Objetivo +- Métricas agregadas y documentación. + +Cambios de código +- src/services/metrics.ts: allowed_groups_total{status}, unknown_groups_discovered_total, commands_blocked_total, admin_actions_total{action}. +- docs/operations.md: documentar variables y comportamiento. +- docs/architecture.md: sección de control de acceso por grupos. +- Confirmar /metrics en src/server.ts. + +Tests nuevos a añadir +- Opcional: tests/unit/services/metrics-health.test.ts puede ampliar cobertura para nuevos contadores (no estricta; tolerar ausencia si METRICS_ENABLED='false'). + +Tests existentes a actualizar +- tests/unit/services/metrics-health.test.ts: si asume conjunto exacto de métricas, flexibilizar aserciones o aislar nuevas métricas tras METRICS_ENABLED. + +Etapa 7 — Hardening, regresión y rollout +Objetivo +- Validar que no hay rutas sin gating y que la activación progresiva no rompe nada. + +Cambios de código +- Ninguno obligatorio más allá de ajustes menores. + +Tests nuevos a añadir +- tests/integration/multi-groups.e2e.test.ts (si procede). +- Casos combinados con varios estados: allowed/pending/blocked + discover/enforce. + +Tests existentes a actualizar +- Ninguno; mantener GROUP_GATING_MODE='off' por defecto y activar explícitamente en tests que cubren gating. + +Checklist por etapa para mantener la suite en verde +- Antes de tocar código de gating: añadir tests nuevos primero (TDD light). +- Default en tests: GROUP_GATING_MODE='off'. +- Donde haya contexto de grupo en tests existentes: seedAllowed(memdb, ['']) en el setup del archivo. +- Limpiar cachés entre tests: AllowedGroups.resetForTests() en afterEach/afterAll cuando el servicio se use. +- initializeDatabase(memdb) siempre presente en suites con DB en memoria para aplicar migraciones (incluida v9).