# 05 — Base de Datos

> Esquema verificado directamente contra la base `kuorum` en MySQL, no solo contra las migraciones.
> Motor: **MySQL / MariaDB**, charset `utf8mb4`, colación `utf8mb4_0900_ai_ci`.

---

## 1. Cifras

| Concepto | Cantidad |
|---|---|
| Tablas base | **124** (incluye `migrations` de control y `graficas`) |
| Vistas SQL (`vista_*`) | **62** |
| Claves foráneas declaradas | **75** |
| Migraciones en disco / aplicadas | 41 / 36 |
| Tabla más voluminosa | `respuestas` (1 552 filas en la BD de desarrollo) |

---

## 2. Convenciones del esquema

| Aspecto | Convención | Excepciones reales |
|---|---|---|
| Nombre de tabla | `snake_case`, plural | `ceco`, `graficas`, `permisos`, `migrations` |
| Clave primaria | `Id` `INT AUTO_INCREMENT` | `configuraciones.Id` es `varchar(100)` (la clave textual); `talento_config` usa `Clave` |
| Columnas | `PascalCase` | `migrations` usa `snake_case` (`id`, `migration`, `batch`, `executed_at`) |
| Borrado lógico | Columna `Estado` `tinyint(1)`, `1` = activo | Algunas tablas usan `Activo`; `planes_desarrollo` y `acciones_desarrollo` usan `EstadoRegistro` |
| Estado de negocio | Columna con nombre propio y valores de texto | `EstadoMeta`, `EstadoSolicitud`, `EstadoProceso`, `EstadoVacante`, `EstadoOferta`, `EstadoContrato`, `EstadoEntrevista`, `SlAEstado` |
| Auditoría (módulos antiguos) | `UsuarioRegistro`, `FechaRegistro`, `UsuarioModificacion`, `FechaModificacion` | |
| Auditoría (módulos nuevos) | `CreatedAt`, `UpdatedAt` | |
| Vista de lectura | `vista_` + nombre de tabla | Muchos modelos apuntan `$VIEW_NAME` a la propia tabla cuando no hay vista |

### 🔴 Dos estilos de clave foránea coexistiendo

Esta es la inconsistencia estructural más importante del esquema:

| Estilo | Módulos que lo usan | Ejemplos |
|---|---|---|
| **`Id{Entidad}`** (prefijo) | metas, evaluaciones, planes de desarrollo, vacaciones, competencias, respuestas, reclutamiento (parcialmente), alertas | `IdColaborador`, `IdPeriodo`, `IdCompetencia`, `IdPolitica`, `IdVacante`, `IdCandidato`, `IdEtapaActual` |
| **`{Entidad}Id`** (sufijo) | asistencia, beneficios, adelantos, capacitación, servicio/tickets, carrera, sucesión, potencial, reconocimientos, comunicación, nómina, línea de ética | `ColaboradorId`, `BeneficioId`, `CursoId`, `TicketId`, `PlanId`, `PeriodoId`, `ConceptoId`, `CasoId` |

**Antes de escribir cualquier consulta, comprueba qué estilo usa la tabla.** No unifiques sin una migración planificada: 20+ tablas y decenas de consultas dependen del nombre actual.

---

## 3. Entidades centrales

```mermaid
erDiagram
    colaboradores ||--o{ metas : "IdColaborador"
    colaboradores ||--o{ evaluaciones : "IdColaborador"
    colaboradores ||--o{ solicitudes_vacaciones : "IdColaborador"
    colaboradores ||--o{ asistencia_registros : "ColaboradorId"
    colaboradores ||--o{ beneficios_solicitudes : "ColaboradorId"
    colaboradores ||--o{ adelanto_solicitudes : "ColaboradorId"
    colaboradores ||--o{ servicio_tickets : "ColaboradorId"
    colaboradores ||--o{ capacitacion_inscripciones : "ColaboradorId"
    colaboradores ||--o{ potencial_evaluaciones : "ColaboradorId"
    colaboradores ||--o{ carrera_plan_colaborador : "ColaboradorId"
    colaboradores ||--o{ planes_desarrollo : "IdColaborador"
    colaboradores ||--o{ nomina_liquidaciones : "ColaboradorId"
    colaboradores ||--o{ reconocimientos : "EmisorId / ReceptorId"
    colaboradores ||--o{ sucesion_candidatos : "ColaboradorId"
    colaboradores }o--|| cargos : "IdCargo"
    colaboradores }o--|| areas : "IdArea"
    colaboradores }o--|| tipos_contrato : "IdTipoContrato"
    colaboradores }o--|| sedes : "IdSede"
    colaboradores }o--|| ceco : "IdCeco"
    colaboradores }o--o| colaboradores : "IdJefeInmediato / IdJefeFuncional"

    usuarios }o--|| roles : "Tipo"
    roles ||--o{ permisos : "IdRol"

    periodos ||--o{ metas : "IdPeriodo"
    periodos ||--o{ evaluaciones : "IdPeriodo"
    periodos ||--o{ respuestas : "IdPeriodo"
    periodos ||--o{ metas_consolidado : "IdPeriodo"
    periodos ||--o{ competencias_colaboradores : "IdPeriodo"

    colaboradores {
        int Id PK
        varchar NoDocumento UK
        varchar Nombres
        varchar Apellidos
        varchar Usuario UK
        varchar Contrasena
        varchar CorreoCorporativo UK
        int IdCargo FK
        int IdJefeInmediato FK
        int Salario
        tinyint LiderEquipo
        tinyint Estado
    }
    usuarios {
        int Id PK
        tinyint Tipo FK
        varchar Usuario UK
        varchar Contrasena
        varchar Email
        tinyint Estado
    }
    roles {
        int Id PK
        varchar Nombre
        varchar Codigo
        varchar Inicio
        varchar Grupo
    }
    permisos {
        int Id PK
        int IdRol FK
        varchar ModuleName
        text Permission
        text Tabs
        text Other
    }
    periodos {
        int Id PK
        varchar Nombre
        date InicioMetas
        date LimiteMetas
        date FinalMetas
        int MinimoMetas
        int MaximoMetas
        decimal PesoJefe
    }
```

### 3.1 `colaboradores` — la entidad raíz

38 columnas. Índices únicos: `NoDocumento`, `CorreoCorporativo`, `Usuario`. Índices adicionales: `IdCargo`, `Estado`.

| Grupo de columnas | Columnas |
|---|---|
| Identificación | `TipoDocumento`, `NoDocumento`, `Nombres`, `Apellidos`, `FechaNacimiento`, `Genero`, `EstadoCivil`, `Nacionalidad` |
| Contacto | `Telefono`, `CorreoPersonal`, `CorreoCorporativo`, `Departamento`, `Ciudad`, `Direccion` |
| Acceso al portal | `Usuario`, `Contrasena`, `Foto` |
| Vínculo laboral | `IdCargo`, `IdArea`, `IdTipoContrato`, `IdCeco`, `IdSede`, `FechaInicio`, `FechaFin`, `Modalidad`, `ModalidadTrabajo`, `JornadaLaboral`, `Salario`, `EstadoActual` |
| Jerarquía | `IdJefeInmediato`, `IdJefeFuncional`, `LiderEquipo` |
| Control | `Estado`, `UsuarioRegistro`, `FechaRegistro`, `UsuarioModificacion`, `FechaModificacion` |

> `Estado` (0/1) = borrado lógico. `EstadoActual` (texto) = estado laboral. **No son lo mismo.**

**Vista `vista_colaboradores`:** todas las columnas de la tabla + `NombreCompleto = TRIM(CONCAT(IFNULL(Nombres,''),' ',IFNULL(Apellidos,'')))`.
🔴 Incluye `Contrasena` y **no** incluye `LiderEquipo`.

### 3.2 `usuarios` — cuentas del backoffice

22 columnas. `Usuario` es único. `Tipo` referencia `roles.Id` (**sin FK declarada**). Conserva columnas de un producto anterior: `Categoria`, `Dependencia`, `Sede` (varchar, no FK), `Firma`.

### 3.3 `roles` y `permisos` — el modelo de autorización

```
roles(Id, Nombre, Codigo, Inicio, Grupo, Descripcion, Estado)
permisos(Id, IdRol, ModuleName, Permission, Tabs, Other)
```

`Permission` y `Tabs` son **JSON almacenado en columnas `text`**:

```json
{"create":1,"view":1,"edit":1,"remove":1,"list":1,"export":1}
```

Distribución actual: rol 1 (Administrador) 48 filas, rol 2 (Analista) 39, rol 5 (Colaborador) 16.

52 `ModuleName` distintos en la BD. Uno de ellos, `SolicitudContratosAdministrativo`, **no corresponde a ningún controlador existente**.

### 3.4 `periodos` — el eje temporal

36 columnas. Gobierna:
- **Ventanas de metas:** `InicioMetas`, `LimiteMetas`, `FinalMetas` → `PeriodosModel::getMetasWindowState()`
- **Ventanas de competencias:** `InicioCompetencias`, `FinalCompetencias`
- **Límites de metas:** `MinimoMetas`, `MaximoMetas`, `CantidadMaximaSeguimientos`
- **Configuración 360:** flags `AutoEvaluacion`, `EvaluacionColaboradorJefe`, `EvaluacionJefeColaborador`, `EvaluacionPares`, `EvaluacionCliente`, `EvaluacionReferencia`; pesos `PesoAutoEvaluacion`, `PesoJefe`, `PesoColaboradores`, `PesoPares`, `PesoEvaluacionCliente`; `CantidadMaximaParesEvaluar`
- **Pesos globales:** `PesoMetas`, `PesoCompetencias`
- **Visibilidad:** `Mostrar`, `Estado`, `Sincronizacion`

El periodo activo vive en `$_SESSION[APP_ID]['filtersSesion']['Periodo']` y se inyecta en las consultas mediante `FIND_IN_SET`.

---

## 4. Mapa de tablas por módulo

### Núcleo y configuración
| Tabla | Papel |
|---|---|
| `usuarios` | Cuentas del backoffice |
| `roles` | Roles del sistema |
| `permisos` | Permisos por rol y módulo (JSON) |
| `configuraciones` | Parámetros clave-valor (`Id` textual, `Grupo`, `Valor`) |
| `periodos` | Periodos de evaluación |
| `sedes`, `cargos`, `areas`, `ceco`, `tipos_contrato`, `festivos` | Catálogos maestros |
| `ips_autorizadas` | Lista blanca de IPs (`IP_BLOCKING`) |
| `alertas`, `alertas_email` | Alertas en pantalla y cola de correo |
| `migrations` | Control de migraciones (`id`, `migration`, `batch`, `executed_at`) |
| `graficas` | **Vacía, sin modelo ni uso detectado** |

### Colaboradores
`colaboradores`, `colaborador_educacion`, `colaborador_experiencia`

### Reclutamiento
`reclutamiento_vacantes`, `reclutamiento_candidatos`, `reclutamiento_postulaciones`, `reclutamiento_etapas_pipeline`, `reclutamiento_historial_etapas_postulacion`, `reclutamiento_entrevistas`, `reclutamiento_ofertas`, `reclutamiento_contratos`, `reclutamiento_motivos_rechazo`, `etapas_embudo_vacante`, `reglas_filtro_vacante`, `historial_entrevista_postulacion`, `procesos_seleccion_colaborador`

> Las migraciones `..._000026` y `..._000027` crearon estas tablas con nombres en inglés (`recruitment_*`) y luego las renombraron a español; `..._000037` normalizó a snake_case. **Las tablas `recruitment_*` ya no existen en la BD.**

### Desempeño
`metas`, `metas_seguimientos`, `metas_categorias`, `metas_consolidado`, `metas_ciclos` (sin uso), `evaluaciones`, `respuestas`, `preguntas`, `escalas`, `competencias`, `competencias_colaboradores`, `consolidado_evaluacion_360`

### Desarrollo
`planes_desarrollo`, `acciones_desarrollo`, `evidencias_accion_desarrollo`, `feedback_plan_desarrollo`, `historial_plan_desarrollo`

### Capacitación (LMS)
`capacitacion_cursos`, `capacitacion_lecciones`, `capacitacion_inscripciones`, `capacitacion_leccion_progreso`, `capacitacion_quiz`, `capacitacion_quiz_preguntas`, `capacitacion_quiz_intentos`, `capacitacion_certificados`

### Talento y carrera
`potencial_periodos`, `potencial_criterios`, `potencial_evaluaciones`, `potencial_evaluacion_detalle`, `talento_categorias`, `talento_celdas_9box`, `talento_config`, `carrera_rutas`, `carrera_ruta_etapas`, `carrera_plan_colaborador`, `carrera_plan_etapas`, `sucesion_puestos_clave`, `sucesion_planes`, `sucesion_candidatos`, `reconocimientos`, `reconocimiento_tipos`, `reconocimiento_reglas`

### Tiempo
`solicitudes_vacaciones`, `saldos_vacaciones`, `politicas_vacaciones`, `asistencia_registros`, `asistencia_turnos`, `asistencia_colaborador_turnos`, `asistencia_horas_extra`, `asistencia_ausencias`

### Compensación
`nomina_periodos`, `nomina_conceptos`, `nomina_parametros`, `nomina_novedades`, `nomina_liquidaciones`, `nomina_liquidacion_detalle`, `adelanto_politicas`, `adelanto_solicitudes`, `adelanto_plan_descuento`, `adelanto_cuotas`, `adelanto_desembolsos`, `adelanto_historial`

### Beneficios
`beneficios`, `beneficios_reglas`, `beneficios_catalogo_programas`, `beneficios_solicitudes`, `beneficios_solicitud_historial`, `beneficios_asignaciones`

### Servicio al colaborador
`servicio_categorias`, `servicio_subcategorias`, `servicio_articulos`, `servicio_sla_politicas`, `servicio_tickets`, `servicio_ticket_comentarios`, `servicio_ticket_historial`

### Línea de ética
`linea_etica_tipos`, `linea_etica_categorias`, `linea_etica_casos`, `linea_etica_mensajes`, `linea_etica_notas_internas`, `linea_etica_bitacora`

### Comunicación interna
`comunicacion_noticias`, `comunicacion_noticias_audiencias`, `comunicacion_murales`, `comunicacion_notificaciones`, `comunicacion_conversaciones`, `comunicacion_mensajes`, `comunicacion_mensajes_estado`

### Auditoría
`log_acceso`, `log_urls`, `log_modules`, `bitacora_auditoria`

---

## 5. Diagramas ER por dominio

### 5.1 Reclutamiento

```mermaid
erDiagram
    reclutamiento_vacantes ||--o{ reclutamiento_postulaciones : IdVacante
    reclutamiento_candidatos ||--o{ reclutamiento_postulaciones : IdCandidato
    reclutamiento_etapas_pipeline ||--o{ reclutamiento_postulaciones : IdEtapaActual
    reclutamiento_postulaciones ||--o{ reclutamiento_historial_etapas_postulacion : IdApplication
    reclutamiento_postulaciones ||--o{ reclutamiento_entrevistas : IdApplication
    reclutamiento_postulaciones ||--o{ reclutamiento_ofertas : IdApplication
    reclutamiento_postulaciones ||--o{ reclutamiento_contratos : IdApplication
    reclutamiento_motivos_rechazo ||--o{ reclutamiento_postulaciones : IdMotivoRechazo
    reclutamiento_vacantes ||--o{ etapas_embudo_vacante : IdVacante
    reclutamiento_etapas_pipeline ||--o{ etapas_embudo_vacante : IdEtapa
    reclutamiento_vacantes ||--o{ reglas_filtro_vacante : IdVacante
    reclutamiento_contratos }o--o| colaboradores : IdColaborador
```

> Estas relaciones **no tienen FK declaradas en la base**; el esquema las mantiene solo por convención de nombres y por la lógica de `RecruitmentApplicationsModel`.

### 5.2 Vacaciones

```mermaid
erDiagram
    politicas_vacaciones ||--o{ solicitudes_vacaciones : IdPolitica
    politicas_vacaciones ||--o{ saldos_vacaciones : IdPolitica
    colaboradores ||--o{ solicitudes_vacaciones : IdColaborador
    colaboradores ||--o{ saldos_vacaciones : IdColaborador
    festivos }o..o{ solicitudes_vacaciones : "descuento de días"

    politicas_vacaciones {
        int Id PK
        varchar Nombre
        int DiasPorAnio
        tinyint CuentaDiasHabiles
        tinyint PermiteSaldoNegativo
        tinyint RequiereAprobacion
        tinyint Activo
    }
    solicitudes_vacaciones {
        int Id PK
        int IdColaborador FK
        int IdPolitica FK
        date FechaInicio
        date FechaFin
        decimal DiasSolicitados
        varchar Estado "SOLICITADA|APROBADA|RECHAZADA|CANCELADA"
        int IdAprobador
    }
    saldos_vacaciones {
        int Id PK
        int IdColaborador FK
        int IdPolitica FK
        int Anio
        decimal DiasDisponibles
        decimal DiasTomados
        decimal DiasPendientes
    }
```

### 5.3 Nómina

```mermaid
erDiagram
    nomina_periodos ||--o{ nomina_liquidaciones : PeriodoId
    nomina_periodos ||--o{ nomina_novedades : PeriodoId
    colaboradores ||--o{ nomina_liquidaciones : ColaboradorId
    colaboradores ||--o{ nomina_novedades : ColaboradorId
    nomina_conceptos ||--o{ nomina_novedades : ConceptoId
    nomina_conceptos ||--o{ nomina_liquidacion_detalle : ConceptoId
    nomina_liquidaciones ||--o{ nomina_liquidacion_detalle : LiquidacionId
    nomina_parametros }o..o{ nomina_conceptos : "BaseCalculo PARAM:CLAVE"

    nomina_conceptos {
        int Id PK
        varchar Codigo
        varchar Nombre
        varchar Tipo "DEVENGADO|DEDUCCION"
        varchar FormulaTipo "FIJO|PORCENTAJE|FORMULA"
        varchar Formula
        varchar BaseCalculo "SALARIO|SALARIO_DIARIO|TOTAL_DEVENGADO|NETO|PARAM:X"
        decimal TopeMin
        decimal TopeMax
        tinyint Activo
    }
    nomina_liquidaciones {
        int Id PK
        int PeriodoId FK
        int ColaboradorId FK
        int DiasLiquidados
        decimal TotalDevengado
        decimal TotalDeduccion
        decimal NetoPagar
        varchar HashCalculo
    }
```

### 5.4 Beneficios y adelantos

```mermaid
erDiagram
    beneficios ||--o{ beneficios_reglas : BeneficioId
    beneficios ||--o{ beneficios_catalogo_programas : BeneficioId
    beneficios ||--o{ beneficios_solicitudes : BeneficioId
    beneficios ||--o{ beneficios_asignaciones : BeneficioId
    beneficios_solicitudes ||--o{ beneficios_solicitud_historial : SolicitudId
    colaboradores ||--o{ beneficios_solicitudes : ColaboradorId
    colaboradores ||--o{ beneficios_asignaciones : ColaboradorId

    adelanto_politicas ||..o{ adelanto_solicitudes : "política activa"
    colaboradores ||--o{ adelanto_solicitudes : ColaboradorId
    adelanto_solicitudes ||--o{ adelanto_plan_descuento : SolicitudId
    adelanto_solicitudes ||--o{ adelanto_cuotas : SolicitudId
    adelanto_solicitudes ||--o{ adelanto_desembolsos : SolicitudId
    adelanto_solicitudes ||--o{ adelanto_historial : SolicitudId
```

### 5.5 Talento (9-Box)

```mermaid
erDiagram
    potencial_periodos ||--o{ potencial_evaluaciones : PeriodoId
    colaboradores ||--o{ potencial_evaluaciones : ColaboradorId
    potencial_evaluaciones ||--o{ potencial_evaluacion_detalle : EvaluacionId
    potencial_criterios ||--o{ potencial_evaluacion_detalle : CriterioId
    metas_consolidado }o..|| potencial_evaluaciones : "PuntajeMetasAuto"
    evaluaciones }o..|| potencial_evaluaciones : "Puntaje360Auto"
    talento_categorias }o..|| potencial_evaluaciones : "CategoriaMapaTalento"
    talento_celdas_9box }o..o{ potencial_evaluaciones : "celda del mapa"
    talento_config }o..o{ potencial_evaluaciones : "pesos y umbral"
```

### 5.6 Servicio al colaborador

```mermaid
erDiagram
    servicio_categorias ||--o{ servicio_subcategorias : CategoriaId
    servicio_categorias ||--o{ servicio_articulos : CategoriaId
    servicio_categorias ||--o{ servicio_tickets : CategoriaId
    servicio_subcategorias ||--o{ servicio_tickets : SubcategoriaId
    colaboradores ||--o{ servicio_tickets : ColaboradorId
    servicio_tickets ||--o{ servicio_ticket_comentarios : TicketId
    servicio_tickets ||--o{ servicio_ticket_historial : TicketId
    servicio_sla_politicas }o..|| servicio_tickets : "por Prioridad"
```

### 5.7 Línea de ética

```mermaid
erDiagram
    linea_etica_tipos ||--o{ linea_etica_casos : TipoId
    linea_etica_categorias ||--o{ linea_etica_casos : CategoriaId
    linea_etica_casos ||--o{ linea_etica_mensajes : CasoId
    linea_etica_casos ||--o{ linea_etica_notas_internas : CasoId
    linea_etica_casos ||--o{ linea_etica_bitacora : CasoId

    linea_etica_casos {
        int Id PK
        varchar Codigo "ETH-YYYY-NNNNNN"
        varchar PinHash "password_hash"
        varchar Estado
        varchar Prioridad
        int AsignadoA
        datetime FechaPrimeraRespuesta
        datetime FechaCierre
    }
```

---

## 6. Catálogo completo de claves foráneas (75)

Agrupadas por tabla de origen. Todas tienen nombre `fk_*`.

| Origen | Columna | Destino |
|---|---|---|
| `acciones_desarrollo` | `IdPlanDesarrollo` | `planes_desarrollo.Id` |
| `acciones_desarrollo` | `IdCompetencia` | `competencias.Id` |
| `adelanto_cuotas` | `SolicitudId` | `adelanto_solicitudes.Id` |
| `adelanto_desembolsos` | `SolicitudId` | `adelanto_solicitudes.Id` |
| `adelanto_historial` | `SolicitudId` | `adelanto_solicitudes.Id` |
| `adelanto_plan_descuento` | `SolicitudId` | `adelanto_solicitudes.Id` |
| `adelanto_solicitudes` | `ColaboradorId` | `colaboradores.Id` |
| `asistencia_ausencias` | `ColaboradorId` | `colaboradores.Id` |
| `asistencia_colaborador_turnos` | `ColaboradorId`, `TurnoId` | `colaboradores.Id`, `asistencia_turnos.Id` |
| `asistencia_horas_extra` | `AsistenciaRegistroId` | `asistencia_registros.Id` |
| `asistencia_registros` | `ColaboradorId` | `colaboradores.Id` |
| `beneficios_asignaciones` | `BeneficioId`, `ColaboradorId` | `beneficios.Id`, `colaboradores.Id` |
| `beneficios_catalogo_programas` | `BeneficioId` | `beneficios.Id` |
| `beneficios_reglas` | `BeneficioId` | `beneficios.Id` |
| `beneficios_solicitud_historial` | `SolicitudId` | `beneficios_solicitudes.Id` |
| `beneficios_solicitudes` | `BeneficioId`, `ColaboradorId` | `beneficios.Id`, `colaboradores.Id` |
| `capacitacion_certificados` | `InscripcionId` | `capacitacion_inscripciones.Id` |
| `capacitacion_inscripciones` | `ColaboradorId`, `CursoId` | `colaboradores.Id`, `capacitacion_cursos.Id` |
| `capacitacion_leccion_progreso` | `InscripcionId`, `LeccionId` | `capacitacion_inscripciones.Id`, `capacitacion_lecciones.Id` |
| `capacitacion_lecciones` | `CursoId` | `capacitacion_cursos.Id` |
| `capacitacion_quiz` | `CursoId` | `capacitacion_cursos.Id` |
| `capacitacion_quiz_intentos` | `InscripcionId` | `capacitacion_inscripciones.Id` |
| `capacitacion_quiz_preguntas` | `QuizId` | `capacitacion_quiz.Id` |
| `carrera_plan_colaborador` | `ColaboradorId`, `RutaId`, `MentorId`, `CargoMetaId` | `colaboradores.Id`, `carrera_rutas.Id`, `colaboradores.Id`, `cargos.Id` |
| `carrera_plan_etapas` | `PlanId`, `EtapaId` | `carrera_plan_colaborador.Id`, `carrera_ruta_etapas.Id` |
| `carrera_ruta_etapas` | `RutaId`, `CargoDestinoId` | `carrera_rutas.Id`, `cargos.Id` |
| `carrera_rutas` | `CargoOrigenId` | `cargos.Id` |
| `comunicacion_conversaciones` | `ParticipanteAId`, `ParticipanteBId` | `colaboradores.Id` |
| `comunicacion_mensajes` | `ConversacionId`, `EmisorId`, `ReceptorId` | `comunicacion_conversaciones.Id`, `colaboradores.Id` |
| `comunicacion_mensajes_estado` | `MensajeId`, `UsuarioId` | `comunicacion_mensajes.Id`, `colaboradores.Id` |
| `comunicacion_noticias_audiencias` | `NoticiaId` | `comunicacion_noticias.Id` |
| `evidencias_accion_desarrollo` | `IdAccionDesarrollo` | `acciones_desarrollo.Id` |
| `feedback_plan_desarrollo` | `IdPlanDesarrollo` | `planes_desarrollo.Id` |
| `historial_plan_desarrollo` | `IdAccionDesarrollo`, `IdPlanDesarrollo` | `acciones_desarrollo.Id`, `planes_desarrollo.Id` |
| `linea_etica_bitacora` | `CasoId` | `linea_etica_casos.Id` |
| `linea_etica_casos` | `CategoriaId`, `TipoId` | `linea_etica_categorias.Id`, `linea_etica_tipos.Id` |
| `linea_etica_mensajes` | `CasoId` | `linea_etica_casos.Id` |
| `linea_etica_notas_internas` | `CasoId` | `linea_etica_casos.Id` |
| `planes_desarrollo` | `IdMeta`, `IdColaborador`, `IdEvaluacion` | `metas.Id`, `colaboradores.Id`, `evaluaciones.Id` |
| `potencial_evaluacion_detalle` | `CriterioId`, `EvaluacionId` | `potencial_criterios.Id`, `potencial_evaluaciones.Id` |
| `potencial_evaluaciones` | `ColaboradorId`, `PeriodoId` | `colaboradores.Id`, `potencial_periodos.Id` |
| `reconocimientos` | `EmisorId`, `ReceptorId`, `TipoId` | `colaboradores.Id`, `colaboradores.Id`, `reconocimiento_tipos.Id` |
| `servicio_articulos` | `CategoriaId` | `servicio_categorias.Id` |
| `servicio_subcategorias` | `CategoriaId` | `servicio_categorias.Id` |
| `servicio_ticket_comentarios` | `TicketId` | `servicio_tickets.Id` |
| `servicio_ticket_historial` | `TicketId` | `servicio_tickets.Id` |
| `servicio_tickets` | `CategoriaId`, `SubcategoriaId`, `ColaboradorId` | `servicio_categorias.Id`, `servicio_subcategorias.Id`, `colaboradores.Id` |
| `sucesion_candidatos` | `ColaboradorId`, `PlanId` | `colaboradores.Id`, `sucesion_planes.Id` |
| `sucesion_planes` | `PuestoClaveId` | `sucesion_puestos_clave.Id` |
| `sucesion_puestos_clave` | `CargoId` | `cargos.Id` |

### 🔴 Dominios SIN claves foráneas declaradas

Estos módulos **no tienen ninguna FK en la base**; la integridad depende exclusivamente del código de aplicación:

- **Reclutamiento completo** (13 tablas)
- **Metas** (`metas`, `metas_seguimientos`, `metas_consolidado`)
- **Evaluaciones 360** (`evaluaciones`, `respuestas`, `preguntas`, `competencias_colaboradores`, `consolidado_evaluacion_360`)
- **Vacaciones** (`solicitudes_vacaciones`, `saldos_vacaciones`)
- **Nómina completa** (6 tablas)
- **Núcleo de autorización** (`usuarios.Tipo` → `roles.Id`, `permisos.IdRol` → `roles.Id`)
- **`colaboradores`** hacia sus catálogos (`IdCargo`, `IdArea`, `IdSede`, `IdCeco`, `IdTipoContrato`) y hacia sí misma (`IdJefeInmediato`, `IdJefeFuncional`)
- **Auditoría** (`log_*`, `bitacora_auditoria`)

**Consecuencia práctica:** borrar un colaborador con `DELETE` directo deja huérfanas decenas de filas. Por eso existe `ColaboradoresModel::deleteCascade()`, que hace el borrado en cascada **manualmente y en transacción**. Usa siempre ese método, nunca un `DELETE` directo sobre `colaboradores`.

---

## 7. Vistas SQL (62)

Todas siguen el patrón `vista_{tabla}`. La mayoría son *passthrough* que añaden campos calculados o desnormalizan nombres.

`vista_acciones_desarrollo`, `vista_adelanto_cuotas`, `vista_adelanto_desembolsos`, `vista_adelanto_historial`, `vista_adelanto_plan_descuento`, `vista_adelanto_politicas`, `vista_adelanto_solicitudes`, `vista_asistencia_ausencias`, `vista_asistencia_colaborador_turnos`, `vista_asistencia_horas_extra`, `vista_asistencia_registros`, `vista_asistencia_turnos`, `vista_beneficios`, `vista_beneficios_asignaciones`, `vista_beneficios_catalogo_programas`, `vista_beneficios_reglas`, `vista_beneficios_solicitud_historial`, `vista_beneficios_solicitudes`, `vista_capacitacion_certificados`, `vista_capacitacion_cursos`, `vista_capacitacion_inscripciones`, `vista_capacitacion_leccion_progreso`, `vista_capacitacion_lecciones`, `vista_capacitacion_quiz`, `vista_capacitacion_quiz_intentos`, `vista_capacitacion_quiz_preguntas`, `vista_carrera_rutas`, `vista_colaboradores`, `vista_evaluaciones`, `vista_linea_etica_bitacora`, `vista_linea_etica_casos`, `vista_linea_etica_categorias`, `vista_linea_etica_mensajes`, `vista_linea_etica_notas_internas`, `vista_linea_etica_tipos`, `vista_metas`, `vista_metas_consolidado`, `vista_metas_seguimientos`, `vista_nomina_conceptos`, `vista_nomina_liquidacion_detalle`, `vista_nomina_liquidaciones`, `vista_nomina_novedades`, `vista_nomina_parametros`, `vista_nomina_periodos`, `vista_planes_desarrollo`, `vista_politicas_vacaciones`, `vista_potencial_evaluaciones`, `vista_preguntas`, `vista_reconocimientos`, `vista_respuestas`, `vista_respuestas_colaborador`, `vista_respuestas_grupo`, `vista_saldos_vacaciones`, `vista_servicio_articulos`, `vista_servicio_categorias`, `vista_servicio_sla_politicas`, `vista_servicio_subcategorias`, `vista_servicio_ticket_comentarios`, `vista_servicio_ticket_historial`, `vista_servicio_tickets`, `vista_solicitudes_vacaciones`, `vista_sucesion_candidatos`

⚠️ **Las vistas se crean con `SQL SECURITY DEFINER` y `DEFINER = root@localhost`.** Al restaurar un volcado en otro servidor donde ese usuario no exista, las vistas fallarán con `The user specified as a definer does not exist`.

**Regla:** si modificas la estructura de una tabla que tiene vista, **debes recrear la vista en la misma migración**. `MysqlPDO` no detecta que una vista quedó obsoleta.

---

## 8. Catálogos y tablas de configuración

| Tabla | Tipo | Contenido |
|---|---|---|
| `configuraciones` | Clave-valor | 12 filas: `metas.max_por_colaborador`, `EmpresaNombre`, `EmpresaNIT`, `EmpresaCorreoNomina`, `EmpresaTelefono`, `UmbralAusentismo`, `UmbralRotacion`, `CorreoAlertasRRHH`, `ReclutamientoContratoExigeFirma`, `RecruitmentContratoExigeFirma`, `DetenerEnvioNotificaciones`, `EnvioMaximoNotificaciones` |
| `nomina_parametros` | Clave-valor | `AUX_TRANSPORTE`, `PORC_SALUD`, `PORC_PENSION`, `HORAS_EXTRA_FACTOR`, … |
| `talento_config` | Clave-valor | Pesos y umbral del ecosistema de talento |
| `talento_categorias` | Catálogo configurable | 5 categorías del mapa de talento |
| `talento_celdas_9box` | Catálogo configurable | 9 celdas de la matriz |
| `adelanto_politicas` | Política única activa | Reglas de elegibilidad de adelantos |
| `politicas_vacaciones` | Políticas múltiples | Días por año, hábiles, saldo negativo |
| `servicio_sla_politicas` | Por prioridad | Minutos de primera respuesta y resolución |
| `reconocimiento_reglas` | Regla única | `LimiteMensualPorEmisor` |
| `metas_categorias` | Catálogo | `Maximo` (peso), `NumeroMetas`, `Estatico` |
| `escalas` | Catálogo | `Opcion0`..`Opcion5` |
| `reclutamiento_etapas_pipeline` | Catálogo ordenado | Pipeline global; `Codigo` con valores especiales `rechazado`, `contratado`, `oferta` |
| `reclutamiento_motivos_rechazo` | Catálogo | |
| `linea_etica_tipos`, `linea_etica_categorias` | Catálogos | |
| `festivos` | Catálogo | Festivos colombianos por fecha |

> ⚠️ Duplicidad detectada: `ReclutamientoContratoExigeFirma` y `RecruitmentContratoExigeFirma` con el mismo valor. Comprobar cuál lee el código antes de cambiar una sola.

---

## 9. Tablas transaccionales, históricas y de auditoría

### Transaccionales (crecen con el uso)
`respuestas`, `metas_seguimientos`, `asistencia_registros`, `nomina_liquidaciones`, `nomina_liquidacion_detalle`, `servicio_ticket_comentarios`, `comunicacion_mensajes`, `comunicacion_notificaciones`, `capacitacion_leccion_progreso`, `beneficios_solicitudes`, `adelanto_solicitudes`, `adelanto_cuotas`, `reclutamiento_postulaciones`

### Históricas / bitácoras de dominio
| Tabla | Registra |
|---|---|
| `adelanto_historial` | `Accion`, `ValorAnterior`, `ValorNuevo`, `UsuarioId` |
| `beneficios_solicitud_historial` | Idem para beneficios |
| `servicio_ticket_historial` | Idem para tickets |
| `linea_etica_bitacora` | Idem para casos éticos |
| `reclutamiento_historial_etapas_postulacion` | `FromStageId` → `ToStageId`, comentario, usuario |
| `historial_entrevista_postulacion` | Reprogramaciones de entrevista |
| `historial_plan_desarrollo` | Eventos de plan y acción |

Todas comparten la forma `(entidadId, Accion/Evento, ValorAnterior, ValorNuevo, UsuarioId, CreatedAt)`. **Es el patrón a seguir para cualquier módulo nuevo con flujo de estados.**

### Auditoría del framework
| Tabla | Escrita por | Contenido |
|---|---|---|
| `log_acceso` | `LogAccionesModel::saveAccess()` (si `LOG_ACCESS`) | `IdUsuario`, `Accion`, `Fecha`, `InicioSesion`, `CierreSesion`, `IpConexion`, `Country`, `Location`, `ZipCode` |
| `log_urls` | Framework (si `LOG_ACTIONS`) | `IdAcceso`, `ValidateAccess`, `Modulo`, `Accion`, `Parametros` |
| `log_modules` | `Model::log()` (si `LOG_MODULES` **y** `$LOG=true` en el modelo) | `IdAcceso`, `IdUrl`, `IdUsuario`, `Accion` (1=Creación, 5=Edición, 9=Eliminación), `IdEnvio`, `Tabla`, `OldData` (JSON), `NewData` (JSON), `Url` |
| `bitacora_auditoria` | `BitacoraAuditoriaModel::registrar()` | `Modulo`, `Entidad`, `EntidadId`, `Accion`, `Detalle`, `UsuarioId` |

> Solo **`ColaboradoresModel`** y **`UsuariosModel`** tienen `$LOG = true`. El resto de modelos **no genera auditoría en `log_modules`**.

---

## 10. Migraciones

### Cómo funcionan

Formato de nombre: `YYYY_MM_DD_HHMMSS_descripcion_snake_case.php`.

Formato estándar del fichero (ver `tests/fixtures/standard_migration.php`):

```php
<?php
return [
    'up'   => function (PDO $pdo) { $pdo->exec("CREATE TABLE …"); },
    'down' => function (PDO $pdo) { $pdo->exec("DROP TABLE …"); },
];
```

`Migrator.php` mantiene compatibilidad con las migraciones históricas que declaran funciones globales.

Control: tabla `migrations(id, migration, batch, executed_at)`.

```bash
php bin/migrate status     # estado
php bin/migrate up         # aplicar pendientes
php bin/migrate down       # revertir el último lote
php bin/migrate validate   # revisión estática sin conectar a la BD
```

### Inventario de las 41 migraciones

| Fichero | Qué hace |
|---|---|
| `2026_02_21_000001_initial_setup` | Configuración inicial |
| `2026_02_21_000002_create_core_tables` | `usuarios`, `roles`, `permisos`, `configuraciones`, `periodos`, `alertas`, `log_*`, … |
| `..._000019_create_cargos_table` | `cargos` |
| `..._000020_create_colaboradores_table` | `colaboradores` |
| `..._000021_drop_tipo_from_colaboradores` | Elimina `colaboradores.Tipo` (por eso el login del portal fuerza rol 5) |
| `..._000022_create_areas_table` | `areas` |
| `..._000023_create_tipos_contrato_table` | `tipos_contrato` |
| `..._000024_create_ceco_table` | `ceco` |
| `..._000026_create_recruitment_selection_tables` | Tablas `recruitment_*` (inglés) |
| `..._000027_rename_recruitment_tables_to_spanish` | Renombra a español |
| `..._000028_create_colaborador_educacion_experiencia_tables` | |
| `..._000029_create_metas_evaluacion360_tables` | `metas`, `evaluaciones`, `respuestas`, … |
| `..._000030_add_rrhh_config_fields_to_periodos` | Campos de configuración en `periodos` |
| `..._000031_create_vacaciones_tables` | |
| `..._000032_create_festivos_table` | |
| `..._000033_create_evaluacion_competencias_tables` | |
| `..._000034_add_phase1_fields_to_metas` | |
| `..._000035_create_planes_desarrollo_tables` | |
| `..._000036_upgrade_reclutamiento_mvp_plus` | Embudo por vacante, reglas de filtro, scoring |
| `2026_02_23_000037_normalize_reclutamiento_tablas_snake_case` | Normalización final de nombres |
| `..._000038_ensure_metas_categorias_structure` | |
| `..._000039_create_nomina_tables` | |
| `..._000040_create_asistencia_tables` | |
| `..._000041_create_servicio_colaborador_tables` | |
| `..._000042_create_linea_etica_tables` | |
| `..._000043_create_capacitacion_lms_tables` | |
| `..._000044_create_carrera_talento_tables` | |
| `..._000045_create_comunicacion_interna_tables` | |
| `..._000046_create_beneficios_tables` | |
| `..._000047_create_adelantos_nomina_tables` | |
| `2026_02_25_000039_add_workflow_audit_fields_to_metas` | ⚠️ **Colisión de secuencia**: comparte el `000039` con la migración de nómina |
| `2026_05_07_000081_consolidar_ecosystem_talento` | Consolidación de talento |
| `2026_05_09_000082_add_maneja_personal_to_cargos_and_config_metas` | |
| `2026_05_09_000083_add_maneja_personal_to_colaboradores` | |
| `2026_05_12_000084_rename_maneja_personal_to_lider_equipo` | `ManejaPersonal` → `LiderEquipo` |
| `2026_05_12_000085_create_talento_categorias` | |
| `2026_05_13_000086_link_potencial_evaluaciones_to_general_periodos` | |
| `2026_05_14_000087_fix_duplicate_metas_peso` | |
| `2026_06_23_000094_fix_text_encoding_competencias_festivos` | Corrección de charset |
| `2026_06_24_000095_fix_text_encoding_planes_desarrollo_colaboradores` | Corrección de charset |
| `2026_06_24_000096_fix_text_encoding_planes_desarrollo_explicit_map` | Corrección de charset |

### ⚠️ Migraciones pendientes en la base de desarrollo actual

Hay **41 ficheros** y **36 registros** en la tabla `migrations`. Las cinco no aplicadas son:

```
2026_05_13_000086_link_potencial_evaluaciones_to_general_periodos
2026_05_14_000087_fix_duplicate_metas_peso
2026_06_23_000094_fix_text_encoding_competencias_festivos
2026_06_24_000095_fix_text_encoding_planes_desarrollo_colaboradores
2026_06_24_000096_fix_text_encoding_planes_desarrollo_explicit_map
```

**Consecuencias observables en la base actual:**
- `potencial_evaluaciones.PeriodoId` sigue apuntando a `potencial_periodos.Id` (la FK `fk_potencial_evaluaciones_periodo` existe), **no** a `periodos`. El diagrama de §5.5 refleja el estado real, no el estado objetivo.
- Los tres arreglos de codificación de texto (competencias, festivos, planes de desarrollo, colaboradores) **no están aplicados**; puede haber caracteres mal codificados en esos datos.

Ejecuta `php bin/migrate status` en cada entorno antes de dar por buena esta lista.

### Regla de oro

> Las migraciones aplicadas son **historial de despliegues**. No se renombran ni se editan. Toda corrección se hace con una migración nueva. (`database/README.md`)

---

## 11. Semillas

Perfil = rango de prefijos numéricos. El orden lo fija el nombre del fichero, no un manifiesto.

| Prefijo | Contenido | Perfil |
|---|---|---|
| `0xx` | Núcleo, catálogos, nómina base, reclutamiento base | `base` |
| `1xx` | Módulos funcionales | `modulos` (incluye `base`) |
| `2xx` | Escenarios de demostración por compañía | `completo` (incluye todo) |

```bash
php bin/seed                 # perfil base
php bin/seed base|modulos|completo
php bin/seed CargosSeeder    # una sola semilla por nombre de clase
php bin/seed --list
php bin/seed --validate      # revisión estática, sin BD
php bin/seed --verify-demo   # cobertura de datos por módulo
```

Alias heredados: `all`→`completo`, `modules`→`modulos`, `demo`→`completo`, `demo-complete`→`completo`.

### Semillas (39)

| Grupo | Ficheros |
|---|---|
| `0xx` núcleo | `010_PeriodosSeeder`, `011_RolesSeeder`, `012_UsuariosSeeder`, `013_PermisosSeeder`, `014_ConfiguracionesSeeder` |
| `0xx` catálogos | `020_SedesSeeder`, `021_CargosSeeder`, `022_AreasSeeder`, `023_TiposContratoSeeder`, `024_CecoSeeder`, `025_FestivosSeeder` |
| `0xx` personas | `030_ColaboradoresSeeder`, `031_ColaboradorEducacionSeeder`, `032_ColaboradorExperienciaSeeder` |
| `0xx` políticas | `040_PoliticasVacacionesSeeder` |
| `0xx` reclutamiento | `050_ReclutamientoEtapasPipelineSeeder`, `051_ReclutamientoMotivosRechazoSeeder`, `052_ReclutamientoVacantesSeeder`, `053_ReclutamientoFlujoDemoSeeder`, `055_ReglasFiltroVacanteSeeder`, `056_EtapasEmbudoVacanteSeeder` |
| `1xx` módulos | `105_ColaboradoresDemoSeeder`, `110_Evaluacion360Seeder`, `115_PlanesDesarrolloSeeder`, `120_NominaSeeder`, `125_AsistenciaSeeder`, `130_ServicioColaboradorSeeder`, `135_LineaEticaSeeder`, `140_CapacitacionSeeder`, `145_CarreraTalentoSeeder`, `150_MapaTalentoSeeder`, `155_MetasCategoriasSeeder`, `160_ComunicacionInternaSeeder`, `165_BeneficiosSeeder`, `170_AdelantosNominaSeeder` |
| `2xx` demo | `210_ModulesDemoSeeder`, `220_CompaniaRealColombiaSeeder`, `230_SolucionesAndinasSeeder` |
| Sin prefijo | `DemoDataSeeder.php` (**no participa en ningún perfil**; solo se carga si se invoca por nombre de clase) |

### Clases de soporte (`database/seeders/support/`)

| Clase | Responsabilidad |
|---|---|
| `Seeder` | Clase base. `upsert($table, $rows, $key)`, `idBy()`, `tableExists()`, `columnExists()`, `columns()`, `getIds()`, `getFirstExistingId()`, `conJefesDerivados()` |
| `ClavesDeNegocio` | Mapa tabla → columna natural que identifica una fila. `tests/ClavesDeNegocioTest` verifica que cada clave declarada corresponde a un **índice único real** del esquema |
| `SeederManifest` | Perfiles y resolución del orden por prefijo |
| `SeederPipeline` | Ejecuta la lista, informa ejecutadas / omitidas / fallidas, `exigirExito()` |

**Reglas verificadas por las pruebas:**
- La escritura pasa siempre por `Seeder::upsert()` — ninguna semilla puede redeclarar el helper (`Evaluacion360SeederSafetyTest::testNingunaSemillaRedeclaraElHelperDeEscritura`).
- La clase base **no** usa `ON DUPLICATE KEY UPDATE`.
- La semilla de Evaluación 360 no borra datos del periodo ni usa rangos de Id reservados.

---

## 12. Índices

El esquema **sí está indexado** en las columnas de filtrado habituales, incluso en tablas sin claves foráneas. Inventario real de las tablas más consultadas:

| Tabla | Índice | Columnas | Tipo |
|---|---|---|---|
| `colaboradores` | `uk_colaboradores_no_documento` | `NoDocumento` | UNIQUE |
| `colaboradores` | `uk_colaboradores_correo_corporativo` | `CorreoCorporativo` | UNIQUE |
| `colaboradores` | `uk_colaboradores_usuario` | `Usuario` | UNIQUE |
| `colaboradores` | `idx_colaboradores_idcargo` | `IdCargo` | INDEX |
| `colaboradores` | `idx_colaboradores_estado` | `Estado` | INDEX |
| `usuarios` | `Usuario` | `Usuario` | UNIQUE |
| `usuarios` | `Tipo` | `Tipo` | INDEX |
| `permisos` | `IdRol_ModuleName` | `IdRol, ModuleName` | INDEX |
| `metas` | 6 índices | `IdColaborador`, `IdPeriodo`, `IdCiclo`, `IdParentMeta`, `EstadoMeta`, `TipoMeta`, `FechaEnvioRevision` | INDEX |
| `metas_consolidado` | 2 índices | `IdColaborador`, `IdPeriodo` | INDEX |
| `evaluaciones` | 2 índices | `IdColaborador`, `IdPeriodo` | INDEX |
| `respuestas` | `uk_respuesta_unica` | `IdPeriodo, IdColaborador, IdPregunta, Tipo, IdCalificador` | **UNIQUE** |
| `respuestas` | 5 índices | `IdPeriodo`, `IdColaborador`, `IdPregunta`, `Tipo`, `IdCalificador` | INDEX |
| `solicitudes_vacaciones` | 3 índices | `IdColaborador`, `Estado`, `(FechaInicio, FechaFin)` | INDEX |
| `nomina_liquidaciones` | `uq_nomina_liquidaciones_periodo_colaborador` | `PeriodoId, ColaboradorId` | **UNIQUE** |
| `nomina_liquidaciones` | 2 índices | `PeriodoId`, `Estado` | INDEX |

Además, MySQL crea automáticamente un índice sobre la columna de cada una de las 75 claves foráneas declaradas.

**Índices únicos que expresan reglas de negocio:**
- `respuestas.uk_respuesta_unica` — un calificador solo puede responder **una vez** cada pregunta de un colaborador en un periodo y para un tipo de fuente.
- `nomina_liquidaciones.uq_...` — **una sola liquidación** por colaborador y periodo. Es lo que hace idempotente a `NominaCalculoModel::recalcularPeriodo()`.
- Los tres únicos de `colaboradores` — documento, correo corporativo y usuario no se repiten.

`database/seeders/support/ClavesDeNegocio.php` declara la columna natural de cada tabla, y `tests/ClavesDeNegocioTest::testCadaClaveCoincideConUnIndiceUnicoDelEsquema` verifica que **cada clave declarada corresponde a un índice único real**. Al añadir una tabla con semilla, hay que registrar su clave allí y garantizar el índice único.

## 13. Cómo consultar el esquema desde el código

```php
// Estructura de una tabla
$columnas = MetasModel::describeTable();   // DESCRIBE metas

// Conexión cruda
$conn = DB::getConnection('klee');
$filas = $conn->queryAllSql("SELECT … FROM …");
```

Desde la línea de comandos:

```bash
php bin/migrate status
php bin/seed --validate
mysql -u <user> -p kuorum -e "SHOW CREATE VIEW vista_colaboradores\G"
```

---

## Documentos relacionados
- [04_MODULES.md](04_MODULES.md) — qué módulo usa cada tabla
- [10_BUSINESS_LOGIC.md](10_BUSINESS_LOGIC.md) — reglas sobre estos datos
- [14_DEVELOPMENT_GUIDELINES.md](14_DEVELOPMENT_GUIDELINES.md) — cómo modificar el esquema
- [18_KNOWN_ISSUES.md](18_KNOWN_ISSUES.md)
