{
  "$ref": "#/definitions/ScenarioSnapshots",
  "definitions": {
    "ScenarioSnapshots": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "scenario_id": {
            "type": "string",
            "minLength": 1,
            "description": "References Scenario.id."
          },
          "computed_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 datetime when this snapshot was computed."
          },
          "polls": {
            "type": "array",
            "items": {
              "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)."
            },
            "description": "One entry per poll in the dataset."
          },
          "extras": {
            "type": "object",
            "additionalProperties": {},
            "description": "Arbitrary extra metadata (model version, parameter summary, etc.)."
          }
        },
        "required": [
          "scenario_id",
          "computed_at",
          "polls"
        ],
        "additionalProperties": false,
        "description": "Output of the scenario derivation step for one scenario.\n\nLayer: compute (intermediate between source and aggregate layers)\n\nContains every source poll normalised to the scenario's choice grouping, with full derivation provenance. This is the input consumed by the simulation/poll-of-polls step that produces EstimateSnapshot.\n\nData flow: Poll (source) + Scenario (config) → ScenarioSnapshot (this) → EstimateSnapshot (aggregate)"
      },
      "description": "Array of ScenarioSnapshot objects."
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}