{
  "$ref": "#/definitions/ScenarioSnapshot",
  "definitions": {
    "ScenarioSnapshot": {
      "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)"
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}