{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://codingplans.dev/schemas/plan.schema.json",
  "title": "Plan",
  "description": "A subscription or seat-based AI coding plan.",
  "type": "object",
  "required": [
    "id",
    "name",
    "vendor",
    "monthly_price",
    "annual_monthly_price",
    "annual_monthly_price_reason",
    "kind",
    "buyer",
    "included_models",
    "usage_limit",
    "main_limitation",
    "vendor_notes",
    "source",
    "source_date"
  ],
  "properties": {
    "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$", "description": "Stable lower-case identifier, vendor-prefixed. Also the anchor of the plan on its vendor page; never changes." },
    "name": { "type": "string", "minLength": 1, "description": "Plan name as the vendor sells it." },
    "vendor": { "type": "string", "minLength": 1, "description": "Company that sells the plan." },
    "monthly_price": { "type": "number", "minimum": 0, "description": "Price in US dollars per month with monthly billing. Per seat for team plans. 0 for free tiers." },
    "annual_monthly_price": { "type": ["number", "null"], "minimum": 0, "description": "Effective monthly price in US dollars with annual billing (the yearly price divided by 12, rounded to cents), or null when the vendor sells no annual option." },
    "annual_monthly_price_reason": { "type": "string", "pattern": "^—", "description": "Why annual_monthly_price is what it is, starting with an em dash, e.g. \"— (monthly billing only)\"." },
    "kind": {
      "description": "Product category: where you use the plan.",
      "type": "string",
      "enum": [
        "IDE extension",
        "Editor / IDE",
        "CLI / Terminal",
        "Cloud agent & IDE",
        "Web chat & coding agent",
        "API / PAYG",
        "Coding subscription"
      ]
    },
    "included_models": {
      "description": "Models the plan gives access to, as the vendor names them. A note in parentheses marks a restriction, e.g. \"(usage credits)\".",
      "type": "array",
      "minItems": 1,
      "items": { "type": "string", "minLength": 1 }
    },
    "usage_limit": { "type": "string", "minLength": 1, "description": "The allowance as the vendor describes it, in plain text. Vendors rarely publish exact request counts." },
    "main_limitation": { "type": "string", "minLength": 1, "description": "The most important catch or restriction, in plain text." },
    "vendor_notes": { "type": "string", "minLength": 1, "description": "Further context from the vendor page." },
    "source": { "type": "string", "pattern": "^https://", "description": "Vendor page the price and limits were taken from." },
    "source_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "Date (YYYY-MM-DD) the source was last checked. Not the launch date." },
    "buyer": {
      "type": "string",
      "enum": ["individual", "team"],
      "description": "Who buys it self-serve. team = multi-seat with shared billing and admin, bought without a sales contract; it is shown as the entry rung of the vendor's enterprise ladder. Contract plans live in enterprise.json."
    },
    "seat_pricing": {
      "type": "object",
      "description": "How a team plan's price scales with seats. The price itself stays in monthly_price / annual_monthly_price (per seat for per_seat and base_plus_seat, for the whole team for per_team).",
      "required": ["model", "base_monthly", "min_seats", "max_seats"],
      "properties": {
        "model": { "type": "string", "enum": ["per_seat", "base_plus_seat", "per_team"] },
        "base_monthly": { "type": ["number", "null"], "minimum": 0, "description": "Team fee on top of seats; base_plus_seat only." },
        "min_seats": { "type": ["integer", "null"], "minimum": 1 },
        "max_seats": { "type": ["integer", "null"], "minimum": 1 }
      },
      "additionalProperties": false
    },
    "popularity_score": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "description": "Adoption index 0-100, log-scaled from the product's published reach and a documented tier share. Not a vendor-published metric."
    },
    "popularity_basis": {
      "type": "string",
      "minLength": 20,
      "description": "The raw adoption signal behind the score plus the tier share used, in plain words."
    },
    "popularity_source": { "type": "string", "pattern": "^https://", "description": "Where the adoption signal behind popularity_score comes from." },
    "popularity_source_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "Date (YYYY-MM-DD) the popularity source was checked." },
    "popularity_confidence": { "type": "string", "enum": ["published", "reported", "proxy", "assumed"], "description": "How solid the adoption signal is: published (vendor figure), reported (credible reporting), proxy (installs or downloads), assumed (explicit estimate)." }
  },
  "additionalProperties": true
}
