System Diagram

The Blast Planning and Explosives Management data model, mapped — 65 models, 199 relationships

🔍 For serious exploration, open the Interactive Diagram Viewer — pan, mouse-wheel zoom, fit-to-screen, full-screen, per-module tabs, the full 199-relationship ERD, and a focus mode that draws one model and its neighbours at a chosen depth. Person (71 links), Document (25) and MineSite (12) are role/ownership columns that fan out to nearly everything, so the viewer hides those links by default — a toggle brings them back. Blast (34 links) is the structural spine and stays visible. The diagrams below are the readable, static summary of the principal links only.


Architecture Overview

Ten areas mirror the sidebar menu. Every arrow means "one row of the left model is referenced by many rows of the right model".

flowchart LR
    subgraph PLAN["🧨 Planning & design"]
        BlastRequest --> Blast
        Blast --> BlastDesign --> BlastDesignApproval
        Blast --> BlastRiskAssessment --> BlastHazard
        Blast --> BlastBoundary
    end

    subgraph DRILL["🕳️ Drilling"]
        BlastHole --> DrillActivity
        BlastHole --> HoleSurvey
        BlastHole --> HoleConditionInspection
    end

    subgraph CHARGE["⚡ Charging & initiation"]
        ChargingPlan --> HoleChargePlan
        ChargingSession --> HoleChargeActual
        ChargingSession --> ChargingQualityCheck
        InitiationPlan --> TieInConnection
    end

    subgraph EXPL["📦 Explosives & stock"]
        ExplosiveProduct --> ExplosiveBatch
        ExplosivesMagazine --> MagazineInspection
        ExplosivesMagazine --> ExplosiveStockLocation
        ExplosiveStockLocation --> ExplosiveStockBalance
        ExplosiveBatch --> ExplosiveStockTransaction
        ExplosivesTransportMovement --> TransportMovementItem
    end

    subgraph CLEAR["📢 Clearance & firing"]
        ClearancePlan --> GuardPost
        ClearancePlan --> AreaClearance
        PreFireChecklist --> BlastFiring
        BlastNotification
        WeatherObservation
    end

    subgraph POST["🌏 Post-blast & environment"]
        PostBlastInspection --> ReentryAuthorisation
        BlastMonitoringInstrument --> BlastMonitoringRecord
        SensitiveReceiver --> BlastReceiverAssessment
        FlyrockObservation
        FumeObservation
        FragmentationAssessment
        MuckpileAssessment
    end

    subgraph MF["🚨 Misfire & reconciliation"]
        Misfire --> MisfireTreatment
        BlastReconciliation
        BlastCostRecord
    end

    subgraph INC["🔎 Incidents"]
        BlastIncident --> Investigation --> InvestigationFinding --> CorrectiveAction
    end

    subgraph ASSUR["📜 Assurance"]
        SourceAuthority --> SourceDocument --> Requirement
        ExplosivesManagementPlan --> PlanRequirementLink
        Audit --> AuditFinding
        PersonAuthorisation
    end

    Blast --> BlastHole
    Blast --> ChargingPlan
    Blast --> ChargingSession
    Blast --> InitiationPlan
    Blast --> ClearancePlan
    Blast --> PreFireChecklist
    Blast --> BlastNotification
    Blast --> WeatherObservation
    Blast --> PostBlastInspection
    Blast --> BlastMonitoringRecord
    Blast --> Misfire
    Blast --> BlastReconciliation
    Blast --> BlastIncident
    Blast --> ExplosiveStockTransaction
    BlastHole --> HoleChargePlan
    BlastHole --> HoleChargeActual
    BlastHole --> Misfire
    ExplosiveBatch --> HoleChargeActual
    BlastBoundary --> ClearancePlan
    Requirement --> PlanRequirementLink
    CorrectiveAction --> AuditFinding

(Blast is the hub — 34 relationships touch it. Everything from the hole pattern to the cost record hangs off it, which is why the viewer's Focus mode defaults to it.)


Design, Approval and Risk

A design revision supersedes its predecessor rather than overwriting it, and approval is split across independent roles so the pack's segregation-of-duties rule is checkable.

erDiagram
    BlastRequest ||--o{ Blast : "blast_request_id"
    Blast ||--o{ BlastDesign : "blast_id"
    BlastDesign ||--o{ BlastDesign : "supersedes_design_id"
    BlastDesign ||--o{ BlastDesignApproval : "blast_design_id"
    Blast ||--o{ BlastRiskAssessment : "blast_id"
    BlastRiskAssessment ||--o{ BlastHazard : "blast_risk_assessment_id"
    Blast ||--o{ BlastBoundary : "blast_id"
    MineSite ||--o{ SensitiveReceiver : "mine_site_id"
    Blast ||--o{ BlastReceiverAssessment : "blast_id"
    SensitiveReceiver ||--o{ BlastReceiverAssessment : "sensitive_receiver_id"

Drilling and Hole Validation

The three records that decide whether a hole may be charged. HoleConditionInspection .suitable_for_charging is the gate — a false here means no HoleChargeActual row will ever exist for that hole.

erDiagram
    Blast ||--o{ BlastHole : "blast_id"
    BlastHole ||--o{ DrillActivity : "blast_hole_id"
    BlastHole ||--o{ HoleSurvey : "blast_hole_id"
    BlastHole ||--o{ HoleConditionInspection : "blast_hole_id"

Explosives Custody

Batch-level traceability from supplier to hole and back. ExplosiveStockBalance is the location × batch join; ExplosiveStockTransaction is the ledger.

erDiagram
    Organisation ||--o{ ExplosiveProduct : "supplier_organisation_id"
    ExplosiveProduct ||--o{ ExplosiveBatch : "explosive_product_id"
    MineSite ||--o{ ExplosivesMagazine : "mine_site_id"
    ExplosivesMagazine ||--o{ MagazineInspection : "explosives_magazine_id"
    ExplosivesMagazine ||--o{ ExplosiveStockLocation : "explosives_magazine_id"
    ExplosiveStockLocation ||--o{ ExplosiveStockBalance : "explosive_stock_location_id"
    ExplosiveBatch ||--o{ ExplosiveStockBalance : "explosive_batch_id"
    ExplosiveBatch ||--o{ ExplosiveStockTransaction : "explosive_batch_id"
    ExplosiveStockLocation ||--o{ ExplosiveStockTransaction : "from_location_id"
    ExplosiveStockLocation ||--o{ ExplosiveStockTransaction : "to_location_id"
    Blast ||--o{ ExplosiveStockTransaction : "blast_id"
    ExplosivesTransportMovement ||--o{ TransportMovementItem : "explosives_transport_movement_id"
    ExplosiveBatch ||--o{ TransportMovementItem : "explosive_batch_id"

Charging and Initiation

Planned versus actual, hole by hole and deck by deck. Note that HoleChargeActual carries three batch references — main charge, booster and detonator — so a suspect batch traces to exact holes.

erDiagram
    Blast ||--o{ ChargingPlan : "blast_id"
    BlastDesign ||--o{ ChargingPlan : "blast_design_id"
    ChargingPlan ||--o{ HoleChargePlan : "charging_plan_id"
    BlastHole ||--o{ HoleChargePlan : "blast_hole_id"
    ExplosiveProduct ||--o{ HoleChargePlan : "explosive_product_id"
    Blast ||--o{ ChargingSession : "blast_id"
    ChargingPlan ||--o{ ChargingSession : "charging_plan_id"
    ChargingSession ||--o{ HoleChargeActual : "charging_session_id"
    BlastHole ||--o{ HoleChargeActual : "blast_hole_id"
    ExplosiveBatch ||--o{ HoleChargeActual : "explosive_batch_id"
    ExplosiveBatch ||--o{ HoleChargeActual : "booster_batch_id"
    ExplosiveBatch ||--o{ HoleChargeActual : "detonator_batch_id"
    ChargingSession ||--o{ ChargingQualityCheck : "charging_session_id"
    Blast ||--o{ InitiationPlan : "blast_id"
    InitiationPlan ||--o{ TieInConnection : "initiation_plan_id"
    BlastHole ||--o{ TieInConnection : "from_blast_hole_id"
    BlastHole ||--o{ TieInConnection : "to_blast_hole_id"

Clearance and Firing

The gate. BlastFiring requires a PreFireChecklist, and the checklist is only true when every guard post and area clearance is.

erDiagram
    Blast ||--o{ BlastNotification : "blast_id"
    Blast ||--o{ ClearancePlan : "blast_id"
    BlastBoundary ||--o{ ClearancePlan : "exclusion_boundary_id"
    ClearancePlan ||--o{ GuardPost : "clearance_plan_id"
    ClearancePlan ||--o{ AreaClearance : "clearance_plan_id"
    OperatingArea ||--o{ AreaClearance : "operating_area_id"
    Blast ||--o{ WeatherObservation : "blast_id"
    Blast ||--o{ PreFireChecklist : "blast_id"
    Blast ||--o{ BlastFiring : "blast_id"
    PreFireChecklist ||--o{ BlastFiring : "pre_fire_checklist_id"

Post-Blast, Misfire and Reconciliation

ReentryAuthorisation hangs off a specific PostBlastInspection — which is how "re-entry was authorised against the inspection that cleared the area, not the one that found the misfire" becomes a fact in the data.

erDiagram
    Blast ||--o{ PostBlastInspection : "blast_id"
    Blast ||--o{ ReentryAuthorisation : "blast_id"
    PostBlastInspection ||--o{ ReentryAuthorisation : "post_blast_inspection_id"
    MineSite ||--o{ BlastMonitoringInstrument : "mine_site_id"
    Blast ||--o{ BlastMonitoringRecord : "blast_id"
    BlastMonitoringInstrument ||--o{ BlastMonitoringRecord : "blast_monitoring_instrument_id"
    SensitiveReceiver ||--o{ BlastMonitoringRecord : "sensitive_receiver_id"
    Blast ||--o{ FlyrockObservation : "blast_id"
    Blast ||--o{ FumeObservation : "blast_id"
    Blast ||--o{ FragmentationAssessment : "blast_id"
    Blast ||--o{ MuckpileAssessment : "blast_id"
    Blast ||--o{ Misfire : "blast_id"
    BlastHole ||--o{ Misfire : "suspected_hole_id"
    Misfire ||--o{ MisfireTreatment : "misfire_id"
    Blast ||--o{ BlastReconciliation : "blast_id"
    Blast ||--o{ BlastCostRecord : "blast_id"

Incidents, Investigation and Assurance

The improvement loop, and the compliance spine it answers to.

erDiagram
    Blast ||--o{ BlastIncident : "blast_id"
    BlastIncident ||--o{ Investigation : "blast_incident_id"
    Investigation ||--o{ InvestigationFinding : "investigation_id"
    InvestigationFinding ||--o{ CorrectiveAction : "investigation_finding_id"
    BlastIncident ||--o{ CorrectiveAction : "blast_incident_id"
    Blast ||--o{ CorrectiveAction : "blast_id"
    BlastDesign ||--o{ InvestigationFinding : "blast_design_id"
    SourceAuthority ||--o{ SourceDocument : "source_authority_id"
    SourceDocument ||--o{ SourceDocument : "superseded_by_id"
    SourceDocument ||--o{ Requirement : "source_document_id"
    MineSite ||--o{ ExplosivesManagementPlan : "mine_site_id"
    ExplosivesManagementPlan ||--o{ PlanRequirementLink : "explosives_management_plan_id"
    Requirement ||--o{ PlanRequirementLink : "requirement_id"
    MineSite ||--o{ Audit : "mine_site_id"
    Audit ||--o{ AuditFinding : "audit_id"
    Requirement ||--o{ AuditFinding : "requirement_id"
    CorrectiveAction ||--o{ AuditFinding : "corrective_action_id"
    Person ||--o{ PersonAuthorisation : "person_id"

Application Flow — Request to Close

The order in which the system is actually used, and what gates each step.

flowchart TD
    A["📝 Blast request raised"] --> B["📐 Blast design revision"]
    B --> C{"Design approved by<br/>geotech · environment · management?"}
    C -->|rejected| B
    C -->|approved| D["⚠️ Risk assessment + hazards<br/>boundaries + receiver assessment"]
    D --> E["🕳️ Drill the pattern"]
    E --> F["📏 Survey + condition inspection"]
    F --> G{"suitable_for_charging?"}
    G -->|no| H["🚫 REDRILL_REQUIRED<br/><i>hole excluded from the charge plan</i>"]
    G -->|yes| I["📦 Issue explosives against the blast<br/><i>batch-level, sealed transport</i>"]
    H --> I
    I --> J["⚡ Charge per plan<br/><i>deviations recorded + approved</i>"]
    J --> K["🔗 Tie-in + continuity verified"]
    K --> L["📢 Notify · clear the zone · post guards"]
    L --> M{"Every guard CONFIRMED_CLEAR<br/>and every area clear?"}
    M -->|no| L
    M -->|yes| N{"✅ Pre-fire checklist<br/>all 15 checks true?"}
    N -->|no| L
    N -->|yes| O["🧨 Shotfirer authorised → FIRE"]
    O --> P["🔍 Post-blast inspection<br/><i>after the wait period</i>"]
    P --> Q{"Misfire indicators?"}
    Q -->|yes| R["🚨 Misfire: isolate → assess →<br/>treat → verify independently"]
    R --> P
    Q -->|no| S["🔓 Re-entry authorised"]
    S --> T["📊 Monitoring · fragmentation · muckpile"]
    T --> U["🧮 Reconciliation<br/><i>holes · explosives · tonnage</i>"]
    U --> V{"Variance within tolerance<br/>and no open misfire?"}
    V -->|no| W["⛔ Blast stays open<br/><i>corrective action raised</i>"]
    W --> U
    V -->|yes| X["🔒 Blast CLOSED"]

Application Flow — Why This Blast Has Not Closed

The demo's BL-2026-00042 sits at RECONCILIATION. Three separate gates explain why, and each is visible as data.

flowchart TD
    S(["BL-2026-00042 · stage RECONCILIATION"]) --> G1{"Reconciliation approved?"}
    G1 -->|"NO — 3.5 kg unexplained variance<br/>exceeds the 2 kg tolerance"| B1["stock_reconciled = false<br/>status = REVIEW, no approver"]
    B1 --> A1["BL-ACT-2026-00019<br/>AWAITING_VERIFICATION"]
    S --> G2{"All reportable incidents<br/>notified and closed?"}
    G2 -->|"Vibration exceedance still ACTIVE"| B2["BL-INC-2026-00007<br/>9.4 mm/s vs 5.0 mm/s limit"]
    B2 --> A2["BL-ACT-2026-00017<br/>recalibrate the prediction model"]
    S --> G3{"All critical actions assigned<br/>and on track?"}
    G3 -->|"One action OVERDUE"| B3["BL-ACT-2026-00021<br/>redrill NP480-007"]
    A1 --> R(["closed_at stays null"])
    A2 --> R
    B3 --> R

Reading the Diagrams

  • A ||--o{ B means one row of A is referenced by many rows of B (the label is the foreign-key column on B).
  • These static diagrams show principal links only — the role columns that appear on nearly every model (*_person_id, *_document_id, mine_site_id) are omitted for readability. The interactive viewer's Full ERD shows all 199, with the person/document/site noise toggleable.
  • Blast is the hub of the domain — the viewer's Focus mode defaults to it, with 34 links. Person has by far the most (71) because every act in blasting records who did it: requested, designed, supervised, approved, drilled, surveyed, inspected, issued, handled, charged, checked, installed, verified, coordinated, guarded, cleared, authorised, fired, detected, treated, reconciled, investigated, owned, audited.
  • No FK was dropped in translation — the proforma graph was acyclic, so every relationship in the source model is present. Document was hoisted above PersonAuthorisation (the pack declares it after first use). OperatingArea.parent_area_id, SourceDocument.superseded_by_id and BlastDesign.supersedes_design_id are self-references.
  • Three FK pairs are deliberately two roles onto the same parent, and the drill-down tabs are labelled accordingly in tab_config.yaml: ExplosiveStockTransaction.from_location_id/to_location_id, TieInConnection.from_blast_hole_id/to_blast_hole_id, and HoleChargeActual's three batch roles (main charge, booster, detonator).
  • AuditRecord from the proforma pack is not present — it duplicated the framework's own audit history and was dropped in translation.

Diagrams are derived from the generated schema metadata (apps/blast/generated/json/blast_relationship_metadata.json). If the DSL schema changes, regenerate the metadata (python -m codegen.cli all blast) and update this page and the viewer's embedded data.