{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://oods-foundry.com/schemas/viz.render.output.json",
  "title": "Viz render output",
  "description": "A real, data-bound visualization spec plus the resolved chart type, accessibility description, and any recommender suggestion. Spec-as-payload (the UiSchema component wrapper is deferred to Phase 2). The five Cartesian families return spec (Vega-Lite) and optional echartsSpec; the eight ECharts-primary families omit spec and always return echartsSpec with output.echarts true and reason echarts-primary-family.",
  "type": "object",
  "required": [
    "status",
    "warnings"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "chartType": {
            "type": "string",
            "enum": [
              "bar",
              "line",
              "area",
              "scatter",
              "heatmap"
            ]
          }
        },
        "required": [
          "status",
          "chartType"
        ]
      },
      "then": {
        "required": [
          "spec"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "chartType": {
            "type": "string",
            "enum": [
              "treemap",
              "sunburst",
              "sankey",
              "force_graph",
              "chord",
              "choropleth",
              "bubble_map",
              "flow_map"
            ]
          }
        },
        "required": [
          "status",
          "chartType"
        ]
      },
      "then": {
        "required": [
          "echartsSpec",
          "output"
        ],
        "not": {
          "required": [
            "spec"
          ]
        },
        "properties": {
          "output": {
            "required": [
              "echarts",
              "reason"
            ],
            "properties": {
              "echarts": {
                "type": "boolean",
                "const": true
              }
            }
          }
        }
      }
    }
  ],
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "ok",
        "error"
      ],
      "description": "Whether rendering succeeded."
    },
    "chartType": {
      "type": "string",
      "description": "Resolved registered chart type; omitted on error."
    },
    "mode": {
      "type": "string",
      "enum": [
        "explicit",
        "suggest"
      ],
      "description": "Whether the chart type was supplied explicitly or chosen by the recommender."
    },
    "spec": {
      "type": "object",
      "description": "Vega-Lite source with scoped OODS tokens, before the renderer's high-contrast quantized legend and output.width/height sizing adjustments. Use output.includeVegaSpec for the exact compiled Vega spec that reproduces the rendered chart. Scope changes alter chart content and pixel hashes.",
      "additionalProperties": true
    },
    "echartsSpec": {
      "type": "object",
      "description": "Compiled ECharts option. Primary ECharts chart types include scoped OODS canvas, borders, labels and title chrome. Series palettes use the same scope; where no themed palette exists they retain the light palette. Formatted axes/tooltips carry JSON-safe __oodsFormat descriptors. Pass the option through restoreEChartsFormats from @oods/foundry/echarts.js before echarts.setOption; no code evaluation is required.",
      "additionalProperties": true
    },
    "normalizedSpec": {
      "type": "object",
      "description": "The intermediate NormalizedVizSpec IR. Present only when output.includeNormalizedSpec is true.",
      "additionalProperties": true
    },
    "a11yDescription": {
      "type": "string",
      "description": "The non-empty accessibility description carried by the spec (always synthesized when not provided)."
    },
    "a11y": {
      "type": "object",
      "description": "Structured two-part text alternative (accessible data table + narrative summary) derived from the same data source the chart renders from. Present only when output.includeA11y is true (additive; default-off keeps the wire byte-identical). An agent reads this to verify/iterate its own chart without re-deriving the data.",
      "properties": {
        "table": {
          "type": "object",
          "description": "Accessible data table. Present when a table could be derived (omitted when the source had no inline rows / the fallback was disabled).",
          "required": [
            "caption",
            "columns",
            "rows"
          ],
          "properties": {
            "caption": {
              "type": "string"
            },
            "columns": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "field",
                  "label",
                  "isNumeric"
                ],
                "properties": {
                  "field": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "isNumeric": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false
              }
            },
            "rows": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "cells"
                ],
                "properties": {
                  "cells": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "field",
                        "text"
                      ],
                      "properties": {
                        "field": {
                          "type": "string"
                        },
                        "text": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": false
                    }
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "additionalProperties": false
        },
        "narrative": {
          "type": "object",
          "description": "Narrative summary + key findings derived from the data.",
          "required": [
            "summary",
            "keyFindings"
          ],
          "properties": {
            "summary": {
              "type": "string"
            },
            "keyFindings": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "suggestion": {
      "type": "object",
      "description": "Present in suggest mode: the recommender pick that drove the chart type, with the data-aware rationale and runner-up alternatives.",
      "required": [
        "patternId",
        "score"
      ],
      "properties": {
        "patternId": {
          "type": "string"
        },
        "score": {
          "type": "number"
        },
        "rationale": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Human-readable scorer signals explaining why this pattern was chosen (data-aware justifications)."
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Normalized confidence in the pick (score / max-match-score, clamped to [0,1])."
        },
        "alternatives": {
          "type": "array",
          "description": "Runner-up recommendations, best first.",
          "items": {
            "type": "object",
            "required": [
              "patternId",
              "score",
              "chartType"
            ],
            "properties": {
              "patternId": {
                "type": "string"
              },
              "score": {
                "type": "number"
              },
              "chartType": {
                "type": "string"
              }
            },
            "additionalProperties": false
          }
        }
      },
      "additionalProperties": false
    },
    "lowConfidence": {
      "type": "boolean",
      "description": "Suggest mode only: true when no pattern matched confidently — the chartType is a low-confidence fallback rather than a positive recommendation (the previously-silent bar default, now surfaced)."
    },
    "svg": {
      "type": "string",
      "description": "Server-rendered SVG bytes, present only for output.svg:true. ECharts allocator tokens are normalized before return; Vega-Lite retains its accessible graphics roles."
    },
    "svgHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "SHA-256 of the exact returned SVG bytes. At default intrinsic dimensions and light/A scope, cartesian svgHash equals artifact_certify determinism.renderHash for the same normalized spec. Scope and dimension changes alter the hash."
    },
    "svgBytes": {
      "type": "integer",
      "minimum": 1,
      "description": "UTF-8 byte length of svg."
    },
    "svgRef": {
      "type": "string",
      "description": "Temporary pipeline reference caching exactly the svg string, with the same lifetime as specRef."
    },
    "render": {
      "type": "object",
      "required": [
        "engine",
        "width",
        "height",
        "theme",
        "brand"
      ],
      "properties": {
        "engine": {
          "type": "string",
          "enum": [
            "vega-lite",
            "echarts"
          ]
        },
        "width": {
          "type": "number",
          "exclusiveMinimum": 0
        },
        "height": {
          "type": "number",
          "exclusiveMinimum": 0
        },
        "theme": {
          "type": "string",
          "enum": [
            "light",
            "dark",
            "hc"
          ]
        },
        "brand": {
          "type": "string",
          "format": "oods-brand"
        }
      },
      "additionalProperties": false,
      "description": "Actual SVG dimensions, primary engine and rendered scope."
    },
    "specRef": {
      "type": "string",
      "description": "Temporary reference to the produced spec for pipeline reuse (mirrors the retired chart scaffold schemaRef)."
    },
    "specRefCreatedAt": {
      "type": "string",
      "description": "ISO timestamp when the specRef was created."
    },
    "specRefExpiresAt": {
      "type": "string",
      "description": "ISO timestamp when the specRef expires."
    },
    "contentHash": {
      "type": "string",
      "description": "Deterministic SHA-256 (hex) over the canonicalized primary payload (the Vega-Lite spec, or the JSON-projected ECharts option for ECharts-primary types) — the content identity of exactly what specRef caches. Unlike specRef (a random, expiring cache handle), contentHash is stable across calls: the same input yields the same hash. Default-on; omitted only on error outputs."
    },
    "tokenCssRef": {
      "type": "string",
      "description": "Content-addressed identity of the active token CSS when compact mode is enabled: tokens_build#sha256:<hex>. Use tokens_build to obtain the full CSS."
    },
    "output": {
      "type": "object",
      "description": "Echoes the normalized output controls used by the renderer.",
      "required": [
        "compact"
      ],
      "properties": {
        "svg": {
          "type": "boolean"
        },
        "width": {
          "type": "integer",
          "minimum": 1
        },
        "height": {
          "type": "integer",
          "minimum": 1
        },
        "compact": {
          "type": "boolean"
        },
        "echarts": {
          "type": "boolean"
        },
        "reason": {
          "type": "string",
          "const": "echarts-primary-family",
          "description": "This chart family requires ECharts, so output.echarts is forced true even when omitted or false."
        },
        "includeNormalizedSpec": {
          "type": "boolean"
        },
        "includeA11y": {
          "type": "boolean"
        },
        "includeVegaSpec": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    },
    "errors": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/issue"
      },
      "description": "Fatal errors (present and non-empty when status is 'error')."
    },
    "warnings": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/issue"
      },
      "description": "Non-fatal issues encountered during rendering."
    },
    "meta": {
      "type": "object",
      "properties": {
        "renderer": {
          "type": "string",
          "enum": [
            "vega-lite",
            "echarts"
          ],
          "description": "Primary renderer for the returned payload."
        },
        "mark": {
          "type": "string",
          "description": "Resolved mark trait (e.g. MarkBar)."
        },
        "rowCount": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of data rows bound into the spec."
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Field names present in the data."
        },
        "inferredFields": {
          "type": "array",
          "description": "Inferred field profiles (present in suggest mode).",
          "items": {
            "type": "object",
            "required": [
              "name",
              "type",
              "role"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "enum": [
                  "quantitative",
                  "temporal",
                  "nominal",
                  "ordinal"
                ]
              },
              "role": {
                "type": "string",
                "enum": [
                  "measure",
                  "dimension"
                ]
              },
              "cardinality": {
                "type": "integer",
                "minimum": 0
              },
              "distinctRatio": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "min": {
                "type": "number"
              },
              "max": {
                "type": "number"
              },
              "mean": {
                "type": "number"
              },
              "stddev": {
                "type": "number"
              },
              "skew": {
                "type": "number"
              },
              "outlierCount": {
                "type": "integer",
                "minimum": 0
              },
              "hasNegative": {
                "type": "boolean"
              },
              "hasZero": {
                "type": "boolean"
              },
              "isInteger": {
                "type": "boolean"
              },
              "temporalGranularity": {
                "type": "string",
                "enum": [
                  "year",
                  "quarter",
                  "month",
                  "day",
                  "time"
                ]
              },
              "temporalRegular": {
                "type": "boolean"
              },
              "geoKind": {
                "type": "string",
                "enum": [
                  "lat",
                  "lon",
                  "region",
                  "none"
                ]
              }
            },
            "additionalProperties": false
          }
        }
      },
      "additionalProperties": false
    },
    "vegaSpec": {
      "type": "object",
      "description": "The exact Vega spec parsed by the renderer after legend and sizing adjustments. Present for Vega charts only when output.includeVegaSpec is true. Rendering with the bundled Vega version reproduces the SVG apart from its accessibility and stable-ID post-processing.",
      "additionalProperties": true
    }
  },
  "additionalProperties": false,
  "$defs": {
    "issue": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "hint": {
          "type": "string"
        },
        "severity": {
          "type": "string",
          "enum": [
            "error",
            "warning"
          ]
        }
      },
      "additionalProperties": false
    }
  }
}
