{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://anthroposophy.ai/index/data/schema.json",
  "title": "index-entry/1.0",
  "description": "One entry of the subject index: a headword, its aliases in English and German, a stated selection principle, and sub-headings each listing the places in the Gesamtausgabe where the concept is treated. An entry carries no quotation and no interpretation; it says where to read. Every reference was verified against the served corpus at build time and names the edition it was verified in. Additive changes stay within 1.x; a breaking change moves the files to data/v2/.",
  "type": "object",
  "required": ["schema", "id", "headword", "aliases", "selection_principle", "usage", "sections", "see_also", "distinguish_from", "external", "counts", "generated"],
  "additionalProperties": false,
  "properties": {
    "schema": {"const": "index-entry/1.0"},
    "id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$", "description": "The entry's stable id and its URL stem: /index/<id>/ and /index/data/<id>.json."},
    "headword": {
      "type": "object",
      "required": ["en", "de"],
      "additionalProperties": false,
      "properties": {"en": {"type": "string", "minLength": 1}, "de": {"type": "string", "minLength": 1}}
    },
    "title": {
      "type": "object",
      "description": "Optional. The name the page shows when it differs from the headword: a phrase that contains the headword (Illness and health for illness). The headword stays the name a lookup matches.",
      "required": ["en", "de"],
      "additionalProperties": false,
      "properties": {"en": {"type": "string", "minLength": 1}, "de": {"type": "string", "minLength": 1}}
    },
    "aliases": {
      "type": "object",
      "required": ["en", "de"],
      "additionalProperties": false,
      "properties": {
        "en": {"type": "array", "items": {"type": "string", "minLength": 1}},
        "de": {"type": "array", "items": {"type": "string", "minLength": 1}}
      },
      "description": "Other names for the same concept. An alias resolves to this entry; it never names a second concept. A name Steiner also uses, in a stated scheme, for a part of the concept is still an alias and carries an item in alias_notes."
    },
    "alias_notes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["name", "lang", "note", "section"],
        "additionalProperties": false,
        "properties": {
          "name": {"type": "string", "minLength": 1, "description": "One of the aliases in that language, exactly as listed."},
          "lang": {"enum": ["en", "de"]},
          "note": {"type": "string", "minLength": 1, "maxLength": 400, "description": "Where the name means the whole concept and where a part of it. Names the works; makes no claim about what the passages say beyond the use of the name."},
          "section": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$", "description": "The id of the section of this entry that lists the places stating the narrower use."}
        }
      },
      "description": "Qualifications of aliases whose use varies. An alias without an item here is used for the whole concept wherever it occurs."
    },
    "section_aliases": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["section", "en", "de"],
        "additionalProperties": false,
        "properties": {
          "section": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$", "description": "The id of the section of this entry the names lead to."},
          "en": {"type": "array", "items": {"type": "string", "minLength": 1}},
          "de": {"type": "array", "items": {"type": "string", "minLength": 1}},
          "note": {"type": "string", "minLength": 1, "maxLength": 400, "description": "What the names name and where the works use them otherwise."}
        }
      },
      "description": "Names that lead to one heading of this entry, not to the whole entry: a part Rudolf Steiner names in a stated scheme. A lookup by one of these names returns the entry narrowed to that section. Unlike an alias, such a name does not name the whole concept. Allowed for the lesser and greater guardian only (the operator's ruling of 2026-10-21)."
    },
    "selection_principle": {"type": "string", "minLength": 1, "description": "What is particular to this entry's selection: what counts beside the headword, which volumes are listed whole, and where the headings come from. The rules every entry shares are in usage."},
    "usage": {"type": "string", "minLength": 1, "description": "The rules every entry follows, the same text in every entry: what is listed, in what order, in which language, and that the entry quotes nothing."},
    "sections": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["id", "heading", "refs"],
        "additionalProperties": false,
        "properties": {
          "id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$"},
          "heading": {"type": "string", "minLength": 1, "description": "The index's short name for the sub-topic, never Steiner's wording."},
          "scope": {"type": "string", "maxLength": 160, "description": "One line, beginning Where, saying which treatments the section lists. Optional. Never a claim about what the passages say."},
          "refs": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/ref"}}
        }
      }
    },
    "see_also": {"type": "array", "items": {"$ref": "#/$defs/link"}},
    "distinguish_from": {"type": "array", "items": {"$ref": "#/$defs/link"}, "description": "Concepts the sources keep apart from this one. A reader who conflates them misreads the passages."},
    "external": {
      "type": "array",
      "description": "Reference pages about the concept on other sites, not texts by Steiner.",
      "items": {
        "type": "object",
        "required": ["label", "url"],
        "additionalProperties": false,
        "properties": {"label": {"type": "string", "minLength": 1, "description": "The site, named in full."}, "url": {"type": "string", "pattern": "^https://"}}
      }
    },
    "counts": {
      "type": "object",
      "description": "refs counts listings: a unit listed under several headings is counted each time.",
      "required": ["sections", "refs", "books", "articles", "lectures"],
      "additionalProperties": false,
      "properties": {
        "sections": {"type": "integer", "minimum": 1},
        "refs": {"type": "integer", "minimum": 1},
        "books": {"type": "integer", "minimum": 0},
        "articles": {"type": "integer", "minimum": 0},
        "lectures": {"type": "integer", "minimum": 0}
      }
    },
    "generated": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"}
  },
  "$defs": {
    "ref": {
      "type": "object",
      "required": ["locus", "ga", "volume", "unit", "lang", "edition", "kind", "title", "date", "url", "json_url", "verified", "extent"],
      "dependentRequired": {"through": ["passage"]},
      "additionalProperties": false,
      "properties": {
        "locus": {"type": "string", "pattern": "^GA [0-9]+[a-z]?/[^/ ]+(/p[0-9]{3})?$", "description": "GA volume / unit id in the served edition, and the paragraph where one passage is the point, or the first paragraph of a range (through names the last). No paragraph means the whole unit. The same form the site's verify endpoint accepts."},
        "ga": {"type": "string", "pattern": "^[0-9]+[a-z]?$", "description": "The GA volume number. A letter after it (266a, 68d) marks one part of a volume the site serves in parts."},
        "volume": {"type": "string", "description": "The title of the GA volume, as the site gives it; empty when the site has no volume page."},
        "unit": {"type": "string", "minLength": 1, "description": "The unit id as served: a date for a lecture (a second text on the same day carries _02), the edition's id for a chapter or essay. The id belongs to the edition named by lang: the German edition can serve the same chapter under another id, so one id in two languages can be two texts."},
        "lang": {"enum": ["en", "de"]},
        "edition": {"type": "string", "minLength": 1, "description": "The edition the unit was verified in for this language, as served: original for German, a translation code such as RSP1965 for English."},
        "kind": {"enum": ["book", "article", "lecture"], "description": "book: GA 1 to 28, his own written works. article: GA 29 to 46, essays, fragments and notes. lecture: everything else."},
        "passage": {"type": "integer", "minimum": 1, "description": "The paragraph number where one passage is the point, or the first paragraph of a range when through is present; absent when the reference is the whole unit."},
        "through": {"type": "integer", "minimum": 2, "description": "The last paragraph of a range, present only with passage and greater than it; a range holds at most twelve paragraphs of the one unit, counted inclusively."},
        "extent": {"enum": ["paragraph", "range", "unit"], "description": "What the reference names: one paragraph (passage), a range (passage to through), or the whole unit. On every reference, so the extent is never read from an absent key."},
        "whole": {"const": true, "description": "Present when the index states that the unit treats the heading's subject throughout, so that any part of it serves. A reference whose extent is unit and that lacks this mark was listed before the rule and does not say where in the unit the subject is treated."},
        "title": {"type": "string", "description": "The unit's title as served, read from the served unit when the record was built. Never typed."},
        "date": {"type": ["string", "null"], "description": "YYYY-MM-DD for a lecture, and for an article or letter where the edition dates it; null for a book chapter."},
        "url": {"type": "string", "pattern": "^https://anthroposophy\\.ai/v1/", "description": "The page for the unit on the site, or for the paragraph or range the reference names."},
        "json_url": {"type": "string", "pattern": "^https://anthroposophy\\.ai/v1/.*\\.json$", "description": "The unit with its passages, the one paragraph when passage is present, or the paragraphs passage to through of a range (the path ends pNNN-pMMM.json), in the verified language. The same path with /de/ in place of /en/ returns the German paragraph of the same number where witnesses lists de:original, which is not necessarily the same text: the paragraph's own reply names its German counterpart (german_anchor), and a range's reply its aligned paragraphs (aligned_witness)."},
        "record_type": {"enum": ["stenogram", "listeners_notes", "own_manuscript", "report_by_others", "mixed_sources", "unknown"], "description": "How the text was recorded, where the publisher states it for the volume or the text's own heading or notes say it; on every lecture (GA 47 and up), and absent on Steiner's books and articles. unknown means no evidence of how the lecture was taken down is on record: it is not an assurance of a stenogram. stenogram means a shorthand transcript. listeners_notes means the wording is the note-taker's or reporter's as much as his; report_by_others means the text is another person's account of a meeting or event; mixed_sources means the volume holds both listeners' records and texts in Steiner's own hand (the esoteric lessons of GA 266c, for one), so the unit's own heading says which it is."},
        "place": {"type": "string", "minLength": 1, "description": "Where the lecture was given, as the unit's own heading line on the site names it (English heading first, else the German). Absent where the heading names no place, and on Steiner's books and articles."},
        "translator": {
          "type": "object",
          "required": ["status", "source"],
          "additionalProperties": false,
          "description": "Who translated this English text, on every English reference; absent on German ones. Resolved per lecture: in the generic English edition from the lecture's own source page (never another lecture's or the volume's credit), in a named edition from the edition's record. not_established means nothing names a translator for this text: the page says no translator on record.",
          "properties": {
            "status": {"enum": ["established", "not_established", "original_english", "automated"], "description": "original_english: the source says the English is the original and the German the translation. automated: the source marks the English as an automated translation."},
            "name": {"type": "string", "minLength": 1, "description": "The translator or translators as the source names them; present when status is established."},
            "edition_code": {"type": "string", "description": "The edition the translation was published in (RSP1966), where known."},
            "source": {"enum": ["unit", "edition"], "description": "unit: the lecture's own credit (generic English edition); edition: the named edition's record."}
          }
        },
        "verified": {
          "type": "object",
          "required": ["at", "exists", "witnesses"],
          "additionalProperties": false,
          "properties": {
            "at": {"type": "string"},
            "exists": {"const": true},
            "witnesses": {"type": "array", "minItems": 1, "items": {"type": "string"}, "description": "lang:edition for every edition in which the site holds the cited unit under this id (and, when passage is present, that paragraph under that number; for a range, both its ends). An edition that numbers differently is not listed."},
            "passage_count": {"type": "integer", "minimum": 1, "description": "The unit's length in paragraphs, not a count of matches."},
            "may_be_abridged": {"type": "boolean", "description": "Present only when the site has compared the translation's length with the German; true means the translation is shorter. Absent means the site reports nothing."}
          }
        }
      }
    },
    "link": {
      "type": "object",
      "required": ["id", "label"],
      "additionalProperties": false,
      "properties": {
        "id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$"},
        "label": {"type": "string", "minLength": 1},
        "href": {"type": "string", "description": "Present only when an entry with that id exists; an id without one is reserved for an entry not yet made."}
      }
    }
  }
}
