Generación de horarios
Cheatsheet para ubicar hard/soft constraints, validaciones de health, precheck, failure reports y violaciones soft en un solo recorrido.
- Hard / bloquea / falla
- Warning / no bloquea
- Soft / preferencia
- Éxito / done
Tipo: cheatsheet operativa (análisis rápido)
Última revisión: 2026-07-11
Vista HTML: schedule-generation-overview.html (npm run docs:schedule-overview-html)
Detalle de flujo: schedule-generation-flow.md
Contrato de validación: schedule-generation-validation.md
Solver interno: kampus-schedule-generator-worker/docs/architecture.md
Documento de consulta rápida. Usa tablas para localizar dónde vive cada regla, qué bloquea y qué reporte sale. Para contratos profundos (V-04/V-05, copy H1, SSE), sigue los enlaces de arriba.
Flujo en una mirada
409 — no encola
Fallo en precheck / solver / admin → failureReport
| Paso | Repo | ¿Bloquea encolado? | Resultado si falla |
|---|---|---|---|
| Health advisory (GET) | API → UI | No | Banner / lista de issues |
| Health gate (POST) | API | Sí (solo ERROR) | 409 + issues[] |
| Payload integrity | API | Sí | 409 / rechazo |
| Precheck core + extended | Worker | No | failureReport phase: precheck |
| CP-SAT clases (hard + soft) | Worker | No | failureReport phase: solver |
| Admin hours Phase-2 | Worker | No | failureReport phase: admin_hours |
| Éxito | Worker → API → UI | — | Schedule + constraintViolationsReport |
status task |
Significado |
|---|---|
queued |
Encolada |
processing |
Worker corriendo |
done |
Schedule creado (+ violaciones soft opcionales) |
failed |
Precheck / solver / admin / error (+ failureReport) |
Hard constraints (CP-SAT clases)
Si se violan → no hay solución (SOLVER_UNSAT o precheck previo). No tienen peso: son factibilidad.
| # | Constraint | Qué impone | Módulo |
|---|---|---|---|
| H1 | Cupo semanal | Exactamente sessionsPerWeek sesiones por lección |
model_builder.py |
| H2 | Unicidad de grupo | A lo sumo 1 clase por grupo/slot | model_builder.py |
| H3 | Unicidad de docente | A lo sumo 1 clase por docente/periodo de reloj | model_builder.py |
| H4 | Solape cross-TF docente | Mismo docente no en periodos de reloj que se solapan (multi-etapa) | model_builder.py |
| H5 | Subject CANNOT |
Slot marcado CANNOT en materia → var = 0 | model_builder.py |
| H6 | Pair-block | Distribución pair-first: máximo run 2 consecutivos; días activos = ceil(n/2); overflow/n≥10 exige par por día |
pair_constraints.py |
| H7 | Capacidad de aula | Σ grupos concurrentes ≤ groupsCapacity por periodo |
model_builder.py |
| H8 | Solape cross-TF aula | Misma capacidad en periodos de reloj que se solapan | model_builder.py |
| H9 | Opciones de aula | Si hay rooms (n>0): exactamente n aulas distintas de rooms ∪ backupRooms (XOR de opciones) |
room_options.py + builder |
Notas rápidas
| Tema | Comportamiento |
|---|---|
Docente CANNOT |
No es hard: es soft avoid_cannot (peso alto) |
Materia CANNOT |
Sí es hard (H5) |
| Multi-grupo sincronizado | Una sola var de asignación para todos los grupos de la lección |
| Grid del grupo | course.timeFrameId → un solo timeframe por grupo |
Hard — Phase-2 (horas admin)
| # | Constraint | Qué impone |
|---|---|---|
| A1 | Cupo admin | Todas las sesiones requeridas por (adminHour, teacher) |
| A2 | No solape con clases | Periodos de clase del docente son bloque duro |
| A3 | Unicidad admin | A lo sumo 1 bloque admin por docente/periodo (incluye solapes) |
| A4 | Gap fill duro | Mientras haya hueco interior SESSION rellenable, no colocar en borde |
Si falla → INSUFFICIENT_ADMIN_HOUR_SLOTS (phase: admin_hours).
Soft constraints (objetivo CP-SAT)
Si se violan → el horario igual se genera; aparecen en constraintViolationsReport (excepto stubs no instrumentados).
Pesos por defecto (override por env SOFT_WEIGHT_*). Jerarquía: 600 (nivel 4 / critical) → 80 (nivel 3 / warning) → 10 (nivel 2 / info). Explícitos de nivel 4 pesan más que derivados de familia (nivel 3).
| Constraint | Peso default | Severidad en reporte | Qué desalienta / prefiere | Instrumentado |
|---|---|---|---|---|
avoid_cannot |
600 | critical | Asignar en franja CANNOT del docente (o materia si llega al soft) | Sí |
avoid_same_day_subject_pairs |
600 | critical | Pares noDayMatchSubjectIds el mismo día (mismo grupo) |
Sí |
avoid_related_subject_adjacency |
600 | critical | Materias “familia” (ej. Biología / Lab. Biología) en SESSION consecutivos | Sí |
minimize_teacher_gaps |
80 | warning | Huecos interiores SESSION del docente (crédito admin en Fase 1; reporte = huecos finales post Fase 2) | Sí |
separate_extra_session |
80 | warning | Singleton de overflow demasiado cerca del bloque doble (max(0, 3−d)) |
Sí |
minimize_teacher_load_variance |
80 | warning | Desviación de carga diaria del docente vs promedio en días balanceables | Sí |
avoid_room_contention |
80 | warning | Compartir aula aunque groupsCapacity lo permita |
Sí |
avoid_related_subject_same_day |
80 | warning | Pares de familia derivados (mismo grupo) el mismo día; flag DERIVE_NO_DAY_MATCH_FROM_FAMILIES |
Sí |
prefer_can_over_maybe |
10 | info | Preferir CAN sobre MAYBE | Sí |
avoid_pair_across_break |
10 | info | Bloque doble que cruza un BREAK en el eje del timeframe |
Sí |
spread_lesson_days |
10 | info | Lección activa en dos días calendario consecutivos (reemplaza stub balance_subjects_per_day) |
Sí |
prefer_room_backup_tier |
10 | info | Usar aulas de backupRooms (tier > 0) |
Sí |
Monitoreo: si en colegios reales avoid_same_day_subject_pairs a 600 sacrifica demasiado otras preferencias, bajar a nivel 3 (80).
Soft — Phase-2 (orden de prioridad)
| Prioridad | Objetivo | Notas |
|---|---|---|
| 1 | Rellenar huecos interiores SESSION | Reward dominante (~10000) + hard A4 |
| 2 | Preferir CAN sobre MAYBE | |
| 3 | Repartir sesiones en la semana | Por work item y por docente |
| 4 | Orden determinista de slots | Reproducibilidad |
Validaciones de health (hoy)
Fuente: kampus-api → ConfigurationHealthService.
Advisory: GET /institutions/configuration-health.
Gate: mismo set ERROR en POST /schedules/generation-tasks.
ERROR — bloquean generación
| Code | Qué valida | Ámbito |
|---|---|---|
COURSE_MISSING_TIMEFRAME |
Curso con grupos sin timeFrameId |
Curso |
COURSE_TIMEFRAME_INVALID |
timeFrameId no existe en DB |
Curso |
SUBJECT_NO_SCHEDULABLE_SLOTS |
Materia sin slots SESSION utilizables en el TF del curso | Materia × TF |
SUBJECT_SESSIONS_EXCEED_ALLOWED_SLOTS |
sessionsPerWeek > slots permitidos |
Materia × TF |
ASSIGNMENT_NO_COMMON_SLOTS |
Teacher ∩ subject = vacío | Asignación × TF |
ASSIGNMENT_INSUFFICIENT_COMMON_SLOTS |
Slots comunes insuficientes (single-group; multi-grupo parcial → worker) | Asignación |
TEACHER_DEMAND_EXCEEDS_STAGE_CAPACITY |
Demanda del docente > capacidad de la etapa | Docente × etapa |
GROUP_DEMAND_EXCEEDS_CAPACITY |
Demanda del grupo > slots del TF del curso | Grupo |
TEACHER_ADMIN_HOUR_SESSIONS_EXCEED_ALLOWED_SLOTS |
Horas admin no caben en slots elegibles | Admin hour |
TEACHER_ADMIN_HOUR_REFERENCES_INVALID_TIMEFRAME |
Admin hour apunta a TF inválido | Admin hour |
SUBJECT_ROOM_POOL_INSUFFICIENT |
Pool rooms ∪ backupRooms insuficiente para n primarias |
Materia |
SUBJECT_ROOM_CONFIGURATION |
Duplicados o solape entre rooms / backupRooms |
Materia |
WARNING — no bloquean (health-only)
| Code | Qué valida | Notas |
|---|---|---|
TEACHER_CROSS_STAGE_OVERLOAD |
Demanda global del docente > periodos SESSION de reloj únicos (CAN/MAYBE) | Espejo de capacidad worker; multi-etapa |
SHARED_ROOM_POOL_PRESSURE (V-06) |
Throughput semanal mismo-día en pool de aulas compartido | Heurística; no es V-05 |
DUPLICATE_SUBJECT_IN_GROUP (V-08) |
Dos Subjects distintos con mismo nombre normalizado en un grupo | Warning de configuración |
Modelo course-centric
Toda regla de slots/capacidad en health resuelve:
course.timeFrameId → timeFramesById[id]
Hoy los cursos de una etapa suelen compartir el mismo TF; el modelo ya soporta multi-TF por etapa.
Precheck worker (antes de CP-SAT)
No bloquean encolado. Si fallan → task failed + failureReport.
| Code | Capa | Qué detecta |
|---|---|---|
PAIR_BLOCK_CAPACITY |
Core (V-04) | Lección n≥2 sin suficientes días con par consecutivo (post-CANNOT; incluye regla n≥10) |
NO_COMPATIBLE_GROUP_SLOTS |
Extended | Lección multi-grupo con cero slots alineados |
LESSON_QUOTA_UNSATISFIABLE |
Extended | Slots alineados insuficientes para el cupo multi-grupo |
ROOM_WINDOW_CONTENTION |
Extended (V-05) | Grupos concurrentes > groupsCapacity en átomo de reloj (demanda inevitable) |
| Code post-UNSAT | Cuándo |
|---|---|
ROOM_WINDOW_CONTENTION (diagnostic) |
Enrichment tras UNSAT (criterio amplio) |
ROOM_CAPACITY_SHORTAGE |
Fallback si V-05 diagnostic no emitió |
TEACHER_PERIOD_SHORTAGE |
Post-UNSAT (o precheck solo si PRECHECK_EXTENDED_PROFILE=legacy) |
Failure reports
Emitido en PATCH failed. Schema v1. UI: modal checklist (title / detail / recommendations / metrics opcionales).
Reporte de generación fallida
-
“Matemáticas” en 5°A necesita 4 días con franjas dobles; solo hay 2 disponibles.
El marco horario no ofrece suficientes días con al menos una pareja consecutiva de sesione…
Requerido: 4 · Disponible: 2
+ 9 códigos más en el ejemplo ampliado
Ejemplos de los 10 códigos del catálogo (precheck, solver, admin hours y processing).
Shape
| Campo | Uso |
|---|---|
schemaVersion |
1 |
kind |
infeasible | error |
phase |
Ver tabla abajo |
summary |
Conteo corto (Se encontraron N problemas.) |
items[] |
Cards: code, title, detail, recommendations[], metrics? |
Fases
phase |
Origen | Códigos típicos |
|---|---|---|
precheck |
check_feasibility() aborta |
PAIR_BLOCK_CAPACITY, NO_COMPATIBLE_GROUP_SLOTS, LESSON_QUOTA_UNSATISFIABLE, ROOM_WINDOW_CONTENTION |
solver |
CP-SAT UNSAT (+ diagnostics) | SOLVER_UNSAT, ROOM_WINDOW_CONTENTION, ROOM_CAPACITY_SHORTAGE, TEACHER_PERIOD_SHORTAGE |
admin_hours |
Phase-2 no coloca todo | INSUFFICIENT_ADMIN_HOUR_SLOTS |
processing |
Excepción / config stale | PROCESSING_ERROR, SUBJECT_ROOM_CONFIGURATION |
Catálogo de ítems (`items[].code`)
| Code | Phase | metrics? |
Idea en una línea |
|---|---|---|---|
PAIR_BLOCK_CAPACITY |
precheck | Sí | Faltan días con franja doble |
NO_COMPATIBLE_GROUP_SLOTS |
precheck | A veces | Multi-grupo sin slots comunes |
LESSON_QUOTA_UNSATISFIABLE |
precheck | Sí | Cupo multi-grupo imposible |
ROOM_WINDOW_CONTENTION |
precheck / solver | Sí | Demasiados grupos a la vez en un aula (1 ítem por aula) |
ROOM_CAPACITY_SHORTAGE |
solver | Sí | Fallback de capacidad de aula |
TEACHER_PERIOD_SHORTAGE |
solver (o legacy precheck) | Sí | Docente sin periodos de reloj suficientes |
SOLVER_UNSAT |
solver | No | UNSAT genérico sin diagnóstico estructurado |
INSUFFICIENT_ADMIN_HOUR_SLOTS |
admin_hours | Sí | No caben todas las horas admin |
SUBJECT_ROOM_CONFIGURATION |
processing | No | rooms/backupRooms inválidos (payload stale) |
PROCESSING_ERROR |
processing | No | Error inesperado |
Constraint violations report (éxito)
Emitido en PATCH done. Schema v1. UI: toggle Avisos en el header de ScheduleDetail + panel lateral + “Ir al bloque”.
Vacío limpio permitido (violations: []).
Avisos del horario
+ 11 tipos más en el ejemplo ampliado
Ejemplos de los 12 constraintType del catálogo (3 críticas, 5 advertencias, 4 informativas).
| Campo top-level | Uso |
|---|---|
totalPenalty |
Objetivo del solver |
optimalityGap |
Gap relativo (null o 0 si OPTIMAL) |
solveTimeMs |
Tiempo de solve |
summary[] |
Conteo por constraintType |
violations[] |
Avisos individuales con userMessage + deepLink |
constraintType |
Severidad | Fuente |
|---|---|---|
avoid_cannot |
critical | Vars en franjas CANNOT |
avoid_same_day_subject_pairs |
critical | Bool same-day (explícito) |
avoid_related_subject_adjacency |
critical | Bool adjacency |
minimize_teacher_gaps |
warning | 1 aviso por hueco final (clases + admin) |
separate_extra_session |
warning | Bool singleton cerca del par |
minimize_teacher_load_variance |
warning | Bool desviación de carga diaria |
avoid_room_contention |
warning | Bool room sharing |
avoid_related_subject_same_day |
warning | Bool same-day (familia derivada) |
prefer_can_over_maybe |
info | Vars en MAYBE |
avoid_pair_across_break |
info | Bool par sobre recreo |
spread_lesson_days |
info | Bool días consecutivos activos |
prefer_room_backup_tier |
info | Opción con tier backup > 0 |
Matriz de decisión — ¿dónde se corta?
| Situación | ¿Cola? | ¿Solver? | Superficie |
|---|---|---|---|
| Health ERROR | No | No | 409 / health UI |
| Health WARNING | Sí | Sí | Banner advisory |
| Precheck fail | Sí | No | failureReport precheck |
| Solver UNSAT | Sí | Sí (sin solución) | failureReport solver |
| Admin hours fail | Sí | Sí (clases OK) | failureReport admin_hours |
| Éxito limpio | Sí | Sí | done + reporte vacío |
| Éxito con softs | Sí | Sí | done + constraintViolationsReport |
API ↔ worker (atajos)
| Escenario | API | Worker |
|---|---|---|
| Curso sin TF / demanda grupo / overload etapa / assignment vacío | 409 | No llega |
Pair-day imposible (n≥2) |
Encola | PAIR_BLOCK_CAPACITY |
| Multi-grupo parcial / cero compatible | Encola | LESSON_QUOTA_* / NO_COMPATIBLE_* |
| Contención concurrente de aula | Encola | ROOM_WINDOW_CONTENTION (V-05) |
| Pressure semanal de pool (V-06) | WARNING | Sin equivalente |
| Materia duplicada en grupo (V-08) | WARNING | Sin equivalente |
| Docente sobrecargado multi-etapa | WARNING CROSS_STAGE | Puede acabar en UNSAT + TEACHER_PERIOD_SHORTAGE |
Mapa de archivos clave
| Área | Dónde |
|---|---|
| Health rules | kampus-api/src/modules/configuration-health/rules/ |
| Health codes | .../constants/configuration-health-codes.constants.ts |
| Enqueue + payload | kampus-api schedules generation tasks service |
| Precheck | worker/.../scheduling/feasibility/ |
| Hard + soft CP-SAT | worker/.../scheduling/solver/ + soft_constraints/ |
| Admin hours | worker/.../scheduling/admin_hours*.py |
failureReport |
worker/.../queues/schedule_generator/failure_report.py |
constraintViolationsReport |
worker/.../queues/schedule_generator/constraint_violations_report.py |
| Modal failure UI | schedule-manager/.../failure-report-modal/ |
| Toggle Avisos + panel softs | schedule-manager/.../schedule-detail/ + constraint-violations-* |
Changelog
| Date | Change |
|---|---|
| 2026-07-12 | Polish visual HTML: layout flat, tipografía, tablas y previews más legibles |
| 2026-07-12 | Previews UI: mocks con catálogo completo §6 (10 códigos) y §7 (12 constraintType) |
| 2026-07-11 | Soft weights final: defaults 600/80/10 y severidades alineadas (nivel 4 critical → 2 info); monitoreo same-day a 600 |
| 2026-07-11 | Soft distribution epic (KMP-111–116): nuevos softs instrumentados; stubs balance_subjects_per_day / variance reemplazados |
| 2026-07-11 | Created — cheatsheet ágil: flujo, hard/soft, health, failure + violations reports |
| 2026-07-11 | HTML interactivo permanente: docs/schedule-generation-overview.html vía npm run docs:schedule-overview-html |