changedInput schema / properties / ops / items / oneOf
Previous value: -[
- {
- "properties": {
- "aggregation": {
- "enum": [
- "sum",
- "average",
- "stock"
- ],
- "type": "string"
- },
- "axis": {
- "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
- "enum": [
- "time",
- "branches",
- "list"
- ],
- "type": "string"
- },
- "branches": {
- "description": "mini_table compared branch ids (axis=branches); first = écart base",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "bridge_config": {
- "properties": {
- "axis": {
- "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
- "enum": [
- "time",
- "branches"
- ],
- "type": "string"
- },
- "branches": {
- "description": "Branch ids compared when axis=branches: [from, to].",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "contributions": {
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "end_period": {
- "maxLength": 50,
- "type": "string"
- },
- "endpoint": {
- "description": "Single endpoint item (stock/balance) read at start & end periods",
- "minLength": 1,
- "type": "string"
- },
- "endpoint_branch": {
- "description": "Read endpoint + contributions under this branch basis (axis=time only).",
- "minLength": 1,
- "type": "string"
- },
- "residual_label": {
- "maxLength": 200,
- "type": "string"
- },
- "start_period": {
- "maxLength": 50,
- "type": "string"
- }
- },
- "required": [
- "endpoint"
- ],
- "type": "object"
- },
- "by_branch": {
- "description": "Chart only: one styled series per branch; false reverts to the checked-out view.",
- "type": "boolean"
- },
- "cagr": {
- "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
- "type": "boolean"
- },
- "chart_type": {
- "enum": [
- "line",
- "bar",
- "area",
- "bridge"
- ],
- "type": "string"
- },
- "check": {
- "anyOf": [
- {
- "properties": {
- "tolerance": {
- "exclusiveMinimum": 0,
- "type": "number"
- }
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "Flags the line as a control judged on its own value: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals); a formula that is itself a comparison / AND / OR / NOT passes when true. `null` removes the check. Failing periods surface in `layerz_read` under `health`, non-blocking."
- },
- "comment": {
- "anyOf": [
- {
- "maxLength": 2000,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Free-form annotation on the item (null clears it)."
- },
- "compare_to": {
- "anyOf": [
- {
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "display": {
- "enum": [
- "abs",
- "pct",
- "both"
- ],
- "type": "string"
- },
- "mode": {
- "enum": [
- "branch",
- "prev_period",
- "prev_year"
- ],
- "type": "string"
- },
- "thresholds": {
- "properties": {
- "green": {
- "type": "number"
- },
- "red": {
- "type": "number"
- }
- },
- "required": [
- "green",
- "red"
- ],
- "type": "object"
- }
- },
- "required": [
- "mode"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "KPI native comparison; null clears it."
- },
- "default_branch": {
- "anyOf": [
- {
- "minLength": 1,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Dashboard-only: branch checked out when this dashboard opens; null clears it."
- },
- "format": {
- "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
- "maxLength": 50,
- "type": "string"
- },
- "formula": {
- "maxLength": 2000,
- "type": "string"
- },
- "inputs": {
- "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "item_style": {
- "enum": [
- "normal",
- "subtotal",
- "total",
- "kpi"
- ],
- "type": "string"
- },
- "list_mode": {
- "description": "Chart only: 'split' (default) = one series per list element, 'total' = the list-backed item's aggregate as one series.",
- "enum": [
- "split",
- "total"
- ],
- "type": "string"
- },
- "list_total": {
- "description": "List-mode only: show the list's total (aggregate row values). Default off; forced ON while the aggregate is referenced.",
- "type": "boolean"
- },
- "liste": {
- "description": "mini_table list whose entries become the columns (axis=list)",
- "minLength": 1,
- "type": "string"
- },
- "liste_ref": {
- "minLength": 1,
- "type": "string"
- },
- "monitor": {
- "anyOf": [
- {
- "properties": {
- "assert": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "label": {
- "maxLength": 200,
- "type": "string"
- }
- },
- "required": [
- "assert"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert is converted, any other rule is rejected; `null` removes the check."
- },
- "name": {
- "description": "New display label (also the formula identifier)",
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "op": {
- "const": "update",
- "type": "string"
- },
- "opening_balance": {
- "type": "number"
- },
- "parent": {
- "anyOf": [
- {
- "minLength": 1,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "New parent UID (null to move to root)"
- },
- "period_window": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "maxLength": 20,
- "type": "string"
- },
- "fit": {
- "enum": [
- "data",
- "full"
- ],
- "type": "string"
- },
- "start": {
- "maxLength": 20,
- "type": "string"
- }
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
- },
- "position": {
- "anyOf": [
- {
- "properties": {
- "col": {
- "maximum": 11,
- "minimum": 0,
- "type": "integer"
- },
- "row": {
- "maximum": 400,
- "minimum": 0,
- "type": "integer"
- }
- },
- "required": [
- "col",
- "row"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
- },
- "role": {
- "description": "Change the item role. Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
- "enum": [
- "assumption",
- "formula",
- "callup",
- "balance",
- "section",
- "chart",
- "dashboard",
- "kpi",
- "mini_table"
- ],
- "type": "string"
- },
- "row_break": {
- "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
- "type": "boolean"
- },
- "rows": {
- "description": "mini_table curated row item UIDs / temp IDs / names",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series": {
- "description": "Chart series UIDs",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series_defs": {
- "description": "Per-series branch; supersedes `series`.",
- "items": {
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "uid"
- ],
- "type": "object"
- },
- "type": "array"
- },
- "source": {
- "description": "Callup source UID",
- "minLength": 1,
- "type": "string"
- },
- "span": {
- "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
- "properties": {
- "cols": {
- "maximum": 12,
- "minimum": 1,
- "type": "integer"
- },
- "rows": {
- "maximum": 4,
- "minimum": 1,
- "type": "integer"
- }
- },
- "type": "object"
- },
- "sparkline": {
- "description": "KPI trend mini-chart.",
- "type": "boolean"
- },
- "target": {
- "description": "KPI/mini_table target UID",
- "minLength": 1,
- "type": "string"
- },
- "target_period": {
- "maxLength": 50,
- "type": "string"
- },
- "timeline_ref": {
- "enum": [
- "constant",
- "monthly",
- "yearly",
- "quarterly"
- ],
- "type": "string"
- },
- "timeline_values": {
- "additionalProperties": {
- "anyOf": [
- {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- }
- ]
- },
- "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
- "propertyNames": {
- "type": "string"
- },
- "type": "object"
- },
- "uid": {
- "description": "Existing item UID to update",
- "minLength": 1,
- "type": "string"
- },
- "unit": {
- "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
- "enum": [
- "none",
- "k",
- "m",
- "auto"
- ],
- "type": "string"
- },
- "value": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "values": {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- },
- "variance": {
- "description": "mini_table écart columns (axis=branches)",
- "properties": {
- "abs": {
- "type": "boolean"
- },
- "color": {
- "type": "boolean"
- },
- "pct": {
- "type": "boolean"
- }
- },
- "type": "object"
- }
- },
- "required": [
- "op",
- "uid"
- ],
- "type": "object"
- },
- {
- "properties": {
- "aggregation": {
- "enum": [
- "sum",
- "average",
- "stock"
- ],
- "type": "string"
- },
- "axis": {
- "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
- "enum": [
- "time",
- "branches",
- "list"
- ],
- "type": "string"
- },
- "branches": {
- "description": "mini_table compared branch ids (axis=branches); first = écart base",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "bridge_config": {
- "properties": {
- "axis": {
- "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
- "enum": [
- "time",
- "branches"
- ],
- "type": "string"
- },
- "branches": {
- "description": "Branch ids compared when axis=branches: [from, to].",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "contributions": {
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "end_period": {
- "maxLength": 50,
- "type": "string"
- },
- "endpoint": {
- "description": "Single endpoint item (stock/balance) read at start & end periods",
- "minLength": 1,
- "type": "string"
- },
- "endpoint_branch": {
- "description": "Read endpoint + contributions under this branch basis (axis=time only).",
- "minLength": 1,
- "type": "string"
- },
- "residual_label": {
- "maxLength": 200,
- "type": "string"
- },
- "start_period": {
- "maxLength": 50,
- "type": "string"
- }
- },
- "required": [
- "endpoint"
- ],
- "type": "object"
- },
- "by_branch": {
- "description": "Chart only: plot one styled series per branch instead of the checked-out view.",
- "type": "boolean"
- },
- "cagr": {
- "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
- "type": "boolean"
- },
- "chart_type": {
- "enum": [
- "line",
- "bar",
- "area",
- "bridge"
- ],
- "type": "string"
- },
- "check": {
- "description": "Flags the line as a control judged on its own value at every period: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals, so a balance netting to 1e-11 passes); a formula that is itself a comparison / AND / OR / NOT (\"Revenue >= 0\") passes when true. Assumption and formula only. Failing periods surface in `layerz_read` under `health`, non-blocking.",
- "properties": {
- "tolerance": {
- "exclusiveMinimum": 0,
- "type": "number"
- }
- },
- "type": "object"
- },
- "comment": {
- "anyOf": [
- {
- "maxLength": 2000,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Free-form annotation on the item (hypothesis source, rationale, review note)."
- },
- "compare_to": {
- "description": "KPI native comparison: a delta vs another branch (`mode:\"branch\"` + `branch_id`) or a shifted period (`prev_period`/`prev_year`). Rendered as the value plus a signed delta %.",
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "display": {
- "enum": [
- "abs",
- "pct",
- "both"
- ],
- "type": "string"
- },
- "mode": {
- "enum": [
- "branch",
- "prev_period",
- "prev_year"
- ],
- "type": "string"
- },
- "thresholds": {
- "properties": {
- "green": {
- "type": "number"
- },
- "red": {
- "type": "number"
- }
- },
- "required": [
- "green",
- "red"
- ],
- "type": "object"
- }
- },
- "required": [
- "mode"
- ],
- "type": "object"
- },
- "default_branch": {
- "description": "Dashboard-only: branch checked out when this dashboard opens; overrides the model default.",
- "minLength": 1,
- "type": "string"
- },
- "format": {
- "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
- "maxLength": 50,
- "type": "string"
- },
- "formula": {
- "maxLength": 2000,
- "type": "string"
- },
- "id": {
- "description": "Temp ID for intra-batch references — other create ops in the same batch can use this value as their `parent`, `source`, `target`, or `series` to wire references before the real UID is generated. Numeric (`_1`, `_2`) keeps it short; descriptive (`_starting_customers`) is fine up to 32 chars.",
- "maxLength": 32,
- "minLength": 1,
- "type": "string"
- },
- "inputs": {
- "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "item_style": {
- "enum": [
- "normal",
- "subtotal",
- "total",
- "kpi"
- ],
- "type": "string"
- },
- "list_mode": {
- "description": "Chart only: how a list-backed series renders — 'split' (default) plots one series per list element, 'total' plots the item's aggregate as one series (a list total charted without a mirror formula).",
- "enum": [
- "split",
- "total"
- ],
- "type": "string"
- },
- "list_total": {
- "description": "List-mode only: show the list's total (aggregate row values). Default off; the total is forced ON while any formula/callup/parent/widget reads the aggregate.",
- "type": "boolean"
- },
- "liste": {
- "description": "mini_table list whose entries become the columns (axis=list)",
- "minLength": 1,
- "type": "string"
- },
- "liste_ref": {
- "minLength": 1,
- "type": "string"
- },
- "monitor": {
- "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert (\"ABS(value) < t\", \"== 0\") is converted; any other rule is rejected: put it in the line's formula and set `check: {}`.",
- "properties": {
- "assert": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "label": {
- "maxLength": 200,
- "type": "string"
- }
- },
- "required": [
- "assert"
- ],
- "type": "object"
- },
- "name": {
- "description": "Display name",
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "op": {
- "const": "create",
- "type": "string"
- },
- "opening_balance": {
- "type": "number"
- },
- "parent": {
- "description": "Parent container: either an 8-char UID of an existing section/balance/dashboard, or a temp `id` from an earlier create op in the same batch (e.g. set `id: \"_pl\"` on a section create, then `parent: \"_pl\"` on each child item).",
- "minLength": 1,
- "type": "string"
- },
- "period_window": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "maxLength": 20,
- "type": "string"
- },
- "fit": {
- "enum": [
- "data",
- "full"
- ],
- "type": "string"
- },
- "start": {
- "maxLength": 20,
- "type": "string"
- }
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
- },
- "position": {
- "anyOf": [
- {
- "properties": {
- "col": {
- "maximum": 11,
- "minimum": 0,
- "type": "integer"
- },
- "row": {
- "maximum": 400,
- "minimum": 0,
- "type": "integer"
- }
- },
- "required": [
- "col",
- "row"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
- },
- "role": {
- "description": "Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
- "enum": [
- "assumption",
- "formula",
- "callup",
- "balance",
- "section",
- "chart",
- "dashboard",
- "kpi",
- "mini_table"
- ],
- "type": "string"
- },
- "row_break": {
- "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
- "type": "boolean"
- },
- "rows": {
- "description": "mini_table curated row item UIDs / temp IDs / names",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series": {
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series_defs": {
- "description": "Per-series branch: supersedes `series`. Each entry plots one item under a specific branch (e.g. Revenue@budget vs Revenue@actuals). `uid` accepts a temp id.",
- "items": {
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "uid"
- ],
- "type": "object"
- },
- "type": "array"
- },
- "source": {
- "description": "Callup source UID or temp ID",
- "minLength": 1,
- "type": "string"
- },
- "span": {
- "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
- "properties": {
- "cols": {
- "maximum": 12,
- "minimum": 1,
- "type": "integer"
- },
- "rows": {
- "maximum": 4,
- "minimum": 1,
- "type": "integer"
- }
- },
- "type": "object"
- },
- "sparkline": {
- "description": "KPI trend mini-chart under the value.",
- "type": "boolean"
- },
- "target": {
- "description": "KPI/mini_table target UID or temp ID",
- "minLength": 1,
- "type": "string"
- },
- "target_period": {
- "maxLength": 50,
- "type": "string"
- },
- "timeline_ref": {
- "enum": [
- "constant",
- "monthly",
- "yearly",
- "quarterly"
- ],
- "type": "string"
- },
- "timeline_values": {
- "additionalProperties": {
- "anyOf": [
- {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- }
- ]
- },
- "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
- "propertyNames": {
- "type": "string"
- },
- "type": "object"
- },
- "unit": {
- "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
- "enum": [
- "none",
- "k",
- "m",
- "auto"
- ],
- "type": "string"
- },
- "value": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "values": {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- },
- "variance": {
- "description": "mini_table écart columns (axis=branches)",
- "properties": {
- "abs": {
- "type": "boolean"
- },
- "color": {
- "type": "boolean"
- },
- "pct": {
- "type": "boolean"
- }
- },
- "type": "object"
- }
- },
- "required": [
- "role",
- "name",
- "op"
- ],
- "type": "object"
- },
- {
- "properties": {
- "from_uid": {
- "description": "Existing item UID to replace atomically (section/dashboard unsupported)",
- "minLength": 1,
- "type": "string"
- },
- "new_item": {
- "description": "Inline payload describing the replacement item",
- "properties": {
- "aggregation": {
- "enum": [
- "sum",
- "average",
- "stock"
- ],
- "type": "string"
- },
- "axis": {
- "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
- "enum": [
- "time",
- "branches",
- "list"
- ],
- "type": "string"
- },
- "branches": {
- "description": "mini_table compared branch ids (axis=branches); first = écart base",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "bridge_config": {
- "properties": {
- "axis": {
- "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
- "enum": [
- "time",
- "branches"
- ],
- "type": "string"
- },
- "branches": {
- "description": "Branch ids compared when axis=branches: [from, to].",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "contributions": {
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "end_period": {
- "maxLength": 50,
- "type": "string"
- },
- "endpoint": {
- "description": "Single endpoint item (stock/balance) read at start & end periods",
- "minLength": 1,
- "type": "string"
- },
- "endpoint_branch": {
- "description": "Read endpoint + contributions under this branch basis (axis=time only).",
- "minLength": 1,
- "type": "string"
- },
- "residual_label": {
- "maxLength": 200,
- "type": "string"
- },
- "start_period": {
- "maxLength": 50,
- "type": "string"
- }
- },
- "required": [
- "endpoint"
- ],
- "type": "object"
- },
- "by_branch": {
- "description": "Chart only: plot one styled series per branch instead of the checked-out view.",
- "type": "boolean"
- },
- "cagr": {
- "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
- "type": "boolean"
- },
- "chart_type": {
- "enum": [
- "line",
- "bar",
- "area",
- "bridge"
- ],
- "type": "string"
- },
- "check": {
- "description": "Flags the line as a control judged on its own value at every period: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals, so a balance netting to 1e-11 passes); a formula that is itself a comparison / AND / OR / NOT (\"Revenue >= 0\") passes when true. Assumption and formula only. Failing periods surface in `layerz_read` under `health`, non-blocking.",
- "properties": {
- "tolerance": {
- "exclusiveMinimum": 0,
- "type": "number"
- }
- },
- "type": "object"
- },
- "comment": {
- "anyOf": [
- {
- "maxLength": 2000,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Free-form annotation on the item (hypothesis source, rationale, review note)."
- },
- "compare_to": {
- "description": "KPI native comparison: a delta vs another branch (`mode:\"branch\"` + `branch_id`) or a shifted period (`prev_period`/`prev_year`). Rendered as the value plus a signed delta %.",
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "display": {
- "enum": [
- "abs",
- "pct",
- "both"
- ],
- "type": "string"
- },
- "mode": {
- "enum": [
- "branch",
- "prev_period",
- "prev_year"
- ],
- "type": "string"
- },
- "thresholds": {
- "properties": {
- "green": {
- "type": "number"
- },
- "red": {
- "type": "number"
- }
- },
- "required": [
- "green",
- "red"
- ],
- "type": "object"
- }
- },
- "required": [
- "mode"
- ],
- "type": "object"
- },
- "default_branch": {
- "description": "Dashboard-only: branch checked out when this dashboard opens; overrides the model default.",
- "minLength": 1,
- "type": "string"
- },
- "format": {
- "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
- "maxLength": 50,
- "type": "string"
- },
- "formula": {
- "maxLength": 2000,
- "type": "string"
- },
- "inputs": {
- "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "item_style": {
- "enum": [
- "normal",
- "subtotal",
- "total",
- "kpi"
- ],
- "type": "string"
- },
- "list_mode": {
- "description": "Chart only: how a list-backed series renders — 'split' (default) plots one series per list element, 'total' plots the item's aggregate as one series (a list total charted without a mirror formula).",
- "enum": [
- "split",
- "total"
- ],
- "type": "string"
- },
- "list_total": {
- "description": "List-mode only: show the list's total (aggregate row values). Default off; the total is forced ON while any formula/callup/parent/widget reads the aggregate.",
- "type": "boolean"
- },
- "liste": {
- "description": "mini_table list whose entries become the columns (axis=list)",
- "minLength": 1,
- "type": "string"
- },
- "liste_ref": {
- "minLength": 1,
- "type": "string"
- },
- "monitor": {
- "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert (\"ABS(value) < t\", \"== 0\") is converted; any other rule is rejected: put it in the line's formula and set `check: {}`.",
- "properties": {
- "assert": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "label": {
- "maxLength": 200,
- "type": "string"
- }
- },
- "required": [
- "assert"
- ],
- "type": "object"
- },
- "name": {
- "description": "Display name",
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "opening_balance": {
- "type": "number"
- },
- "period_window": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "maxLength": 20,
- "type": "string"
- },
- "fit": {
- "enum": [
- "data",
- "full"
- ],
- "type": "string"
- },
- "start": {
- "maxLength": 20,
- "type": "string"
- }
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
- },
- "position": {
- "anyOf": [
- {
- "properties": {
- "col": {
- "maximum": 11,
- "minimum": 0,
- "type": "integer"
- },
- "row": {
- "maximum": 400,
- "minimum": 0,
- "type": "integer"
- }
- },
- "required": [
- "col",
- "row"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
- },
- "role": {
- "description": "Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
- "enum": [
- "assumption",
- "formula",
- "callup",
- "balance",
- "section",
- "chart",
- "dashboard",
- "kpi",
- "mini_table"
- ],
- "type": "string"
- },
- "row_break": {
- "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
- "type": "boolean"
- },
- "rows": {
- "description": "mini_table curated row item UIDs / temp IDs / names",
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series": {
- "items": {
- "minLength": 1,
- "type": "string"
- },
- "type": "array"
- },
- "series_defs": {
- "description": "Per-series branch: supersedes `series`. Each entry plots one item under a specific branch (e.g. Revenue@budget vs Revenue@actuals). `uid` accepts a temp id.",
- "items": {
- "properties": {
- "branch_id": {
- "minLength": 1,
- "type": "string"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "uid"
- ],
- "type": "object"
- },
- "type": "array"
- },
- "source": {
- "description": "Callup source UID or temp ID",
- "minLength": 1,
- "type": "string"
- },
- "span": {
- "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
- "properties": {
- "cols": {
- "maximum": 12,
- "minimum": 1,
- "type": "integer"
- },
- "rows": {
- "maximum": 4,
- "minimum": 1,
- "type": "integer"
- }
- },
- "type": "object"
- },
- "sparkline": {
- "description": "KPI trend mini-chart under the value.",
- "type": "boolean"
- },
- "target": {
- "description": "KPI/mini_table target UID or temp ID",
- "minLength": 1,
- "type": "string"
- },
- "target_period": {
- "maxLength": 50,
- "type": "string"
- },
- "timeline_ref": {
- "enum": [
- "constant",
- "monthly",
- "yearly",
- "quarterly"
- ],
- "type": "string"
- },
- "timeline_values": {
- "additionalProperties": {
- "anyOf": [
- {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- }
- ]
- },
- "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
- "propertyNames": {
- "type": "string"
- },
- "type": "object"
- },
- "unit": {
- "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
- "enum": [
- "none",
- "k",
- "m",
- "auto"
- ],
- "type": "string"
- },
- "value": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "values": {
- "items": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ]
- },
- "type": "array"
- },
- "variance": {
- "description": "mini_table écart columns (axis=branches)",
- "properties": {
- "abs": {
- "type": "boolean"
- },
- "color": {
- "type": "boolean"
- },
- "pct": {
- "type": "boolean"
- }
- },
- "type": "object"
- }
- },
- "required": [
- "role",
- "name"
- ],
- "type": "object"
- },
- "op": {
- "const": "replace",
- "type": "string"
- },
- "preserve_order": {
- "type": "boolean"
- },
- "preserve_parent": {
- "type": "boolean"
- },
- "preserve_presentation": {
- "description": "Default true: every presentation / annotation field `new_item` omits (item_style, format, comment, check, span, position, period_window, compare_to, sparkline, series_defs, …) is carried over from the replaced item when the new role can hold it; `false` rebuilds the item from `new_item` alone.",
- "type": "boolean"
- },
- "preserve_style": {
- "type": "boolean"
- }
- },
- "required": [
- "op",
- "from_uid",
- "new_item"
- ],
- "type": "object"
- },
- {
- "properties": {
- "op": {
- "const": "delete",
- "type": "string"
- },
- "uid": {
- "description": "UID of item to delete",
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "op",
- "uid"
- ],
- "type": "object"
- },
- {
- "properties": {
- "after": {
- "description": "UID of item to place after (for ordering)",
- "minLength": 1,
- "type": "string"
- },
- "op": {
- "const": "move",
- "type": "string"
- },
- "parent": {
- "description": "New parent UID",
- "minLength": 1,
- "type": "string"
- },
- "uid": {
- "description": "UID of item to move",
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "op",
- "uid"
- ],
- "type": "object"
- },
- {
- "properties": {
- "id": {
- "description": "Temp ID for intra-batch references — other create ops in the same batch can pass this value as their `liste_ref` so a list and its consumers can ship in one patch (e.g. set `id: \"_emp\"` on `list_create`, then `liste_ref: \"_emp\"` on an assumption created in the same batch).",
- "maxLength": 32,
- "minLength": 1,
- "type": "string"
- },
- "items": {
- "items": {
- "properties": {
- "id": {
- "maxLength": 32,
- "minLength": 1,
- "type": "string"
- },
- "label": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "mapped_to": {
- "additionalProperties": {
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "propertyNames": {
- "type": "string"
- },
- "type": "object"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "label"
- ],
- "type": "object"
- },
- "maxItems": 500,
- "type": "array"
- },
- "name": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "op": {
- "const": "list_create",
- "type": "string"
- }
- },
- "required": [
- "op",
- "name"
- ],
- "type": "object"
- },
- {
- "properties": {
- "items": {
- "items": {
- "properties": {
- "id": {
- "maxLength": 32,
- "minLength": 1,
- "type": "string"
- },
- "label": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "mapped_to": {
- "additionalProperties": {
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "propertyNames": {
- "type": "string"
- },
- "type": "object"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "label"
- ],
- "type": "object"
- },
- "maxItems": 500,
- "type": "array"
- },
- "name": {
- "maxLength": 200,
- "minLength": 1,
- "type": "string"
- },
- "op": {
- "const": "list_update",
- "type": "string"
- },
- "uid": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "op",
- "uid"
- ],
- "type": "object"
- },
- {
- "properties": {
- "op": {
- "const": "list_delete",
- "type": "string"
- },
- "uid": {
- "description": "UID of list to delete",
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "op",
- "uid"
- ],
- "type": "object"
- },
- {
- "properties": {
- "default_branch": {
- "anyOf": [
- {
- "minLength": 1,
- "type": "string"
- },
- {
- "type": "null"
- }
- ]
- },
- "finance_md": {
- "anyOf": [
- {
- "maxLength": 5000,
- "minLength": 1,
- "type": "string"
- },
- {
- "type": "null"
- }
- ]
- },
- "formats": {
- "additionalProperties": {
- "properties": {
- "display_name": {
- "maxLength": 80,
- "type": "string"
- },
- "excel_format": {
- "maxLength": 120,
- "minLength": 1,
- "type": "string"
- },
- "name": {
- "maxLength": 50,
- "minLength": 1,
- "type": "string"
- }
- },
- "required": [
- "name",
- "excel_format"
- ],
- "type": "object"
- },
- "propertyNames": {
- "maxLength": 50,
- "minLength": 1,
- "type": "string"
- },
- "type": "object"
- },
- "glyph": {
- "anyOf": [
- {
- "maxLength": 60,
- "minLength": 1,
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Model emblem icon: a kebab-case Lucide icon name (https://lucide.dev/icons), e.g. \"trending-up\", \"coffee\", \"landmark\", \"rocket\", \"house\", \"users\". Shown in-app and on the social/OG card. Emojis and free text are rejected with suggestions. Aliases normalized: cash/money→banknote, bank→landmark, cart→shopping-cart, chart/graph→chart-line. `null` clears it (falls back to a monogram of the model name)."
- },
- "name": {
- "maxLength": 100,
- "minLength": 1,
- "type": "string"
- },
- "number_locale": {
- "anyOf": [
- {
- "enum": [
- "en",
- "fr",
- "de"
- ],
- "type": "string"
- },
- {
- "type": "null"
- }
- ]
- },
- "op": {
- "const": "meta",
- "type": "string"
- },
- "subtitle": {
- "anyOf": [
- {
- "maxLength": 160,
- "type": "string"
- },
- {
- "type": "null"
- }
- ]
- },
- "timelines": {
- "properties": {
- "monthly": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- },
- "start": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- }
- },
- "required": [
- "start",
- "end"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ]
- },
- "quarterly": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- },
- "start": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- }
- },
- "required": [
- "start",
- "end"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ]
- },
- "yearly": {
- "anyOf": [
- {
- "properties": {
- "end": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- },
- "start": {
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
- "type": "string"
- }
- },
- "required": [
- "start",
- "end"
- ],
- "type": "object"
- },
- {
- "type": "null"
- }
- ]
- }
- },
- "type": "object"
- }
- },
- "required": [
- "op"
- ],
- "type": "object"
- }
-]New value: +[
+ {
+ "properties": {
+ "aggregation": {
+ "enum": [
+ "sum",
+ "average",
+ "stock"
+ ],
+ "type": "string"
+ },
+ "axis": {
+ "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
+ "enum": [
+ "time",
+ "branches",
+ "list"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "mini_table compared branch ids (axis=branches); first = écart base",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "bridge_config": {
+ "properties": {
+ "axis": {
+ "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
+ "enum": [
+ "time",
+ "branches"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "Branch ids compared when axis=branches: [from, to].",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "contributions": {
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "end_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "endpoint": {
+ "description": "Single endpoint item (stock/balance) read at start & end periods",
+ "minLength": 1,
+ "type": "string"
+ },
+ "endpoint_branch": {
+ "description": "Read endpoint + contributions under this branch basis (axis=time only).",
+ "minLength": 1,
+ "type": "string"
+ },
+ "residual_label": {
+ "maxLength": 200,
+ "type": "string"
+ },
+ "start_period": {
+ "maxLength": 50,
+ "type": "string"
+ }
+ },
+ "required": [
+ "endpoint"
+ ],
+ "type": "object"
+ },
+ "by_branch": {
+ "description": "Chart only: one styled series per branch; false reverts to the checked-out view.",
+ "type": "boolean"
+ },
+ "cagr": {
+ "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
+ "type": "boolean"
+ },
+ "chart_type": {
+ "enum": [
+ "line",
+ "bar",
+ "area",
+ "bridge"
+ ],
+ "type": "string"
+ },
+ "check": {
+ "anyOf": [
+ {
+ "properties": {
+ "tolerance": {
+ "exclusiveMinimum": 0,
+ "type": "number"
+ }
+ },
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Flags the line as a control judged on its own value: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals); a formula that is itself a comparison / AND / OR / NOT passes when true. `null` removes the check. Failing periods surface in `layerz_read` under `health`, non-blocking."
+ },
+ "comment": {
+ "anyOf": [
+ {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Free-form annotation on the item (null clears it)."
+ },
+ "compare_to": {
+ "anyOf": [
+ {
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "display": {
+ "enum": [
+ "abs",
+ "pct",
+ "both"
+ ],
+ "type": "string"
+ },
+ "mode": {
+ "enum": [
+ "branch",
+ "prev_period",
+ "prev_year"
+ ],
+ "type": "string"
+ },
+ "thresholds": {
+ "properties": {
+ "green": {
+ "type": "number"
+ },
+ "red": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "green",
+ "red"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "mode"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "KPI native comparison; null clears it."
+ },
+ "default_branch": {
+ "anyOf": [
+ {
+ "minLength": 1,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Dashboard-only: branch checked out when this dashboard opens; null clears it."
+ },
+ "format": {
+ "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
+ "maxLength": 50,
+ "type": "string"
+ },
+ "formula": {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ "inputs": {
+ "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "item_style": {
+ "enum": [
+ "normal",
+ "subtotal",
+ "total",
+ "kpi"
+ ],
+ "type": "string"
+ },
+ "list_mode": {
+ "description": "Chart only: 'split' (default) = one series per list element, 'total' = the list-backed item's aggregate as one series.",
+ "enum": [
+ "split",
+ "total"
+ ],
+ "type": "string"
+ },
+ "list_total": {
+ "description": "List-mode only: show the list's total (aggregate row values). Default off; forced ON while the aggregate is referenced.",
+ "type": "boolean"
+ },
+ "liste": {
+ "description": "mini_table list whose entries become the columns (axis=list)",
+ "minLength": 1,
+ "type": "string"
+ },
+ "liste_ref": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "monitor": {
+ "anyOf": [
+ {
+ "properties": {
+ "assert": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "label": {
+ "maxLength": 200,
+ "type": "string"
+ }
+ },
+ "required": [
+ "assert"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert is converted, any other rule is rejected; `null` removes the check."
+ },
+ "name": {
+ "description": "New display label (also the formula identifier)",
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "op": {
+ "const": "update",
+ "type": "string"
+ },
+ "opening_balance": {
+ "type": "number"
+ },
+ "parent": {
+ "anyOf": [
+ {
+ "minLength": 1,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "New parent UID (null to move to root)"
+ },
+ "period_mode": {
+ "description": "kpi / mini_table period mode. 'ytd' shows the year to date: the cumulative from January through the target period (each column for a time-axis mini_table). Flows are summed, an `average` line averaged, a `stock` line read at the period, and a ratio line (formula `a / b`) recomputed on the cumulated operands. Branch columns, écarts and the KPI comparison follow (prev_year = year to date of the same month a year earlier). Default 'period'.",
+ "enum": [
+ "period",
+ "ytd"
+ ],
+ "type": "string"
+ },
+ "period_window": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "maxLength": 20,
+ "type": "string"
+ },
+ "fit": {
+ "enum": [
+ "data",
+ "full"
+ ],
+ "type": "string"
+ },
+ "start": {
+ "maxLength": 20,
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
+ },
+ "position": {
+ "anyOf": [
+ {
+ "properties": {
+ "col": {
+ "maximum": 11,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "row": {
+ "maximum": 400,
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "col",
+ "row"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
+ },
+ "role": {
+ "description": "Change the item role. Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
+ "enum": [
+ "assumption",
+ "formula",
+ "callup",
+ "balance",
+ "section",
+ "chart",
+ "dashboard",
+ "kpi",
+ "mini_table"
+ ],
+ "type": "string"
+ },
+ "row_break": {
+ "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
+ "type": "boolean"
+ },
+ "rows": {
+ "description": "mini_table curated row item UIDs / temp IDs / names",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series": {
+ "description": "Chart series UIDs",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series_defs": {
+ "description": "Per-series branch; supersedes `series`.",
+ "items": {
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "uid"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "source": {
+ "description": "Callup source UID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "span": {
+ "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
+ "properties": {
+ "cols": {
+ "maximum": 12,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "rows": {
+ "maximum": 4,
+ "minimum": 1,
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "sparkline": {
+ "description": "KPI trend mini-chart.",
+ "type": "boolean"
+ },
+ "target": {
+ "description": "KPI/mini_table target UID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "target_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "timeline_ref": {
+ "enum": [
+ "constant",
+ "monthly",
+ "yearly",
+ "quarterly"
+ ],
+ "type": "string"
+ },
+ "timeline_values": {
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ ]
+ },
+ "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "uid": {
+ "description": "Existing item UID to update",
+ "minLength": 1,
+ "type": "string"
+ },
+ "unit": {
+ "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
+ "enum": [
+ "none",
+ "k",
+ "m",
+ "auto"
+ ],
+ "type": "string"
+ },
+ "value": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "values": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ },
+ "variance": {
+ "description": "mini_table écart columns (axis=branches)",
+ "properties": {
+ "abs": {
+ "type": "boolean"
+ },
+ "color": {
+ "type": "boolean"
+ },
+ "pct": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "op",
+ "uid"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "aggregation": {
+ "enum": [
+ "sum",
+ "average",
+ "stock"
+ ],
+ "type": "string"
+ },
+ "axis": {
+ "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
+ "enum": [
+ "time",
+ "branches",
+ "list"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "mini_table compared branch ids (axis=branches); first = écart base",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "bridge_config": {
+ "properties": {
+ "axis": {
+ "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
+ "enum": [
+ "time",
+ "branches"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "Branch ids compared when axis=branches: [from, to].",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "contributions": {
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "end_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "endpoint": {
+ "description": "Single endpoint item (stock/balance) read at start & end periods",
+ "minLength": 1,
+ "type": "string"
+ },
+ "endpoint_branch": {
+ "description": "Read endpoint + contributions under this branch basis (axis=time only).",
+ "minLength": 1,
+ "type": "string"
+ },
+ "residual_label": {
+ "maxLength": 200,
+ "type": "string"
+ },
+ "start_period": {
+ "maxLength": 50,
+ "type": "string"
+ }
+ },
+ "required": [
+ "endpoint"
+ ],
+ "type": "object"
+ },
+ "by_branch": {
+ "description": "Chart only: plot one styled series per branch instead of the checked-out view.",
+ "type": "boolean"
+ },
+ "cagr": {
+ "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
+ "type": "boolean"
+ },
+ "chart_type": {
+ "enum": [
+ "line",
+ "bar",
+ "area",
+ "bridge"
+ ],
+ "type": "string"
+ },
+ "check": {
+ "description": "Flags the line as a control judged on its own value at every period: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals, so a balance netting to 1e-11 passes); a formula that is itself a comparison / AND / OR / NOT (\"Revenue >= 0\") passes when true. Assumption and formula only. Failing periods surface in `layerz_read` under `health`, non-blocking.",
+ "properties": {
+ "tolerance": {
+ "exclusiveMinimum": 0,
+ "type": "number"
+ }
+ },
+ "type": "object"
+ },
+ "comment": {
+ "anyOf": [
+ {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Free-form annotation on the item (hypothesis source, rationale, review note)."
+ },
+ "compare_to": {
+ "description": "KPI native comparison: a delta vs another branch (`mode:\"branch\"` + `branch_id`) or a shifted period (`prev_period`/`prev_year`). Rendered as the value plus a signed delta %.",
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "display": {
+ "enum": [
+ "abs",
+ "pct",
+ "both"
+ ],
+ "type": "string"
+ },
+ "mode": {
+ "enum": [
+ "branch",
+ "prev_period",
+ "prev_year"
+ ],
+ "type": "string"
+ },
+ "thresholds": {
+ "properties": {
+ "green": {
+ "type": "number"
+ },
+ "red": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "green",
+ "red"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "mode"
+ ],
+ "type": "object"
+ },
+ "default_branch": {
+ "description": "Dashboard-only: branch checked out when this dashboard opens; overrides the model default.",
+ "minLength": 1,
+ "type": "string"
+ },
+ "format": {
+ "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
+ "maxLength": 50,
+ "type": "string"
+ },
+ "formula": {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ "id": {
+ "description": "Temp ID for intra-batch references — other create ops in the same batch can use this value as their `parent`, `source`, `target`, or `series` to wire references before the real UID is generated. Numeric (`_1`, `_2`) keeps it short; descriptive (`_starting_customers`) is fine up to 32 chars.",
+ "maxLength": 32,
+ "minLength": 1,
+ "type": "string"
+ },
+ "inputs": {
+ "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "item_style": {
+ "enum": [
+ "normal",
+ "subtotal",
+ "total",
+ "kpi"
+ ],
+ "type": "string"
+ },
+ "list_mode": {
+ "description": "Chart only: how a list-backed series renders — 'split' (default) plots one series per list element, 'total' plots the item's aggregate as one series (a list total charted without a mirror formula).",
+ "enum": [
+ "split",
+ "total"
+ ],
+ "type": "string"
+ },
+ "list_total": {
+ "description": "List-mode only: show the list's total (aggregate row values). Default off; the total is forced ON while any formula/callup/parent/widget reads the aggregate.",
+ "type": "boolean"
+ },
+ "liste": {
+ "description": "mini_table list whose entries become the columns (axis=list)",
+ "minLength": 1,
+ "type": "string"
+ },
+ "liste_ref": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "monitor": {
+ "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert (\"ABS(value) < t\", \"== 0\") is converted; any other rule is rejected: put it in the line's formula and set `check: {}`.",
+ "properties": {
+ "assert": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "label": {
+ "maxLength": 200,
+ "type": "string"
+ }
+ },
+ "required": [
+ "assert"
+ ],
+ "type": "object"
+ },
+ "name": {
+ "description": "Display name",
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "op": {
+ "const": "create",
+ "type": "string"
+ },
+ "opening_balance": {
+ "type": "number"
+ },
+ "parent": {
+ "description": "Parent container: either an 8-char UID of an existing section/balance/dashboard, or a temp `id` from an earlier create op in the same batch (e.g. set `id: \"_pl\"` on a section create, then `parent: \"_pl\"` on each child item).",
+ "minLength": 1,
+ "type": "string"
+ },
+ "period_mode": {
+ "description": "kpi / mini_table period mode. 'ytd' shows the year to date: the cumulative from January through the target period (each column for a time-axis mini_table). Flows are summed, an `average` line averaged, a `stock` line read at the period, and a ratio line (formula `a / b`) recomputed on the cumulated operands. Branch columns, écarts and the KPI comparison follow (prev_year = year to date of the same month a year earlier). Default 'period'.",
+ "enum": [
+ "period",
+ "ytd"
+ ],
+ "type": "string"
+ },
+ "period_window": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "maxLength": 20,
+ "type": "string"
+ },
+ "fit": {
+ "enum": [
+ "data",
+ "full"
+ ],
+ "type": "string"
+ },
+ "start": {
+ "maxLength": 20,
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
+ },
+ "position": {
+ "anyOf": [
+ {
+ "properties": {
+ "col": {
+ "maximum": 11,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "row": {
+ "maximum": 400,
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "col",
+ "row"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
+ },
+ "role": {
+ "description": "Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
+ "enum": [
+ "assumption",
+ "formula",
+ "callup",
+ "balance",
+ "section",
+ "chart",
+ "dashboard",
+ "kpi",
+ "mini_table"
+ ],
+ "type": "string"
+ },
+ "row_break": {
+ "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
+ "type": "boolean"
+ },
+ "rows": {
+ "description": "mini_table curated row item UIDs / temp IDs / names",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series": {
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series_defs": {
+ "description": "Per-series branch: supersedes `series`. Each entry plots one item under a specific branch (e.g. Revenue@budget vs Revenue@actuals). `uid` accepts a temp id.",
+ "items": {
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "uid"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "source": {
+ "description": "Callup source UID or temp ID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "span": {
+ "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
+ "properties": {
+ "cols": {
+ "maximum": 12,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "rows": {
+ "maximum": 4,
+ "minimum": 1,
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "sparkline": {
+ "description": "KPI trend mini-chart under the value.",
+ "type": "boolean"
+ },
+ "target": {
+ "description": "KPI/mini_table target UID or temp ID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "target_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "timeline_ref": {
+ "enum": [
+ "constant",
+ "monthly",
+ "yearly",
+ "quarterly"
+ ],
+ "type": "string"
+ },
+ "timeline_values": {
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ ]
+ },
+ "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "unit": {
+ "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
+ "enum": [
+ "none",
+ "k",
+ "m",
+ "auto"
+ ],
+ "type": "string"
+ },
+ "value": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "values": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ },
+ "variance": {
+ "description": "mini_table écart columns (axis=branches)",
+ "properties": {
+ "abs": {
+ "type": "boolean"
+ },
+ "color": {
+ "type": "boolean"
+ },
+ "pct": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "role",
+ "name",
+ "op"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "from_uid": {
+ "description": "Existing item UID to replace atomically (section/dashboard unsupported)",
+ "minLength": 1,
+ "type": "string"
+ },
+ "new_item": {
+ "description": "Inline payload describing the replacement item",
+ "properties": {
+ "aggregation": {
+ "enum": [
+ "sum",
+ "average",
+ "stock"
+ ],
+ "type": "string"
+ },
+ "axis": {
+ "description": "mini_table column axis: 'time' (periods, default), 'branches', or 'list' (list entries + Total)",
+ "enum": [
+ "time",
+ "branches",
+ "list"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "mini_table compared branch ids (axis=branches); first = écart base",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "bridge_config": {
+ "properties": {
+ "axis": {
+ "description": "Bridge dimension: 'time' (default, delta start→end period) or 'branches' (gap between two branches at one period — budget vs actual waterfall).",
+ "enum": [
+ "time",
+ "branches"
+ ],
+ "type": "string"
+ },
+ "branches": {
+ "description": "Branch ids compared when axis=branches: [from, to].",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "contributions": {
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "end_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "endpoint": {
+ "description": "Single endpoint item (stock/balance) read at start & end periods",
+ "minLength": 1,
+ "type": "string"
+ },
+ "endpoint_branch": {
+ "description": "Read endpoint + contributions under this branch basis (axis=time only).",
+ "minLength": 1,
+ "type": "string"
+ },
+ "residual_label": {
+ "maxLength": 200,
+ "type": "string"
+ },
+ "start_period": {
+ "maxLength": 50,
+ "type": "string"
+ }
+ },
+ "required": [
+ "endpoint"
+ ],
+ "type": "object"
+ },
+ "by_branch": {
+ "description": "Chart only: plot one styled series per branch instead of the checked-out view.",
+ "type": "boolean"
+ },
+ "cagr": {
+ "description": "mini_table (axis=time): show the trailing CAGR column. Default true",
+ "type": "boolean"
+ },
+ "chart_type": {
+ "enum": [
+ "line",
+ "bar",
+ "area",
+ "bridge"
+ ],
+ "type": "string"
+ },
+ "check": {
+ "description": "Flags the line as a control judged on its own value at every period: a plain amount passes when within `tolerance` of 0 (ABS(value) < tolerance, default 1, on values rounded to 2 decimals, so a balance netting to 1e-11 passes); a formula that is itself a comparison / AND / OR / NOT (\"Revenue >= 0\") passes when true. Assumption and formula only. Failing periods surface in `layerz_read` under `health`, non-blocking.",
+ "properties": {
+ "tolerance": {
+ "exclusiveMinimum": 0,
+ "type": "number"
+ }
+ },
+ "type": "object"
+ },
+ "comment": {
+ "anyOf": [
+ {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Free-form annotation on the item (hypothesis source, rationale, review note)."
+ },
+ "compare_to": {
+ "description": "KPI native comparison: a delta vs another branch (`mode:\"branch\"` + `branch_id`) or a shifted period (`prev_period`/`prev_year`). Rendered as the value plus a signed delta %.",
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "display": {
+ "enum": [
+ "abs",
+ "pct",
+ "both"
+ ],
+ "type": "string"
+ },
+ "mode": {
+ "enum": [
+ "branch",
+ "prev_period",
+ "prev_year"
+ ],
+ "type": "string"
+ },
+ "thresholds": {
+ "properties": {
+ "green": {
+ "type": "number"
+ },
+ "red": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "green",
+ "red"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "mode"
+ ],
+ "type": "object"
+ },
+ "default_branch": {
+ "description": "Dashboard-only: branch checked out when this dashboard opens; overrides the model default.",
+ "minLength": 1,
+ "type": "string"
+ },
+ "format": {
+ "description": "Number format name. Pre-installed: default, decimal_2, decimal_neg, integer, multiple, percent, percent_neg, percent_2, date_dmy, date_dmmmyy, date_mmmyy, date_dmmm (+ the magic `date`, and `currency`/`currency_2` — resolved live from the FINANCE.md `currency` code: `$1,234` for USD, `1 234 €` for EUR). Custom names refer to entries in model.formats — add one with the `meta` op `formats` field (same batch OK); the excel_format follows Excel / Google Sheets, bracket colours and conditions included (`[Green]0;[Red](0);-` paints negatives red on the web and in Excel). An unknown name is rejected with code UNKNOWN_FORMAT listing what is available (`percent` is the current name of the legacy `\"%\"`). Full catalog with each excel_format and rendered positive/negative/zero examples (`default` = dash-for-zero accounting style, `integer` = plain 0): MCP resource `format-spec://reference`.",
+ "maxLength": 50,
+ "type": "string"
+ },
+ "formula": {
+ "maxLength": 2000,
+ "type": "string"
+ },
+ "inputs": {
+ "description": "Direct contributors override (item UIDs or same-batch temp ids). Useful for `balance` items, which carry no formula but can declare the flows that roll into the stock; for `formula` items inputs are derived from the expression, so set this only to override that derivation.",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "item_style": {
+ "enum": [
+ "normal",
+ "subtotal",
+ "total",
+ "kpi"
+ ],
+ "type": "string"
+ },
+ "list_mode": {
+ "description": "Chart only: how a list-backed series renders — 'split' (default) plots one series per list element, 'total' plots the item's aggregate as one series (a list total charted without a mirror formula).",
+ "enum": [
+ "split",
+ "total"
+ ],
+ "type": "string"
+ },
+ "list_total": {
+ "description": "List-mode only: show the list's total (aggregate row values). Default off; the total is forced ON while any formula/callup/parent/widget reads the aggregate.",
+ "type": "boolean"
+ },
+ "liste": {
+ "description": "mini_table list whose entries become the columns (axis=list)",
+ "minLength": 1,
+ "type": "string"
+ },
+ "liste_ref": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "monitor": {
+ "description": "Deprecated (MDK 0.13.0) — use `check`. A canonical assert (\"ABS(value) < t\", \"== 0\") is converted; any other rule is rejected: put it in the line's formula and set `check: {}`.",
+ "properties": {
+ "assert": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "label": {
+ "maxLength": 200,
+ "type": "string"
+ }
+ },
+ "required": [
+ "assert"
+ ],
+ "type": "object"
+ },
+ "name": {
+ "description": "Display name",
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "opening_balance": {
+ "type": "number"
+ },
+ "period_mode": {
+ "description": "kpi / mini_table period mode. 'ytd' shows the year to date: the cumulative from January through the target period (each column for a time-axis mini_table). Flows are summed, an `average` line averaged, a `stock` line read at the period, and a ratio line (formula `a / b`) recomputed on the cumulated operands. Branch columns, écarts and the KPI comparison follow (prev_year = year to date of the same month a year earlier). Default 'period'.",
+ "enum": [
+ "period",
+ "ytd"
+ ],
+ "type": "string"
+ },
+ "period_window": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "maxLength": 20,
+ "type": "string"
+ },
+ "fit": {
+ "enum": [
+ "data",
+ "full"
+ ],
+ "type": "string"
+ },
+ "start": {
+ "maxLength": 20,
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Display period window of a chart / kpi sparkline / time mini_table. `start`/`end` are period labels at the widget's grain (bounds independent — either alone is valid); `fit` governs the unset bounds: 'data' (the default, absent field included) auto-fits to the periods actually carrying data and follows imports, 'full' spans the whole timeline. `null` clears it (back to fit-to-data)."
+ },
+ "position": {
+ "anyOf": [
+ {
+ "properties": {
+ "col": {
+ "maximum": 11,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "row": {
+ "maximum": 400,
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "col",
+ "row"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "dashboard-child pinned grid origin {col 0–11, row ≥ 0}: the widget sits at these cells (pushed down when it collides with an earlier pinned widget). Absent = flow placement (first-fit dense in sort order — a new widget fills the topmost free hole); null clears the pin. The web canvas pins every widget on the first drag"
+ },
+ "role": {
+ "description": "Role of the item. assumption: a typed input (value / values / timeline_values), the only role a branch overlays. formula: a computed transformation (formula). formula with no expression and children: an aggregator that sums its children (a subtotal value; a nested section is structural grouping with no value). callup: a typed mirror of one source whose value flows into its parent (source): balance flows, a value surfaced in a section or subtotal; a formula references its source directly instead. balance: a rolling stock (opening_balance) that sums its callup-child flows. section: structural container, no value. dashboard / chart / kpi / mini_table: presentational. A formula carrying both an expression and children is rejected (FORMULA_EXPR_AND_CHILDREN). A single bare reference written as a formula is auto-posed as a callup mirror.",
+ "enum": [
+ "assumption",
+ "formula",
+ "callup",
+ "balance",
+ "section",
+ "chart",
+ "dashboard",
+ "kpi",
+ "mini_table"
+ ],
+ "type": "string"
+ },
+ "row_break": {
+ "description": "dashboard-child line break (flow widgets only): start this widget on a fresh grid row. Inert once the widget has a position",
+ "type": "boolean"
+ },
+ "rows": {
+ "description": "mini_table curated row item UIDs / temp IDs / names",
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series": {
+ "items": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "series_defs": {
+ "description": "Per-series branch: supersedes `series`. Each entry plots one item under a specific branch (e.g. Revenue@budget vs Revenue@actuals). `uid` accepts a temp id.",
+ "items": {
+ "properties": {
+ "branch_id": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "uid"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "source": {
+ "description": "Callup source UID or temp ID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "span": {
+ "description": "dashboard-child grid footprint (chart/kpi/mini_table): cols on the 12-column grid × 140-px row units. Defaults: kpi 3×1, chart/mini_table 6×2",
+ "properties": {
+ "cols": {
+ "maximum": 12,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "rows": {
+ "maximum": 4,
+ "minimum": 1,
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "sparkline": {
+ "description": "KPI trend mini-chart under the value.",
+ "type": "boolean"
+ },
+ "target": {
+ "description": "KPI/mini_table target UID or temp ID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "target_period": {
+ "maxLength": 50,
+ "type": "string"
+ },
+ "timeline_ref": {
+ "enum": [
+ "constant",
+ "monthly",
+ "yearly",
+ "quarterly"
+ ],
+ "type": "string"
+ },
+ "timeline_values": {
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ ]
+ },
+ "description": "Periodic values, keyed by context. List-mode item: one series per list entry, keyed by the element UID, label, or a `list_create` temp id of the same batch (a temp id is scoped to the batch that declares it; later batches key by label or UID). Non-list item: a sparse per-period patch keyed by period id (`{\"2026-06\": -119.19}`, keys YYYY / YYYY-MM / YYYY-Qn) that rewrites the named cells of the typed `values` series and leaves the rest intact; a full series keyed by a canonical grain (`monthly` / `quarterly` / `yearly` / `constant`) is import-owned and rejected here. A single number is coerced to a 1-element array. List cells left unset read as 0 (no forward-fill).",
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "unit": {
+ "description": "mini_table amount scaling: 'none' (default), 'k' (thousands), 'm' (millions), 'auto' (per-table scale); %, multiples and dates are not divided",
+ "enum": [
+ "none",
+ "k",
+ "m",
+ "auto"
+ ],
+ "type": "string"
+ },
+ "value": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "values": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "type": "array"
+ },
+ "variance": {
+ "description": "mini_table écart columns (axis=branches)",
+ "properties": {
+ "abs": {
+ "type": "boolean"
+ },
+ "color": {
+ "type": "boolean"
+ },
+ "pct": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "role",
+ "name"
+ ],
+ "type": "object"
+ },
+ "op": {
+ "const": "replace",
+ "type": "string"
+ },
+ "preserve_order": {
+ "type": "boolean"
+ },
+ "preserve_parent": {
+ "type": "boolean"
+ },
+ "preserve_presentation": {
+ "description": "Default true: every presentation / annotation field `new_item` omits (item_style, format, comment, check, span, position, period_window, compare_to, sparkline, series_defs, …) is carried over from the replaced item when the new role can hold it; `false` rebuilds the item from `new_item` alone.",
+ "type": "boolean"
+ },
+ "preserve_style": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "op",
+ "from_uid",
+ "new_item"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "op": {
+ "const": "delete",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of item to delete",
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "op",
+ "uid"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "after": {
+ "description": "UID of item to place after (for ordering)",
+ "minLength": 1,
+ "type": "string"
+ },
+ "op": {
+ "const": "move",
+ "type": "string"
+ },
+ "parent": {
+ "description": "New parent UID",
+ "minLength": 1,
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of item to move",
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "op",
+ "uid"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "id": {
+ "description": "Temp ID for intra-batch references — other create ops in the same batch can pass this value as their `liste_ref` so a list and its consumers can ship in one patch (e.g. set `id: \"_emp\"` on `list_create`, then `liste_ref: \"_emp\"` on an assumption created in the same batch).",
+ "maxLength": 32,
+ "minLength": 1,
+ "type": "string"
+ },
+ "items": {
+ "items": {
+ "properties": {
+ "id": {
+ "maxLength": 32,
+ "minLength": 1,
+ "type": "string"
+ },
+ "label": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "mapped_to": {
+ "additionalProperties": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "label"
+ ],
+ "type": "object"
+ },
+ "maxItems": 500,
+ "type": "array"
+ },
+ "name": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "op": {
+ "const": "list_create",
+ "type": "string"
+ }
+ },
+ "required": [
+ "op",
+ "name"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "items": {
+ "items": {
+ "properties": {
+ "id": {
+ "maxLength": 32,
+ "minLength": 1,
+ "type": "string"
+ },
+ "label": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "mapped_to": {
+ "additionalProperties": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "label"
+ ],
+ "type": "object"
+ },
+ "maxItems": 500,
+ "type": "array"
+ },
+ "name": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "op": {
+ "const": "list_update",
+ "type": "string"
+ },
+ "uid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "op",
+ "uid"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "op": {
+ "const": "list_delete",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of list to delete",
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "op",
+ "uid"
+ ],
+ "type": "object"
+ },
+ {
+ "properties": {
+ "default_branch": {
+ "anyOf": [
+ {
+ "minLength": 1,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "finance_md": {
+ "anyOf": [
+ {
+ "maxLength": 5000,
+ "minLength": 1,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "formats": {
+ "additionalProperties": {
+ "properties": {
+ "display_name": {
+ "maxLength": 80,
+ "type": "string"
+ },
+ "excel_format": {
+ "maxLength": 120,
+ "minLength": 1,
+ "type": "string"
+ },
+ "name": {
+ "maxLength": 50,
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "excel_format"
+ ],
+ "type": "object"
+ },
+ "propertyNames": {
+ "maxLength": 50,
+ "minLength": 1,
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "glyph": {
+ "anyOf": [
+ {
+ "maxLength": 60,
+ "minLength": 1,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Model emblem icon: a kebab-case Lucide icon name (https://lucide.dev/icons), e.g. \"trending-up\", \"coffee\", \"landmark\", \"rocket\", \"house\", \"users\". Shown in-app and on the social/OG card. Emojis and free text are rejected with suggestions. Aliases normalized: cash/money→banknote, bank→landmark, cart→shopping-cart, chart/graph→chart-line. `null` clears it (falls back to a monogram of the model name)."
+ },
+ "name": {
+ "maxLength": 100,
+ "minLength": 1,
+ "type": "string"
+ },
+ "number_locale": {
+ "anyOf": [
+ {
+ "enum": [
+ "en",
+ "fr",
+ "de"
+ ],
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "op": {
+ "const": "meta",
+ "type": "string"
+ },
+ "subtitle": {
+ "anyOf": [
+ {
+ "maxLength": 160,
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "timelines": {
+ "properties": {
+ "monthly": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ },
+ "start": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ }
+ },
+ "required": [
+ "start",
+ "end"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "quarterly": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ },
+ "start": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ }
+ },
+ "required": [
+ "start",
+ "end"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "yearly": {
+ "anyOf": [
+ {
+ "properties": {
+ "end": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ },
+ "start": {
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
+ "type": "string"
+ }
+ },
+ "required": [
+ "start",
+ "end"
+ ],
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "op"
+ ],
+ "type": "object"
+ }
+]