{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Naderu model board",
  "description": "Comparison data for third-party foundation models. Every model row must carry at least one source URL and the date it was verified — see web/tools/check_board.py.",
  "type": "object",
  "required": [
    "schema_version",
    "updated",
    "next_update",
    "currency",
    "price_unit",
    "providers",
    "jobs",
    "constraints",
    "postures",
    "models",
    "hosts",
    "news",
    "changelog",
    "coverage"
  ],
  "properties": {
    "schema_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
    "updated": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
    "next_update": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
    "currency": { "type": "string", "enum": ["USD"] },
    "price_unit": { "type": "string" },

    "coverage": {
      "type": "object",
      "description": "Honesty ledger: what this pass did NOT cover, and why. Rendered on the page.",
      "required": ["deferred"],
      "properties": {
        "deferred": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["name", "reason"],
            "properties": {
              "name": { "type": "string" },
              "reason": { "type": "string" }
            }
          }
        }
      }
    },

    "providers": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "name", "kind", "pricing_url"],
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "name": { "type": "string" },
          "kind": { "type": "string", "enum": ["lab", "inference_host", "hub"] },
          "pricing_url": {
            "type": ["string", "null"],
            "pattern": "^https://",
            "description": "The provider's OWN pricing page. Must never be an inference host's domain — a host's price is not the lab's price, and citing one as the other misstates provenance. null means the provider publishes no first-party pricing (e.g. Meta has no first-party API); the validator then requires a note saying so."
          },
          "docs_url": { "type": ["string", "null"], "pattern": "^https://" },
          "notes": {
            "type": "array",
            "description": "Benefits and challenges. Each needs a severity and, for documented behaviour, a source.",
            "items": {
              "type": "object",
              "required": ["severity", "text"],
              "properties": {
                "severity": { "type": "string", "enum": ["benefit", "watch", "blocker"] },
                "text": { "type": "string" },
                "source": { "type": ["string", "null"], "pattern": "^https://" }
              }
            }
          },
          "mitigation": { "type": ["string", "null"] }
        }
      }
    },

    "jobs": {
      "type": "array",
      "description": "What the work is. Model ids are checked against models[] by the validator, so a card can never name a model the board no longer carries. The ids are a published vocabulary served at /data/model-board.json — rename one deliberately or not at all, and ship a job only when its card ships.",
      "items": {
        "type": "object",
        "required": ["id", "label", "title", "models", "catch"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
          "label": { "type": "string" },
          "title": { "type": "string" },
          "models": { "type": "array", "items": { "type": "string" } },
          "catch": { "type": "string" }
        }
      }
    },

    "constraints": {
      "type": "array",
      "description": "What the request or deployment must satisfy. These filter the model table rather than choosing from it, so they carry prose and no models[] list.",
      "items": {
        "type": "object",
        "required": ["id", "label", "title", "note"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
          "label": { "type": "string" },
          "title": { "type": "string" },
          "note": { "type": "string" }
        }
      }
    },

    "postures": {
      "type": "array",
      "description": "The economic stance a reader is buying from: saver, balanced, quality.",
      "items": {
        "type": "object",
        "required": ["id", "label", "title", "note"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
          "label": { "type": "string" },
          "title": { "type": "string" },
          "note": { "type": "string" }
        }
      }
    },

    "models": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "id", "provider", "name", "generation", "weights", "price", "context", "caps", "verified"
        ],
        "properties": {
          "id": { "type": "string" },
          "provider": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "name": { "type": "string" },
          "generation": { "type": "string", "enum": ["latest", "previous", "deprecated"] },
          "weights": { "type": "string", "enum": ["open", "closed", "partial", "unverified"] },
          "license": { "type": ["string", "null"] },

          "price": {
            "type": "object",
            "required": ["in", "out"],
            "description": "USD per 1M tokens, list rate. null means the sourced page did not state it — never a guess.",
            "properties": {
              "in": { "type": ["number", "null"], "minimum": 0 },
              "cached_in": { "type": ["number", "null"], "minimum": 0 },
              "cache_write": { "type": ["number", "null"], "minimum": 0 },
              "out": { "type": ["number", "null"], "minimum": 0 },
              "batch_discount": { "type": ["number", "null"], "minimum": 0 },
              "note": { "type": ["string", "null"] }
            }
          },

          "context": {
            "type": "object",
            "properties": {
              "window": { "type": ["integer", "null"], "minimum": 0 },
              "max_output": { "type": ["integer", "null"], "minimum": 0 },
              "note": { "type": ["string", "null"] }
            }
          },

          "caps": {
            "type": "object",
            "required": ["reasoning", "vision", "tools", "caching"],
            "properties": {
              "reasoning": { "type": "string", "enum": ["none", "always", "controllable", "unverified"] },
              "vision": { "type": ["boolean", "null"] },
              "audio_in": { "type": ["boolean", "null"] },
              "tools": { "type": ["boolean", "null"] },
              "caching": {
                "type": "object",
                "required": ["kind"],
                "properties": {
                  "kind": { "type": "string", "enum": ["explicit", "automatic", "both", "none", "host-dependent", "unverified"] },
                  "ttl": { "type": ["string", "null"] },
                  "min_tokens": { "type": ["integer", "null"], "minimum": 0 },
                  "note": { "type": ["string", "null"] }
                }
              }
            }
          },

          "best_for": { "type": ["string", "null"] },
          "watch_out": { "type": ["string", "null"] },

          "verified": {
            "type": "object",
            "required": ["on", "sources"],
            "properties": {
              "on": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
              "sources": {
                "type": "array",
                "items": { "type": "string", "pattern": "^https://" }
              }
            }
          }
        }
      }
    },

    "hosts": {
      "type": "array",
      "description": "Inference hosts and hubs — they price other people's models, so they get their own table.",
      "items": {
        "type": "object",
        "required": ["provider", "summary", "rows", "verified"],
        "properties": {
          "provider": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "summary": { "type": "string" },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["model", "in", "out"],
              "properties": {
                "model": { "type": "string" },
                "in": { "type": ["number", "null"], "minimum": 0 },
                "cached_in": { "type": ["number", "null"], "minimum": 0 },
                "out": { "type": ["number", "null"], "minimum": 0 },
                "context": { "type": ["integer", "null"], "minimum": 0 },
                "tps": { "type": ["integer", "null"], "minimum": 0 },
                "note": { "type": ["string", "null"] }
              }
            }
          },
          "verified": {
            "type": "object",
            "required": ["on", "sources"],
            "properties": {
              "on": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
              "sources": { "type": "array", "items": { "type": "string", "pattern": "^https://" } }
            }
          }
        }
      }
    },

    "caching_example": {
      "type": "object",
      "description": "The worked 'real bill' comparison. prompt_tokens/calls/output_tokens drive the arithmetic; the renderer computes the costs so they can never disagree with the price table.",
      "required": ["prompt_tokens", "calls", "output_tokens", "models"],
      "properties": {
        "prompt_tokens": { "type": "integer", "minimum": 1 },
        "calls": { "type": "integer", "minimum": 1 },
        "output_tokens": { "type": "integer", "minimum": 1 },
        "models": { "type": "array", "items": { "type": "string" } }
      }
    },

    "news": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["date", "headline", "why", "source"],
        "properties": {
          "date": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
          "headline": { "type": "string" },
          "why": { "type": "string" },
          "source": { "type": "string", "pattern": "^https://" }
        }
      }
    },

    "changelog": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["date", "entries"],
        "properties": {
          "date": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
          "entries": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }
}
