WQuestions

Parte V · En la práctica

23

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 quién · agentes Quispe, Mamani (operadores) el supervisor, el sensor de agua la empresa, el ente regulador O qué · eventos y objetos la extracción, el desprendimiento el accidente, cada medición el camión, la norma, el reporte L dónde · lugares yacimiento, Tajo Norte, niveles bancos, Frente A · B · C Estación 7, la quebrada T cuándo · tiempo el turno de 12 horas el instante del desprendimiento la vigencia de cada estado del equipo N cuánto · magnitudes 2.480 t, 8,6 g/t, 685,7 oz troy 0,34 mg/L, umbral 0,10 mg/L 15 años de vida útil, 9 bar K cuál · clases mineral_oro, arsenico condicion_geomecanica estados: operativo, en_mantenimiento M cómo · los predicados que enlazan los seis ejes dentro_de · causado_por · parte_de · medido_de · justificado_por · paciente El cable estrella aquí es causado_por: reconstruye la cadena de un accidente sin culpable. El Yacimiento San Marcos, repartido en sus siete coordenadas
Figura 23.1. El dominio minero sobre los siete ejes. Nota dos detalles que ningún capítulo anterior había forzado a la vez. Primero: en 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.

frente_cL dentro_deM(L→L) banco_04L dentro_deM(L→L) nivel_4250L
python
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.

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

python
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ó.

python
# 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)
produccion_oroO calculado_deM(O→O) extraccion_n19O montoM(O→N) 685,7 oz troyN

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.

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

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

python
# 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ó.

Q quién · agente — sin agente: ningún humano «hizo» estos eventos — O · eventos físicos debilitamiento condición geomecánica preexistente · sin fecha de acto causado_por desprendimiento evento físico puro 23:40 · Frente A causado_por accidente paciente: Quispe contusión brazo der. lectura_inclinometro deformación 12 mm · agente: sensor evidencia previa Se lee al revés de como ocurrió: del daño, hacia atrás, hasta la causa raíz.
Figura 23.2. Una cadena causal sin culpable. El accidente apunta por 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 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.

python
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"),
})
sensor_agua_e7Q agenteM(O→Q) medicion_altaO montoM(O→N) 0,34 mg/LN

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.

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

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

Figura 23.3. Concentración de arsénico medida por el sensor de la Estación 7 a lo largo del 15 de mayo, contra el umbral normativo (línea verde, constante en 0,10 mg/L). Casi todo el día transcurre por debajo del límite; el pico de las 14:00 (0,34 mg/L) es el que dispara el reporte. Cada punto de la serie roja es una medición reificada con el sensor por agente. Pasa el cursor sobre los puntos para ver las cifras.

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 · cada fase es una situación reificada (D4) pre_comision limpieza, energización cerrada · 02 mar resp.: Mamani pruebas en vacío y con carga en curso · 18 mar resp.: ITO Vargas aceptacion protocolo firmado pendiente resp.: cliente entrega a operac. pendiente precede_a Una fase no abre hasta que la anterior cierra: el estado no es un sí/no, es una posición en la secuencia.
Figura 23.4. El comisionamiento de la planta de procesamiento como secuencia de cuatro fases reificadas. Cada fase es un nodo en 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.

python
# 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,
})
prueba_cargaO precede_aM(O→O) prueba_precomO responsableM(O→Q) ito_vargasQ

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.

python
# Í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"),
})
item_punch_aO categoriaM(O→K) categoria_aK detectado_enM(O→O) prueba_cargaO

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.

Punchlist de la planta de procesamiento · estado al 20 de marzo
Í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
Figura 23.5. El punchlist de la planta de procesamiento como proyección del grafo. Cada fila es una situación 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.

python
# 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")
orden_trabajo_88O activoM(O→O) camion_007O motivado_porM(O→O) lectura_vibracion_altaO

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.

T · el historial bitemporal del activo (D6) tiempo comisionar operar mantener (órdenes de trabajo) dar de baja pruebas + aceptación producción · preventivo / correctivo / predictivo retiro de servicio
Figura 23.6. El ciclo de vida del activo: 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.

python
# Ó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.