Application Overview

What the Blast Planning and Explosives Management app models, and the ideas behind it


Core Operating Chain

BlastRequest → Blast                                (why we are blasting)
  Blast → BlastDesign (self-superseding) → BlastDesignApproval
  Blast → BlastRiskAssessment → BlastHazard         (initial → residual rating)
  Blast → BlastBoundary                             (exclusion / vehicle / advisory)
  MineSite → SensitiveReceiver → BlastReceiverAssessment
                                                    (predicted vibration + airblast)

Blast → BlastHole → DrillActivity                   (what was actually drilled)
                  → HoleSurvey                      (deviation accepted?)
                  → HoleConditionInspection         (suitable_for_charging — the gate)

ExplosiveProduct → ExplosiveBatch                   (the custody unit)
ExplosivesMagazine → MagazineInspection
                   → ExplosiveStockLocation → ExplosiveStockBalance
ExplosiveBatch → ExplosiveStockTransaction          (RECEIPT · ISSUE · CONSUMPTION ·
                                                     RETURN · QUARANTINE · STOCKTAKE)
ExplosivesTransportMovement → TransportMovementItem  (magazine → bench, sealed)

Blast → ChargingPlan → HoleChargePlan               (planned, per hole and deck)
      → ChargingSession → HoleChargeActual          (actual product, batch, quantity)
                        → ChargingQualityCheck      (stemming, charge weight, primer)
Blast → InitiationPlan → TieInConnection            (continuity verified)

Blast → BlastNotification                           (neighbours, pipeline, regulator)
      → ClearancePlan → GuardPost / AreaClearance   (every post must confirm clear)
      → WeatherObservation                          (suitable_to_fire)
      → PreFireChecklist → BlastFiring              (15 checks, then authorisation)

Blast → PostBlastInspection → ReentryAuthorisation  (blocked while a misfire is open)
      → BlastMonitoringRecord (via BlastMonitoringInstrument, per receiver)
      → FlyrockObservation / FumeObservation
      → FragmentationAssessment / MuckpileAssessment
      → Misfire → MisfireTreatment                  (isolate → assess → treat → verify)
      → BlastReconciliation                         (holes, explosives, tonnage)
      → BlastCostRecord

BlastIncident → Investigation → InvestigationFinding → CorrectiveAction
MineSite → Audit → AuditFinding                     (findings link to actions)
ExplosivesManagementPlan → PlanRequirementLink → Requirement → SourceDocument
Person → PersonAuthorisation                        (licence, expiry, verification)

Key Concepts

A hole that fails its condition inspection cannot be charged. HoleConditionInspection.suitable_for_charging is a required boolean, and it is the gate the pack's RejectedHoleCannotBeCharged rule is written against. In the demo, hole NP480-007 collapsed at 4.2 m against a planned 10.8 m with 1.8 m of standing water. It is set REDRILL_REQUIRED, it is absent from the charge plan, it has no HoleChargeActual row at all, and the tie-in sequence skips it. Twelve holes were drilled; eleven were charged. That difference propagates all the way into the reconciliation.

Explosives are traced by batch, not by quantity. Every movement is an ExplosiveStockTransaction against an ExplosiveBatch — issue to the mobile processing unit, consumption into holes, return of the unused product, quarantine of a water-damaged batch. HoleChargeActual records the batch used for the main charge, the booster and the detonator as three separate columns, so a suspect batch can be traced to the exact holes it went into. This is what makes the reconciliation meaningful rather than arithmetic.

The pre-fire checklist is a gate, not a form. PreFireChecklist carries fifteen required booleans — design approved, drilling accepted, charging complete, tie-in verified, explosives reconciled to field, notifications complete, guards confirmed, exclusion zone clear, weather acceptable, communications operational, firing system test passed, misfire procedure available, emergency response available, and more. all_checks_passed is a derived field, and authorised_to_fire_by_person_id is separate from completed_by_person_id: the person who completes the checks is not the person who authorises the fire.

Every guard post must confirm clear — and one nearly didn't. The pack's ClearanceGate and AbortOnLostCommunication rules turn on GuardPost.status == CONFIRMED_CLEAR for every post. The demo's GP-03 (the public road boundary) failed its initial radio check at 13:38; firing was held until the guard re-established communications on the backup channel and confirmed clear at 13:52. The event is visible in the guard post record rather than smoothed away, because a clearance that always succeeds tells you nothing.

A suspected misfire blocks re-entry. PostBlastInspection.area_safe_for_reentry is required, and when the first inspection found an intact collar and undisturbed stemming at NP480-012 it was recorded false with the exclusion maintained. The Misfire then runs its own lifecycle — SUSPECTED → AREA_ISOLATED → ASSESSED → TREATMENT_APPROVED → RESOLVED → VERIFIED → CLOSED — through four MisfireTreatment steps: isolate and wait, task risk assessment, wash out and recover, then independent verification by a different person. Only after that does a second inspection record area_safe_for_reentry = true and a ReentryAuthorisation exist. The two inspections, in order, are the record.

Approval roles are deliberately separable. The pack ships a segregation-of-duties matrix — approve a design ≠ prepare it, approve a stock adjustment ≠ create it, verify a misfire resolution ≠ perform the treatment — and the schema gives each of those its own column so the separation is checkable. The demo honours all three: Okonkwo prepared the design and Hale/Dube/Pearce approved it; McLeod washed out the misfired hole and Reyes verified it. tab_config.yaml labels every one of these roles so a Person page reads "Designs Prepared" and "Design Approvals Given" as different facts.

The blast has not closed, and that is the point. BlastReconciliation records a 3.5 kg unexplained variance between explosives issued (4200 kg), charged (4028.5 kg) and returned (168 kg) — above the site's 2 kg tolerance. So stock_reconciled is false, the reconciliation sits at REVIEW with no approver, corrective action BL-ACT-2026-00019 is open, and Blast.blast_stage remains RECONCILIATION with closed_at null. That is the pack's ExplosivesVariance and BlastClosureGate rules made visible as data.

The application is not the design tool. The boundary carried over from the pack, and worth repeating: this is a configurable operational record system, not a blast-design calculator, an electronic initiation controller, an explosives manufacturing control system or a monitoring historian. Specialist design, firing and high-frequency monitoring systems remain authoritative for their technical functions; what is stored here is the governed summary, the file reference, the approval, the decision and the outcome.

What Is Phase 2

The proforma pack ships seven workflows (BlastDesignAndApproval, DrillValidation, ExplosivesIssueAndCharging, ClearanceAndFiring, PostBlastAndReentry, MisfireResponse, BlastReconciliationAndClose), a sixteen-state BlastStage lifecycle with explicit transitions, thirteen business rules (CurrentAuthorisationRequired, ApprovedDesignRequired, RejectedHoleCannotBeCharged, StockCannotGoNegative, BatchTraceabilityRequired, ChargingDeviationControl, ClearanceGate, AbortOnLostCommunication, MisfireIsolation, MonitoringExceedance, FlyrockOutsideZone, ExplosivesVariance, BlastClosureGate), the segregation-of-duties matrix, numbering masks (BL-{YYYY}-{SEQ:05} and friends), an offline field-capture profile with immutable-after-sync models, and five dashboards and seven reports. None of that is enforced by the schema — the demo data is consistent with those rules rather than validated against them, and the reference numbers follow the masks by hand. Derived fields (quantity_available, discrepancy, all_checks_passed, exceeded, unexplained_variance_kg, actual_powder_factor) are stored columns populated by the seed, not computed. See the header of apps/blast/schema/blast.dsl for the full translation record.