{
  "$ref": "#/definitions/Scenarios",
  "definitions": {
    "Scenarios": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "description": "Unique slug. Referenced by ScenarioSnapshot.scenario_id."
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "description": "Human-readable label in the instance's default language."
          },
          "region": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "ISO 3166-1 alpha-2 / ISO 3166-2 region code, or array. Matches Poll.region."
          },
          "election_type": {
            "type": "string",
            "description": "Election type, e.g. \"parliamentary\". Matches Poll.type."
          },
          "is_default": {
            "type": "boolean",
            "description": "Whether this is the default scenario shown to end users. At most one scenario per region/election_type should be true."
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 1,
            "description": "Ordered list of choice_ids to include. Order controls display order in charts and tables."
          },
          "preferred_tags": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 1,
            "description": "Priority-ordered list of poll output tags to try when looking for a direct value. First matching tag wins."
          },
          "fallback": {
            "type": "string",
            "enum": [
              "members_sum",
              "none"
            ],
            "default": "members_sum",
            "description": "What to do when no raw output has the choice directly. \"members_sum\": sum member choice values. \"none\": leave as absent."
          },
          "composition": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "description": "Coalition composition map: choice_id → member choice_ids. Authoritative source for member lookups during derivation."
          },
          "others_id": {
            "type": "string",
            "description": "choice_id of the catch-all \"others\" bucket. Unmapped parties flow here automatically."
          },
          "estimation": {
            "type": "object",
            "properties": {
              "allow_partial_members": {
                "type": "boolean",
                "default": true,
                "description": "Include a coalition even if some member values are missing (partial sum). Default: true."
              },
              "active_min_polls": {
                "type": "integer",
                "minimum": 1,
                "default": 2,
                "description": "Minimum number of recent polls in which a choice must appear (at ≥ active_min_value) to be considered \"known active\". Default: 2."
              },
              "active_window_polls": {
                "type": "integer",
                "minimum": 1,
                "default": 10,
                "description": "How many of the most recent polls to search when applying the known-active filter. Default: 10."
              },
              "active_min_value": {
                "type": "number",
                "minimum": 0,
                "default": 0.3,
                "description": "Minimum value_percent a choice must have in a poll to count as an \"active\" appearance. Default: 0.3."
              },
              "cut_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "default": 0.5,
                "description": "Fraction of lower_cut_percent to assign as the estimated value for a choice under the cut. Default: 0.5 (half the cut)."
              },
              "pool_method": {
                "type": "string",
                "enum": [
                  "equal",
                  "proportional"
                ],
                "default": "equal",
                "description": "How the unaccounted residual pool is distributed among absent known-active choices. Default: \"equal\"."
              },
              "pool_cap_per_party": {
                "type": "number",
                "minimum": 0,
                "description": "Maximum value_percent any single choice may receive from the pool. No cap if omitted."
              },
              "max_total": {
                "type": "number",
                "minimum": 0,
                "default": 100,
                "description": "Hard ceiling: sum of all derived values in a scenario poll may not exceed this. Default: 100."
              }
            },
            "additionalProperties": false,
            "description": "Policy for filling missing values when deriving a poll's results under a scenario.\n\nLayer: config (embedded in Scenario)"
          },
          "extras": {
            "type": "object",
            "additionalProperties": {},
            "description": "Arbitrary extra fields."
          }
        },
        "required": [
          "id",
          "label",
          "region",
          "choices",
          "preferred_tags",
          "estimation"
        ],
        "additionalProperties": false,
        "description": "Definition of a display/simulation grouping.\n\nLayer: config (input to the compute layer)\n\nSpecifies which choices are included and how to fill missing values from raw poll data. Input to the compute-scenarios script which produces ScenarioSnapshot files."
      },
      "description": "Array of Scenario objects."
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}