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.


1

Flujo en una mirada

Health advisory POST task ¿Gate ERROR?

409 — no encola

No
Cola Precheck CP-SAT Admin hours done

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 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)

2

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 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).


3

Soft constraints (objetivo CP-SAT)

Si se violan → el horario igual se genera. Todos los softs de esta tabla pesan en el objetivo CP-SAT. El panel de avisos al admin solo muestra un subconjunto accionable — ver §7.

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 Qué desalienta / prefiere Instrumentado
avoid_cannot 600 critical Asignar en franja CANNOT del docente (o materia si llega al soft)
avoid_same_day_subject_pairs 600 critical Pares noDayMatchSubjectIds el mismo día (mismo grupo)
avoid_related_subject_adjacency 600 critical Materias “familia” (ej. Biología / Lab. Biología) en SESSION consecutivos
minimize_teacher_gaps 80 warning Huecos interiores SESSION del docente (crédito admin en Fase 1; métrica final en log post Fase 2)
separate_extra_session 80 warning Singleton de overflow demasiado cerca del bloque doble (max(0, 3−d))
minimize_teacher_load_variance 80 warning Desviación de carga diaria del docente vs promedio en días balanceables
avoid_room_contention 80 warning Compartir aula aunque groupsCapacity lo permita
avoid_related_subject_same_day 80 warning Pares de familia derivados (mismo grupo) el mismo día; flag DERIVE_NO_DAY_MATCH_FROM_FAMILIES
prefer_can_over_maybe 10 info Preferir CAN sobre MAYBE
avoid_pair_across_break 10 info Bloque doble que cruza un BREAK en el eje del timeframe
spread_lesson_days 10 info Lección activa en dos días calendario consecutivos (reemplaza stub balance_subjects_per_day)
prefer_room_backup_tier 10 info Usar aulas de backupRooms (tier > 0)

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

4

Validaciones de health (hoy)

Fuente: kampus-apiConfigurationHealthService.
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.timeFrameIdtimeFramesById[id]

Hoy los cursos de una etapa suelen compartir el mismo TF; el modelo ya soporta multi-TF por etapa.


5

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)

6

Failure reports

Emitido en PATCH failed. Schema v1. UI: modal checklist (title / detail / recommendations / metrics opcionales).

Ejemplo de UI

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 Faltan días con franja doble
NO_COMPATIBLE_GROUP_SLOTS precheck A veces Multi-grupo sin slots comunes
LESSON_QUOTA_UNSATISFIABLE precheck Cupo multi-grupo imposible
ROOM_WINDOW_CONTENTION precheck / solver Demasiados grupos a la vez en un aula (1 ítem por aula)
ROOM_CAPACITY_SHORTAGE solver Fallback de capacidad de aula
TEACHER_PERIOD_SHORTAGE solver (o legacy precheck) Docente sin periodos de reloj suficientes
SOLVER_UNSAT solver No UNSAT genérico sin diagnóstico estructurado
INSUFFICIENT_ADMIN_HOUR_SLOTS admin_hours No caben todas las horas admin
SUBJECT_ROOM_CONFIGURATION processing No rooms/backupRooms inválidos (payload stale)
PROCESSING_ERROR processing No Error inesperado

7

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: []). totalPenalty = suma de weight de las violaciones del payload; optimalityGap / solveTimeMs son métricas del solve.

Ejemplo de UI

Avisos del horario

  • Crítica

    María López quedó el martes en la franja de las 10:00 (Matemáticas, 5°A), en un bloque donde indicó no estar disponible.

+ 5 tipos más en el ejemplo ampliado

Ejemplo del panel con los tipos de aviso que puede ver el admin.

Avisos del horario

Tipos de aviso · 6

TodasCríticas (3)Advertencias (1)Informativas (2)
  • Crítica

    María López quedó el martes en la franja de las 10:00 (Matemáticas, 5°A), en un bloque donde indicó no estar disponible.

    Ir al bloqueVer horario del profesor
  • Crítica

    “Matemáticas” y “Lengua” quedaron el mismo día (miércoles) en 4°B, aunque preferías separarlas.

    Ir al bloque
  • Crítica

    “Historia” y “Geografía” quedaron en franjas seguidas el viernes en 2°B.

    Ir al bloque
  • Advertencia

    “Matemáticas” y “Cálculo” (materias relacionadas) quedaron el mismo día (jueves) en 6°A.

    Ir al bloque
  • Informativa

    María López quedó en 5°A el jueves en la franja de las 11:00 (Inglés), en una franja donde solo marcó disponibilidad parcial.

    Ir al bloqueVer horario del profesor
  • Informativa

    “Educación Física” en 4°C usó un aula de respaldo (“Patio cubierto”) el viernes en la franja de las 08:00.

    Ir al bloque
Campo top-level Uso
totalPenalty Suma de weight de violations[] (0 si vacío)
optimalityGap Gap relativo del solve (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
avoid_related_subject_same_day warning Bool same-day (familia derivada)
prefer_can_over_maybe info Vars en MAYBE
prefer_room_backup_tier info Opción con tier backup > 0

8

Matriz de decisión — ¿dónde se corta?

Situación ¿Cola? ¿Solver? Superficie
Health ERROR No No 409 / health UI
Health WARNING Banner advisory
Precheck fail No failureReport precheck
Solver UNSAT Sí (sin solución) failureReport solver
Admin hours fail Sí (clases OK) failureReport admin_hours
Éxito limpio done + reporte vacío
Éxito con softs 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

9

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-13 §7: solo tipos de aviso del payload admin (sin columna Reportable); mock UI alineado
2026-07-13 Soft report: filtro admin en worker; totalPenalty = suma del payload
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