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 aun así, 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 desde que lo instalaron, y registra 0,34 miligramos de arsénico por litro. El umbral que la norma 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 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 separa a este dominio de los siete anteriores.
Hasta ahora, cada evento tenía detrás una voluntad. Alguien vendía, alguien pagaba, una médica diagnosticaba, un funcionario emitía una ordenanza. Y cuando el actor no era humano (la app del taxi asignando conductor, el motor antifraude marcando una operación) había una intención programada empujando la acción.
Una mina de gran escala desmonta esa comodidad. Ahí pasan cosas todo el tiempo que ningún agente «hace». La roca se desprende de la pared. El suelo cede. Una junta geomecánica se debilita con los años. Una vena de agua subterránea altera la química del río.
Son eventos físicos puros. Ocurren porque las condiciones materiales se cumplen. Y pesan tanto como cualquier acto deliberado, porque cuando 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.
Fíjate 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í 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 que más se ejercitan aquí ya las conoces. Este dominio no estrena ninguna; solo las combina con una intensidad nueva.
La agencia contextual (D5) se estira hasta admitir eventos sin agente, y
también sensores como agente. El reparto del porqué (D7) reconstruye cadenas
con causado_por, sin intención de nadie.
La vigencia (D6) sigue quince años de estados de un equipo. Y la situación articuladora, la misma que organizó el viaje del taxi, 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 divide en bancos, los escalones físicos de la pared. 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)
«¿Dónde ocurrió exactamente este accidente?» devuelve la cadena entera con un solo
recorrido hacia arriba: Frente C → Banco 04 → Nivel 4250 →
Tajo Norte → Yacimiento San Marcos.
Toda la geografía queda disponible para cualquier reporte de seguridad, cualquier auditoría o
cualquier análisis de productividad por zona. Sin una sola columna id_tajo,
id_nivel o 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, con quince años de vida útil por delante. En ese tramo pasa por 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 (D6), la misma maquinaria que en el banco siguió la vida de un préstamo y en la universidad la 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.
«¿El camión 007 estaba operativo el 15 de marzo de 2024?» devuelve
mantenimiento_programado. «¿Y el 1 de junio de 2026?»,
operativo.
La trazabilidad viene de serie, no como módulo añadido. El rango
valid_from/valid_to es parte del hecho, no una columna que alguien
tuvo que acordarse de 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 sale directo del grafo: se filtran las
tripletas estado de camion_007 por la fecha y se devuelve la única
cuyo rango la contiene. Sin lógica de aplicación y 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 mide su producción en una cascada de unidades que se convierten unas en otras.
Una extracción se mide en toneladas métricas de mineral. Ese mineral tiene una ley, en gramos de oro por tonelada. Multiplicando ambos sale el oro contenido, y tras el proceso metalúrgico quedan las onzas troy de oro fino vendible.
Tres unidades distintas, cada una con su significado. Ninguna intercambiable con otra.
El turno de noche del 19 de mayo extrae 2.480 toneladas con una ley de 8,6 g/t en el Frente A.
La extracción se vuelve una situación en O. Lo merece: agrupa varios roles,
tiene momento y participa en otras relaciones. Y se describe con sus magnitudes, 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, una
sub-situación parte_de la extracción y calculada de ella.
El cálculo (2.480 t × 8,6 g/t = 21.328 g, y a 31,1034768 g/oz troy,
685,7 oz) se asienta como hecho, y un cable calculado_de apunta hacia atrás,
a la extracción de la que salió.
# La regla vive en el grafo, no en el código de la aplicación
u.assert_fact(regla_oro, "expresion", u.add_individual(Individual(
id="expr_oro_fino", axis=Axis.K, label="monto * ley_mineral")))
u.assert_fact(regla_oro, "unidad_destino", onza_troy)
# El motor la aplica: nadie escribe el 685,7 a mano
produccion_oro = u.derive(regla_oro, sobre=extraccion,
destino_id="prod_oro_extr_001")
u.assert_fact(produccion_oro, "parte_de", extraccion)
u.assert_fact(produccion_oro, "unidad", onza_troy)
# y el hecho derivado sale con su procedencia puesta:
# (prod_oro_extr_001, monto, 685.7 K:OnzaTroy)
# (prod_oro_extr_001, calculado_de, extr_001)
# (prod_oro_extr_001, justificado_por, regla_oro_fino)
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 vinieron estas 685,7 onzas?», la
respuesta es un salto por calculado_de.
Y si se descubre que la ley estaba mal medida, la producción corregida entra como un hecho
nuevo que rectifica al anterior. Es el patrón que ya vimos en el rediagnóstico de
la clínica y en la multa revocada 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 en turnos de doce. Cada turno reúne unos operadores, varios equipos y toda la producción y los incidentes de ese lapso.
Modelar el turno como entidad articuladora, igual que el viaje_001 del taxi o la
transferencia_001 del banco, hace que todo lo que pasa cuelgue 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)
«¿Qué pasó en el turno de noche del 19 de mayo?» devuelve la cadena completa con un
salto 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 que justifica el capítulo entero.
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. Contusión en el brazo derecho.
La pregunta que la auditoría hará primero es brutal de tan simple:
¿Quién causó el accidente?La pregunta forense
La respuesta, y la que el grafo registra, es nadie. La roca se desprendió. Nadie la tiró.
Pero tampoco se desprendió de la nada. Hubo una condición previa: un debilitamiento de la pared del Banco 04 que la inspección geomecánica anterior debió detectar.
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 sencillamente no aparece en las situaciones físicas, porque la
firma del verbo no lo exige.
Y el reparto 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ó.
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 de un barrido.
«¿Qué causó el accidente?» El desprendimiento. «¿Y qué causó el desprendimiento?» El debilitamiento de la pared. «¿Y por qué no se detectó?» Ahí empieza el siguiente tramo 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 porque el grafo
entrega la cadena exacta, no una explicación diluida en 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 id_responsable. O peor: inventar un
registro ficticio «la naturaleza» para que la clave foránea no se queje.
Las dos salidas mienten. La primera confunde «no hubo agente» con «no sabemos quién». La segunda ensucia el catálogo de personas con una entrada que no es una persona.
WQuestions no necesita ninguna. El rol agente no se asienta, y
la ausencia del cable es el dato. Un agente desconocido y un evento que nadie causó
no son lo mismo, y el modelo los distingue.
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 viven bajo regulación ambiental estricta. Sensores automáticos vigilan la calidad del aire y del agua en estaciones repartidas por el yacimiento, midiendo 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 justo 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» o la cajera en «cobrar». No es una metáfora ni un comodín.
«¿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 y 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 de 0,10 mg/L que fija la norma ambiental.
La medición se registra 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 detecta que el valor supera el umbral, dispara el reporte regulatorio. Ese motor aún no está en el prototipo, y el capítulo 30 lo declara como trabajo pendiente.
El 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 que cruzó el umbral, y
justificado_por la norma que obliga a reportar. El grafo guarda las dos por
separado, sin confundirlas.
Si dentro de cinco años un auditor pregunta «¿por qué se emitió este reporte ambiental?», hay dos respuestas distintas y precisas: por la medición de 0,34 mg/L del 15 de mayo, y porque el estándar ambiental lo exigía.
La causa concreta y la justificación normativa son cosas distintas, y el modelo las mantiene separadas. Es el patrón que la municipalidad usó para sus ordenanzas, ahora con normas ambientales en lugar de decretos.
El pulso del yacimiento
Un dominio lleno de 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 casi todas fueron 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 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 esquema de telemetría propio. Es el mismo tipo de hecho que todo lo demás, repetido en el tiempo.
Y porque comparte forma con el resto, «dame todas las mediciones de arsénico de la Estación 7 que superaron el umbral este mes» se resuelve sobre los mismos cables de siempre, no contra 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, hubo un proyecto de ingeniería (diseño, procura, construcción, lo que la industria 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 que van demostrando, uno a uno, que lo construido hace lo que el diseño prometía.
En un sistema convencional tienta tratar el comisionamiento como un campo del equipo:
comisionado = true. Pero eso tira 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 merece nodo propio (D4), 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 maquinaria de cualquier situación: identificador propio, estado,
responsable (que puede ser una persona o el cliente) y 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 y
quién lo tiene a su cargo?») se contesta proyectando las situaciones que tienen a
planta_procesamiento como objeto_de, ordenadas por
precede_a.
Y cuando cierre la fase de aceptación, la planta recibirá su primer estado
operativo, con el mismo cable y la misma vigencia (D6) que
usamos para el camión 007.
El comisionamiento no es un mundo aparte. Es la antesala 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 y remates que faltan 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 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 con identidad propia: un defecto concreto, con su categoría, su responsable de cierre, su estado y, cuando se cierra, su fecha.
No es una fila muerta en una hoja de cálculo. Es un nodo del mismo grafo, atado a la fase 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 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.
| Í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. 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 como órdenes de trabajo ligadas al historial del equipo (D6), el mismo 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 engancha 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 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_* 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, 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 «¿qué estado tenías el día del incidente?», aunque para entonces ya no exista.
El ciclo de vida completo queda en el grafo, del primer protocolo de comisionamiento a la última orden de trabajo, sin que haya hecho falta inventar ninguna pieza nueva.
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 entero.
La pregunta que ordena el presupuesto no es «¿en qué estado está este equipo?». Es «¿qué activo acumula más intervenciones correctivas?» o «¿qué causa raíz reaparece trimestre tras trimestre?».
Y no son consultas de otro sistema. Cada falla quedó registrada con su activo y su disparador. El reporte de confiabilidad es contar esas órdenes, 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 solo grafo donde todo se cruza por los mismos cables. El turno 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,7 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 tendrí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, ni de la producción al medio ambiente.
Esa invariancia es lo que permite contestar de una vez la pregunta del supervisor («¿qué pasó anoche?») 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.
La primera grieta aparece enseguida. Reconstruir la cadena causal de un accidente sin autor obliga a cruzar 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 quince años del camión tampoco cabe limpio. La tabla
estados_equipo acumula filas sin noción de vigencia, y cualquier consulta
(¿cuál era el estado exacto el 15 de marzo de 2024?) exige lógica extra que el
esquema no da.
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 solo grafo.
Cada evento físico (el debilitamiento, el desprendimiento, el accidente) es un nodo conectado
al siguiente por causado_por. 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 de serie (D6). Cada
tripleta (camion_007, estado, operativo) lleva su valid_from y su
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. Los absorbe a todos con la maquinaria de siempre.
Qué quedó probado
La minería pone a trabajar D5 en su forma más radical. Hay eventos físicos puros (el desprendimiento, el debilitamiento de la pared) que ningún humano hizo, y pesan tanto como cualquier acto deliberado.
El modelo los acepta sin esfuerzo porque la firma 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. Lo absorbió combinando piezas que ya conocíamos. El motor sigue siendo el mismo que el del spa.
Lo único que crece es el lexicon, con cinco verbos (extraer,
operar_equipo, dar_mantenimiento, medir_calidad,
reportar) y unos roles de dominio: dentro_de,
ley_mineral, operador_asignado, calculado_de,
umbral_arsenico, medido_de.
La arquitectura 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 de ocho dominios industriales: spa, taxi, clínica, banco, ERP, universidad, municipalidad y minera.
Cada uno empujó al modelo en una dirección distinta. El impuesto del comercio. La concurrencia del taxi. Los dos tiempos del banco. La causalidad sin agente de la mina. Y cada uno fue absorbido sin extensiones.
La arquitectura no cambió 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. Toca cambiar el ritmo y someterlo a cuatro dominios de otra naturaleza (música, química, fútbol y contratos) buscando a propósito dónde se resiste.
Algunos se resolverán con elegancia. Otros exigirán extensiones al catálogo o dejarán pendientes, y los documentaremos sin disimular.
Antes de eso, un desvío revelador. Tomar un sistema real, viejo y vivo (no un ejemplo diseñado para lucirse) y excavar su esquema para ver cuánto de todo esto ya estaba ahí, enterrado bajo nombres de columna. Es la arqueología del próximo capítulo.