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

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)

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.

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

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

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

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)

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.

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

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

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

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

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, 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 · 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 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.

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

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

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

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

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

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