From 0426f679403599b0e0fe2478702e393bfa9e0d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Ben=C3=ADtez?= Date: Tue, 7 Jul 2026 17:39:36 -0300 Subject: [PATCH] =?UTF-8?q?docs(sprint-8):=20BRIEF.md=20=E2=80=94=20consol?= =?UTF-8?q?idaci=C3=B3n=20org=E2=86=92areas,=20client=5Feditor=20BPMN,=20v?= =?UTF-8?q?ersionado,=20Bloque=20B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sprints/sprint-8/BRIEF.md | 610 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 sprints/sprint-8/BRIEF.md diff --git a/sprints/sprint-8/BRIEF.md b/sprints/sprint-8/BRIEF.md new file mode 100644 index 0000000..79018ca --- /dev/null +++ b/sprints/sprint-8/BRIEF.md @@ -0,0 +1,610 @@ +# Sprint 8 BRIEF — InQ ROI + +> **Estado:** borrador pendiente aprobación del director +> **Abierto:** 2026-07-07 +> **Sprint anterior:** Sprint 7 — multi-tenancy (organizations, 4 roles, Edge Functions, Admin UI) +> **Versión base:** v0.8.0 (según SemVer: Sprint 8 arranca en v0.8.0) + +--- + +## Contexto y dirección estratégica + +Sprint 7 entregó la fundación de multi-tenancy. El producto puede invitar clientes, asignarles roles, y que vean solo sus procesos. Ahora hay tres gaps que bloquean el uso real con clientes: + +1. **El modelo de "cliente" en la UI es inconsistente** — `process_groups` (agrupador visual), `clientName` (texto libre), y `org_id` (FK real) son tres conceptos que intentan modelar lo mismo sin coordinación. Un admin que invita a Solar Banco no puede ver sus procesos agrupados como "Solar Banco" en la biblioteca. + +2. **client_editor no puede subir su propio BPMN** — si Solar Banco tiene un proceso que quiere modelar, necesita pedirle a InQuality que lo importe. Esto no escala. + +3. **No hay versión visible ni changelog** — el producto está en producción sin que nadie sepa qué versión está corriendo ni qué cambió. + +**Objetivo del sprint:** consolidar el modelo de cliente, habilitar autonomía de client_editor para BPMN, y establecer el ciclo profesional de versiones. Sprint relativamente arquitectónico — ~40% DB/repos, ~40% UI/features, ~20% infra/deuda. + +--- + +## Prioridades del sprint + +### Bloque A — Comprometidas (must have) + +| # | Feature | Tipo | +|---|---|---| +| Etapa 0 | Versionado: Conventional Commits + CHANGELOG + footer versión | Infra | +| Etapa 1 | DB: tabla `areas`, `area_id` en `processes`, drop `process_groups` y `clientName` | DB/migración | +| Etapa 2 | Repositorios: `area-repo.ts`, actualizar `process-repo.ts`, tipos y stores | Backend | +| Etapa 3 | LibraryPage: agrupar por org → área + ocultar Settings para roles cliente | UI | +| Etapa 4 | Workspace sidebar: selector de área reemplaza `clientName` + Admin CRUD de áreas | UI + Admin | +| Etapa 5 | client_editor BPMN: política INSERT + UPDATE bpmn_xml, botón importar visible | DB/UI | + +### Bloque B — Si hay tiempo (prioridad 2) + +| # | Feature | Tipo | +|---|---|---| +| Etapa 6 | Deuda técnica: guards → `beforeLoad` + hard delete + email en admin | Refactor | +| Etapa 7 | Página `/help` con documentación de uso para todos los roles | UI | + +**Nota del director al Bloque B:** si las Etapas 0-5 consumen el sprint, Etapas 6-7 pasan a BACKLOG. No forzar a sprint siguiente sin capacidad real. + +--- + +## Reglas del sprint (no negociables) + +- `simulador-web/CLAUDE.md` tiene la guía completa de stack, reglas de marca, y la nueva sección de versionado (agregada como Etapa 0) +- **Validación visual mandatoria** en cada etapa que modifique UI antes del OK formal +- **Brief como contrato:** ninguna etapa arranca sin `ETAPA_X_PROMPT.md` aprobado +- **git diff antes de reportar:** incluir en cada reporte la lista de archivos modificados vs. los autorizados +- **Iniciativa agéntica clasificada** — Nivel 3 (marca, arquitectura, datos, TECH_DEBT.md) requiere consulta antes de ejecutar +- **`isProfileLoaded` como prerequisito** en cualquier componente nuevo condicionado por rol (aprendizaje Sprint 7) +- **JOIN vs fetch separado** — cuando un componente necesita datos relacionales para display, preferir JOIN en repo (aprendizaje Sprint 7) + +--- + +## Etapa 0: Versionado profesional + +**Objetivo:** establecer el ciclo de versiones antes de abrir cualquier feature nueva. + +### Alcance + +**1. Actualizar `simulador-web/CLAUDE.md`** — agregar sección "Gestión de versiones y commits" con: + +``` +Conventional Commits: + feat(scope): descripción + fix(scope): descripción + refactor(scope): descripción + test(scope): descripción + docs(scope): descripción + chore(scope): descripción + +scopes comunes: auth, workspace, library, report, pdf, admin, db, e2e, help + +SemVer: + MAJOR: cambio incompatible (nuevo modelo de datos, cambio de rol fundamental) + MINOR: feature nueva visible al usuario + PATCH: fix de bug o mejora de UX sin feature nueva + +Sprint 8 arranca en v0.8.0 +``` + +**2. Crear `CHANGELOG.md`** en raíz de `simulador-web/`, formato Keep a Changelog: + +```markdown +# Changelog + +All notable changes to InQ ROI will be documented in this file. + +## [Unreleased] + +## [0.7.0] - 2026-07-07 +### Added +- Multi-tenancy: tabla organizations, 4 roles de plataforma +- Edge Functions invite-user y revoke-user +- Panel de administración /admin (Organizations, Users) +- Login email/password + página /reset-password +- back button contextual en workspace (orgName via JOIN en getById) +- Política simulations_update (fix para client_editor) + +### Fixed +- Race condition AuthContext: isProfileLoaded guard +- syncProfileFromDB leyendo platform_role desde DB (no inferido por email) +- normalizeGroupLabel: limpia artículos iniciales de labels de grupos + +## [0.6.0] - 2026-06-24 +... +``` + +Completar el historial con los sprints anteriores hasta v0.1.0 (MVP). + +**3. Actualizar `package.json`** — `"version": "0.8.0"` (actualmente tiene algún valor anterior, verificar). + +**4. Footer con versión visible** — crear `src/components/AppFooter.tsx`: +- Muestra `InQ ROI v{version}` leyendo de `src/lib/version.ts` (que importa de `package.json`) +- Visible en todas las páginas (LibraryPage, ReportPage, al menos) +- Texto pequeño, discreto, esquina inferior + +**5. Tag de cierre de sprint:** al finalizar Sprint 8, `git tag -a v0.8.0 -m "Release v0.8.0: Sprint 8"`. + +### Criterios de validación +- [ ] `CHANGELOG.md` existe con historial desde v0.1.0 hasta v0.7.0 +- [ ] `package.json` tiene `"version": "0.8.0"` +- [ ] Footer visible en LibraryPage mostrando "InQ ROI v0.8.0" +- [ ] Build sin errores + +### NO entra +- Página de "qué hay de nuevo" o modal de release notes +- Versionado de procesos (FASE-2) + +--- + +## Etapa 1: Migración DB — tabla `areas` + +**Objetivo:** crear la infraestructura de datos para el modelo `organizations → areas → processes`. + +### Contexto + +Los tres conceptos de "cliente" actuales: + +| Concepto | Tabla/Campo | Problema | +|---|---|---| +| Agrupador visual | `process_groups` | Nombre confuso, sin FK a organizations | +| Campo libre | `clientName` (text en processes) | Sin normalización, sin validación | +| Tenant real | `org_id` en processes | Correcto pero desconectado de la UI | + +### Alcance — Migración 017 + +```sql +-- 1. Crear tabla areas +CREATE TABLE public.areas ( + id uuid DEFAULT gen_random_uuid() PRIMARY KEY, + org_id uuid REFERENCES public.organizations(id) ON DELETE CASCADE NOT NULL, + name text NOT NULL, + description text, + created_at timestamptz DEFAULT now() NOT NULL, + updated_at timestamptz DEFAULT now() NOT NULL +); + +-- 2. Agregar area_id en processes (reemplaza group_id) +ALTER TABLE public.processes ADD COLUMN area_id uuid REFERENCES public.areas(id) ON DELETE SET NULL; + +-- 3. Eliminar client_name (texto libre) de processes +-- Verificar que clientName existe con su nombre exacto en la tabla +ALTER TABLE public.processes DROP COLUMN IF EXISTS client_name; +-- Nota: verificar nombre exacto antes de ejecutar (puede ser "client_name" o "clientName" en postgres) + +-- 4. RLS para areas +ALTER TABLE public.areas ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "areas_select" ON public.areas + FOR SELECT USING ( + -- InQuality ve todas; otros ven solo las de su org + public.is_inquality() + OR org_id = public.current_org() + ); + +CREATE POLICY "areas_insert" ON public.areas + FOR INSERT WITH CHECK ( + -- Solo platform_admin puede crear áreas + public.current_platform_role() = 'platform_admin' + ); + +CREATE POLICY "areas_update" ON public.areas + FOR UPDATE USING ( + public.current_platform_role() = 'platform_admin' + ); + +CREATE POLICY "areas_delete" ON public.areas + FOR DELETE USING ( + public.current_platform_role() = 'platform_admin' + ); + +-- 5. Eliminar process_groups si existe +-- Primero verificar que processes.group_id existe y hacer SET NULL antes de drop +UPDATE public.processes SET area_id = NULL WHERE area_id IS NULL; -- no-op, sanity check +-- Si process_groups existe: +-- ALTER TABLE public.processes DROP COLUMN IF EXISTS group_id; +-- DROP TABLE IF EXISTS public.areas_old; -- nombre provisional +-- DROP TABLE IF EXISTS public.process_groups; + +-- 6. Migración de datos: bulk-assign procesos sin org a InQuality +-- UPDATE public.processes SET org_id = '' WHERE org_id IS NULL; +-- (ejecutar con el UUID real de InQuality — buscar en SELECT id FROM organizations WHERE name = 'InQuality') +``` + +**Precaución importante:** antes de escribir los DROP/ALTER, auditar el schema real con: +```sql +SELECT column_name, data_type FROM information_schema.columns +WHERE table_name = 'processes' AND table_schema = 'public'; + +SELECT table_name FROM information_schema.tables +WHERE table_schema = 'public' AND table_name LIKE '%group%'; +``` + +Reportar nombres exactos antes de ejecutar cualquier DROP. + +### Criterios de validación +- [ ] Tabla `areas` creada con RLS activa +- [ ] `area_id` en `processes` nullable con FK +- [ ] `client_name` (o equivalente) eliminado de `processes` +- [ ] `process_groups` eliminada (verificar si existe) +- [ ] `group_id` en `processes` eliminado (si existía) +- [ ] Bulk-assign: todos los procesos tienen `org_id NOT NULL` (o decision explícita sobre los NULL) +- [ ] Tests unitarios de RLS: platform_admin puede CRUD en areas, client_editor no puede + +### NO entra +- CRUD en la UI (eso es Etapa 4) +- Cambios en los repositorios de TypeScript (eso es Etapa 2) + +--- + +## Etapa 2: Repositorios y tipos + +**Objetivo:** crear `area-repo.ts` y actualizar `process-repo.ts` y los tipos de dominio para reflejar el nuevo modelo. + +### Alcance + +**1. Actualizar `src/domain/types.ts`:** +```typescript +// Nuevo tipo +export interface Area { + id: string + orgId: string + name: string + description: string | null + createdAt: string +} + +// Actualizar Process — eliminar clientName, agregar areaId y areaName +export interface Process { + // ... campos existentes ... + orgId: string | null + orgName?: string | null // poblado via JOIN, no se persiste + areaId: string | null // ← nuevo, reemplaza groupId + areaName?: string | null // ← nuevo, poblado via JOIN + // eliminar: clientName, groupId (si existían) +} +``` + +**2. Crear `src/persistence/supabase/area-repo.ts`:** +```typescript +export interface AreaRepo { + getByOrg(orgId: string): Promise + create(data: { orgId: string; name: string; description?: string }): Promise + update(id: string, data: { name?: string; description?: string }): Promise + delete(id: string): Promise +} +``` + +**3. Actualizar `src/persistence/supabase/process-repo.ts`:** +- `fromRow()`: extraer `areaId` y `areaName` (via JOIN cuando el query lo incluya) +- `getById()`: JOIN con `areas` para traer `areaName` embebido (patrón ya establecido con `orgName`) +- `getAll()` / `getByOrg()`: incluir `area_id` en select, agregar optional JOIN para `areaName` +- `create()`: incluir `area_id` en el objeto de insert (si está presente) +- `updateEditable()`: incluir `area_id` en los campos actualizables +- Eliminar cualquier referencia a `clientName` o `groupId` + +**4. Actualizar Zustand store** (`src/store/process-store.ts` o equivalente): +- Si el store tenía lógica de `clientName`, actualizarla +- Verificar que el tipo `Process` del store usa los nuevos campos + +**5. Actualizar `src/persistence/supabase/settings-repo.ts`** (si mantenía `process_groups`): +- Identificar si hay lógica ligada a `process_groups` y eliminarla o migrarla + +### Criterios de validación +- [ ] `Area` type definido en `domain/types.ts` +- [ ] `Process.areaId` y `Process.areaName` presentes; `clientName`/`groupId` eliminados +- [ ] `area-repo.ts` con CRUD completo +- [ ] `process-repo.ts` actualizado: fromRow, getById (con JOIN), create, updateEditable +- [ ] Build TypeScript sin errores +- [ ] Tests unitarios de `area-repo.ts` + +### NO entra +- Cambios en componentes React (Etapas 3 y 4) +- Settings page todavía puede romper — se verá en Etapa 3 + +--- + +## Etapa 3: LibraryPage — agrupación org → área + +**Objetivo:** reemplazar la agrupación actual por `clientName` con agrupación real por `org → área`. + +### Contexto de la UI actual + +LibraryPage muestra procesos agrupados por `clientName` (texto libre). Hay un panel de "Clientes" (renderizado desde `process_groups`). Para roles cliente, muestra lista plana. El ícono de Settings (⚙) lleva a la página de configuración de grupos — visible para todos los roles. + +### Alcance + +**1. Lógica de agrupación:** +- Para `platform_admin` (InQuality): agrupar por `orgName` → dentro de cada org, sub-agrupar por `areaName` +- Para `client_editor` / `client_viewer`: mostrar lista plana de sus procesos (sin agrupación — la org es implícita) +- Grupos sin área asignada: mostrar en sección "Sin área" dentro de la org correspondiente + +**2. Estructura visual (admin view):** +``` +┌─ Solar Banco ───────────────────────────────────────────┐ +│ ┌─ Área: Operaciones ──────────────────────────────┐ │ +│ │ [proceso A] [proceso B] │ │ +│ └──────────────────────────────────────────────────┘ │ +│ ┌─ Área: Recursos Humanos ─────────────────────────┐ │ +│ │ [proceso C] │ │ +│ └──────────────────────────────────────────────────┘ │ +│ ┌─ Sin área ───────────────────────────────────────┐ │ +│ │ [proceso D] │ │ +│ └──────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +┌─ InQuality ─────────────────────────────────────────────┐ +│ ... │ +└─────────────────────────────────────────────────────────┘ +``` + +**3. Eliminar el panel de "Clientes" (process_groups):** +- El componente o sección que renderizaba grupos desde `process_groups` se elimina +- Verificar qué componentes lo referencian + +**4. Ocultar Settings para roles cliente:** +- El ícono ⚙ en LibraryPage se oculta para `client_editor` y `client_viewer` +- Agregar guard en la ruta `/settings` (o el path real): `client_editor` y `client_viewer` redirigen a `/library` +- Verificar si `/recursos` también debe ocultarse para clientes (reportar, no decidir unilateralmente) + +**5. Criterio de wow:** al colapsar/expandir grupos, usar animación suave (Tailwind `transition-all`). Los grupos de org colapsan con click en el header. + +### Criterios de validación +- [ ] Admin ve procesos agrupados por org → área (validación visual del director) +- [ ] client_editor ve lista plana de sus procesos (sin panel de grupos) +- [ ] ⚙ Settings no visible para client_editor y client_viewer +- [ ] Ruta `/settings` tiene guard de rol +- [ ] Animación de colapso funciona +- [ ] Build sin errores, tests existentes pasan + +### NO entra +- CRUD de áreas (Etapa 4) +- Workspace sidebar (Etapa 4) + +--- + +## Etapa 4: Workspace sidebar + Admin CRUD de áreas + +**Objetivo:** (a) reemplazar el campo `clientName` libre en el workspace por un selector de área normalizado; (b) habilitar a admin para crear/editar/eliminar áreas desde `/admin`. + +### 4A — Workspace sidebar + +**Campo "Área" en el panel de configuración del proceso:** +- Reemplaza el campo de texto libre "Cliente" (`clientName`) +- Para `platform_admin`: dropdown de todas las áreas de la org del proceso (si tiene org asignada) + opción "Sin área". Si el proceso no tiene org, mostrar selector de org primero. +- Para `client_editor`: dropdown de áreas de su propia org (su org es implícita — no puede cambiarla) +- Para `client_viewer`: read-only, muestra nombre del área si existe +- Al cambiar área, actualizar `processStore` y hacer persist en Supabase vía `updateEditable()` + +**Campo "Organización" en el workspace:** +- Para `platform_admin`: selector de org (dropdown de todas las orgs) — permite reasignar un proceso a otra org +- Para `client_editor`: read-only, muestra el nombre de su org +- Actualizar `org_id` en Supabase cuando el admin cambia la org + +**Eliminar campo "Cliente" (clientName):** +- El campo de texto libre que existía en el sidebar se elimina completamente + +### 4B — Admin CRUD de áreas + +En `/admin/organizations/[orgId]` (o en la página de detalle de org que exista): +- Agregar sección "Áreas" con lista de áreas de la org +- CRUD: crear área (nombre + descripción opcional), renombrar, eliminar +- Al eliminar: si hay procesos con esa área, mostrar confirmación "X procesos quedarán sin área asignada" +- RLS garantiza que solo platform_admin puede hacer CRUD + +**Estructura sugerida:** +``` +/admin + /organizations + → lista de orgs (ya existe) + /[orgId] + → detalle de org: nombre, miembros, ÁREAS (nuevo) +``` + +Si `/admin/organizations/[orgId]` no existe, crear la página. Si ya existe (verificar), agregar la sección de Áreas. + +### Criterios de validación +- [ ] Campo "Cliente" (texto libre) eliminado del workspace sidebar +- [ ] Selector de área funciona para platform_admin y client_editor (validación visual) +- [ ] Cambio de área persiste en Supabase (verificar Network tab) +- [ ] CRUD de áreas en admin funciona (crear, editar, eliminar con confirmación) +- [ ] Tests E2E: admin crea área, asigna proceso, client_editor ve sus procesos con área correcta + +### NO entra +- Jerarquía de áreas (sub-áreas) — FASE-2 +- Migrar todos los procesos existentes a áreas manualmente — eso se hace en bulk en la migración DB + +--- + +## Etapa 5: client_editor — permisos de BPMN + +**Objetivo:** habilitar a `client_editor` para importar nuevos procesos y actualizar el BPMN de procesos existentes de su org. + +### Contexto + +En Sprint 7 Etapa 5, `client_editor` fue bloqueado explícitamente de: +- Ver el botón "Importar BPMN" (redireccionado desde `/import`) +- Actualizar `bpmn_xml` vía `updateEditable()` (el campo no estaba en la lista de campos editables para ese rol) + +Esta decisión fue provisional. Sprint 8 la revierte con las restricciones correctas. + +### Alcance + +**1. Migración 018 — política INSERT en `processes` para client_editor:** + +```sql +-- Ampliar la política de INSERT para incluir client_editor +-- (auditar el nombre real de la política INSERT actual antes de DROP) +DROP POLICY IF EXISTS "processes_insert" ON public.processes; + +CREATE POLICY "processes_insert" ON public.processes + FOR INSERT WITH CHECK ( + -- platform_admin y member pueden insertar sin restricción de org + public.current_platform_role() IN ('platform_admin', 'member') + OR ( + -- client_editor puede crear procesos solo en su propia org + public.current_platform_role() = 'client_editor' + AND org_id = public.current_org() + ) + ); +``` + +**2. Actualizar `updateEditable()` en `process-repo.ts`:** +- Incluir `bpmn_xml` en los campos actualizables cuando el caller tiene permisos +- Verificar que la política UPDATE existente ya permite que `client_editor` actualice `bpmn_xml` (debería estar cubierta por la política general de UPDATE que verifica org_id) +- Si no, agregar rama en la política UPDATE + +**3. Botón "Importar BPMN" visible para client_editor:** +- En `ImportPage.tsx`: eliminar el guard que redirigía a `client_editor` +- `client_editor` accede a `/import`, selecciona BPMN, el proceso se crea con `org_id = current_org()` +- El `owner_id` se asigna como `auth.uid()` (el client_editor que importó) + +**4. Restricciones que NO cambian:** +- `client_editor` no puede mover procesos entre orgs (no puede cambiar `org_id` a otra org) +- `client_editor` no puede cambiar `owner_id` +- `client_editor` no puede ver ni modificar procesos de otra org + +### Criterios de validación +- [ ] Validar con el director usando cambio temporal de rol en Supabase Studio (técnica de Sprint 7) +- [ ] `client_editor` puede importar un BPMN nuevo → proceso creado con `org_id` correcto (verificar en Supabase Studio) +- [ ] `client_editor` puede abrir un proceso de su org y actualizar el BPMN (re-import) +- [ ] `client_editor` NO puede crear procesos con `org_id` de otra org (test E2E adversarial) +- [ ] Tests E2E: flujo completo import → workspace → update bpmn como client_editor + +### NO entra +- client_editor con permisos en `resources` o en `admin` — no cambia +- Eliminar procesos (sigue siendo solo platform_admin) + +--- + +## Etapa 6: Deuda técnica consolidada + +**Objetivo:** resolver items de TECH_DEBT.md acumulados de Sprint 7. Bloque B — solo si hay tiempo. + +### 6A — Guards de rol → `beforeLoad` (3 componentes) + +Migrar de `useEffect` a `beforeLoad` de TanStack Router en: +- `AdminLayout.tsx` +- `WorkspacePage.tsx` +- `ImportPage.tsx` + +Ejemplo de patrón correcto: +```typescript +// En el router (router.tsx o route file) +export const adminRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/admin', + beforeLoad: ({ context }) => { + if (!context.auth.isProfileLoaded) return // esperar + if (context.auth.user?.platformRole !== 'platform_admin') { + throw redirect({ to: '/library' }) + } + }, +}) +``` + +**Restricción:** no cambiar el comportamiento de los guards, solo el mecanismo. Los tests E2E existentes deben seguir pasando. + +### 6B — Hard delete de usuarios + +Crear Edge Function `delete-user`: +- Llama a Supabase Auth Admin API: `supabase.auth.admin.deleteUser(userId)` +- Elimina registro de `public.users` +- Solo accesible para `platform_admin` +- Agregar botón "Eliminar permanentemente" en Admin → Users (con AlertDialog de confirmación doble: "Esta acción no puede deshacerse. El usuario perderá acceso permanentemente.") +- Diferencia respecto al soft delete (revoke-user): hard delete borra el auth record + +### 6C — Email visible en Admin → Users + +La tabla de usuarios en admin no muestra el email (que está en `auth.users`, no en `public.users`). Fix: +- En la Edge Function de listado de usuarios (o crear una nueva `list-users`), hacer join con `auth.users` via service_role +- Mostrar email en la tabla de Admin → Users + +### Criterios de validación +- [ ] Guards migrados: no hay flash visual al navegar a ruta protegida (verificar con conexión lenta) +- [ ] Hard delete funciona y el usuario desaparece de la auth +- [ ] Email visible en Admin → Users + +--- + +## Etapa 7: Página `/help` + +**Objetivo:** documentación de uso del producto para todos los roles. Bloque B — solo si hay tiempo. + +### Alcance + +**Nueva ruta `/help`** accesible para todos los roles autenticados. + +**Contenido mínimo (4 secciones):** + +1. **Cómo parametrizar actividades** + - Tiempos de ejecución (tiempo nominal y AS-IS/TO-BE) + - Probabilidad de ejecución + - Recursos asignados y minutos de utilización + - Costo fijo por actividad + +2. **Cómo simular y leer el reporte** + - Qué hace el botón "Simular" + - Indicador de simulación desactualizada + - Qué significa cada KPI del reporte + - Cómo leer el heatmap + - Columnas de la tabla comparativa (ahorro, eficiencia) + +3. **Cómo exportar** + - PDF: qué incluye cada documento (Actual, Automatizado, ROI) + - CSV: estructura del archivo exportado + - Cuándo usar PDF vs. CSV + +4. **Configuración del proceso (panel Global)** + - Campos del panel: nombre, descripción, volumen mensual, horas laborables + - Escenario automatizado: costo fijo TO-BE, inversión inicial, ROI + +**UX:** +- Layout con sidebar de navegación a las 4 secciones +- Link a `/help` en el footer (junto a la versión) +- Criterio de wow: buscador simple de texto en la página + +### Criterios de validación +- [ ] Las 4 secciones tienen contenido completo y preciso (validación visual del director) +- [ ] Link desde footer funciona +- [ ] Accesible para todos los roles + +--- + +## Definiciones estables durante Sprint 8 (no reabrir) + +- Stack tecnológico (sin cambios) +- Paleta cromática y sistema de componentes (sin cambios) +- Modelo de escenarios Modelo A (sin cambios) +- ROI: cálculos nominales simples (sin cambios) +- 4 roles de plataforma: `platform_admin`, `member`, `client_editor`, `client_viewer` (sin cambios) + +--- + +## Convención de commits para Sprint 8 + +``` +feat(areas): tabla areas + RLS en migración 017 +feat(area-repo): CRUD de áreas por organización +feat(library): agrupación por org → área reemplaza clientName +feat(workspace): selector de área reemplaza campo clientName libre +feat(admin): CRUD de áreas en panel de organización +feat(client-editor): permisos BPMN insert + update vía migración 018 +feat(help): página /help con 4 secciones de documentación de uso +fix(import): botón importar visible para client_editor +refactor(router): guards AdminLayout + WorkspacePage + ImportPage → beforeLoad +chore(version): v0.8.0, CHANGELOG.md, footer con versión +docs(changelog): etapa N sprint 8 +``` + +--- + +## Archivos de referencia + +| Archivo | Por qué leerlo antes | +|---|---| +| `simulador-web/CLAUDE.md` | Reglas de implementación, stack, marca, versionado | +| `simulador-web/STRATEGIC_DIRECTION.md` | Decisiones estratégicas para Sprint 8 (sección final) | +| `simulador-web/BACKLOG.md` | Item `[SPRINT-8]` con scope de org→areas | +| `simulador-web/TECH_DEBT.md` | Items guards + auth para Etapa 6 | +| `supabase/migrations/` | Auditar schema actual antes de escribir migraciones 017-018 | +| `src/persistence/supabase/process-repo.ts` | Entender fromRow y getById para extender con areaId | +| `src/features/library/LibraryPage.tsx` | Entender agrupación actual para reemplazarla | +| `src/features/workspace/WorkspacePage.tsx` | Entender sidebar actual para reemplazar clientName | +| `src/features/admin/` | Entender estructura de Admin UI para agregar CRUD de áreas |