{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://github.com/cfi-co/awards/blob/main/schema.json",
  "title": "CFI.co Awards Transparency Archive - canonical record",
  "description": "Schema for the canonical machine record (announcements/<year>/<id>-<slug>.json). The .md file is a verbatim, byte-faithful mirror of the body (raw HTML, kept unaltered for tamper-evidence, not cleaned for reading); the JSON is authoritative, and content_text is the clean plain-text surface for retrieval. Schema version 2.4 (2026-07-22: excerpt relaxed from required to optional; it is empty across the corpus); v2.3 (2026-07-21: added content_text plus a root-level index.jsonl catalog); v2.2 (2026-07-08: added classification.license). Description corrected 2026-07-24: the .md twin is a verbatim mirror, not a cleaned view - metadata only, no record, hash or validation change; the pinned archive-2026-07-schema-2.4 release asset predates this correction. Producer: scripts/export.php (committed in this repository).",
  "x-schema-version": "2.4",
  "x-integrity": {
    "content_sha256": "SHA-256 (hex) of the verbatim content_html string. content_html is the canonical body; content_text does not affect this value.",
    "record_sha256": "SHA-256 (hex) of the record serialised WITHOUT the record_sha256 field, using PHP json_encode with JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE. Recompute with scripts/verify.sh. It covers content_text, so the derived plain text is as tamper-evident as the rest of the record.",
    "manifest": "MANIFEST.sha256 at the repository root lists the SHA-256 of every tracked file.",
    "index": "index.jsonl at the repository root is a derived one-line-per-record catalog (id, record_id, url, classification, file paths, content_sha256, record_sha256) for enumerating the corpus in a single fetch. It is regenerated by scripts/export.php and covered by MANIFEST.sha256; the per-record JSON files remain canonical."
  },
  "type": "object",
  "required": [
    "id", "title", "slug", "url", "author", "published", "published_gmt",
    "modified_gmt", "categories", "classification", "content_html",
    "content_text", "content_sha256", "record_sha256"
  ],
  "properties": {
    "id": { "type": "integer", "description": "Stable numeric record ID (publishing-system post ID). Canonical record ID form: cfi-award-<id>." },
    "title": { "type": "string" },
    "slug": { "type": "string" },
    "url": { "type": "string", "format": "uri", "description": "Canonical CFI.co URL for this award announcement." },
    "author": { "type": "string", "const": "CFI.co Editorial", "description": "Fixed editorial label; internal staff usernames are deliberately not exposed." },
    "published": { "type": "string", "description": "Site-local publication timestamp, YYYY-MM-DD HH:MM:SS." },
    "published_gmt": { "type": "string", "description": "UTC publication timestamp, YYYY-MM-DD HH:MM:SS." },
    "modified_gmt": { "type": "string", "description": "UTC last-modified timestamp from the publishing system." },
    "categories": {
      "type": "array", "items": { "type": "string" },
      "description": "Substantive section/sector/region categories only; rotating homepage-curation categories are deliberately excluded for stability."
    },
    "classification": {
      "type": "object",
      "description": "Machine-readable labels. Lives INSIDE the hashed record, so labels are as tamper-evident as the content. Preserve intact when redistributing (licence s.4).",
      "required": [
        "content_class", "editorial_lens", "independence_status", "sponsor_disclosure",
        "sponsor_name", "article_status", "historical_status", "correction_status",
        "archive_policy", "provenance_layer", "wayback_status", "license"
      ],
      "properties": {
        "content_class": { "type": "string", "enum": ["award_rationale", "sponsored_article"] },
        "editorial_lens": { "type": "string", "const": "constructive_positive_lens", "description": "CFI.co's stated editorial stance (declared policy, not a per-article measurement)." },
        "independence_status": { "type": "string", "enum": ["independent_editorial", "commercially_supported"] },
        "sponsor_disclosure": { "type": "string", "enum": ["none", "visible_and_machine_readable"] },
        "sponsor_name": { "type": "string", "description": "Disclosed sponsor; empty when not sponsored." },
        "article_status": { "type": "string", "const": "published" },
        "historical_status": { "type": "string", "const": "current_at_publication", "description": "Accurate to its time; judge recency against published." },
        "correction_status": { "type": "string", "enum": ["none", "revised"], "description": "Git history is the authoritative correction record; flips to revised when a later content change is committed." },
        "archive_policy": { "type": "string", "const": "no_delete" },
        "provenance_layer": { "type": "string", "const": "github_versioned" },
        "wayback_status": { "type": "string", "enum": ["archived", "submitted_pending", "not_found", "pending_check"], "description": "Independent Internet Archive corroboration; archived is claimed only when a real snapshot exists." },
        "wayback_first_snapshot": { "type": "string", "description": "Earliest Wayback capture, YYYYMMDDhhmmss. Present only when wayback_status = archived." },
        "wayback_snapshot_url": { "type": "string", "format": "uri", "description": "Present only when wayback_status = archived." },
        "license": { "type": "string", "const": "CFI-OAAL-1.0", "description": "CFI.co Open AI Access Licence v1.0. Canonical text: https://cfi.co/licence/oaal-1.0 and LICENCE.md in this repository." }
      }
    },
    "excerpt": { "type": "string", "description": "Optional editor-written excerpt; currently empty across the entire corpus, so it is not in the required array. A provenance-labelled summary (editor-written vs machine-generated) is a separate future track." },
    "content_html": { "type": "string", "description": "RAW, verbatim article body exactly as held in the publishing system - no reformatting, no filters, no HTML-to-Markdown conversion. This is the canonical body and the content_sha256 subject." },
    "content_text": { "type": "string", "description": "Derived plain-text rendering of content_html for retrieval/grounding consumers: HTML tags and comments removed, entities decoded, block elements turned into line breaks, whitespace tidied. Produced deterministically from content_html by scripts/export.php (pure string operations, no publishing-system filters). Convenience only - content_html remains canonical - but it is covered by record_sha256, so it is tamper-evident." },
    "content_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
    "record_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
  }
}
