{
  "$ref": "#/definitions/ScenarioPoll",
  "definitions": {
    "ScenarioPoll": {
      "type": "object",
      "properties": {
        "poll_id": {
          "type": "string",
          "minLength": 1,
          "description": "References Poll.id."
        },
        "results": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "choice_id": {
                "type": "string",
                "minLength": 1,
                "description": "References Choice.id."
              },
              "value_percent": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Percentage value (0–100). Null when derivation.method = \"absent\" (value could not be estimated)."
              },
              "is_estimated": {
                "type": "boolean",
                "description": "True when any part of this value was computed rather than directly measured by the pollster. False only for method=\"direct\" and method=\"members_sum\" (where all source values are direct)."
              },
              "derivation": {
                "type": "object",
                "properties": {
                  "method": {
                    "type": "string",
                    "enum": [
                      "direct",
                      "members_sum",
                      "members_partial",
                      "cut_estimate",
                      "pool_estimate",
                      "temporal",
                      "others_sum",
                      "absent"
                    ],
                    "description": "How a DerivedResult value was obtained.\n\ndirect: value came directly from a matching poll output (not estimated).\nmembers_sum: sum of all member choices; every member resolved directly (not estimated).\nmembers_partial: sum of present members; one or more members estimated.\ncut_estimate: half of lower_cut_percent from the same poll output.\npool_estimate: distributed share of the unaccounted pool value.\nothers_sum: sum of all poll values not mapped to any named choice (for others_id).\ntemporal: estimated from neighboring polls by date (reserved, not yet implemented).\nabsent: no basis for estimation; value_percent will be null."
                  },
                  "source_output_type": {
                    "type": "string",
                    "description": "output_type of the raw PollOutput used as source."
                  },
                  "source_tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "tags[] of the raw PollOutput used as source."
                  },
                  "source_members": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "choice_ids of member choices that were successfully resolved."
                  },
                  "missing_members": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "choice_ids of member choices that could not be resolved."
                  },
                  "cut_value": {
                    "type": "number",
                    "description": "The lower_cut_percent value from the poll output (for cut_estimate)."
                  },
                  "cut_fraction": {
                    "type": "number",
                    "description": "Fraction of cut_value assigned as the estimate. Default 0.5."
                  },
                  "pool_choice_id": {
                    "type": "string",
                    "description": "choice_id of the pool choice, typically the \"others\" bucket (for pool_estimate)."
                  },
                  "pool_value": {
                    "type": "number",
                    "description": "Measured value of the pool choice in the poll output."
                  },
                  "pool_peers": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Other choice_ids sharing the pool with this one."
                  },
                  "pool_method": {
                    "type": "string",
                    "enum": [
                      "equal",
                      "proportional"
                    ],
                    "description": "Distribution method applied to the pool."
                  },
                  "temporal_source_polls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Poll ids used as source for temporal estimation (reserved)."
                  },
                  "temporal_method": {
                    "type": "string",
                    "description": "Method used to combine temporal sources (reserved)."
                  },
                  "temporal_window_days": {
                    "type": "number",
                    "description": "Time window (days) used to select neighboring polls (reserved)."
                  },
                  "extras": {
                    "type": "object",
                    "additionalProperties": {},
                    "description": "Arbitrary extra provenance fields."
                  }
                },
                "required": [
                  "method"
                ],
                "additionalProperties": false,
                "description": "Full provenance record attached to every DerivedResult.\n\nLayer: compute (embedded in DerivedResult)\n\nRecords exactly how the value was obtained from source poll data. Only fields relevant to the specific method need to be populated."
              }
            },
            "required": [
              "choice_id",
              "value_percent",
              "is_estimated",
              "derivation"
            ],
            "additionalProperties": false,
            "description": "A single choice's value within a scenario-normalised poll.\n\nLayer: compute (contained in ScenarioPoll → ScenarioSnapshot)\n\nReplaces PollResult at the scenario layer. Every value carries full provenance via the Derivation object. Contrast with PollResult (source layer) which is the raw agency-reported value."
          },
          "description": "Derived results for each choice in the scenario. Every choice listed in Scenario.choices has an entry here (with value_percent=null and method=\"absent\" if unresolvable)."
        }
      },
      "required": [
        "poll_id",
        "results"
      ],
      "additionalProperties": false,
      "description": "All derived results for one poll under one scenario.\n\nLayer: compute (contained in ScenarioSnapshot)\n\nMaps a single source Poll to the scenario's choice grouping. Input: Poll (source) + Scenario (config). Output: feeds into EstimateSnapshot (aggregate)."
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}