{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://shirokoff.ca/architecture-radar/scorecards.schema.json",
  "title": "Architecture Radar scorecards",
  "description": "Schema for scorecards.json — the source of truth behind the Architecture Radar assessments. Every scorecard table and radar chart on the site is generated from that file, so the published data and the published pages cannot drift.",
  "type": "object",
  "required": ["scale", "assessments"],
  "properties": {
    "$schema": { "type": "string", "format": "uri" },
    "note": { "type": "string" },
    "scale": {
      "type": "object",
      "description": "The scoring scale. Scores are ordinal judgement, not measurement.",
      "required": ["min", "max", "meaning"],
      "properties": {
        "min": { "type": "number", "const": 0 },
        "max": { "type": "number", "const": 5 },
        "meaning": { "type": "string" }
      }
    },
    "assessments": {
      "type": "object",
      "description": "Keyed by slug. The slug is the last path segment of the assessment URL.",
      "minProperties": 1,
      "propertyNames": { "pattern": "^[a-z0-9-]+$" },
      "additionalProperties": { "$ref": "#/$defs/assessment" }
    }
  },
  "$defs": {
    "assessment": {
      "type": "object",
      "required": ["title", "url", "category", "reviewed", "axes", "axes_short", "target", "platforms"],
      "properties": {
        "title": {
          "type": "string",
          "description": "Human-readable name of the decision being assessed."
        },
        "url": {
          "type": "string",
          "pattern": "^/architecture-radar/[a-z0-9-]+$",
          "description": "Site-relative URL of the full assessment page."
        },
        "category": {
          "enum": ["data", "ai"],
          "description": "Radar category filter this assessment belongs to."
        },
        "reviewed": {
          "type": "string",
          "pattern": "^[0-9]{4}-Q[1-4]$",
          "description": "The quarter the scores were last reviewed. Scores older than about two quarters should be treated as stale."
        },
        "published": {
          "type": "string",
          "format": "date",
          "description": "ISO date the assessment was first published."
        },
        "use_case": {
          "type": "string",
          "description": "The specific use case the scores are relative to. Scores are NOT context-free quality ratings; changing the use case changes the scores."
        },
        "axes": {
          "type": "array",
          "description": "Full axis names. Axes differ per assessment and are chosen for the decision at hand.",
          "minItems": 3,
          "items": { "type": "string" }
        },
        "axes_short": {
          "type": "array",
          "description": "Abbreviated axis labels for chart rendering. Same length and order as axes.",
          "minItems": 3,
          "items": { "type": "string" }
        },
        "target": {
          "type": "string",
          "description": "The option the assessment is primarily about. Must be a key of platforms."
        },
        "platforms": {
          "type": "object",
          "description": "Option name to score vector. Every vector has the same length as axes, in the same order. Where an option appears in more than one assessment on identical axes, its vector is byte-identical across them.",
          "minProperties": 2,
          "additionalProperties": {
            "type": "array",
            "minItems": 3,
            "items": { "type": "number", "minimum": 0, "maximum": 5 }
          }
        },
        "verdict": {
          "type": "string",
          "description": "One-line recommendation in radar-ring language (Adopt / Trial / Assess / Hold), with the qualifying context."
        }
      }
    }
  }
}
