{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:cede:schema:model-run:v0",
  "title": "CEDE model run result, v0",
  "description": "The result of running a registry model against a risk object (SPEC.md section 2A outputs): the index level the model measured, the payout ratio at that level, and a technical price for the structure. SPEC.md section 4 P8 puts a user model's outputs under exactly the rules a platform model's outputs live under, so this schema pins the same technical-price label and the same disclosure sentence the Price response pins, and P9 makes the model-and-version label required on the result itself.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "run_id",
    "verb",
    "model",
    "index",
    "indicated_payout",
    "expected_loss",
    "technical_price",
    "assumptions"
  ],
  "properties": {
    "schema_version": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
    },
    "run_id": {
      "type": "string",
      "minLength": 1,
      "description": "Identity of this run. Content-addressed: the same model over the same period against the same pinned snapshot yields the same id, because SPEC.md section 2A requires a result to replay byte for byte — including its id."
    },
    "verb": { "const": "model_run" },
    "model": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "version", "author", "note"],
      "description": "SPEC.md section 4 P9, in the schema itself: every result is labelled with the model and version that produced it. Required, so a result cannot exist without it.",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string", "minLength": 1 },
        "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
        "author": { "type": "string", "minLength": 1 },
        "note": { "type": "string", "minLength": 1 }
      }
    },
    "index": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "level",
        "unit",
        "payout_ratio",
        "measured_from",
        "measured_to",
        "coverage",
        "disclosure"
      ],
      "properties": {
        "level": { "type": "number" },
        "unit": { "type": "string", "minLength": 1 },
        "payout_ratio": { "type": "number", "minimum": 0, "maximum": 1 },
        "measured_from": { "type": ["string", "null"] },
        "measured_to": { "type": ["string", "null"] },
        "coverage": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "days_in_window",
            "days_reported",
            "days_missing",
            "complete_windows",
            "incomplete_windows",
            "note"
          ],
          "properties": {
            "days_in_window": { "type": "integer", "minimum": 1 },
            "days_reported": { "type": "integer", "minimum": 0 },
            "days_missing": { "type": "integer", "minimum": 0 },
            "complete_windows": { "type": "integer", "minimum": 0 },
            "incomplete_windows": { "type": "integer", "minimum": 0 },
            "note": { "type": "string", "minLength": 1 }
          }
        },
        "disclosure": { "$ref": "#/$defs/measurement_disclosure" }
      }
    },
    "indicated_payout": {
      "type": "object",
      "additionalProperties": false,
      "required": ["amount", "currency", "basis", "disclosure"],
      "description": "What the payout function returns at the measured index level, applied to the limit. An arithmetic consequence of a measurement, not a determination: SPEC.md section 3.6 holds that when a trigger condition is met Cede reports the measurement and never determines a claim, settles, or moves money, and the disclosure constant below says so on the field itself.",
      "properties": {
        "amount": { "type": "number", "minimum": 0 },
        "currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
        "basis": { "const": "payout_ratio x limit" },
        "disclosure": { "$ref": "#/$defs/measurement_disclosure" }
      }
    },
    "expected_loss": { "$ref": "#/$defs/money" },
    "technical_price": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "basis",
        "amount",
        "currency",
        "rate_on_line",
        "assumptions_ref",
        "disclosure"
      ],
      "properties": {
        "basis": {
          "const": "technical",
          "description": "SPEC.md section 4 P3 and P8: every price output is labelled technical, whoever authored the model."
        },
        "amount": { "type": "number", "minimum": 0 },
        "currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
        "rate_on_line": { "type": "number", "minimum": 0 },
        "assumptions_ref": { "type": "string", "minLength": 1 },
        "disclosure": {
          "const": "Technical price: an actuarial estimate produced by analytics software from the assumptions stated in this response. It is not a price at which any party stands ready to transact, and it carries no capacity, no acceptance and no validity period.",
          "description": "The same constant sentence price-response.v0.schema.json pins, and identical to it on purpose: P8 means a user model's price says exactly what a platform model's price says. A test asserts the two constants are byte-identical so they cannot drift apart."
        }
      }
    },
    "assumptions": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "inputs_read",
        "data_sources",
        "method",
        "burn",
        "cover_period",
        "loadings",
        "currency_basis",
        "limitations"
      ],
      "description": "The complete assumption set as first-class fields, not footnotes — the same standard SPEC.md section 3.3 sets for the Price verb.",
      "properties": {
        "inputs_read": {
          "type": "object",
          "additionalProperties": false,
          "required": ["risk_object_fields", "note"],
          "properties": {
            "risk_object_fields": {
              "type": "array",
              "items": { "type": "string", "minLength": 1 }
            },
            "note": { "type": "string", "minLength": 1 }
          }
        },
        "data_sources": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "version", "measurement", "unit"],
            "properties": {
              "id": { "type": "string", "minLength": 1 },
              "version": { "type": "string", "minLength": 1 },
              "measurement": { "type": "string", "minLength": 1 },
              "unit": { "type": "string", "minLength": 1 }
            }
          }
        },
        "method": { "type": "string", "minLength": 1 },
        "burn": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "burn_rate",
            "standard_deviation",
            "years",
            "triggering_years",
            "source"
          ],
          "properties": {
            "burn_rate": { "type": "number", "minimum": 0 },
            "standard_deviation": { "type": "number", "minimum": 0 },
            "years": { "type": "integer", "minimum": 1 },
            "triggering_years": { "type": "integer", "minimum": 0 },
            "source": { "type": "string", "minLength": 1 }
          }
        },
        "cover_period": {
          "type": "object",
          "additionalProperties": false,
          "required": ["start", "end", "days", "factor", "day_boundaries"],
          "properties": {
            "start": { "type": "string", "minLength": 1 },
            "end": { "type": "string", "minLength": 1 },
            "days": { "type": "integer", "minimum": 1 },
            "factor": { "type": "number", "minimum": 0 },
            "day_boundaries": { "type": "string", "minLength": 1 }
          }
        },
        "loadings": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["name", "rate", "applies_to", "amount", "rationale"],
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "rate": { "type": "number", "minimum": 0 },
              "applies_to": { "type": "string", "minLength": 1 },
              "amount": { "$ref": "#/$defs/money" },
              "rationale": { "type": "string", "minLength": 1 }
            }
          }
        },
        "currency_basis": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "currency",
            "minor_units",
            "rounding",
            "fx_conversion_applied"
          ],
          "properties": {
            "currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
            "minor_units": { "type": "integer", "minimum": 0 },
            "rounding": { "type": "string", "minLength": 1 },
            "fx_conversion_applied": { "const": false }
          }
        },
        "limitations": {
          "type": "array",
          "minItems": 1,
          "items": { "type": "string", "minLength": 1 }
        }
      }
    }
  },
  "$defs": {
    "money": {
      "type": "object",
      "additionalProperties": false,
      "required": ["amount", "currency"],
      "properties": {
        "amount": { "type": "number", "minimum": 0 },
        "currency": { "type": "string", "pattern": "^[A-Z]{3}$" }
      }
    },
    "measurement_disclosure": {
      "const": "Cede reports the measurement. An index at or above the attachment level is an observation of public data, not a determination that anything is payable; Cede determines nothing, settles nothing and moves no money.",
      "description": "SPEC.md section 3.6's sharpest clause, pinned as a constant on every field a reader could mistake for a determination."
    }
  }
}
