Parte V · En la práctica
Una operación minera
Casi todo lo que modelamos hasta aquí lo hizo alguien. En una mina, la mitad de lo que importa no lo hizo nadie: la roca cede, el agua se contamina, un sensor dispara una alarma a las tres de la madrugada. Es el dominio donde las preguntas tienen que sostener lo físico.
Son las 03:14 de un martes en el Yacimiento San Marcos, a 4.250 metros sobre el nivel del mar. No hay nadie en el Frente C del Banco 04: el turno de noche extrae en el tajo contiguo y la zona está vacía hasta el amanecer. Y, sin embargo, algo ocurre. En la Estación 7, junto a la quebrada que baja del tajo, un sensor de calidad de agua toma su lectura de rutina (una cada quince minutos, sin descanso, desde que se instaló) y registra 0,34 miligramos de arsénico por litro. El umbral que la norma ambiental tolera es 0,10. El número rojo no espera a que llegue un humano: dispara un evento, el evento engancha con una norma, la norma obliga a un reporte, y el reporte queda listo para viajar al organismo regulador antes de que el supervisor termine su turno. Nadie tomó esa decisión. La tomó el grafo, porque los hechos estaban tendidos de antemano para que la tomara.
Esa escena resume lo que distingue a este dominio de los siete que lo preceden. Hasta ahora, cada evento que modelamos tenía detrás una voluntad: alguien vendía, alguien pagaba, una médica diagnosticaba, un funcionario emitía una ordenanza. Aun cuando el actor no era humano (la app del taxi asignando un conductor, el motor antifraude del banco marcando una operación) había una intención programada empujando la acción. Una operación minera de gran escala desmonta esa comodidad. En la mina pasan continuamente cosas que ningún agente «hace»: la roca se desprende de la pared, el suelo cede, una junta geomecánica se debilita con el tiempo, una vena de agua subterránea altera la química del río. Son eventos físicos puros, que ocurren porque las condiciones materiales se cumplen, y son tan consecuentes como cualquier acto deliberado: porque cuando esos eventos lesionan a un trabajador o contaminan un cauce, la cadena causal entera tiene que quedar trazada para la auditoría, para la responsabilidad legal y para que no vuelva a ocurrir.
Lo que pone a prueba este capítulo
Un mismo yacimiento nos obliga a sostener, a la vez, cuatro exigencias que ningún capítulo anterior había pedido juntas:
- Eventos sin agente. Lo físico ocurre solo; el modelo debe registrarlo sin inventar un culpable.
- Sensores como agentes. Quien toma la medición no es una persona sino un instrumento que mide cada quince minutos, sin parar.
- Cadenas causales largas. Un accidente se explica retrocediendo de hecho en hecho hasta una condición que precede a todos.
- Trazabilidad de lote y de equipo. Un mineral hay que seguirlo de la roca a la onza; un camión, a lo largo de quince años de estados.
Por qué la mina no se parece a nada anterior
Antes de modelar conviene fijar las cuatro fricciones propias del dominio, porque cada una empuja al modelo en una dirección distinta y todas comparecen a la vez.
Estructura espacial profunda
Una mina no es un lugar: es una jerarquía de lugares. Yacimiento → tajo → nivel → banco → frente. Cinco escalones físicos anidados, cada uno con sus propios metros cúbicos extraíbles y su propia condición geomecánica.
Equipos de vida muy larga
Un camión de acarreo cuesta tres millones de dólares y opera quince años. En ese lapso atraviesa decenas de estados (opera, entra a taller, sale, falla, se repara) que hay que poder reconstruir a cualquier fecha pasada.
Producción en cascada de unidades
Toneladas de mineral, gramos de oro por tonelada de ley, onzas troy de oro fino. Tres unidades distintas, encadenadas por multiplicación, y cada cifra debe conservar su semántica y su trazabilidad hacia atrás.
Eventos automáticos y umbrales
Sensores miden aire y agua sin pausa; cuando un valor cruza el límite normativo, el reporte regulatorio se dispara solo. La operación corre 24/7 y los hechos se acumulan estén o no presentes las personas.
Repara en la asimetría. En el spa, los hechos llegaban cuando una clienta se acercaba al mostrador; en el banco, cuando alguien ordenaba una transferencia. Aquí los hechos llegan aunque no haya nadie: el sensor mide a las tres de la mañana, la pared se debilita mientras todos duermen, el camión registra horas de motor en un turno desierto. El grafo de una mina respira solo. Esa es la novedad que el modelo tiene que absorber sin doblarse.
El yacimiento sobre los siete ejes
El primer trabajo de quien modela es repartir las entidades del negocio en las coordenadas correctas. La decisión se paga después: si una cosa cae en el eje equivocado, el sistema cojea para siempre. Así se acomoda el Yacimiento San Marcos.
Q conviven personas y un sensor de agua, porque la pregunta «quién
tomó esta medición» admite que la respuesta sea un instrumento. Segundo: en O
aparecen eventos como el desprendimiento que carecen por completo de un actor
en Q —son hechos físicos puros—. La banda inferior, el eje M, no
contiene entidades sino los cables que las amarran; aquí el protagonista es
causado_por.Las decisiones de diseño que más se ejercitan aquí ya las conoces de capítulos previos;
este dominio no estrena ninguna, solo las combina con una intensidad nueva. La agencia
contextual (D5) se estira hasta admitir eventos sin agente y sensores como
agente. La descomposición del porqué (D7) reconstruye cadenas causales con
causado_por sin intención, y justifica reportes con justificado_por
apuntando a normas. La vigencia bitemporal (D6) sigue los quince años de
estados de un equipo. Y el patrón de la situación articuladora (el mismo que organizó
el viaje del taxi y la transferencia del banco) aquí organiza el turno.
Caso 1 · La estructura espacial profunda
Empecemos por el espacio, porque todo lo demás ocurre en algún sitio. El nivel más alto es el yacimiento entero. Dentro hay uno o más tajos a cielo abierto. Cada tajo tiene niveles definidos por altitud. Cada nivel se subdivide en bancos (los escalones físicos de la pared del tajo). Y cada banco tiene varios frentes de trabajo activos a la vez. Cinco escalones anidados.
No hace falta inventar nada: es exactamente la subdivisión territorial que ya modelamos en
la municipalidad, con un rol de dominio
dentro_de que la política liberal del catálogo acepta sin protestar. Un cable
L→L que encadena lugar dentro de lugar.
u.assert_fact(tajo_norte, "dentro_de", yacimiento)
u.assert_fact(nivel_4250, "dentro_de", tajo_norte)
u.assert_fact(banco_04, "dentro_de", nivel_4250)
u.assert_fact(frente_c, "dentro_de", banco_04)
La consulta «¿dónde ocurrió exactamente este accidente?» devuelve la cadena entera
con un solo recorrido ascendente sobre el grafo:
Frente C → Banco 04 → Nivel 4250 → Tajo Norte → Yacimiento San
Marcos. Toda la geografía jerárquica disponible para cualquier reporte de seguridad,
cualquier auditoría regulatoria, cualquier análisis de productividad por zona —sin una sola
columna id_tajo, id_nivel, id_banco repetida en cada
tabla que mencione un lugar.
Caso 2 · El camión que vive quince años
Un camión de acarreo CAT 793F entra en servicio en abril de 2018. Su vida útil planeada es de quince años. Durante ese tramo atraviesa muchos estados: opera, entra a mantenimiento programado, sale, vuelve a operar, sufre una falla, se repara, sigue operando. El modelo registra todo ese ciclo con vigencia temporal (D6, la misma maquinaria bitemporal que en el banco siguió la vida de un préstamo y en la universidad la historia de un salario). Cada estado es una tripleta con su rango de validez.
u.assert_fact(camion_007, "estado", operativo,
valid_from=t_alta_2018, valid_to=t_mant_2024)
u.assert_fact(camion_007, "estado", mantenimiento_prog,
valid_from=t_mant_2024, valid_to=t_post_mant)
u.assert_fact(camion_007, "estado", operativo,
valid_from=t_post_mant, valid_to=t_falla_2025)
u.assert_fact(camion_007, "estado", mantenimiento_correctivo,
valid_from=t_falla_2025, valid_to=t_reparado)
u.assert_fact(camion_007, "estado", operativo,
valid_from=t_reparado) # sigue vigente
Cinco tripletas. El historial completo del camión a lo largo de ocho años de operación. La
consulta «¿el camión 007 estaba operativo el 15 de marzo de 2024?» devuelve
mantenimiento_programado; «¿y el 1 de junio de 2026?» devuelve
operativo. La trazabilidad para auditoría es nativa, no un módulo añadido: el
rango valid_from/valid_to es parte del propio hecho, no una columna
que alguien tuvo que recordar mantener.
Por qué esto importa en un activo de tres millones de dólares
Cuando un regulador o una aseguradora pregunta «¿en qué estado estaba
este equipo el día del incidente?», la respuesta no puede ser «depende de qué tabla
mires». El estado vigente a una fecha es una proyección directa del grafo: se filtran las
tripletas estado de camion_007 por la fecha consultada y se
devuelve la única cuyo rango la contiene. Sin lógica de aplicación, sin riesgo de que dos
sistemas satélite discrepen sobre el pasado.
Caso 3 · La producción en cascada de unidades
La minería de oro registra producción en una cascada de unidades convertibles. Una extracción se mide en toneladas métricas de mineral. Ese mineral tiene una ley expresada en gramos de oro por tonelada. Multiplicando ambos se obtiene el oro contenido; tras el proceso metalúrgico quedan las onzas troy de oro fino vendible. Tres unidades distintas, cada una con su semántica, ninguna intercambiable con la otra.
El turno de noche del 19 de mayo extrae 2.480 toneladas de mineral con una ley de 8,6 g/t
en el Frente A. La extracción se reifica como una situación en O (lo merece:
agrupa varios roles, tiene momento y participa en otras relaciones) y se describe con sus
magnitudes tipadas, cada una con su unidad.
extraccion = ingest_situation("extraer", roles={
"agente": operador_quispe,
"extraido": mineral_oro,
"monto": n(2480, "toneladas"),
"unidad": tonelada_metrica,
"lugar_de": frente_a,
"ley_mineral": n(8.6, "g/t"),
"ley_unidad": gramo_por_tonelada,
})
La producción de oro fino no es un campo más de la extracción: es una entidad propia,
modelada como sub-situación parte_de la extracción y calculada
de ella. El cálculo (2.480 t × 8,6 g/t ÷ 31,1 g/oz ≈ 685,8 oz) se asienta
como hecho, y un cable calculado_de apunta hacia atrás, a la extracción de la que
salió.
u.assert_fact(produccion_oro, "parte_de", extraccion)
u.assert_fact(produccion_oro, "monto", n(685.8, "onzas_troy"))
u.assert_fact(produccion_oro, "unidad", onza_troy)
u.assert_fact(produccion_oro, "calculado_de", extraccion)
Esto es lo que en un dominio físico se llama trazabilidad de lote: el grafo nunca
pierde de vista de qué roca salió cada onza. Si mañana alguien pregunta «¿de qué
extracción específica vinieron estas 685,8 onzas?», la respuesta es directa, un solo
salto por calculado_de. Y si se descubre que la ley estaba mal medida y hay que
recalcular, la producción corregida se modela como un hecho nuevo que rectifica al
anterior (el mismo patrón de auditoría que vimos en el rediagnóstico de la clínica y la
rectificación de multa de la municipalidad). El dato viejo no se borra: queda en el grafo con
su historia, porque en minería, igual que en banca, sobrescribir el pasado es perder la
auditoría.
Caso 4 · El turno como entidad articuladora
Una operación minera trabaja 24 horas divididas en turnos de doce. Cada turno reúne un puñado
de operadores, varios equipos y toda la producción y los incidentes que ocurran en ese lapso.
Modelar el turno como entidad articuladora (exactamente como el viaje_001 del taxi
o la transferencia_001 del banco) hace que todo lo que pasa quede colgado del
mismo nodo.
turno = u.add_individual(Individual(id="turno_noche_2026_05_19", axis=Axis.O))
u.assert_fact(turno, "inicio", at("2026-05-19T18:00"))
u.assert_fact(turno, "fin", at("2026-05-20T06:00"))
u.assert_fact(turno, "lugar_de", tajo_norte)
u.assert_fact(turno, "supervisor", supervisor_mamani)
u.assert_fact(turno, "operador_asignado", operador_quispe)
u.assert_fact(turno, "operador_asignado", operador_calle)
u.assert_fact(turno, "operador_asignado", operador_rojas)
Y la extracción, la producción derivada, el accidente, las mediciones ambientales: todos
quedan parte_de el turno.
u.assert_fact(extraccion, "parte_de", turno)
u.assert_fact(accidente, "parte_de", turno)
u.assert_fact(medicion_alta, "parte_de", turno)
La consulta «¿qué pasó en el turno de noche del 19 de mayo?» devuelve la cadena
completa en una sola proyección por parte_de: tres operadores asignados, una
extracción de 2.480 toneladas, un accidente en el Frente A, una medición ambiental sobre el
umbral. Sin tablas, sin joins, sin un esquema turno_operadores con
claves foráneas. Solo el grafo, recorrido hacia abajo desde un nodo.
Caso 5 · La cadena causal sin agente
Aquí llega el caso emblemático del dominio, el que justifica todo el capítulo. A las 23:40 de ese mismo turno, mientras el operador Quispe trabaja en el Frente A del Banco 04, una roca se desprende de la pared y lo golpea. Sufre una contusión en el brazo derecho. La pregunta que la auditoría hará primero es brutal en su simpleza:
¿Quién causó el accidente?La pregunta forense
La respuesta (y la que el grafo registra fielmente) es:
nadie. La roca se desprendió. Nadie la tiró. Pero tampoco se desprendió «de
la nada»: hubo una condición previa, un debilitamiento estructural de la pared del Banco 04
que la inspección geomecánica anterior debió haber detectado. Dos hechos físicos encadenados,
ninguno con autor, y entre ellos el cable que lo explica todo: causado_por.
El modelo lo registra con dos eslabones causales. Lo decisivo es lo que no está: en
ninguno de los dos eventos aparece el rol agente.
# El debilitamiento: una condición geomecánica preexistente
debilitamiento = u.add_individual(Individual(
id="debilitamiento_pared_b04", axis=Axis.O,
label="Debilitamiento estructural Banco 04"))
u.assert_fact(debilitamiento, "instancia_de", category("condicion_geomecanica"))
u.assert_fact(debilitamiento, "lugar_de", banco_04)
# El desprendimiento: evento físico SIN AGENTE, causado por el debilitamiento
desprendimiento = u.add_individual(Individual(id="evento_desprendimiento_07", axis=Axis.O))
u.assert_fact(desprendimiento, "lugar_de", frente_a)
u.assert_fact(desprendimiento, "momento", at("2026-05-19T23:40"))
u.assert_fact(desprendimiento, "causado_por", debilitamiento)
# Nota: NO existe u.assert_fact(desprendimiento, "agente", ...)
# El accidente que sufre Quispe, causado_por el desprendimiento
u.assert_fact(accidente, "paciente", operador_quispe)
u.assert_fact(accidente, "causado_por", desprendimiento)
u.assert_fact(accidente, "parte_de", turno)
La agencia contextual (D5) absorbe esto sin gimnasia: el rol
agente simplemente no aparece en las situaciones físicas, porque la
signatura del verbo no lo exige. Y la descomposición del porqué (D7) deja
que causado_por reconstruya la cadena hacia atrás hasta su origen (la condición
que el sistema geomecánico no detectó). Veámoslo dibujado.
causado_por al desprendimiento, y este al debilitamiento
estructural. Ninguno de los tres tiene agente en Q (el carril superior está
deliberadamente vacío) y, aun así, el grafo permite reconstruir la responsabilidad completa.
La lectura de inclinómetro (en K, con un sensor por agente) cuelga del
debilitamiento como evidencia previa: la deformación de 12 mm que sí se midió
y que la inspección debió interpretar.Cualquier auditor de seguridad recorre esta cadena hacia atrás con un solo barrido sobre el
grafo. «¿Qué causó el accidente?» → el desprendimiento. «¿Y qué causó el
desprendimiento?» → el debilitamiento estructural. «¿Y por qué no se detectó ese
debilitamiento?» → ahí empieza el siguiente capítulo de la investigación, que cruzará la
lectura del inclinómetro con el informe geomecánico. La pregunta forense (¿quién es
responsable?) se vuelve precisa justamente porque el grafo entrega la cadena exacta, no
una explicación diluida en un párrafo de texto libre dentro de un campo
observaciones.
La trampa que el modelo evita
En un esquema relacional, la tentación es resolver «un evento sin autor»
poniendo NULL en la columna id_responsable (o, peor, inventando un
registro ficticio «la naturaleza» para que la clave foránea no se queje). Ambas salidas
mienten: la primera confunde «no hubo agente» con «no sabemos quién»; la segunda contamina el
catálogo de personas con una entrada que no es una persona. WQuestions no necesita ninguna:
el rol agente simplemente no se asienta, y la ausencia del cable es el
dato. No es lo mismo un agente desconocido que un evento que nadie causó, y el modelo sabe
distinguirlos.
Caso 6 · El sensor como agente y el reporte por umbral
Volvamos a la alarma con la que abrió el capítulo. Las operaciones mineras grandes están bajo regulación ambiental estricta. Sensores automáticos vigilan la calidad del aire y del agua en estaciones repartidas por el yacimiento, midiendo concentraciones de contaminantes cada cierto intervalo. Aquí el agente de la medición no es una persona: es el sensor. Y eso, lejos de ser una excepción incómoda, es precisamente lo que la agencia contextual (D5) anticipó.
Agencia no humana
Un sensor es, para el modelo, un habitante de pleno derecho de
Q: ocupa el rol agente en el verbo «medir» igual que el vendedor lo
ocupa en «vender» una camiseta o la cajera en «cobrar». No es una metáfora ni un comodín. La pregunta
«¿quién tomó esta lectura?» tiene una respuesta legítima que no es una persona, y el grafo la
guarda como tal (con su identificador, su estación, su intervalo de muestreo) para que más
tarde se pueda auditar el propio instrumento.
A las 14:00 del 15 de mayo, el sensor de la Estación 7 mide 0,34 mg/L de arsénico en la quebrada que baja del tajo (por encima del umbral normativo de 0,10 mg/L que fija la norma de calidad ambiental). La medición se reifica como cualquier otra situación, con el sensor en el rol de agente.
medicion_alta = ingest_situation("medir_calidad", roles={
"agente": sensor_agua_e7, # un sensor en Q (D5)
"medido_de": arsenico,
"monto": n(0.34, "mg/L"),
"unidad": miligramo_por_litro,
"lugar_de": estacion_07,
"momento": at("2026-05-15T14:00"),
})
La norma ambiental vive en O, con su umbral declarado como un hecho más. No es
un valor escondido en el código de validación: es un dato del grafo, citable y versionable.
norma_ambiental = u.add_individual(Individual(
id="eca_agua_cat3", axis=Axis.O,
label="Estándar de calidad ambiental · agua, categoría 3"))
u.assert_fact(norma_ambiental, "umbral_arsenico", n(0.10, "mg/L"))
Cuando el motor de reglas (que el prototipo aún no trae, y que el capítulo 30 declara como trabajo pendiente) detecta que el valor medido supera el umbral, dispara automáticamente el reporte regulatorio. Y ese reporte ejercita las dos caras del porqué que D7 separa.
reporte = ingest_situation("reportar", roles={
"agente": minera_san_marcos, # la empresa reporta
"tema": medicion_alta, # qué se reporta
"beneficiario": ente_regulador, # a quién
"momento": at("2026-05-15T18:00"),
"motivado_por": medicion_alta, # la medición concreta que disparó
"justificado_por": norma_ambiental, # la norma que lo exige
})
El reporte está motivado_por la medición específica que cruzó el umbral, y
justificado_por la norma que obliga a reportar. El grafo registra ambas
vinculaciones por separado, sin confundirlas. Si dentro de cinco años un auditor pregunta
«¿por qué se emitió este reporte ambiental?», hay dos respuestas precisas y distintas:
por la medición de 0,34 mg/L del 15 de mayo, y porque el estándar de calidad ambiental lo
exigía. La causa concreta y la justificación normativa son cosas diferentes, y el modelo las
mantiene diferenciadas: exactamente el patrón que la municipalidad usó para sus ordenanzas,
ahora con normas ambientales en lugar de decretos.
El pulso del yacimiento
Un dominio intensivo en sensores no produce hechos de a uno: produce series. El sensor de la Estación 7 no tomó una sola lectura el 15 de mayo; tomó una cada quince minutos durante todo el día, y la mayoría fueron perfectamente normales. La alarma es el pico que rompe la rutina. Verlo en el tiempo aclara por qué el umbral importa y por qué el disparo tiene que ser automático: a las 14:00, ningún humano estaba mirando ese número.
Cada uno de esos puntos es, en el grafo, una situación medir_calidad con su
agente, su monto, su unidad, su momento y su lugar. La serie no es una tabla aparte con un
esquema propio de telemetría: es el mismo tipo de hecho atómico que todo lo demás, repetido en
el tiempo. Y porque comparte forma con el resto del grafo, la pregunta «dame todas las
mediciones de arsénico de la Estación 7 que superaron el umbral este mes» es una
proyección sobre los mismos cables que ya usamos para todo, no una consulta a un subsistema
especializado.
Comisionamiento: poner en marcha como secuencia de pruebas
Hasta aquí miramos el yacimiento en operación. Pero antes de que el camión 007 mueva su primera tonelada o de que el sensor de la Estación 7 tome su primera lectura, hubo un proyecto de ingeniería (diseño, procura, construcción, lo que en la industria se abrevia EPC) y, al final de ese proyecto, un momento crítico: el comisionamiento. Comisionar es entregar un equipo o una planta a operaciones, pero no de golpe: es una secuencia ordenada de pruebas y protocolos de aceptación que prueban, uno a uno, que lo construido hace lo que el diseño prometía.
La tentación, en un sistema convencional, es tratar el comisionamiento como un campo de
estado del equipo: comisionado = true. Pero eso tira a la basura justo lo que la
auditoría necesita: cómo se llegó a ese sí, qué pruebas se corrieron, quién las firmó
y cuándo. Cada etapa del comisionamiento es, en realidad, una situación reificada
(D4): merece nodo propio porque tiene estado, responsable y fecha, y participa
en otras relaciones. La planta de procesamiento del Yacimiento San Marcos recorre cuatro fases.
O con su propio estado (cerrada, en_curso,
pendiente), su responsable en Q y su fecha en T, y se
encadena con la siguiente por precede_a. El estado no se aplana a un booleano
«comisionado»: se conserva el camino completo, que es justo lo que la entrega contractual
exige poder mostrar.Cada fase se asienta con la misma maquinaria de cualquier situación: un identificador fresco,
un estado, un responsable que puede ser una persona o el cliente, y una fecha. El cable
precede_a ordena la secuencia, de modo que ninguna fase abre antes de que cierre
la anterior.
# Fase 1 · pre-comisionamiento (cerrada)
prueba_precom = ingest_situation("pre_comisionar", roles={
"objeto_de": planta_procesamiento,
"estado": cerrada,
"responsable": supervisor_mamani,
"momento": at("2026-03-02T17:00"),
})
# Fase 2 · pruebas en vacío y con carga (en curso)
prueba_carga = ingest_situation("probar_aceptacion", roles={
"objeto_de": planta_procesamiento,
"estado": en_curso,
"responsable": ito_vargas, # inspección técnica de obra
"momento": at("2026-03-18T09:00"),
"precede_a": prueba_precom, # va después de la fase 1
})
# Fase 3 · aceptación (protocolo de aceptación, pendiente)
prueba_aceptacion = ingest_situation("aceptar_entrega", roles={
"objeto_de": planta_procesamiento,
"estado": pendiente,
"responsable": minera_san_marcos, # el cliente firma la aceptación
"precede_a": prueba_carga,
})
La pregunta de cualquier gerente de proyecto («¿en qué fase está el comisionamiento de
la planta y quién la tiene a su cargo?») se contesta con una proyección sobre las
situaciones que tienen a planta_procesamiento como objeto_de,
ordenadas por precede_a. Y cuando la fase de aceptación cierre, la planta podrá
recibir su primer estado operativo —exactamente el mismo cable
estado con vigencia (D6) que ya usamos para el camión 007—. El
comisionamiento no es un mundo aparte: es la antesala bitemporal de la vida operativa.
Punchlist: las deudas pendientes antes de la entrega
Ninguna obra se entrega perfecta. Entre las pruebas de carga y la firma de aceptación, el inspector levanta un punchlist: la lista de pendientes, los defectos y remates que faltan resolver antes de que el cliente reciba la planta. Una baranda sin pintar, una bomba que vibra de más, un manómetro descalibrado. Cada ítem del punchlist bloquea —o no— la aceptación según su gravedad, y eso obliga a clasificarlos.
Las tres categorías de un ítem de punchlist
- Categoría A. Impide la operación segura. Bloquea la aceptación: hasta que no se cierre, la planta no se entrega.
- Categoría B. No impide operar, pero debe resolverse en un plazo corto pactado. No bloquea la aceptación; sí la condiciona.
- Categoría C. Remate menor o estético. Se cierra cuando se pueda, sin afectar la entrega.
Cada ítem es una situación reificada: un defecto concreto, con su categoría, su responsable de cierre, su estado abierto o cerrado y (cuando se cierra) su fecha de cierre. No es una fila muerta en una hoja de cálculo: es un nodo del mismo grafo, ligado a la fase de comisionamiento que lo destapó y al equipo o lugar donde vive el defecto.
# Ítem A: bloqueante, todavía abierto
item_punch_a = ingest_situation("registrar_pendiente", roles={
"defecto": "fuga en sello de bomba P-201",
"categoria": categoria_a, # bloquea la aceptación
"objeto_de": planta_procesamiento,
"lugar_de": sala_bombas,
"responsable": ito_vargas,
"estado": abierto,
"detectado_en": prueba_carga, # nació en la prueba con carga
"momento": at("2026-03-18T11:20"),
})
# Ítem C: estético, ya cerrado
item_punch_c = ingest_situation("registrar_pendiente", roles={
"defecto": "baranda nivel 2 sin pintura de seguridad",
"categoria": categoria_c,
"objeto_de": planta_procesamiento,
"responsable": supervisor_mamani,
"estado": cerrado,
"detectado_en": prueba_carga,
"fecha_cierre": at("2026-03-20T16:00"),
})
La gracia es que el punchlist no es una tabla con un esquema propio: es una consulta. «Dame
los ítems abiertos de categoría A de la planta» se proyecta sobre las situaciones
registrar_pendiente con objeto_de = planta_procesamiento,
categoria = categoria_a y estado = abierto. Y la regla de negocio
(no se firma la aceptación mientras quede un ítem A abierto) es una pregunta al grafo,
no un disparador escondido en código de aplicación. Veámoslo como tabla de control.
| Ítem | Defecto | Cat. | Responsable | Estado | Cierre |
|---|---|---|---|---|---|
item_punch_a |
Fuga en sello de bomba P-201 | A | ITO Vargas | abierto | — |
item_punch_b |
Vibración en motor de faja FA-3 | B | ITO Vargas | abierto | — |
item_punch_c |
Baranda nivel 2 sin pintura | C | Mamani | cerrado | 20 mar 16:00 |
registrar_pendiente con su
categoría en K, su responsable en Q, su estado y (si está cerrado)
su fecha en T. Mientras la fila item_punch_a siga
abierta, la fase de aceptación de la Figura 23.4 no puede cerrar: la regla
«sin ítems A abiertos no hay entrega» es una consulta, no un candado oculto.Por qué el punchlist vive mejor en el grafo
En el flujo habitual, el punchlist nace como un Excel que viaja por correo
y muere desconectado de todo lo demás: nadie puede cruzar «¿qué pendientes destapó la prueba
de carga?» con «¿en qué equipo?» sin copiar y pegar. Aquí cada ítem ya está enlazado a la
prueba que lo detectó (detectado_en), al equipo afectado
(objeto_de) y al lugar (lugar_de). El historial de cierres queda en
el mismo tejido que el comisionamiento y, más tarde, que el mantenimiento.
Mantenimiento: órdenes de trabajo sobre el historial del activo
Comisionada la planta y entregada a operaciones, empieza la vida larga del activo (y con ella el mantenimiento). La industria distingue tres modos, y los tres caben en el modelo como órdenes de trabajo ligadas al historial bitemporal (D6) del equipo, exactamente el mismo historial que en el Caso 2 siguió los quince años del camión 007.
Preventivo. Programado por calendario o por horas de uso: cambiar el aceite cada 500 horas, sin que nada haya fallado. La orden nace de un plan, no de un síntoma.
Correctivo. Reacciona a una falla ya ocurrida: la bomba se rompió, hay que repararla. La orden nace de un evento —y ese evento puede ser, como vimos, sin agente.
Predictivo. Se adelanta a la falla leyendo señales: el inclinómetro o el sensor de vibración avisa que algo se degrada antes de romperse. La orden nace de una medición que cruza un umbral: el mismo patrón del sensor del Caso 6.
Una orden de trabajo es una situación más, con su tipo, su activo, su disparador y su estado.
La predictiva es la más interesante porque enlaza con la telemetría que ya modelamos: una
lectura de vibración alta motiva la orden, igual que la medición de arsénico
motivaba el reporte ambiental.
# Orden PREDICTIVA: la dispara una lectura de vibración del propio camión
orden_trabajo_88 = ingest_situation("dar_mantenimiento", roles={
"tipo": predictivo,
"activo": camion_007,
"motivado_por": lectura_vibracion_alta, # señal del sensor (D5)
"responsable": taller_mecanico,
"estado": programada,
"momento": at("2026-06-10T08:00"),
})
# Al ejecutarse, abre un tramo de estado en el HISTORIAL del activo (D6)
u.assert_fact(camion_007, "estado", mantenimiento_predictivo,
valid_from=t_inicio_ot88, valid_to=t_fin_ot88)
u.assert_fact(orden_trabajo_88, "resultado", "rodamiento sustituido")
La orden no flota suelta: cada ejecución abre un tramo en el historial bitemporal del activo,
el mismo estado con valid_from/valid_to del Caso 2. Así,
la pregunta de la aseguradora («¿cuántas horas estuvo el camión 007 en mantenimiento este
año, y por qué entró cada vez?») se contesta cruzando los tramos
mantenimiento_* del historial con las órdenes que los abrieron, sin un sistema de
gestión de mantenimiento aparte.
Y ese historial cubre toda la existencia del activo, no solo su operación. El equipo no aparece de la nada ni desaparece sin rastro: se comisiona, opera, se mantiene y, al final, se da de baja. Cuatro etapas que son cuatro tramos de un mismo eje temporal.
comisionar → operar → mantener → dar de baja. No son cuatro tablas ni cuatro
sistemas, sino cuatro clases de tramo sobre el mismo historial bitemporal (D6)
del equipo. El mantenimiento (en azul) se intercala dentro de la operación como pulsos (cada
uno, una orden de trabajo), y el retiro final cierra el último tramo con su fecha. El estado
vigente a cualquier fecha pasada sigue siendo una proyección directa del grafo, como en el
Caso 2.Dar de baja no borra al equipo: cierra su último tramo de estado y abre uno nuevo
(fuera_de_servicio) con su fecha. El camión 007 seguirá respondiendo, dentro de
diez años, a la pregunta «¿qué estado tenías el día del incidente?», aunque para
entonces ya no exista físicamente. El ciclo de vida completo queda en el grafo, del primer
protocolo de comisionamiento a la última orden de trabajo, sin que ninguna pieza nueva del
modelo haya tenido que inventarse para sostenerlo.
Del camión 007 al parque entero
Reconstruimos la vida de un activo: el camión 007, sus quince años de estados, sus órdenes de trabajo. Pero quien planifica el mantenimiento no decide sobre un camión, decide sobre el parque. Y la pregunta que ordena el presupuesto no es «¿en qué estado está este equipo?», sino «¿qué activo acumula más intervenciones correctivas?» o «¿qué causa raíz reaparece trimestre tras trimestre?». Esas no son consultas de otro sistema: cada falla quedó registrada con su activo y su disparador, y el reporte de confiabilidad es contar esas órdenes correctivas, activo por activo.
# Órdenes correctivas sobre un activo — un corte; el reporte recorre todo el parque
count(u, Pattern(fixed={"activo": u.ind("camion_007"), "tipo": u.ind("correctivo")},
type_constraint=u.ind("dar_mantenimiento")))
Una operación entera, vista como hechos
Conviene detenerse y mirar el conjunto. Lo que en una arquitectura tradicional serían cinco o seis subsistemas con sus propios esquemas (flota, producción, seguridad, ambiental, mantenimiento) aquí es un único grafo de hechos donde todo se cruza por los mismos cables. El turno de noche del 19 de mayo, leído de punta a punta, es una sola constelación.
El turno articula: reúne a Quispe, Calle y Rojas como operadores
asignados, ocurre en el Tajo Norte entre las 18:00 y las 06:00, y lo supervisa Mamani. De
él cuelga, por parte_de, todo lo que sucede.
La extracción aporta la producción: 2.480 toneladas con ley de
8,6 g/t en el Frente A, de las que se calculan 685,8 onzas de oro fino (trazables por
calculado_de hasta la roca original).
El accidente aporta la cadena causal: Quispe como paciente, causado por un desprendimiento, causado a su vez por un debilitamiento estructural —tres eventos físicos sin ningún agente humano.
Y lo ambiental aporta el pulso automático: el sensor de la Estación 7 midiendo arsénico sin descanso, el pico que cruza el umbral y el reporte que se dispara solo, justificado por la norma y motivado por la lectura.
Cuatro mundos que un sistema convencional mantendría en bases separadas, hablándose a duras penas por exportaciones nocturnas, conviven aquí en una sola estructura porque todos responden a las mismas siete preguntas. Quién, qué, dónde, cuándo, cuánto, cuál y cómo no cambian de significado al pasar de la flota a la seguridad o de la producción al medio ambiente. Esa invariancia es justamente lo que permite que la pregunta del supervisor («¿qué pasó anoche?») se conteste de una vez, sin saber de antemano en qué subsistema vivía la respuesta.
El antes y el después: del esquema fragmentado al grafo único
Antes (relacional). Una operación minera tradicional reparte su información
entre tablas separadas (equipos, estados_equipo,
eventos, inspecciones_geomecanicas, mediciones_sensor)
y la primera grieta aparece enseguida: reconstruir la cadena causal de un accidente que no
tuvo autor obliga a cruzar al menos cuatro tablas con joins parciales, y aun así el
esquema no tiene dónde guardar la relación entre el debilitamiento previo y el desprendimiento
posterior sin inventar columnas ad hoc. El historial de estados de un camión a lo
largo de quince años tampoco cabe limpio: la tabla estados_equipo acumula filas
sin semántica de vigencia, y cualquier consulta (¿cuál era el estado exacto del camión el
15 de marzo de 2024?) exige lógica extra que el esquema no provee. Y la medición del
sensor, ¿de quién es? La columna id_operador no admite que el autor sea una
máquina sin estirarse o mentir.
Después (WQuestions). La misma información vive en un único grafo de hechos.
Cada evento físico (el debilitamiento, el desprendimiento, el accidente) es un nodo conectado
al siguiente por causado_por, y la cadena se recorre hacia atrás de un tirón, sin
agente humano en ningún punto. Los quince años de estados del camión son vigencia nativa
(D6): cada tripleta (camion_007, estado, operativo) lleva su
propio valid_from y valid_to, y la consulta por fecha devuelve el
estado correcto sin lógica añadida. El sensor es un agente legítimo en Q
(D5): la pregunta «quién midió» se responde con un instrumento, no con un
rodeo. El modelo no necesita tablas especiales para la causalidad sin culpable, ni historiales
improvisados para equipos de larga vida, ni un esquema de telemetría aparte para los sensores
—los absorbe a todos con la maquinaria estándar.
Qué quedó probado
La minería pone a trabajar a D5 en su forma más radical: hay eventos físicos
puros (el desprendimiento de roca, el debilitamiento de la pared) que ningún agente humano hizo,
y son tan consecuentes como cualquier acto deliberado. El modelo los acepta sin esfuerzo porque
la signatura del verbo físico no exige agente; basta causado_por para reconstruir
la cadena entera hasta su origen. Y el mismo D5, por el otro lado, admite que el autor de una
medición sea un sensor: agencia no humana de pleno derecho.
L Estructura espacial profunda
Cinco escalones anidados se modelan con el mismo
dentro_de de la municipalidad. La política liberal admite el rol de dominio
L→L sin declararlo en el catálogo.
T Equipo de vida larga
Quince años de estados quedan trazables con la vigencia bitemporal de D6, la misma que el banco usó para préstamos y la universidad para salarios y notas.
N Cascada de unidades
Toneladas, gramos por tonelada y onzas troy conviven,
cada cifra con su unidad; la producción derivada se ancla por calculado_de a
la extracción que la originó. Trazabilidad de lote nativa.
M Causalidad sin agente
El cable causado_por de D7 reconstruye la
responsabilidad forense sin necesidad de un culpable en ningún eslabón. La ausencia del
rol agente es, ella misma, un dato.
En suma: el dominio minero no exigió ninguna pieza nueva del modelo. Lo
absorbió combinando piezas que ya conocíamos. El motor sigue siendo el mismo que el del spa
más simple. Lo único que crece es el lexicon (cinco verbos nuevos:
extraer, operar_equipo, dar_mantenimiento,
medir_calidad, reportar) y la lista de roles de dominio que la
política liberal acepta sin chistar: dentro_de, ley_mineral,
operador_asignado, calculado_de, umbral_arsenico,
medido_de, entre otros. La arquitectura conceptual no movió una línea entre el
comercio más inofensivo y el yacimiento más exigente.
Cierre de la serie de dominios profundos
Con la minera cerramos la serie completa de ocho dominios industriales modelados en profundidad: spa, taxi, clínica, banco, ERP, universidad, municipalidad y minera. Cada uno empujó al modelo en una dimensión distinta (el impuesto del comercio, la concurrencia del taxi, la bitemporalidad del banco, la causalidad sin agente de la mina) y cada uno fue absorbido sin extensiones especiales. La arquitectura no cambió ni una línea entre el primero y el último; lo único que cambió fue el lexicon y los roles de dominio. Cuando ocho problemas independientes convergen en la misma respuesta estructural, esa respuesta deja de ser una hipótesis.
Pero el modelo tiene que medirse también con los casos incómodos. Conviene ahora cambiar el ritmo y someter al modelo a cuatro dominios cualitativamente distintos (música, química, fútbol y contratos) buscando explícitamente dónde se resiste: algunos se resuelven con elegancia; otros exigen extensiones al catálogo o dejan pendientes que documentaremos sin disimular. Antes de eso, sin embargo, vale la pena un desvío revelador: tomar un sistema real, viejo y vivo (no un ejemplo diseñado para lucirse) y excavar su esquema para ver qué de todo esto ya estaba ahí, enterrado bajo nombres de columna. Es la arqueología del próximo capítulo.