{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://changegamer.ai/api/corpus.schema.json",
  "title": "ChangeGamer Corpus NDJSON Record",
  "description": "One line of /api/corpus.jsonl or /api/corpus.full.jsonl, JSON-parsed. The first line of the stream is always a \"meta\" record; every subsequent line is a \"resource\" record. In /api/corpus.jsonl (the free sample), \"text\" is null for premium resources; in /api/corpus.full.jsonl (the keyed Corpus/Enterprise deliverable), \"text\" is always a string.",
  "oneOf": [
    {
      "$ref": "#/$defs/metaRecord"
    },
    {
      "$ref": "#/$defs/resourceRecord"
    }
  ],
  "$defs": {
    "metaRecord": {
      "type": "object",
      "title": "Meta record (first line)",
      "required": [
        "record",
        "site",
        "url",
        "description",
        "corpus_version",
        "count",
        "note",
        "license"
      ],
      "additionalProperties": false,
      "properties": {
        "record": {
          "type": "string",
          "const": "meta"
        },
        "site": {
          "type": "string"
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "description": {
          "type": "string"
        },
        "corpus_version": {
          "type": "string",
          "description": "Semver stamp of the corpus this export was built from (see /api/stats.json corpus_version for the live value — if it is higher, re-pull)."
        },
        "count": {
          "type": "integer"
        },
        "note": {
          "type": "string"
        },
        "license": {
          "type": "string",
          "format": "uri"
        }
      }
    },
    "resourceRecord": {
      "type": "object",
      "title": "Resource record (every subsequent line)",
      "required": [
        "record",
        "url",
        "slug",
        "title",
        "description",
        "category",
        "tags",
        "updated",
        "premium",
        "text"
      ],
      "additionalProperties": false,
      "properties": {
        "record": {
          "type": "string",
          "const": "resource"
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "slug": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "category": {
          "type": "string",
          "enum": [
            "Guide",
            "Meta",
            "Policy",
            "Reference"
          ]
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "updated": {
          "type": "string",
          "format": "date",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "YYYY-MM-DD. Bumped only on a genuinely re-verified content delta (a small text amendment), not on a structural/formatting change."
        },
        "premium": {
          "type": "boolean"
        },
        "access": {
          "type": "object",
          "description": "Present only on premium records in the free /api/corpus.jsonl sample (where text is null): how to unlock this record's body — the keyed fetch URL, the entry-price key offer, its checkout URL, and the offer-catalog/payment-manifest pointers. Absent on free records and in /api/corpus.full.jsonl. Kept in lockstep with buildPremiumAccessPointer() in src/lib/payment.ts.",
          "required": [
            "reason",
            "unlock_with_key",
            "key_offer",
            "checkout_url",
            "offer_catalog",
            "payment_manifest"
          ],
          "additionalProperties": false,
          "properties": {
            "reason": {
              "type": "string"
            },
            "unlock_with_key": {
              "type": "string",
              "format": "uri"
            },
            "key_offer": {
              "type": "string"
            },
            "checkout_url": {
              "type": "string",
              "format": "uri"
            },
            "offer_catalog": {
              "type": "string",
              "format": "uri"
            },
            "payment_manifest": {
              "type": "string",
              "format": "uri"
            }
          }
        },
        "text": {
          "type": [
            "string",
            "null"
          ],
          "description": "The resource body. Null only when premium is true and the record came from the free /api/corpus.jsonl sample; always a string in /api/corpus.full.jsonl."
        },
        "premium_summary": {
          "type": "string",
          "description": "Present only on premium records in the free sample (/api/corpus.jsonl or /api/corpus.json, where text/body is null): a free teaser/preview of the withheld body. Absent on free records and absent in /api/corpus.full.jsonl (which never withholds a body, so the field has no purpose there)."
        },
        "sources": {
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          },
          "description": "Absolute citation URLs mechanically extracted from this resource's own \"## Verified sources\" section (or standalone \"Sources:\" line), if any. Always present (including on premium/stubbed records, since it is metadata about the resource, not paywalled body content); an empty array means no dedicated citation section was found — not that the resource has no real-world sources."
        }
      }
    }
  }
}