{
  "$defs": {
    "About": {
      "additionalProperties": false,
      "description": "The plain-language card of one catalog item.\n\nEvery field except the expert ones is required, so an About that validates is\nstructurally complete; :func:`about_issues` adds the editorial rules (one or two\nsentences, no jargon in the summary, known chooser tags, related ids that exist).",
      "examples": [
        {
          "assumptions": [
            "Numbers can be added up (spend, sales); averages are averaged"
          ],
          "avoid_when": [
            "Your data already has one row per week"
          ],
          "data_needs": [
            "A date column"
          ],
          "difficulty": "beginner",
          "example": "Daily TV spend of 100 each day becomes 700 for the week.",
          "fits": {
            "goal": [
              "check_data"
            ]
          },
          "outputs": [
            "The same columns, one row per week"
          ],
          "related": [
            "core.calendar"
          ],
          "summary_plain": "Adds up daily numbers into one number per week (or month), so that every row covers the same length of time.",
          "title": "Weekly totals",
          "typical_runtime": "instant",
          "use_when": [
            "Your data is daily but you want to plan or report by week"
          ]
        }
      ],
      "properties": {
        "assumptions": {
          "items": {
            "type": "string"
          },
          "maxItems": 10,
          "minItems": 1,
          "title": "Assumptions",
          "type": "array"
        },
        "avoid_when": {
          "items": {
            "type": "string"
          },
          "maxItems": 10,
          "minItems": 1,
          "title": "Avoid When",
          "type": "array"
        },
        "data_needs": {
          "items": {
            "type": "string"
          },
          "maxItems": 10,
          "minItems": 1,
          "title": "Data Needs",
          "type": "array"
        },
        "difficulty": {
          "$ref": "#/$defs/Difficulty"
        },
        "example": {
          "description": "A tiny example",
          "maxLength": 600,
          "minLength": 10,
          "title": "Example",
          "type": "string"
        },
        "expert_notes": {
          "anyOf": [
            {
              "maxLength": 4000,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Maths, estimator details, caveats for experts",
          "title": "Expert Notes"
        },
        "fits": {
          "additionalProperties": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "description": "Chooser tags: question id -> the answers this item suits",
          "maxProperties": 16,
          "title": "Fits",
          "type": "object"
        },
        "outputs": {
          "items": {
            "type": "string"
          },
          "maxItems": 10,
          "minItems": 1,
          "title": "Outputs",
          "type": "array"
        },
        "references": {
          "items": {
            "$ref": "#/$defs/AboutReference"
          },
          "maxItems": 10,
          "title": "References",
          "type": "array"
        },
        "related": {
          "description": "Ids of related catalog items (models, steps, nodes, checks, templates)",
          "items": {
            "type": "string"
          },
          "maxItems": 16,
          "title": "Related",
          "type": "array"
        },
        "summary_plain": {
          "description": "One or two sentences without jargon: what it does for you",
          "maxLength": 400,
          "minLength": 20,
          "title": "Summary Plain",
          "type": "string"
        },
        "title": {
          "description": "A human name",
          "maxLength": 80,
          "minLength": 3,
          "title": "Title",
          "type": "string"
        },
        "typical_runtime": {
          "$ref": "#/$defs/TypicalRuntime"
        },
        "use_when": {
          "items": {
            "type": "string"
          },
          "maxItems": 10,
          "minItems": 1,
          "title": "Use When",
          "type": "array"
        }
      },
      "required": [
        "title",
        "summary_plain",
        "use_when",
        "avoid_when",
        "data_needs",
        "assumptions",
        "outputs",
        "difficulty",
        "typical_runtime",
        "example"
      ],
      "title": "About",
      "type": "object"
    },
    "AboutReference": {
      "additionalProperties": false,
      "description": "A source for the expert detail (a paper, a book chapter, library documentation).",
      "properties": {
        "title": {
          "maxLength": 300,
          "minLength": 3,
          "title": "Title",
          "type": "string"
        },
        "url": {
          "anyOf": [
            {
              "maxLength": 500,
              "pattern": "^https://\\S+$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Url"
        }
      },
      "required": [
        "title"
      ],
      "title": "AboutReference",
      "type": "object"
    },
    "AsOfSplit": {
      "additionalProperties": false,
      "description": "Train on coords up to and including ``as_of``; hold out the rest (optionally ``gap``).",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "as_of": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "integer"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "title": "As Of"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "gap": {
          "default": 0,
          "minimum": 0,
          "title": "Gap",
          "type": "integer"
        },
        "kind": {
          "const": "as_of",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "kind",
        "dim",
        "as_of"
      ],
      "title": "AsOfSplit",
      "type": "object"
    },
    "Binding": {
      "additionalProperties": false,
      "description": "Canonical name \u2192 dataset name, for dims and variables (``geo: market``).\n\nNames that are not bound keep their dataset name. The binding is stored in the bundle\nand reversed on every output by default.",
      "properties": {
        "dims": {
          "maxProperties": 256,
          "patternProperties": {
            "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$": {
              "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
              "type": "string"
            }
          },
          "title": "Dims",
          "type": "object"
        },
        "vars": {
          "maxProperties": 4096,
          "patternProperties": {
            "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$": {
              "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
              "type": "string"
            }
          },
          "title": "Vars",
          "type": "object"
        }
      },
      "title": "Binding",
      "type": "object"
    },
    "BusinessPriors": {
      "additionalProperties": false,
      "description": "Beliefs in business units, per channel (keys are channel *labels* as in the data),\nand/or about the baseline. At least one belief is stated: a baseline belief alone is\nfine (channels without a belief keep the model's default priors).",
      "examples": [
        {
          "baseline_share": {
            "credibility": 0.8,
            "share": [
              0.5,
              0.8
            ]
          },
          "channels": {
            "search": {
              "credibility": 0.9,
              "roi": [
                2.0,
                6.0
              ]
            },
            "social": {
              "contribution_share": [
                0.02,
                0.1
              ]
            },
            "tv": {
              "credibility": 0.8,
              "roi": [
                1.5,
                4.0
              ]
            }
          },
          "kpi_units": "EUR",
          "spend_units": "EUR"
        }
      ],
      "properties": {
        "baseline_share": {
          "anyOf": [
            {
              "$ref": "#/$defs/ShareBelief"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "channels": {
          "additionalProperties": {
            "$ref": "#/$defs/ChannelBelief"
          },
          "maxProperties": 64,
          "title": "Channels",
          "type": "object"
        },
        "kpi_units": {
          "anyOf": [
            {
              "maxLength": 64,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "examples": [
            "EUR"
          ],
          "title": "Kpi Units"
        },
        "spend_units": {
          "anyOf": [
            {
              "maxLength": 64,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "examples": [
            "EUR"
          ],
          "title": "Spend Units"
        }
      },
      "title": "BusinessPriors",
      "type": "object"
    },
    "Calibration": {
      "additionalProperties": false,
      "description": "The ``calibration`` block of a ``ModelSpec``: beliefs and experiment results the model\nshould respect. ``prior_translation_run`` records which ``translate_priors`` run produced\nthe priors in ``model.settings.priors`` (lineage only).",
      "examples": [
        {
          "business_priors": {
            "baseline_share": {
              "credibility": 0.8,
              "share": [
                0.5,
                0.8
              ]
            },
            "channels": {
              "search": {
                "credibility": 0.9,
                "roi": [
                  2.0,
                  6.0
                ]
              },
              "social": {
                "contribution_share": [
                  0.02,
                  0.1
                ]
              },
              "tv": {
                "credibility": 0.8,
                "roi": [
                  1.5,
                  4.0
                ]
              }
            },
            "kpi_units": "EUR",
            "spend_units": "EUR"
          },
          "lift_tests": [
            {
              "channel": "tv",
              "ci": [
                -380000.0,
                -140000.0
              ],
              "ci_level": 0.9,
              "description": "TV off in Germany for four weeks",
              "end": "2026-05-03",
              "geo": "de",
              "id": "tv-holdout-2026q2",
              "kpi_delta": -260000.0,
              "spend_delta": -120000.0,
              "start": "2026-04-06"
            }
          ]
        }
      ],
      "properties": {
        "business_priors": {
          "anyOf": [
            {
              "$ref": "#/$defs/BusinessPriors"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "lift_tests": {
          "items": {
            "$ref": "#/$defs/LiftTest"
          },
          "maxItems": 500,
          "title": "Lift Tests",
          "type": "array"
        },
        "prior_translation_run": {
          "anyOf": [
            {
              "pattern": "^run_[0-9a-z]{26}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Prior Translation Run"
        }
      },
      "title": "Calibration",
      "type": "object"
    },
    "ChannelBelief": {
      "additionalProperties": false,
      "description": "What the user believes about one channel: exactly one of ``roi`` or\n``contribution_share``, held with ``credibility``.\n\n* ``roi``: KPI units gained per spend unit over the fit window (``[1.5, 4]`` = \"every\n  euro returns between 1.5 and 4 euros of revenue\");\n* ``contribution_share``: the channel's share of the KPI over the fit window.",
      "properties": {
        "contribution_share": {
          "anyOf": [
            {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "number"
                },
                {
                  "type": "number"
                }
              ],
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "examples": [
            [
              0.05,
              0.15
            ]
          ],
          "title": "Contribution Share"
        },
        "credibility": {
          "default": 0.8,
          "description": "Probability that the true value lies inside the range (0.8 = 'I am 80% sure')",
          "maximum": 0.99,
          "minimum": 0.5,
          "title": "Credibility",
          "type": "number"
        },
        "roi": {
          "anyOf": [
            {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "number"
                },
                {
                  "type": "number"
                }
              ],
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "examples": [
            [
              1.5,
              4.0
            ]
          ],
          "title": "Roi"
        }
      },
      "title": "ChannelBelief",
      "type": "object"
    },
    "Checkpoint": {
      "additionalProperties": false,
      "description": "Resumable checkpoints: every N draws (samplers) or every N epochs (v0.15: networks).\n\nExactly one is set. The model must be checkpointable; otherwise validation warns and\nthe run ignores it.",
      "properties": {
        "every_draws": {
          "anyOf": [
            {
              "maximum": 1000000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Every Draws"
        },
        "every_epochs": {
          "anyOf": [
            {
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "v0.15: networks trained in epochs",
          "title": "Every Epochs"
        }
      },
      "title": "Checkpoint",
      "type": "object"
    },
    "CollectionRead": {
      "additionalProperties": false,
      "description": "How a model that reads datasets sees a collection: a seeded sample as\n``pixels(image, y, x, channel)`` (``ModelSpec`` ``data.collection``).",
      "properties": {
        "max_samples": {
          "default": 5000,
          "maximum": 100000,
          "minimum": 1,
          "title": "Max Samples",
          "type": "integer"
        },
        "mode": {
          "default": "auto",
          "enum": [
            "auto",
            "gray",
            "rgb"
          ],
          "title": "Mode",
          "type": "string"
        },
        "resize": {
          "default": "stretch",
          "enum": [
            "stretch",
            "crop",
            "pad"
          ],
          "title": "Resize",
          "type": "string"
        },
        "seed": {
          "anyOf": [
            {
              "exclusiveMaximum": 4294967296,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Default: the fit seed",
          "title": "Seed"
        },
        "size": {
          "anyOf": [
            {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "integer"
                },
                {
                  "type": "integer"
                }
              ],
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "(height, width); default: the collection's size (needed when it has none)",
          "title": "Size"
        },
        "stratify": {
          "anyOf": [
            {
              "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": "label",
          "description": "Keep the share of each level of this var or coord (none: uniform)",
          "title": "Stratify"
        }
      },
      "title": "CollectionRead",
      "type": "object"
    },
    "ColumnSplit": {
      "additionalProperties": false,
      "description": "Hold out the rows whose value in ``column`` is one of ``holdout`` (a fixed split read\nfrom the data, v0.14).\n\n``column`` is a coord or a 1-D data variable along ``dim``. Values compare as text, so\n``holdout: [1]`` matches a stored ``1`` or ``\"1\"``. With ``train`` set, rows in neither\nlist are dropped; else every other row trains.",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "column": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Column",
          "type": "string"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "holdout": {
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              }
            ]
          },
          "maxItems": 64,
          "minItems": 1,
          "title": "Holdout",
          "type": "array"
        },
        "kind": {
          "const": "column",
          "title": "Kind",
          "type": "string"
        },
        "train": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "integer"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              },
              "maxItems": 64,
              "minItems": 1,
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Train"
        }
      },
      "required": [
        "kind",
        "dim",
        "column"
      ],
      "title": "ColumnSplit",
      "type": "object"
    },
    "DataBlock": {
      "additionalProperties": false,
      "description": "The dataset, side tables, binding to canonical names and the split.\n\nFor a tree dataset (M4), ``nodes`` picks the nodes whose variables are merged into the\nONE flat dataset the pipeline sees (:meth:`mapi_spec.tree.TreeSchema.flat`), aligned per\n``join``. Without ``nodes`` a tree dataset's root node is read (when it has variables).\n``nodes`` on a plain dataset is an error. The binding applies to the merged names.\n\nFor an image collection (v0.15), ``collection`` says how a model that reads datasets\nsees it (a seeded sample); a model that trains on batches reads every image.",
      "properties": {
        "bind": {
          "$ref": "#/$defs/Binding"
        },
        "collection": {
          "anyOf": [
            {
              "$ref": "#/$defs/CollectionRead"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "v0.15: an image collection read by a model that reads datasets: a seeded sample as pixels(image, y, x, channel)"
        },
        "dataset": {
          "description": "Dataset reference dataset://project/name@version",
          "examples": [
            "dataset://growth/eu_media_daily@latest"
          ],
          "pattern": "^dataset://([a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?)/([a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?)(?:@(latest|v\\d{1,9}|\\d{1,9}|sha256:[0-9a-f]{64}))?$",
          "title": "Dataset",
          "type": "string"
        },
        "join": {
          "default": "exact",
          "description": "M4: how the coords of the selected nodes align (xarray merge join)",
          "enum": [
            "exact",
            "inner",
            "outer"
          ],
          "title": "Join",
          "type": "string"
        },
        "nodes": {
          "anyOf": [
            {
              "maxProperties": 64,
              "minProperties": 1,
              "patternProperties": {
                "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$": {
                  "$ref": "#/$defs/NodeSelect"
                }
              },
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "M4: tree datasets: the nodes merged into the pipeline input",
          "title": "Nodes"
        },
        "side_tables": {
          "maxProperties": 32,
          "patternProperties": {
            "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$": {
              "$ref": "#/$defs/SideTable"
            }
          },
          "title": "Side Tables",
          "type": "object"
        },
        "split": {
          "anyOf": [
            {
              "discriminator": {
                "mapping": {
                  "as_of": "#/$defs/AsOfSplit",
                  "column": "#/$defs/ColumnSplit",
                  "last_per_group": "#/$defs/LastPerGroupSplit",
                  "leave_group_out": "#/$defs/LeaveGroupOutSplit",
                  "mask_block": "#/$defs/MaskBlockSplit",
                  "stratified": "#/$defs/StratifiedSplit",
                  "time_holdout": "#/$defs/TimeHoldoutSplit"
                },
                "propertyName": "kind"
              },
              "oneOf": [
                {
                  "$ref": "#/$defs/TimeHoldoutSplit"
                },
                {
                  "$ref": "#/$defs/LastPerGroupSplit"
                },
                {
                  "$ref": "#/$defs/AsOfSplit"
                },
                {
                  "$ref": "#/$defs/LeaveGroupOutSplit"
                },
                {
                  "$ref": "#/$defs/MaskBlockSplit"
                },
                {
                  "$ref": "#/$defs/ColumnSplit"
                },
                {
                  "$ref": "#/$defs/StratifiedSplit"
                }
              ]
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Split"
        }
      },
      "required": [
        "dataset"
      ],
      "title": "DataBlock",
      "type": "object"
    },
    "Difficulty": {
      "description": "How much statistical background an item needs to use well.",
      "enum": [
        "beginner",
        "intermediate",
        "expert"
      ],
      "title": "Difficulty",
      "type": "string"
    },
    "EvaluateBlock": {
      "additionalProperties": false,
      "properties": {
        "metrics": {
          "items": {
            "pattern": "^[a-z][a-z0-9_]{0,63}$",
            "type": "string"
          },
          "maxItems": 32,
          "title": "Metrics",
          "type": "array"
        }
      },
      "title": "EvaluateBlock",
      "type": "object"
    },
    "ExecutionBlock": {
      "additionalProperties": false,
      "properties": {
        "resource_class": {
          "$ref": "#/$defs/ResourceClass",
          "default": "S"
        },
        "timeout": {
          "description": "Duration such as '45m', '1h30m' or '90s'.",
          "examples": [
            "45m",
            "1h30m"
          ],
          "pattern": "^(?=.)(\\d+h)?(\\d+m)?(\\d+s)?$",
          "title": "Timeout",
          "type": "string"
        }
      },
      "title": "ExecutionBlock",
      "type": "object"
    },
    "FitConfig": {
      "additionalProperties": false,
      "description": "Platform fit settings. Plugin-specific limits come from the manifest's ``fit_schema``.",
      "properties": {
        "chains": {
          "anyOf": [
            {
              "maximum": 64,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Chains"
        },
        "checkpoint": {
          "anyOf": [
            {
              "$ref": "#/$defs/Checkpoint"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "draws": {
          "anyOf": [
            {
              "maximum": 1000000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Draws"
        },
        "engine": {
          "anyOf": [
            {
              "pattern": "^[a-z][a-z0-9_]{0,31}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Engine"
        },
        "keep": {
          "$ref": "#/$defs/KeepConfig"
        },
        "mode": {
          "$ref": "#/$defs/FitMode",
          "default": "evaluate_then_refit"
        },
        "prior_predictive": {
          "anyOf": [
            {
              "$ref": "#/$defs/PriorPredictive"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "seed": {
          "anyOf": [
            {
              "exclusiveMaximum": 4294967296,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Seed"
        },
        "target_accept": {
          "anyOf": [
            {
              "exclusiveMaximum": 1,
              "exclusiveMinimum": 0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Target Accept"
        },
        "tune": {
          "anyOf": [
            {
              "maximum": 1000000,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Tune"
        }
      },
      "title": "FitConfig",
      "type": "object"
    },
    "FitMode": {
      "enum": [
        "evaluate_then_refit",
        "train_only"
      ],
      "title": "FitMode",
      "type": "string"
    },
    "JsonValue": {},
    "KeepConfig": {
      "additionalProperties": false,
      "description": "Optional groups kept in the bundle.",
      "properties": {
        "log_likelihood": {
          "default": false,
          "title": "Log Likelihood",
          "type": "boolean"
        },
        "posterior_predictive": {
          "anyOf": [
            {
              "$ref": "#/$defs/PosteriorPredictiveKeep"
            },
            {
              "type": "boolean"
            }
          ],
          "default": false,
          "title": "Posterior Predictive"
        },
        "prior": {
          "default": false,
          "title": "Prior",
          "type": "boolean"
        }
      },
      "title": "KeepConfig",
      "type": "object"
    },
    "LastPerGroupSplit": {
      "additionalProperties": false,
      "description": "Hold out the last ``last`` rows along ``dim`` within each group (``min_len`` kept).",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "kind": {
          "const": "last_per_group",
          "title": "Kind",
          "type": "string"
        },
        "last": {
          "default": 1,
          "minimum": 1,
          "title": "Last",
          "type": "integer"
        },
        "min_len": {
          "default": 1,
          "minimum": 1,
          "title": "Min Len",
          "type": "integer"
        },
        "within": {
          "items": {
            "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
            "type": "string"
          },
          "maxItems": 8,
          "minItems": 1,
          "title": "Within",
          "type": "array"
        }
      },
      "required": [
        "kind",
        "dim",
        "within"
      ],
      "title": "LastPerGroupSplit",
      "type": "object"
    },
    "LeaveGroupOutSplit": {
      "additionalProperties": false,
      "description": "Hold out whole groups of ``dim`` (explicit ``groups`` or ``n`` random ones).",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "groups": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "integer"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              },
              "maxItems": 10000,
              "minItems": 1,
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Groups"
        },
        "kind": {
          "const": "leave_group_out",
          "title": "Kind",
          "type": "string"
        },
        "n": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "N"
        },
        "seed": {
          "anyOf": [
            {
              "exclusiveMaximum": 4294967296,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Seed"
        }
      },
      "required": [
        "kind",
        "dim"
      ],
      "title": "LeaveGroupOutSplit",
      "type": "object"
    },
    "LiftTest": {
      "additionalProperties": false,
      "description": "One experiment: ``spend_delta`` extra spend on ``channel`` (optionally in one ``geo``)\nfrom ``start`` to ``end`` (inclusive) caused ``kpi_delta`` incremental KPI.\n\nGive the measurement's uncertainty as a standard error ``se`` *or* a confidence interval\n``ci`` at ``ci_level``. A holdout (\"turn the channel off\") is a negative ``spend_delta``\nwith (usually) a negative ``kpi_delta``.",
      "examples": [
        {
          "channel": "tv",
          "ci": [
            -380000.0,
            -140000.0
          ],
          "ci_level": 0.9,
          "description": "TV off in Germany for four weeks",
          "end": "2026-05-03",
          "geo": "de",
          "id": "tv-holdout-2026q2",
          "kpi_delta": -260000.0,
          "spend_delta": -120000.0,
          "start": "2026-04-06"
        }
      ],
      "properties": {
        "channel": {
          "maxLength": 128,
          "minLength": 1,
          "title": "Channel",
          "type": "string"
        },
        "ci": {
          "anyOf": [
            {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "number"
                },
                {
                  "type": "number"
                }
              ],
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Confidence interval of kpi_delta",
          "title": "Ci"
        },
        "ci_level": {
          "default": 0.9,
          "maximum": 0.999,
          "minimum": 0.5,
          "title": "Ci Level",
          "type": "number"
        },
        "description": {
          "anyOf": [
            {
              "maxLength": 500,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Description"
        },
        "end": {
          "format": "date",
          "title": "End",
          "type": "string"
        },
        "geo": {
          "anyOf": [
            {
              "maxLength": 128,
              "minLength": 1,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Geo"
        },
        "id": {
          "anyOf": [
            {
              "pattern": "^[a-z0-9][a-z0-9_-]{0,62}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Id"
        },
        "kpi_delta": {
          "description": "Measured incremental KPI (KPI units)",
          "title": "Kpi Delta",
          "type": "number"
        },
        "se": {
          "anyOf": [
            {
              "exclusiveMinimum": 0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Standard error of kpi_delta",
          "title": "Se"
        },
        "spend_delta": {
          "description": "Extra spend during the test (spend units)",
          "title": "Spend Delta",
          "type": "number"
        },
        "start": {
          "format": "date",
          "title": "Start",
          "type": "string"
        }
      },
      "required": [
        "channel",
        "start",
        "end",
        "spend_delta",
        "kpi_delta"
      ],
      "title": "LiftTest",
      "type": "object"
    },
    "MaskBlockSplit": {
      "additionalProperties": false,
      "description": "Hold out random blocks over ``dims`` (block sizes per dim) covering ``fraction``.",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "block": {
          "patternProperties": {
            "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$": {
              "type": "integer"
            }
          },
          "title": "Block",
          "type": "object"
        },
        "dims": {
          "items": {
            "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
            "type": "string"
          },
          "maxItems": 8,
          "minItems": 1,
          "title": "Dims",
          "type": "array"
        },
        "fraction": {
          "exclusiveMaximum": 1,
          "exclusiveMinimum": 0,
          "title": "Fraction",
          "type": "number"
        },
        "kind": {
          "const": "mask_block",
          "title": "Kind",
          "type": "string"
        },
        "seed": {
          "anyOf": [
            {
              "exclusiveMaximum": 4294967296,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Seed"
        }
      },
      "required": [
        "kind",
        "dims",
        "fraction"
      ],
      "title": "MaskBlockSplit",
      "type": "object"
    },
    "Metadata": {
      "additionalProperties": false,
      "description": "Identity and labels of a document.",
      "properties": {
        "about": {
          "anyOf": [
            {
              "$ref": "#/$defs/About"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "M3.5: a plain-language About card so others can choose this spec, pipeline or workflow (see mapi_spec.about)"
        },
        "description": {
          "anyOf": [
            {
              "maxLength": 4000,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Description"
        },
        "labels": {
          "maxProperties": 64,
          "patternProperties": {
            "^[a-z0-9]([a-z0-9_.\\-/]{0,62})$": {
              "maxLength": 63,
              "pattern": "^[A-Za-z0-9_.\\-]*$",
              "type": "string"
            }
          },
          "title": "Labels",
          "type": "object"
        },
        "name": {
          "pattern": "^[a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?$",
          "title": "Name",
          "type": "string"
        },
        "project": {
          "pattern": "^[a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?$",
          "title": "Project",
          "type": "string"
        }
      },
      "required": [
        "name",
        "project"
      ],
      "title": "Metadata",
      "type": "object"
    },
    "ModelBlock": {
      "additionalProperties": false,
      "description": "The model plugin, its settings and fit configuration.\n\n``settings.dim_policies`` is platform-reserved: a mapping ``dim \u2192 policy`` that may only\n*tighten* the plugin's policies. It is removed before plugin settings validation.",
      "properties": {
        "fit": {
          "$ref": "#/$defs/FitConfig"
        },
        "settings": {
          "additionalProperties": {
            "$ref": "#/$defs/JsonValue"
          },
          "title": "Settings",
          "type": "object"
        },
        "use": {
          "description": "Registered id with an optional semver range",
          "examples": [
            "core.resample@^1",
            "mmm.bayesian@^1.3"
          ],
          "pattern": "^(?:(?:t|vetted)/[a-z0-9][a-z0-9-]{0,62}/)?[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+(@[^@]{1,256})?$",
          "title": "Use",
          "type": "string"
        }
      },
      "required": [
        "use"
      ],
      "title": "ModelBlock",
      "type": "object"
    },
    "NodeSelect": {
      "additionalProperties": false,
      "description": "One node a spec reads from a tree dataset (an entry of ``data.nodes``).\n\nIts variables (all, or ``vars``) are merged into the flat pipeline input, renamed\n``prefix + name`` when ``prefix`` is given (so two nodes with the same variable names can\nboth be read). Dims are never renamed: a dim shared by several selected nodes must be\nthe same dim (equal :class:`~mapi_spec.schema.DimSpec`).",
      "properties": {
        "path": {
          "pattern": "^/(?:[A-Za-z_][A-Za-z0-9_.\\-]{0,63}(?:/[A-Za-z_][A-Za-z0-9_.\\-]{0,63}){0,7})?$",
          "title": "Path",
          "type": "string"
        },
        "prefix": {
          "anyOf": [
            {
              "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,31}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Prepended to the node's variable names in the flat dataset",
          "title": "Prefix"
        },
        "vars": {
          "anyOf": [
            {
              "items": {
                "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
                "type": "string"
              },
              "maxItems": 4096,
              "minItems": 1,
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Default: every variable of the node",
          "title": "Vars"
        }
      },
      "required": [
        "path"
      ],
      "title": "NodeSelect",
      "type": "object"
    },
    "OutputsBlock": {
      "additionalProperties": false,
      "properties": {
        "derived": {
          "items": {
            "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
            "type": "string"
          },
          "maxItems": 64,
          "title": "Derived",
          "type": "array"
        },
        "plots": {
          "items": {
            "description": "Registered id with an optional semver range",
            "examples": [
              "core.resample@^1",
              "mmm.bayesian@^1.3"
            ],
            "pattern": "^(?:(?:t|vetted)/[a-z0-9][a-z0-9-]{0,62}/)?[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+(@[^@]{1,256})?$",
            "type": "string"
          },
          "maxItems": 64,
          "title": "Plots",
          "type": "array"
        },
        "ui_draws": {
          "default": 300,
          "maximum": 2000,
          "minimum": 0,
          "title": "Ui Draws",
          "type": "integer"
        }
      },
      "title": "OutputsBlock",
      "type": "object"
    },
    "PosteriorPredictiveKeep": {
      "additionalProperties": false,
      "properties": {
        "thin": {
          "default": 1,
          "maximum": 10000,
          "minimum": 1,
          "title": "Thin",
          "type": "integer"
        }
      },
      "title": "PosteriorPredictiveKeep",
      "type": "object"
    },
    "PriorPredictive": {
      "additionalProperties": false,
      "properties": {
        "draws": {
          "maximum": 100000,
          "minimum": 1,
          "title": "Draws",
          "type": "integer"
        }
      },
      "required": [
        "draws"
      ],
      "title": "PriorPredictive",
      "type": "object"
    },
    "ResourceClass": {
      "description": "Executor resource class. Ordered: ``S < M < L < XL < GPU``.\n\n``GPU`` is the last (highest-rank) member on purpose: a quota whose\n``max_resource_class`` is below it refuses it, and :meth:`next_up` never offers it.",
      "enum": [
        "S",
        "M",
        "L",
        "XL",
        "GPU"
      ],
      "title": "ResourceClass",
      "type": "string"
    },
    "ScorePolicy": {
      "description": "Which side-table version is used at score time.",
      "enum": [
        "pinned",
        "latest",
        "supplied"
      ],
      "title": "ScorePolicy",
      "type": "string"
    },
    "ShareBelief": {
      "additionalProperties": false,
      "description": "A share range (for example of the baseline: KPI that happens without any media).",
      "properties": {
        "credibility": {
          "default": 0.8,
          "description": "Probability that the true value lies inside the range (0.8 = 'I am 80% sure')",
          "maximum": 0.99,
          "minimum": 0.5,
          "title": "Credibility",
          "type": "number"
        },
        "share": {
          "maxItems": 2,
          "minItems": 2,
          "prefixItems": [
            {
              "type": "number"
            },
            {
              "type": "number"
            }
          ],
          "title": "Share",
          "type": "array"
        }
      },
      "required": [
        "share"
      ],
      "title": "ShareBelief",
      "type": "object"
    },
    "SideTable": {
      "additionalProperties": false,
      "description": "A small constant dataset (FX rates, holidays ...) used by steps.\n\nA side table that is a tree dataset (M4) reads ONE node, named by ``node``.",
      "properties": {
        "node": {
          "anyOf": [
            {
              "pattern": "^/(?:[A-Za-z_][A-Za-z0-9_.\\-]{0,63}(?:/[A-Za-z_][A-Za-z0-9_.\\-]{0,63}){0,7})?$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "M4: the node of a tree dataset to read (needed when the table is a tree)",
          "title": "Node"
        },
        "ref": {
          "description": "Dataset reference dataset://project/name@version",
          "examples": [
            "dataset://growth/eu_media_daily@latest"
          ],
          "pattern": "^dataset://([a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?)/([a-z0-9](?:[a-z0-9_-]{0,126}[a-z0-9])?)(?:@(latest|v\\d{1,9}|\\d{1,9}|sha256:[0-9a-f]{64}))?$",
          "title": "Ref",
          "type": "string"
        },
        "score_policy": {
          "$ref": "#/$defs/ScorePolicy",
          "default": "pinned"
        }
      },
      "required": [
        "ref"
      ],
      "title": "SideTable",
      "type": "object"
    },
    "StepCall": {
      "additionalProperties": false,
      "description": "One pipeline step: ``{id, use: core.resample@^1, with: {...settings}}``.",
      "properties": {
        "id": {
          "pattern": "^[a-z_][a-z0-9_]{0,62}$",
          "title": "Id",
          "type": "string"
        },
        "use": {
          "description": "Registered id with an optional semver range",
          "examples": [
            "core.resample@^1",
            "mmm.bayesian@^1.3"
          ],
          "pattern": "^(?:(?:t|vetted)/[a-z0-9][a-z0-9-]{0,62}/)?[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+(@[^@]{1,256})?$",
          "title": "Use",
          "type": "string"
        },
        "with": {
          "additionalProperties": {
            "$ref": "#/$defs/JsonValue"
          },
          "title": "With",
          "type": "object"
        }
      },
      "required": [
        "id",
        "use"
      ],
      "title": "StepCall",
      "type": "object"
    },
    "StratifiedSplit": {
      "additionalProperties": false,
      "description": "Hold out a random ``fraction`` of the rows along ``dim`` in each level of ``by`` (the\nclass label; v0.14). A level with one row stays in training.",
      "properties": {
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "by": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "By",
          "type": "string"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "fraction": {
          "exclusiveMaximum": 1,
          "exclusiveMinimum": 0,
          "title": "Fraction",
          "type": "number"
        },
        "kind": {
          "const": "stratified",
          "title": "Kind",
          "type": "string"
        },
        "seed": {
          "anyOf": [
            {
              "exclusiveMaximum": 4294967296,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Seed"
        }
      },
      "required": [
        "kind",
        "dim",
        "by",
        "fraction"
      ],
      "title": "StratifiedSplit",
      "type": "object"
    },
    "TimeHoldoutSplit": {
      "additionalProperties": false,
      "description": "Hold out the end of an ordered dim: ``last`` periods, a ``fraction`` or ``after`` a coord.",
      "properties": {
        "after": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "integer"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "After"
        },
        "after_step": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Stage the split happens at (a step id or 'raw'); default: end of the stateless prefix",
          "title": "After Step"
        },
        "allow_leakage": {
          "default": false,
          "description": "Downgrade LEAKAGE_RISK to a warning (acknowledged)",
          "title": "Allow Leakage",
          "type": "boolean"
        },
        "dim": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_.\\-]{0,127}$",
          "title": "Dim",
          "type": "string"
        },
        "fraction": {
          "anyOf": [
            {
              "exclusiveMaximum": 1,
              "exclusiveMinimum": 0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Fraction"
        },
        "kind": {
          "const": "time_holdout",
          "title": "Kind",
          "type": "string"
        },
        "last": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last"
        }
      },
      "required": [
        "kind",
        "dim"
      ],
      "title": "TimeHoldoutSplit",
      "type": "object"
    },
    "TypicalRuntime": {
      "description": "Rough wall-clock time on typical data (``S`` resource class).",
      "enum": [
        "instant",
        "seconds",
        "minutes",
        "long"
      ],
      "title": "TypicalRuntime",
      "type": "string"
    }
  },
  "$id": "https://schemas.mapi.dev/v1/modelspec.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "description": "The declarative modelling file.",
  "properties": {
    "apiVersion": {
      "const": "mapi/v1",
      "title": "Apiversion",
      "type": "string"
    },
    "calibration": {
      "anyOf": [
        {
          "$ref": "#/$defs/Calibration"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Business priors and lift tests the model should respect (M3); needs a model whose capabilities declare them"
    },
    "data": {
      "$ref": "#/$defs/DataBlock"
    },
    "evaluate": {
      "$ref": "#/$defs/EvaluateBlock"
    },
    "execution": {
      "$ref": "#/$defs/ExecutionBlock"
    },
    "kind": {
      "const": "ModelSpec",
      "title": "Kind",
      "type": "string"
    },
    "metadata": {
      "$ref": "#/$defs/Metadata"
    },
    "model": {
      "$ref": "#/$defs/ModelBlock"
    },
    "outputs": {
      "$ref": "#/$defs/OutputsBlock"
    },
    "pipeline": {
      "items": {
        "$ref": "#/$defs/StepCall"
      },
      "maxItems": 64,
      "title": "Pipeline",
      "type": "array"
    }
  },
  "required": [
    "apiVersion",
    "kind",
    "metadata",
    "data",
    "model"
  ],
  "title": "ModelSpec",
  "type": "object",
  "x-mapi-contract-version": "0.2"
}
