{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://app.shockai.co/developers/schemas/v0/ImportDocument.json",
  "title": "ImportDocument",
  "type": "object",
  "properties": {
    "specVersion": {
      "type": "string",
      "const": "0.1",
      "description": "Version of the ShockAI Model API specification this document follows."
    },
    "blast": {
      "type": "object",
      "properties": {
        "platformBlastId": {
          "description": "The blast on the platform, when the importer knows it.",
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
        },
        "externalRef": {
          "description": "The blast’s name or number in the source tool.",
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        }
      }
    },
    "source": {
      "type": "object",
      "properties": {
        "tool": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "description": "The software that produced the results."
        },
        "version": {
          "type": "string",
          "minLength": 1,
          "maxLength": 60
        },
        "vendor": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120
        },
        "method": {
          "$ref": "#/$defs/ModelMethod"
        },
        "file": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "sha256": {
              "type": "string",
              "pattern": "^sha256:[0-9a-f]{64}$",
              "description": "A content hash: `sha256:` followed by 64 lower-case hex digits."
            }
          },
          "required": [
            "name",
            "sha256"
          ]
        },
        "producedAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
          "description": "An ISO 8601 timestamp in UTC, for example `2026-09-26T04:12:00Z`."
        }
      },
      "required": [
        "tool",
        "version"
      ]
    },
    "outputs": {
      "minItems": 1,
      "maxItems": 5000,
      "type": "array",
      "items": {
        "$ref": "#/$defs/ImportedOutput"
      }
    },
    "unavailable": {
      "default": [],
      "maxItems": 5000,
      "type": "array",
      "items": {
        "$ref": "#/$defs/Unavailable"
      }
    },
    "warnings": {
      "default": [],
      "maxItems": 500,
      "type": "array",
      "items": {
        "$ref": "#/$defs/Warning"
      }
    }
  },
  "required": [
    "specVersion",
    "blast",
    "source",
    "outputs"
  ],
  "description": "Predictions made by another tool, brought onto the platform with their provenance.",
  "$defs": {
    "ModelMethod": {
      "type": "string",
      "enum": [
        "empirical",
        "kinematic",
        "numerical",
        "statistical",
        "hybrid"
      ],
      "description": "The family of method, so a user can tell a fitted law from a simulation."
    },
    "ImportedOutput": {
      "oneOf": [
        {
          "$ref": "#/$defs/ReceiverPpv"
        },
        {
          "$ref": "#/$defs/MaxCharge"
        },
        {
          "$ref": "#/$defs/ReceiverWaveform"
        },
        {
          "$ref": "#/$defs/ReceiverSpectrum"
        },
        {
          "$ref": "#/$defs/PpvField"
        },
        {
          "$ref": "#/$defs/Airblast"
        },
        {
          "$ref": "#/$defs/DamageEnvelope"
        },
        {
          "$ref": "#/$defs/StrainField"
        },
        {
          "$ref": "#/$defs/FlyrockZone"
        },
        {
          "$ref": "#/$defs/FragmentationCurve"
        },
        {
          "$ref": "#/$defs/MovementField"
        },
        {
          "$ref": "#/$defs/TimingProposal"
        }
      ],
      "description": "One prediction made elsewhere, discriminated by `kind`: every kind a prediction case asks."
    },
    "ReceiverPpv": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "receiver_ppv"
        },
        "receiverId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "basis": {
          "$ref": "#/$defs/PpvBasis"
        },
        "ppvMmS": {
          "description": "Peak particle velocity, mm/s.",
          "$ref": "#/$defs/Estimate"
        },
        "dominantFrequencyHz": {
          "description": "Frequency at the peak, Hz.",
          "$ref": "#/$defs/Estimate"
        },
        "scaledDistance": {
          "description": "The scaled distance the prediction was made at.",
          "type": "number",
          "exclusiveMinimum": 0
        },
        "exceedanceProbability": {
          "description": "Probability of exceeding the receiver’s PPV limit, when it has one.",
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "receiverId",
        "basis",
        "ppvMmS",
        "validity"
      ],
      "description": "Predicted peak particle velocity at a receiver."
    },
    "PpvBasis": {
      "type": "string",
      "enum": [
        "max_axis",
        "pvs"
      ],
      "description": "How peak particle velocity is taken: the largest single component (`max_axis`) or the peak vector sum (`pvs`)."
    },
    "Estimate": {
      "type": "object",
      "properties": {
        "median": {
          "type": "number",
          "description": "The best estimate (P50)."
        },
        "percentiles": {
          "description": "Further points on the predictive distribution, usually those the request asked for.",
          "maxItems": 99,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "p": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Percentile, 0 to 100."
              },
              "value": {
                "type": "number",
                "description": "The value at that percentile, in the unit of the parent quantity."
              }
            },
            "required": [
              "p",
              "value"
            ]
          }
        },
        "ensemble": {
          "description": "Raw ensemble members, for providers that sample rather than summarise.",
          "maxItems": 10000,
          "type": "array",
          "items": {
            "type": "number"
          }
        }
      },
      "required": [
        "median"
      ],
      "description": "A predicted quantity with its uncertainty. A point value with no spread is allowed but a spread is expected: a prediction without one cannot be scored for coverage."
    },
    "Validity": {
      "type": "object",
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "within",
            "extrapolated",
            "outside"
          ],
          "description": "Whether the inputs sit inside the range the model was validated on."
        },
        "code": {
          "description": "Which way it is outside, as a stable code: for example `below_fitted_range`, `above_fitted_range`, `near_field`.",
          "type": "string",
          "maxLength": 60,
          "pattern": "^[a-z][a-z0-9_]*$"
        },
        "note": {
          "type": "string",
          "maxLength": 500
        }
      },
      "required": [
        "status"
      ],
      "description": "How far to trust one output."
    },
    "MaxCharge": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "max_charge"
        },
        "receiverId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "limitMmS": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "The PPV limit the charge was held to: the receiver’s own limit."
        },
        "percentile": {
          "type": "number",
          "minimum": 50,
          "maximum": 99.9,
          "description": "The percentile the limit was read at."
        },
        "maxChargeKg": {
          "anyOf": [
            {
              "type": "number",
              "exclusiveMinimum": 0
            },
            {
              "type": "null"
            }
          ],
          "description": "The largest maximum instantaneous charge whose prediction at that percentile stays at or under the limit. Null when even a token charge exceeds it."
        },
        "scaledDistance": {
          "description": "The scaled distance at that charge.",
          "type": "number",
          "exclusiveMinimum": 0
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "receiverId",
        "limitMmS",
        "percentile",
        "maxChargeKg",
        "validity"
      ],
      "description": "The inverse question: how much charge per delay a receiver’s limit allows. The design value blasters plan with."
    },
    "ReceiverWaveform": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "receiver_waveform"
        },
        "receiverId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "percentile": {
          "description": "Which member of the distribution this trace represents, when the model samples.",
          "type": "number",
          "minimum": 0,
          "maximum": 100
        },
        "sampleRateHz": {
          "type": "number",
          "exclusiveMinimum": 0
        },
        "startMs": {
          "type": "number",
          "description": "Time of the first sample from T0, ms."
        },
        "frame": {
          "type": "string",
          "enum": [
            "xyz",
            "tvl"
          ]
        },
        "samples": {
          "$ref": "#/$defs/ParticleVelocity"
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "receiverId",
        "sampleRateHz",
        "startMs",
        "frame",
        "samples",
        "validity"
      ],
      "description": "A predicted ground-motion time history at a receiver."
    },
    "ParticleVelocity": {
      "anyOf": [
        {
          "maxItems": 200000,
          "type": "array",
          "items": {
            "minItems": 3,
            "maxItems": 3,
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "description": "One row per sample, `[a, b, c]` in frame order, mm/s."
        },
        {
          "$ref": "#/$defs/BlobRef"
        }
      ],
      "description": "Three-component particle velocity, mm/s: one `[a, b, c]` row per sample in frame order, so every component has the same length; or a reference to the same rows."
    },
    "BlobRef": {
      "type": "object",
      "properties": {
        "uri": {
          "type": "string",
          "format": "uri",
          "description": "A signed HTTPS URL. It expires; fetch it once when the run starts."
        },
        "sha256": {
          "type": "string",
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "A content hash: `sha256:` followed by 64 lower-case hex digits."
        },
        "mediaType": {
          "type": "string",
          "minLength": 3,
          "maxLength": 120,
          "description": "IANA media type, for example `image/tiff` or `application/vnd.las`."
        },
        "bytes": {
          "description": "Size in bytes.",
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "uri",
        "sha256",
        "mediaType"
      ],
      "description": "A large payload passed by reference rather than inline: terrain, point clouds, long waveforms."
    },
    "ReceiverSpectrum": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "receiver_spectrum"
        },
        "receiverId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "spectrum": {
          "maxItems": 10000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "frequencyHz": {
                "type": "number",
                "exclusiveMinimum": 0
              },
              "amplitude": {
                "type": "number",
                "minimum": 0,
                "description": "Fourier amplitude, mm/s·s."
              }
            },
            "required": [
              "frequencyHz",
              "amplitude"
            ]
          },
          "description": "The spectrum, by ascending frequency."
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "receiverId",
        "spectrum",
        "validity"
      ],
      "description": "A predicted amplitude spectrum at a receiver."
    },
    "PpvField": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "ppv_field"
        },
        "grid": {
          "$ref": "#/$defs/Grid"
        },
        "percentile": {
          "type": "number",
          "minimum": 0,
          "maximum": 100
        },
        "basis": {
          "$ref": "#/$defs/PpvBasis"
        },
        "columns": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "Nodes along x: ⌊(maxX − minX) / cellM⌋ + 1."
        },
        "rows": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "Nodes along y: ⌊(maxY − minY) / cellM⌋ + 1."
        },
        "valuesMmS": {
          "anyOf": [
            {
              "maxItems": 4000000,
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "type": "number",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            {
              "$ref": "#/$defs/BlobRef"
            }
          ],
          "description": "Row-major from the south-west corner; null where the model declines to predict."
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "grid",
        "percentile",
        "basis",
        "columns",
        "rows",
        "valuesMmS",
        "validity"
      ],
      "description": "Peak particle velocity over a plan-view grid, at one percentile."
    },
    "Grid": {
      "type": "object",
      "properties": {
        "minX": {
          "type": "number"
        },
        "minY": {
          "type": "number"
        },
        "maxX": {
          "type": "number"
        },
        "maxY": {
          "type": "number"
        },
        "cellM": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Cell size, m."
        },
        "elevation": {
          "type": "string",
          "enum": [
            "terrain",
            "collar_plane"
          ],
          "description": "Where grid points sit vertically."
        }
      },
      "required": [
        "minX",
        "minY",
        "maxX",
        "maxY",
        "cellM",
        "elevation"
      ],
      "description": "A plan-view grid for field outputs. A field’s values sit at the nodes minX + i·cellM, minY + j·cellM for i below its `columns` and j below its `rows`, the last node within a cell of maxX and maxY; a reader takes `columns` and `rows` from the output and never recomputes them."
    },
    "Airblast": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "airblast"
        },
        "receiverId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "peakPa": {
          "description": "Peak overpressure, Pa. dB(L) = 20·log10(Pa / 20 µPa).",
          "$ref": "#/$defs/Estimate"
        },
        "peakDbL": {
          "description": "The same peak in dB(L), as the model computed it, so a limit in decibels is read without a round trip.",
          "$ref": "#/$defs/Estimate"
        },
        "scaledDistance": {
          "description": "The cube-root scaled distance the prediction was made at.",
          "type": "number",
          "exclusiveMinimum": 0
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "receiverId",
        "peakPa",
        "validity"
      ],
      "description": "Predicted peak overpressure at a receiver."
    },
    "DamageEnvelope": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "damage_envelope"
        },
        "thresholdMmS": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "The PPV taken as the onset of damage for this envelope."
        },
        "criterion": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "description": "Name of the damage criterion, for example `holmberg-persson`, `charge-column-integration` or `hoek-brown-strain`."
        },
        "holes": {
          "maxItems": 5000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "holeId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
                "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
              },
              "radiusM": {
                "description": "Radius from the hole axis at which the threshold is reached.",
                "$ref": "#/$defs/Estimate"
              }
            },
            "required": [
              "holeId",
              "radiusM"
            ]
          }
        },
        "surface": {
          "description": "The envelope as a closed mesh, when available.",
          "$ref": "#/$defs/BlobRef"
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "thresholdMmS",
        "criterion",
        "holes",
        "validity"
      ],
      "description": "The zone around the charges where rock is expected to be damaged: the basis for wall control."
    },
    "StrainField": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "strain_field"
        },
        "grid": {
          "$ref": "#/$defs/Grid"
        },
        "depthM": {
          "type": "number",
          "minimum": 0,
          "description": "How far below the grid's plane the field was read, m, the plane being the charged holes' mean collar height for `collar_plane`: the strain the rock sees at the charges' level, not at the surface."
        },
        "columns": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "Nodes along x: ⌊(maxX − minX) / cellM⌋ + 1."
        },
        "rows": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "Nodes along y: ⌊(maxY − minY) / cellM⌋ + 1."
        },
        "valuesMicrostrain": {
          "anyOf": [
            {
              "maxItems": 4000000,
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "type": "number",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            {
              "$ref": "#/$defs/BlobRef"
            }
          ],
          "description": "Peak principal extension of the rock at each cell over the whole blast, microstrain, row-major from the south-west corner; null where the model declines to predict."
        },
        "thresholdMicrostrain": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "The extension at which a plane P wave reaches the model's criterion, for a viewer to contour: the strain of the PPV its `damage_envelope` reports as `thresholdMmS`."
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "grid",
        "depthM",
        "columns",
        "rows",
        "valuesMicrostrain",
        "thresholdMicrostrain",
        "validity"
      ],
      "description": "Peak extension of the rock over a plan-view grid at a depth: where a blast strains the rock past its strength, and by how much."
    },
    "FlyrockZone": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "flyrock_zone"
        },
        "maxThrowM": {
          "description": "Maximum expected throw, m.",
          "$ref": "#/$defs/Estimate"
        },
        "mechanism": {
          "type": "string",
          "enum": [
            "face_burst",
            "cratering",
            "rifling",
            "combined"
          ]
        },
        "clearance": {
          "description": "Recommended clearance distances.",
          "type": "object",
          "properties": {
            "personnelM": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "equipmentM": {
              "type": "number",
              "exclusiveMinimum": 0
            }
          },
          "required": [
            "personnelM",
            "equipmentM"
          ]
        },
        "holes": {
          "maxItems": 5000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "holeId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
                "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
              },
              "maxThrowM": {
                "type": "number",
                "minimum": 0
              },
              "faceBurstM": {
                "description": "How far the face would throw rock out through the hole’s burden, m.",
                "type": "number",
                "minimum": 0
              },
              "crateringM": {
                "description": "How far the hole’s stemming and the rock over its charge would be thrown if they blew out, m. Whether they do is not this figure: the hole’s confinement says that.",
                "type": "number",
                "minimum": 0
              },
              "stemmingM": {
                "description": "The stemming the hole was read at, m: as loaded where the case’s as-built record states the hole’s decks, else as designed.",
                "type": "number",
                "minimum": 0
              },
              "stemmingAsLoaded": {
                "description": "True where `stemmingM` is the as-built record’s.",
                "type": "boolean"
              }
            },
            "required": [
              "holeId",
              "maxThrowM"
            ]
          }
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "maxThrowM",
        "mechanism",
        "validity"
      ],
      "description": "Predicted flyrock range and clearance."
    },
    "FragmentationCurve": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "fragmentation_curve"
        },
        "domainId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
          "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
        },
        "curve": {
          "minItems": 2,
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "sizeMm": {
                "type": "number",
                "exclusiveMinimum": 0,
                "description": "Sieve size, mm."
              },
              "passingPercent": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Percent of the rock passing that size."
              }
            },
            "required": [
              "sizeMm",
              "passingPercent"
            ]
          },
          "description": "The curve, by ascending sieve size."
        },
        "x50Mm": {
          "$ref": "#/$defs/Estimate"
        },
        "x80Mm": {
          "$ref": "#/$defs/Estimate"
        },
        "uniformityIndex": {
          "type": "number",
          "minimum": 0
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "curve",
        "x50Mm",
        "x80Mm",
        "validity"
      ],
      "description": "Predicted size distribution of the broken rock."
    },
    "MovementField": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "movement_field"
        },
        "points": {
          "maxItems": 50000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
                "description": "Names the point within this field."
              },
              "from": {
                "$ref": "#/$defs/Point3"
              },
              "to": {
                "$ref": "#/$defs/Point3"
              },
              "flight": {
                "description": "When the point moves. Without a `touchdown`, a viewer may draw it as an arc under gravity that joins `from` and `to` in that time; the model’s own path may differ, such as a block that rolls after it lands. Left out, only the displacement is stated.",
                "type": "object",
                "properties": {
                  "launchMs": {
                    "type": "number",
                    "description": "When the point leaves `from`, milliseconds from T0."
                  },
                  "durationMs": {
                    "type": "number",
                    "minimum": 0,
                    "description": "How long it then takes to come to rest at `to`, milliseconds."
                  },
                  "touchdown": {
                    "description": "Where the point first comes down on something, or the rock round it closes on it: the end of its flight through the air, before it rolls, slides, slumps or drops to `to`, or is carried up to it. The arc under gravity that joins `from` and `at` in `afterMs` is the model’s own flight; from there the point moves to `to`, arriving at `durationMs`. Left out, the flight is not told apart from what follows it.",
                    "type": "object",
                    "properties": {
                      "at": {
                        "$ref": "#/$defs/Point3"
                      },
                      "afterMs": {
                        "type": "number",
                        "minimum": 0,
                        "description": "When it gets there, milliseconds after it leaves `from`: never after `durationMs`."
                      }
                    },
                    "required": [
                      "at",
                      "afterMs"
                    ]
                  }
                },
                "required": [
                  "launchMs",
                  "durationMs"
                ]
              },
              "source": {
                "description": "The point as a source for a model of the broken rock’s surface, a flow over the ground the blast empties: how much rock, how heavy, the momentum it starts with and whether the mass carries it. A field that states sources states one for every point with a flight (conformance holds it to that). Left out, a viewer infers the flow from where the points rest.",
                "type": "object",
                "properties": {
                  "volumeM3": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "description": "The rock the point stands for as it stood in the bench, m³: in situ, before any swell."
                  },
                  "massKg": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "description": "That rock’s mass, kg."
                  },
                  "momentum": {
                    "type": "object",
                    "properties": {
                      "x": {
                        "type": "number",
                        "description": "Eastward, kg·m/s."
                      },
                      "y": {
                        "type": "number",
                        "description": "Northward, kg·m/s."
                      }
                    },
                    "required": [
                      "x",
                      "y"
                    ],
                    "description": "The level momentum the rock leaves `from` with at `flight.launchMs`, kg·m/s, once whatever confines or relieves it has taken its share: what a flow of the broken rock over the ground starts with. Its vertical part is not stated; the flight is."
                  },
                  "carried": {
                    "type": "boolean",
                    "description": "True where the mass carries the rock: it rides the burden as its cover (a model may hold the stemming above the charge so wherever it comes down), or it comes down and rests over the ground the blast breaks or the bench round it. False where it is thrown clear, out through the free face onto the floor in front, a piece of its own."
                  }
                },
                "required": [
                  "volumeM3",
                  "massKg",
                  "momentum",
                  "carried"
                ]
              }
            },
            "required": [
              "id",
              "from",
              "to"
            ]
          }
        },
        "muckpileSurface": {
          "description": "The predicted muckpile surface.",
          "$ref": "#/$defs/BlobRef"
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "points",
        "validity"
      ],
      "description": "Predicted displacement of the burden, and the muckpile it forms."
    },
    "Point3": {
      "type": "object",
      "properties": {
        "x": {
          "type": "number",
          "description": "Easting, metres."
        },
        "y": {
          "type": "number",
          "description": "Northing, metres."
        },
        "z": {
          "type": "number",
          "description": "Elevation, metres. Up is positive."
        }
      },
      "required": [
        "x",
        "y",
        "z"
      ],
      "description": "A position in the case coordinate system, metres."
    },
    "TimingProposal": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "timing_proposal"
        },
        "objective": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "What the proposal optimises, for example `minimise P95 PPV at receiver R3`."
        },
        "holes": {
          "maxItems": 5000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "holeId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
                "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
              },
              "totalMs": {
                "type": "number"
              }
            },
            "required": [
              "holeId",
              "totalMs"
            ]
          }
        },
        "expected": {
          "description": "What the model expects the proposed timing to achieve.",
          "maxItems": 1000,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "receiverId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
                "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
              },
              "ppvMmS": {
                "$ref": "#/$defs/Estimate"
              }
            },
            "required": [
              "receiverId",
              "ppvMmS"
            ]
          }
        },
        "validity": {
          "$ref": "#/$defs/Validity"
        }
      },
      "required": [
        "kind",
        "objective",
        "holes",
        "validity"
      ],
      "description": "A proposed set of firing times."
    },
    "Unavailable": {
      "oneOf": [
        {
          "$ref": "#/$defs/ReceiverUnavailable"
        },
        {
          "$ref": "#/$defs/CaseUnavailable"
        }
      ],
      "description": "A requested output the model declined to produce, and why."
    },
    "ReceiverUnavailable": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "receiver_ppv",
            "max_charge",
            "receiver_waveform",
            "receiver_spectrum",
            "airblast"
          ]
        },
        "receiverId": {
          "anyOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
              "description": "An opaque identifier: letters, digits, `.`, `_`, `:` and `-`, up to 128 characters."
            },
            {
              "type": "null"
            }
          ],
          "description": "The receiver the model declined at, or null when it declines the kind at every receiver (for example, the case names none)."
        },
        "reason": {
          "type": "string",
          "enum": [
            "not_supported",
            "missing_input",
            "outside_validity",
            "insufficient_calibration"
          ],
          "description": "Why the model did not answer."
        },
        "detail": {
          "type": "string",
          "maxLength": 500
        }
      },
      "required": [
        "kind",
        "receiverId",
        "reason"
      ],
      "description": "A kind answered per receiver that the model declined, at one receiver or at all of them."
    },
    "CaseUnavailable": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "ppv_field",
            "damage_envelope",
            "strain_field",
            "flyrock_zone",
            "fragmentation_curve",
            "movement_field",
            "timing_proposal",
            "hole_diagnosis",
            "strain_amplitude"
          ]
        },
        "reason": {
          "type": "string",
          "enum": [
            "not_supported",
            "missing_input",
            "outside_validity",
            "insufficient_calibration"
          ],
          "description": "Why the model did not answer."
        },
        "detail": {
          "type": "string",
          "maxLength": 500
        }
      },
      "required": [
        "kind",
        "reason"
      ],
      "description": "A kind answered once for the case that the model declined."
    },
    "Warning": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string",
          "maxLength": 80,
          "pattern": "^[a-z][a-z0-9_.-]*$",
          "description": "A stable machine-readable code. Reserved: `interval.approximate`, the intervals on this result are approximate (for example, the fit came without its leverage statistics); `target` narrows it to a receiver. Reserved for a diagnosis: `diagnosis.not_converged`, the fit had not settled when it stopped; `diagnosis.single_record`, only one record was read, so nothing cross-checks it; `diagnosis.det_log_disagrees`, a hole the firing record says did not fire shows in the records; `diagnosis.heading_unknown`, no record’s heading is known, so shear is not read."
        },
        "message": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1000
        },
        "target": {
          "description": "What the warning is about: a hole id, a receiver id or a field path.",
          "type": "string",
          "maxLength": 200
        }
      },
      "required": [
        "code",
        "message"
      ],
      "description": "Something the model wants a person to see."
    }
  }
}
