Motor del Código de Trabajo
El corazón determinista de Axel, un motor puro sin DB que calcula finiquitos y valida contratos con citas legales, y que nunca toca un LLM.
El motor del Código de Trabajo es la feature diferenciadora de Axel: la “verdad” jurídica sobre la que el LLM (la “intención”) se apoya. Entra un DTO de contrato más un corpus de reglas, sale un resultado con montos, citas legales y trazabilidad.
Motor puro, sin DB
El motor vive en app/Domain/Labor/: PHP puro, cero dependencias de Laravel,
Eloquent o HTTP. Recibe arrays / DTOs y devuelve arrays / DTOs, como el PoC. Los
modelos Eloquent son la capa de persistencia que hidrata y persiste, pero la
lógica vive en Domain.
app/Domain/Labor/
ContractKinds.php — KINDS, kindOf(), isLaboralHiring()
ValidationRules.php — DEFAULT_RULES, rulesFor($type, $overrides)
Citations.php — CT_ARTICULOS (data) + corpus curado de sentencias CSJ
AnalyzeContract.php — el motor: findings, verdict, citas
SlaEngine.php — slaInfo(), pendingSlaEvent()
TerminationCalc.php — finiquito (Arts. 58/59/177/187/196-202)
Employees.php — deriva empleados desde contratos (sin store aparte)
Mtps.php — ventana de 8 días, feriados SV, computus
Geography.php — departamentos / municipios
Qué calcula
TerminationCalc implementa la matemática de los Arts. 58/59/177/187/196-202
del Código de Trabajo: indemnización, tope de 4× salario mínimo, vacaciones,
aguinaldo proporcional.
AnalyzeContract produce findings deterministas (“salario por debajo del Art.
144”, “prueba excede Art. 28”) con su verdict y las citas legales que lo
fundamentan.
Parametrizado por jurisdicción
El MVP carga solo el contenido legal de El Salvador, pero el motor nace parametrizado por jurisdicción desde el día 1: las reglas laborales son datos, no constantes fijas en el código.
CT_ARTICULOS, las ValidationRules, los parámetros de TerminationCalc, los
feriados de Mtps y la Geography se cargan indexados por jurisdicción (sv
hoy). Agregar Guatemala mañana es cargar un corpus nuevo (gt), no reprogramar
el motor. El disparador de graduar app/Domain/Labor/ a un paquete Composer:
cuando entre la 2ª jurisdicción y se quiera versionar el motor aparte del deploy.
Contract como aggregate root
Contract es un aggregate root: las mutaciones pasan por métodos que disparan
eventos, nunca por un update crudo de status.
Cada transición escribe una fila en contract_events (tipo, actor, meta, ts).
No hay accessor de escritura para status: solo métodos de transición. La
auditoría es completa por construcción.
Los empleados se derivan de los contratos firmados (Employees), no viven
en un segundo store. La tenura y el pasivo laboral se computan desde el
contrato, no se duplican.
Generación del documento: mail-merge legal
El dinamismo vive en la selección de bloques pre-validados, no en la generación de texto libre. El generador combina una plantilla + cláusulas atómicas + datos variables, como un mail-merge legal. Nunca prosa inventada.
El modelo de datos son dos capas con dueños distintos:
| Capa | Tabla | Dueño |
|---|---|---|
Global (sin tenant_id) | clauses (code, legal_text, legal_citation, requires_fields, version) | El curador legal, vía backoffice |
Por tenant (con tenant_id) | positions (los cargos de cada empresa) | Cada empresa piloto |
Por tenant (con tenant_id) | position_clause (qué cláusulas globales aplican a cada cargo) | Cada empresa piloto |
Las cláusulas legales son transversales (derecho salvadoreño, igual para todos),
así que viven en la tabla global sin tenant_id. Las posiciones/cargos son
propias de cada tenant. El pivote position_clause vive en el tenant pero apunta
a cláusulas del central: cada empresa combina su cargo con las cláusulas legales
que le aplican.
Escala a 100+ contratos sin volverse inmanejable porque no hay una fila por “tipo de contrato”: hay ~30-50 cláusulas atómicas reutilizables, combinadas de formas distintas. Un contrato nuevo típicamente reutiliza el 80% de cláusulas existentes y aporta 1-2 nuevas. El catálogo escala con cláusulas (decenas), no con contratos (cientos).
DocumentGenerator toma la plantilla base + las cláusulas del cargo (con sus
condiciones evaluadas) + los datos del contrato, e interpola y concatena, sin
generación libre de texto.
Tests golden
Los tests del motor usan finiquitos calculados a mano por abogados como fixtures. Motor puro = tests corren en milisegundos sin DB, en cada commit. Para la pieza donde un error es daño legal, no es negociable.
El documento generado se compara por equivalencia de contenido legal (mismos datos, cálculos y texto de cláusula), no byte a byte: el formato/maquetación puede diferir del PoC.
Una vez especificado, el motor determinista se replica en meses. El moat real es el corpus jurídico-laboral salvadoreño curado (incluye el corpus curado de jurisprudencia clave de la CSJ vinculado a cada cálculo), el producto integrado y la distribución. Ver Capa LLM para cómo el copilot cita ese corpus sin inventar.