# Especificación del módulo DOFA v2 — OKRFEDEF

Versión 1.0 · 3 de septiembre de 2026 · Contrato para todos los agentes que construyen el módulo.
Uso previsto: Retiro 1 de FEDEF, sábado 5 de septiembre de 2026, 8:00 a 13:00 (bloque 3 del día 1).

Este documento manda sobre el código existente. Donde el módulo actual (`DofaController`, `DofaService`, tablas `dofa_factors` y `dofa_sessions`) contradiga esta especificación, se reemplaza. Las tablas viejas se dejan en la base de datos sin usar; no se borran.

---

## 1. Objetivo

Replicar en la plataforma la metodología del archivo `MATRIZ DOFA Consolidado.xls` (MEFI, MEFE, MIME, FAC INT, FAC EXT y FODA cruzada) con cálculos idénticos, agregando:

- Una fase de **aporte** en la que cada participante escribe factores desde su celular.
- Una **consolidación con IA** (Claude por API) que simplifica y depura los aportes según una skill escrita por la consultoría, con revisión y aprobación del consultor.
- Una categoría nueva, **Tendencias (T)**, que no entra en los cálculos: se registra, se vota como "top 3" y se muestra como contexto.
- **Calificación individual** por participante (cada voto queda ligado a la persona) y **cruces** individuales.
- **Resultados** en vivo para el video beam, exportables a PDF y a un Excel con la misma estructura del archivo original.

Todo el texto de la interfaz en español. Diseño mobile-first para las pantallas de participante.

---

## 2. Metodología y fórmulas (fuente: `analisis/fuentes/dofa.md`)

Notación: participantes p = 1..P. Factores internos i (F y D), externos j (O y A). Categorías: F fortaleza, D debilidad, O oportunidad, A amenaza, T tendencia.

### 2.1 Entradas por participante y factor (fase de calificación)

| Campo | Escala | Aplica a | Regla |
|---|---|---|---|
| `importancia` | entero 1 a 10 | F, D, O, A | Peso relativo del factor dentro de su matriz |
| `calificacion` | entero 1 a 4 | F, D, O, A | F y O solo pueden ser 3 (menor) o 4 (mayor). D y A solo 1 (mayor) o 2 (menor). La interfaz solo ofrece los dos valores válidos. |
| `prioridad` | 0, 1, 3, 6, 9 | F, D, O, A | Ninguna / Baja / Regular / Media / Alta |
| `top_tendencias` | hasta 3 factores T | T | Selección, sin escala |

### 2.2 Entradas por participante y pareja (fase de cruces)

`cruce[p][i][j]` ∈ {0, 1, 3, 6, 9} para cada interno i y externo j. Etiquetas: Ninguna 0, Baja 1, Regular 3, Media 6, Alta 9. Pregunta implícita: "¿qué tan importante es la relación entre este factor interno y este factor externo?".

### 2.3 Normalización de pesos por participante

Para cada participante y cada matriz (interna: F+D; externa: O+A):

`peso[p][k] = importancia[p][k] / Σ_k importancia[p][k]` (suma 1 dentro de la matriz).

Esto reproduce el Excel, donde cada votante tenía pesos que sumaban 1. Un participante que no calificó un factor no cuenta en ese factor (su fila se ignora en el promedio de ese factor); el participante debe calificar todos los factores de una matriz antes de poder enviar.

### 2.4 Agregados de grupo (una sola ponderación)

Para cada factor k:

- `peso_k = promedio_p(peso[p][k])` (suma 1 por matriz por construcción cuando todos calificaron todo; si hay faltantes, renormalizar al final para que sume 1 y dejarlo anotado).
- `calificacion_k = promedio_p(calificacion[p][k])` (múltiplos de 1/P, decimal).
- `prioridad_k = promedio_p(prioridad[p][k])` (decimal).
- `valor_k = peso_k × calificacion_k` (MEFI si k es interno, MEFE si es externo).
- `ponderacion_k = importancia_fac_k × prioridad_k`, donde `importancia_fac_k = peso_k` por defecto (decisión: una sola ponderación para MEFI/MEFE y FAC). El motor debe aceptar opcionalmente un `importancia_fac_k` distinto por factor, solo para reproducir el Excel de 2024 en la prueba de aceptación.

Totales:

- `MEFI = Σ_{k interno} valor_k`, con subtotales de F y de D.
- `MEFE = Σ_{k externo} valor_k`, con subtotales de O y de A.
- `Σ ponderacion` internas y externas (informativo).

### 2.5 MIME

Punto `(X = MEFE, Y = MEFI)` en una malla 3 × 3, escala 1 a 4, cortes en 2,0 y 3,0. Convención de ejes igual al Excel (X = MEFE) y documentada en pantalla.

Zonas (regla explícita):

- **ATAQUE** (crecer y construir): (X ≥ 3 y Y ≥ 3) o (X ≥ 3 y 2 ≤ Y < 3) o (2 ≤ X < 3 y Y ≥ 3). Celdas I, II, IV.
- **DESPOSEER** (cosechar o desinvertir): (X < 2 y Y < 2) o (X < 2 y 2 ≤ Y < 3) o (2 ≤ X < 3 y Y < 2). Celdas VI, VIII, IX.
- **RESISTA** (retener y mantener): los tres casos restantes de la diagonal. Celdas III, V, VII.

Numeración de celdas: fila superior (Y alto) I, II, III de izquierda (X alto) a derecha (X bajo); fila media IV, V, VI; fila inferior VII, VIII, IX. El resultado guarda X, Y, celda romana, zona y una nota de sensibilidad si el punto está a menos de 0,15 de un corte.

### 2.6 FODA cruzada

- `calif_ij = promedio_p(cruce[p][i][j])`.
- `cruce_ij = ponderacion_i × ponderacion_j × calif_ij`.
- Totales por fila (interno i), por columna (externo j), por cuadrante (FO, FA, DO, DA: suma, número de cruces, porcentaje del gran total, calificación promedio del cuadrante) y gran total.
- Rankings: top 30 cruces por `cruce_ij` y top 30 por `calif_ij` (sin ponderar), factores dominantes por fila y por columna.
- Dispersión: para cada factor y cada cruce, desviación estándar entre participantes; lista de los 10 puntos de mayor desacuerdo.
- Si la sesión limita los cruces (`cruces_limitados_a = N`), solo se califican las parejas entre los N internos y los N externos de mayor `ponderacion`; el resto se marca "no calificado" y no entra en los totales. Los totales y porcentajes se calculan sobre las parejas calificadas y se indica cuántas parejas quedaron fuera.

### 2.7 Tendencias

Lista de factores T consolidados. Cada participante marca hasta 3. Resultado: conteo de votos por tendencia, ordenado, con porcentaje de participantes que la marcó. No entra en MEFE, MIME ni FODA.

### 2.8 Redondeo y precisión

Cálculos en `float` con precisión completa; se redondea solo al presentar (4 decimales en tablas de cálculo, 2 en resúmenes). Los totales que se comparan con el Excel deben coincidir con error menor a 0,0005.

---

## 3. Prueba de aceptación obligatoria

`tests/Feature/Dofa/CalculadoraExcel2024Test.php` con fixture `tests/Fixtures/dofa/excel_2024.json`.

- El fixture se genera con `docker/scripts/extraer_dofa_2024.py` leyendo `../MATRIZ DOFA Consolidado.xls` con xlrd (hojas MEFI, MEFE, FAC INT, FAC EXT, FODA), no a mano. Contiene: factores con código, texto y categoría; peso y calificación de MEFI/MEFE; importancia y prioridad de FAC INT/FAC EXT; matriz de calificaciones promedio 13 × 21; y los valores esperados: MEFI 2,6267, MEFE 2,5000, valores por factor, ponderaciones, matriz de cruces, totales por fila y columna, cuadrantes (FO 98,88; FA 86,31; DO 83,48; DA 72,28) y gran total 340,96.
- La prueba alimenta el motor con un solo "participante" cuyos valores son los promedios del Excel (peso ya normalizado, `importancia_fac` distinta del peso, prioridad y calificaciones de cruce) y verifica todos los valores esperados, más la MIME = celda V, zona RESISTA.
- Una segunda prueba usa 3 participantes con importancias 1 a 10 y verifica normalización, promedios, subtotales, límites de cruces y tendencias.
- Se ejecuta con `docker compose -f docker/compose.yaml exec -T app php artisan test --filter Dofa`.

---

## 4. Modelo de datos (nuevas tablas, prefijo `dofa2_`)

Todas con `id`, `timestamps`. Claves foráneas con `cascadeOnDelete` salvo usuarios (`nullOnDelete`).

| Tabla | Columnas principales |
|---|---|
| `dofa2_sesiones` | `project_id`, `nombre`, `fase` enum('configuracion','aporte','consolidacion','calificacion','cruces','cerrada'), `config` json, `abierta_en`, `cerrada_en`, `created_by` |
| `dofa2_participantes` | `sesion_id`, `user_id`, `rol_en_sesion` enum('participante','facilitador','observador'), `aporte_enviado_en`, `calificacion_enviada_en`, `cruces_enviados_en`, `tendencias_enviadas_en`, `ultimo_visto_en`. Única (`sesion_id`,`user_id`) |
| `dofa2_aportes` | `sesion_id`, `user_id`, `categoria` enum('F','D','O','A','T'), `texto` (≤ 300), `estado` enum('activo','fusionado','descartado'), `factor_id` nullable (a qué factor consolidado fue) |
| `dofa2_factores` | `sesion_id`, `categoria`, `codigo` (F1, D1, O1, A1, T1…), `texto` (≤ 200), `detalle` nullable text, `orden`, `estado` enum('borrador','aprobado'), `fuente` enum('ia','consultor','banco_2024','entrevistas','aporte'), `origen` json (ids de aportes y notas), `created_by` |
| `dofa2_calificaciones` | `sesion_id`, `factor_id`, `user_id`, `importancia` tinyint, `calificacion` tinyint, `prioridad` tinyint, `peso_normalizado` decimal(8,6) nullable. Única (`factor_id`,`user_id`) |
| `dofa2_cruces` | `sesion_id`, `factor_interno_id`, `factor_externo_id`, `user_id`, `calificacion` tinyint. Única (`factor_interno_id`,`factor_externo_id`,`user_id`) |
| `dofa2_tendencias_votos` | `sesion_id`, `user_id`, `factor_id`. Única (`user_id`,`factor_id`) |
| `dofa2_consolidaciones` | `sesion_id`, `skill_version`, `modelo`, `prompt` longtext, `respuesta_cruda` longtext, `propuesta` json, `estado` enum('propuesta','aplicada','descartada'), `tokens_entrada`, `tokens_salida`, `created_by` |
| `dofa2_resultados` | `sesion_id`, `version`, `calculado_en`, `datos` json (estructura de la sección 8), `analisis_ia` longtext nullable, `participantes_incluidos` json |
| `insumos_diagnostico` | `project_id`, `tipo` enum('entrevistas','banco_2024','cifras','tendencias_sugeridas'), `titulo`, `contenido` json, `orden`, `visible_participantes` bool |

`config` json de la sesión: `{ "factores_por_categoria": 7, "modo_cruce": "individual"|"plenaria", "cruces_limitados_a": null|int, "mostrar_banco_2024": true, "mostrar_insumos": true, "min_aportes_por_categoria": 1, "max_aportes_por_categoria": 5, "permitir_editar_tras_enviar": false }`.

Usuarios: agregar a `users` las columnas `codigo_acceso` (string 6, único, nullable), `token_acceso` (string 64, único, nullable), `acceso_expira_en` (timestamp nullable), `cargo` (string nullable), `es_participante_retiro` (bool default false).

Roles y permisos (Spatie): nuevo rol `participante`. Permisos nuevos: `participar_dofa`, `facilitar_dofa`. `participar_dofa` se asigna a participante, junta, sponsor, champion y owner. `facilitar_dofa` y `participar_dofa` a consultor. Migración con seeder idempotente.

---

## 5. Fases y máquina de estados

```
configuracion → aporte → consolidacion → calificacion → cruces → cerrada
                  ↑            |             ↑            |
                  └── volver ──┘  (el consultor puede retroceder una fase; retroceder de calificacion a consolidacion conserva los votos pero los marca como "previos" y exige reenviar)
```

- Solo un usuario con `facilitar_dofa` cambia de fase. Cada cambio se registra (`fase`, `abierta_en`, en `config.historial_fases[]`).
- **aporte**: los participantes crean/editan/borran sus aportes y pulsan "Enviar mis aportes" (marca `aporte_enviado_en`; con `permitir_editar_tras_enviar=false` quedan bloqueados).
- **consolidacion**: los participantes ven "El facilitador está consolidando"; el consultor lanza la IA, edita y aprueba la lista. Al avanzar a calificación todos los factores deben estar `aprobado` y con código.
- **calificacion**: cada participante califica todos los F/D/O/A (autoguardado por factor) y marca top 3 tendencias; "Enviar" valida completitud.
- **cruces**: si `modo_cruce = individual`, cada participante califica sus parejas; si `plenaria`, solo el facilitador captura desde el monitor (se guarda a nombre del facilitador con `user_id` del consultor y se marca en resultados "cruces en plenaria").
- **cerrada**: se calcula y guarda `dofa2_resultados` (versión incremental cada recálculo); los participantes ven el resumen.
- El consultor puede "Calcular" en cualquier momento desde calificación en adelante (resultados parciales con los que hayan enviado), marcando `participantes_incluidos`.

---

## 6. Acceso de participantes (sin contraseña)

- Pantalla `Setup/Participantes`: carga masiva pegando líneas `Nombre; correo (opcional); cargo; rol` (rol por defecto `participante`). Crea usuarios con `email_verified_at`, contraseña aleatoria, rol, `codigo_acceso` (6 caracteres A-Z y 2-9 sin O/0/I/1, único), `token_acceso` (64 hex), `acceso_expira_en` = ahora + 72 h (configurable), `es_participante_retiro = true`. Si no hay correo, se genera `<codigo>@participantes.fedef.local`. Tabla con estado, regenerar token, eliminar, y botón "Imprimir tarjetas" → PDF (dompdf) con una tarjeta por participante: nombre, cargo, código, QR del enlace mágico y URL corta. QR generado con `bacon/bacon-qr-code` (ya está en vendor) como SVG embebido.
- Rutas en `routes/acceso.php` (registrado en `bootstrap/app.php` con `then:`), sin middleware `auth`:
  - `GET /acceso` → página con campo de código (6 caracteres, mayúsculas automáticas) y el QR general de la sesión.
  - `POST /acceso` → valida código y expiración, `Auth::login($user, remember: true)`, regenera sesión, redirige a `/participar`.
  - `GET /acceso/{token}` → igual con el token (enlace del QR). Throttle 20/min por IP.
- `GET /participar` (auth): busca el proyecto activo y la sesión DOFA más reciente no cerrada (o la última cerrada) y renderiza `Dofa/Participar`. Si no hay sesión, pantalla de espera con sondeo cada 5 s a `/participar/estado`.
- Los usuarios con `facilitar_dofa` que entren a `/participar` ven un aviso y enlace al panel.
- El consultor y la gerente siguen con login normal de Jetstream.

---

## 7. Rutas y contratos (routes/dofa.php, prefijo `projects/{project}/dofa`, middleware `auth`)

Los recursos que devuelven páginas usan Inertia; los que devuelven JSON responden `{ ok: true, ... }` o `{ ok: false, error: '...' }` con código HTTP adecuado. Autorización: `facilitar_dofa` para rutas de facilitador; `participar_dofa` y ser participante de la sesión para rutas de participante.

### Facilitador

| Método y ruta | Nombre | Devuelve |
|---|---|---|
| GET `/` | `dofa.panel` | Página `Dofa/Panel` con `sesiones[]`, `sesion_activa`, `participantes[]` (id, nombre, cargo, rol, estados), `config`, `resumen` (conteos por fase) |
| POST `/sesiones` | `dofa.sesiones.store` | Crea sesión (nombre, config) → JSON `{sesion}` |
| PATCH `/sesiones/{sesion}` | `dofa.sesiones.update` | Actualiza nombre/config |
| POST `/sesiones/{sesion}/fase` | `dofa.sesiones.fase` | body `{fase}`; valida transición; JSON `{sesion}` |
| POST `/sesiones/{sesion}/participantes` | `dofa.sesiones.participantes` | body `{user_ids: []}` sincroniza participantes |
| GET `/sesiones/{sesion}/estado` | `dofa.sesiones.estado` | JSON de sondeo: `{fase, participantes: [{id, nombre, iniciales, aporte: n, aporte_enviado, calificados: n/total, calificacion_enviada, cruces: n/total, cruces_enviados, tendencias_enviadas, visto_hace_seg}], totales, factores_aprobados, actualizado_en}` |
| GET `/sesiones/{sesion}/consolidar` | `dofa.consolidar` | Página `Dofa/Consolidar` con `aportes` (agrupados por categoría, con autor visible solo al facilitador), `factores` (borrador/aprobados), `consolidaciones[]`, `insumos`, `banco_2024` |
| POST `/sesiones/{sesion}/consolidar/ia` | `dofa.consolidar.ia` | Ejecuta la consolidación (síncrona, hasta 5 min) → JSON `{consolidacion, propuesta}` |
| POST `/sesiones/{sesion}/factores` | `dofa.factores.guardar` | body `{factores: [{id?, categoria, texto, detalle?, orden, origen?}], eliminar: [ids]}` guarda en lote (borrador) |
| POST `/sesiones/{sesion}/factores/aplicar-propuesta` | `dofa.factores.aplicar` | body `{consolidacion_id}` convierte la propuesta en factores borrador y enlaza aportes |
| POST `/sesiones/{sesion}/factores/publicar` | `dofa.factores.publicar` | Asigna códigos (F1…, D1…, O1…, A1…, T1…) por orden, marca aprobado |
| POST `/sesiones/{sesion}/calcular` | `dofa.calcular` | Calcula y guarda resultados → JSON `{resultados}` |
| GET `/sesiones/{sesion}/resultados` | `dofa.resultados` | Página `Dofa/Resultados` con `resultados` (sección 8), `factores`, `sesion` |
| GET `/sesiones/{sesion}/resultados.json` | `dofa.resultados.json` | Mismo JSON, para el monitor |
| POST `/sesiones/{sesion}/analisis-ia` | `dofa.analisis.ia` | Genera lectura de IA y la guarda en `analisis_ia` |
| GET `/sesiones/{sesion}/export/pdf` | `dofa.export.pdf` | PDF |
| GET `/sesiones/{sesion}/export/xlsx` | `dofa.export.xlsx` | Excel |
| GET `/sesiones/{sesion}/monitor` | `dofa.monitor` | Página `Dofa/Monitor` (beam) |
| POST `/sesiones/{sesion}/cruces-plenaria` | `dofa.cruces.plenaria` | body `{cruces: [{interno_id, externo_id, calificacion}]}` (modo plenaria) |
| GET `/insumos` | `dofa.insumos` | Página `Dofa/Insumos` |

### Participante (sesión activa resuelta en `/participar`)

| Método y ruta | Nombre | Devuelve |
|---|---|---|
| GET `/participar` | `participar` | Página `Dofa/Participar` con `sesion`, `fase`, `config`, `factores` (aprobados; en fase aporte va vacío), `mis_aportes`, `mis_calificaciones`, `mis_cruces`, `mis_tendencias`, `insumos` (si visible), `banco_2024` (si visible), `cruces` (ids que se cruzan, solo en fase cruces), `progreso` |
| GET `/participar/estado` | `participar.estado` | JSON `{fase, factores_version, actualizado_en}` para sondeo cada 5 s |
| POST `/dofa/sesiones/{sesion}/aportes` | `dofa.aportes.store` | body `{categoria, texto}` → `{aporte}` |
| PATCH `/dofa/sesiones/{sesion}/aportes/{aporte}` / DELETE | `dofa.aportes.update` / `.destroy` | Solo el autor y en fase aporte |
| POST `/dofa/sesiones/{sesion}/enviar/{fase}` | `dofa.enviar` | Marca la fase como enviada tras validar completitud → `{ok, faltantes: []}` |
| POST `/dofa/sesiones/{sesion}/calificaciones` | `dofa.calificaciones.store` | body `{calificaciones: [{factor_id, importancia?, calificacion?, prioridad?}]}` upsert parcial (autoguardado) |
| POST `/dofa/sesiones/{sesion}/cruces` | `dofa.cruces.store` | body `{cruces: [{interno_id, externo_id, calificacion}]}` upsert |
| POST `/dofa/sesiones/{sesion}/tendencias` | `dofa.tendencias.store` | body `{factor_ids: [≤3]}` reemplaza |

Todas las rutas de participante rechazan escrituras si la fase no corresponde (`409`) o si ya envió y no se permite editar.

---

## 8. Estructura del JSON de resultados (`dofa2_resultados.datos`)

```json
{
  "sesion": {"id": 1, "nombre": "...", "calculado_en": "...", "participantes_incluidos": 9, "modo_cruce": "individual"},
  "factores": [{"id": 1, "codigo": "F1", "categoria": "F", "texto": "...", "peso": 0.0867, "calificacion": 4.0, "valor": 0.3467, "importancia_fac": 0.0867, "prioridad": 9.0, "ponderacion": 0.78, "desv_peso": 0.01, "desv_calificacion": 0.0, "desv_prioridad": 0.5, "n_votos": 9}],
  "mefi": {"total": 2.6267, "subtotal_F": 1.8589, "subtotal_D": 0.7678},
  "mefe": {"total": 2.5, "subtotal_O": 1.8533, "subtotal_A": 0.6467},
  "mime": {"x": 2.5, "y": 2.6267, "celda": "V", "zona": "RESISTA", "sensibilidad": "MEFE sobre el corte 2,0-3,0 ..."},
  "fac": {"internos": [...], "externos": [...], "suma_pond_int": 8.17, "suma_pond_ext": 7.14},
  "cruzada": {
    "internos": ["F1", ...], "externos": ["O1", ...],
    "calificaciones": [[6.65, ...]], "valores": [[1.23, ...]], "desviaciones": [[0.9, ...]],
    "totales_fila": {"F1": 40.03}, "totales_columna": {"O1": 34.54}, "gran_total": 340.96,
    "cuadrantes": {"FO": {"suma": 98.88, "n": 84, "n_posibles": 84, "n_posibles_en_limite": 84, "pct": 29.0, "calif_prom": 6.65}, "FA": {...}, "DO": {...}, "DA": {...}},
    "cuadrante_dominante": "FO",
    "cuadrantes_empatados": [],
    "top_valor": [{"interno": "D1", "externo": "A3", "valor": 4.316, "calif": 8.67}], "top_calif": [...],
    "dominantes_fila": [...], "dominantes_columna": [...], "parejas_no_calificadas": 0
  },
  "tendencias": [{"codigo": "T1", "texto": "...", "votos": 7, "pct": 77.8}],
  "desacuerdos": [{"tipo": "factor|cruce", "ref": "D1 x A3", "desv": 3.2}],
  "por_participante": {"resumen": [{"user_id": 3, "iniciales": "NF", "mefi": 2.7, "mefe": 2.4}]}
}
```

Nombres de personas no aparecen en los resultados que ven los participantes ni en exportaciones; solo iniciales en `por_participante`, y esa sección solo la ve el facilitador.

---

## 9. Pantallas

Todas con `AppLayout` salvo las de acceso y `Participar`, que usan un layout liviano `ParticipanteLayout` (sin menú, logo, nombre de la persona, indicador de fase, botón salir). Componentes en `resources/js/Pages/Dofa/` y `resources/js/Components/Dofa/`. Sondeo con un composable `usePolling(url, ms)` que se detiene cuando la pestaña no está visible.

### Facilitador (laptop)

- **Dofa/Panel.vue**: cabecera con sesión activa y fase; selector/creador de sesión; configuración (form); tarjetas de participantes con estado por fase (sondeo cada 5 s); botones: "Abrir aportes", "Pasar a consolidación", "Abrir calificación", "Abrir cruces", "Cerrar y calcular", "Volver"; accesos a Consolidar, Monitor, Resultados, Insumos, Participantes; QR general (`/acceso`).
- **Dofa/Consolidar.vue**: tres columnas en escritorio: aportes por categoría (chips con conteo, autor visible, filtro), propuesta de la IA (factores con origen y motivo, marcar aceptar/editar/descartar), lista final editable (arrastrar para ordenar, fusionar seleccionando dos, dividir, agregar desde banco 2024 o desde insumos, editar texto/detalle). Botón "Consolidar con IA" con parámetros (factores por categoría, instrucciones adicionales) y estado de progreso; "Aplicar propuesta"; "Guardar"; "Publicar y abrir calificación". Muestra la skill en un panel plegable de solo lectura con su versión.
- **Dofa/Monitor.vue** (beam, tipografía grande, fondo oscuro): fase actual y reloj; en aporte: contador de aportes por categoría y participantes que enviaron (iniciales en verde); en calificación y cruces: barras de progreso por participante; en cerrada: MEFI, MEFE, punto en la MIME (SVG), cuadrantes y top 5 cruces, tendencias top; QR general en la esquina; modo plenaria: rejilla de cruces para capturar con clic.
- **Dofa/Resultados.vue**: pestañas MEFI, MEFE, MIME, FAC, FODA cruzada (mapa de calor con tooltips), Cuadrantes y ranking, Tendencias, Desacuerdos, Lectura de IA; botones exportar PDF y Excel; selector de versión de resultados.
- **Dofa/Insumos.vue**: pestañas Voces del equipo (temas consolidados sin nombres), Factores 2024 (con pesos y ranking del ejercicio anterior), Cifras clave, Tendencias sugeridas.
- **Setup/Participantes.vue**: sección 6.

### Participante (celular)

- **Dofa/Participar.vue** con subcomponentes por fase:
  - `Espera.vue`: mensaje y sondeo.
  - `Aporte.vue`: cinco pestañas F/D/O/A/T con definición corta y ejemplo, lista de mis aportes (editar/borrar), campo de texto con contador (≤ 300), botón "Agregar"; panel plegable "Ideas" con insumos y banco 2024 si están visibles; botón "Enviar mis aportes" con validación de mínimo por categoría.
  - `Calificacion.vue`: tarjetas por factor, agrupadas por categoría, con: chips 1 a 10 para importancia (o slider grande), dos botones para calificación con etiqueta ("Fortaleza mayor 4 / menor 3", "Debilidad mayor 1 / menor 2", etc.), cinco chips para prioridad; autoguardado con indicador; barra de progreso; al final selección de hasta 3 tendencias; "Enviar".
  - `Cruces.vue`: navegación por factor interno (uno a la vez, con flechas y resumen de progreso), lista de externos con cinco chips cada uno; autoguardado; "Enviar mis cruces".
  - `Cierre.vue`: gracias, resumen (MIME y cuadrante dominante) cuando el facilitador cierre.
- **Acceso/Codigo.vue** y **Acceso/Espera.vue**.

Accesibilidad: botones de al menos 44 px, contraste alto, sin depender del color para distinguir categorías (usar etiqueta). Pruebas en 360 px de ancho.

---

## 10. IA

### 10.1 Cliente

Mantener `App\Services\ClaudeService` (HTTP directo a `https://api.anthropic.com/v1/messages`) y añadir:

- Modelo por defecto `claude-opus-5` (`config/services.php` → `anthropic.model`, y `.env.docker` actualizado; no tocar `.env` de producción).
- `askJson(string $prompt, string $system, array $jsonSchema, int $maxTokens = 16000, int $timeout = 300): array` usando salida estructurada: en el cuerpo `output_config: { format: { type: 'json_schema', schema: <schema> } }`; omitir `thinking` (adaptativo por defecto); registrar tokens; lanzar excepción con mensaje claro si la respuesta no es JSON válido; reintentar una vez ante 429/5xx.
- Registrar cada llamada en `ai_messages` con `module = 'dofa'`.

### 10.2 Skill de consolidación (`resources/ai/skills/dofa_consolidacion.md`, versión en el encabezado)

Reglas que la skill debe contener (redactarla completa, en español):

1. Rol: consultor senior en planeación estratégica de entidades solidarias colombianas; conoce la metodología MEFI/MEFE/FODA cruzada.
2. Entrada: aportes por categoría con id y texto; opcionalmente insumos de entrevistas, banco 2024 y cifras.
3. Salida: exactamente `N` factores por categoría F, D, O, A (N configurable, por defecto 7; si hay menos material real, menos, nunca inventar) y hasta `N` tendencias.
4. Redacción: sustantivo + calificador, máximo 14 palabras, sin verbos en infinitivo, sin juicios ("excelente"), sin cifras salvo que vengan del aporte, sin nombres de personas. Debe poder leerse como "FEDEF tiene/enfrenta: …".
5. Reclasificar cuando corresponda: interno vs externo; fortaleza vs oportunidad; una tendencia es un cambio del entorno de 3 a 5 años, no una oportunidad puntual.
6. Fusionar aportes equivalentes; conservar la trazabilidad (`aportes_origen`); no fusionar cosas distintas solo para cumplir N.
7. Marcar `descartados` con motivo (duplicado, fuera de alcance, no es un factor, ambiguo).
8. Sugerir `detalle` de una frase por factor (qué se quiso decir, con evidencia de los aportes).
9. Ordenar por número de aportes que lo respaldan y luego por relevancia estratégica.
10. Prohibido: proponer estrategias, calificar, ponderar o emitir recomendaciones. Solo simplificar.

Esquema JSON de salida: `{ "factores": [{"categoria": "F|D|O|A|T", "texto": "", "detalle": "", "aportes_origen": [ids], "reclasificado_desde": "F|D|O|A|T|null", "respaldo": n}], "descartados": [{"aporte_id": n, "motivo": ""}], "notas_para_el_facilitador": [""] }`.

### 10.3 Lectura de resultados (`resources/ai/skills/dofa_lectura.md`)

Adaptar `DofaAnalysisJob` a los nuevos resultados y ejecutar de forma síncrona desde `dofa.analisis.ia` (con timeout 300 s) o en cola. Salida: posicionamiento (MIME), tres tensiones, capacidades distintivas candidatas, renuncias candidatas, tendencias más votadas y cómo condicionan, tres preguntas para el bloque 4. Máximo 600 palabras, sin nombres.

---

## 11. Exportaciones

- **PDF** (dompdf, `resources/views/pdf/dofa2_resultados.blade.php`): portada, factores, MEFI, MEFE, MIME (SVG), FAC, FODA cruzada (tabla), cuadrantes, rankings, tendencias, lectura de IA.
- **Excel** (`phpoffice/phpspreadsheet`, instalar con composer dentro del contenedor): hojas `MEFI`, `MEFE`, `MIME`, `FAC INT`, `FAC EXT`, `FODA`, `TENDENCIAS`, `VOTOS` (una fila por participante-factor y por participante-cruce, con iniciales, no nombres), con la misma disposición y encabezados del archivo original en lo posible y fórmulas Excel reales en las celdas calculadas (para que el consultor pueda auditar).

---

## 12. Insumos del diagnóstico (seeder)

`database/seeders/InsumosDiagnosticoSeeder.php` carga desde `resources/data/fedef/`:

- `insumos_entrevistas.json`: temas consolidados de las cinco entrevistas del 20 de marzo de 2026, sin nombres ni cargos identificables: balance del plan anterior, dolores, palancas, brechas, bloqueos digitales, renuncias insinuadas, capacidades mencionadas, métricas de "ganar", recomendaciones al proceso. Cada tema con `titulo`, `sintesis`, `menciones` (n de 5), `categoria_sugerida` (F/D/O/A/T) y `citas` (máximo 2, cortas, sin atribución). Fuente: `analisis/ANALISIS_CONTEXTO_FEDEF.md` sección 6 y `analisis/fuentes/transcripcion_*.md`.
- `factores_2024.json`: los 34 factores de `MATRIZ DOFA Consolidado.xls` con código, texto, peso, calificación, valor, importancia, prioridad, ponderación y totales. Fuente: `analisis/fuentes/dofa.md`.
- `cifras_clave.json`: 20 a 30 cifras con unidad, año y fuente (Cifras.xlsx, Supersolidaria, BSC, bases) tomadas de `analisis/ANALISIS_CONTEXTO_FEDEF.md`.
- `tendencias_sugeridas.json`: 10 a 15 tendencias del entorno relevantes para un fondo de empleados (regulación, demografía de la base social, digital e IA, competencia bancaria y fintech, tasas, empleo y sector floricultor, etc.) con una línea de sustento cada una. Fuente: consolidado, diagnóstico v2 y `analisis/fuentes/cronograma_evolucion.md`.

---

## 13. Horario del retiro (para temporizadores y textos)

Sábado 5 de septiembre de 2026, 8:00 a 12:30 sin pausa formal; el refrigerio se sirve a las 10:00 sin detener la sesión.

| Hora | Bloque | Min |
|---|---|---|
| 8:00 | Apertura: agenda, reglas, propósito | 10 |
| 8:10 | Bloque 1: FEDEF en contexto y diagnóstico (cifras, sector, plan 2024-2026, voces del equipo) | 55 |
| 9:05 | Bloque 2: contexto global y de negocio (tendencias, digital, IA aplicada a fondos) | 35 |
| 9:40 | Bloque 3a: cómo haremos el DOFA, acceso con QR y aporte de factores (refrigerio desde las 10:00 mientras terminan) | 30 |
| 10:10 | Consolidación con IA y aprobación del consultor (el grupo sigue en refrigerio) | 15 |
| 10:25 | Bloque 3b: calificación (20), cruces (20) y resultados en vivo (10) | 50 |
| 11:15 | Bloque 4: pensamiento estratégico (qué es ganar, dónde, capacidades, apuestas, renuncias) | 65 |
| 12:20 | Cierre del día y anticipo del domingo | 10 |
| 12:30 | Fin | |

Domingo 6 de septiembre: mismo horario (8:00 a 12:30).

Regla de cálculo: los resultados se calculan únicamente con los participantes que enviaron cada fase. Quien no calificó no cuenta en promedios ni en cruces, y `participantes_incluidos` registra cuántos y quiénes (iniciales) entraron en cada versión del cálculo.

---

## 14. Convenciones y reparto de archivos

- PHP 8.4, Laravel 13, Inertia + Vue 3 `<script setup>`, Tailwind. Nombres en español para tablas, rutas, componentes y textos; nombres de clases PHP en inglés con prefijo `Dofa2` para evitar choques (`Dofa2Session`, `Dofa2Factor`, …). Espacio de nombres de servicios `App\Services\Dofa\`.
- Motor de cálculo: `App\Services\Dofa\DofaCalculadora` (clase pura, sin Eloquent, entradas y salidas en arreglos) más `App\Services\Dofa\DofaResultadosService` (lee la base, llama al motor, guarda).
- Cada agente edita solo los archivos de su reparto:
  - **Base**: migraciones, modelos, `DofaCalculadora`, fixture y pruebas, `routes/dofa.php` y `routes/acceso.php` vacíos registrados, migración de usuarios y permisos, `docs/CONTRATO_RUTAS.md` (lista final de rutas y props).
  - **Backend DOFA**: `app/Http/Controllers/Dofa2/*`, `app/Services/Dofa/*` (salvo el motor), `app/Policies/Dofa2SessionPolicy.php`, `routes/dofa.php`, `resources/ai/skills/*`, `resources/views/pdf/dofa2_*.blade.php`, `app/Exports/*`, cambios en `ClaudeService`, `config/services.php`.
  - **Frontend DOFA**: `resources/js/Pages/Dofa/**`, `resources/js/Components/Dofa/**`, `resources/js/Layouts/ParticipanteLayout.vue`, `resources/js/composables/usePolling.js`.
  - **Acceso**: `app/Http/Controllers/Acceso/*`, `routes/acceso.php`, `resources/js/Pages/Acceso/**`, `resources/js/Pages/Setup/Participantes.vue`, `app/Http/Controllers/ParticipantesController.php`, `resources/views/pdf/tarjetas.blade.php`, `app/Services/AccesoService.php`, y la ruta `/participar` (controlador `Acceso/ParticiparController` que renderiza `Dofa/Participar` con los props del contrato).
  - **Insumos**: `resources/data/fedef/*.json`, `database/seeders/InsumosDiagnosticoSeeder.php`, `analisis/insumos_diagnostico.md`.
- Nadie edita `routes/web.php` salvo Base (para registrar los archivos de rutas). Nadie toca `.env` de producción.
- Compilar frontend: `docker compose -f docker/compose.yaml exec -T app npm run build`. Migrar: `... exec -T app php artisan migrate`. Sembrar: `... exec -T app php artisan db:seed --class=InsumosDiagnosticoSeeder`. Pruebas: `... exec -T app php artisan test --filter Dofa`. Prueba de humo: `... exec -T app php docker/smoke_get_routes.php consultor@fycls.com 1 1`.
- La app corre en http://localhost:8080 (usuario consultor@fycls.com / Fedef2026! solo local).
