{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://rootandbranch.org/schema/lineage-entry-bulk/0.1.json",
  "title": "Root & Branch bulk Lineage Entry, v0.1",
  "description": "The format a program uses to route pairings it already administers into the public register. This carries Lineage ENTRIES, not Practice Records: an Entry states that a named person taught a named person a named practice, attested by the program that administered it. A Record additionally describes how the practice is done and carries the master's own correctness attestation, and only the master can make one. A program cannot attest on a master's behalf, and this format gives it no way to try.",
  "type": "object",
  "required": ["format_version", "program", "attestation", "entries"],
  "additionalProperties": false,
  "properties": {
    "format_version": { "const": "0.1" },

    "program": {
      "type": "object",
      "required": ["organization", "program_name", "public_url", "jurisdiction"],
      "additionalProperties": false,
      "properties": {
        "organization": { "type": "string", "minLength": 2, "maxLength": 200 },
        "program_name": { "type": "string", "minLength": 2, "maxLength": 200 },
        "public_url": { "type": "string", "format": "uri" },
        "jurisdiction": {
          "type": "string",
          "description": "US state or territory code, or the country code for programs outside the US.",
          "pattern": "^[A-Z]{2}$"
        },
        "contact_route": {
          "type": "string",
          "description": "An organizational address for register correspondence. Optional. Never an individual's personal contact details.",
          "maxLength": 200
        }
      }
    },

    "attestation": {
      "type": "object",
      "description": "Made by a person authorized to speak for the program. It is the only attestation in this file, and it is narrow on purpose.",
      "required": ["by_name", "by_title", "date", "statement_accepted"],
      "additionalProperties": false,
      "properties": {
        "by_name": { "type": "string", "minLength": 2, "maxLength": 120 },
        "by_title": { "type": "string", "minLength": 2, "maxLength": 120 },
        "date": { "type": "string", "format": "date" },
        "statement_accepted": {
          "const": "I am authorized to make this statement for the program named above. Each entry in this file records a pairing the program administered or funded. The program does not attest that any practice was performed well, and makes no statement about any participant's skill. Every entry is drawn from material the program has already published, or from records the program holds and is free to share.",
          "description": "Reproduced verbatim as acceptance. Any other value fails validation."
        }
      }
    },

    "entries": {
      "type": "array",
      "minItems": 1,
      "maxItems": 5000,
      "items": {
        "type": "object",
        "required": ["cycle_year", "practice", "consent", "source_url"],
        "additionalProperties": false,
        "properties": {
          "local_id": {
            "type": "string",
            "maxLength": 64,
            "description": "The program's own identifier for this pairing, carried through so later corrections can be matched without guessing."
          },
          "cycle_year": { "type": "integer", "minimum": 1900, "maximum": 2100 },
          "master_name": {
            "type": "string",
            "maxLength": 160,
            "description": "Omit entirely if the program does not publish it. Do not write a placeholder."
          },
          "successor_name": {
            "type": "string",
            "maxLength": 160,
            "description": "Omit entirely if not published. Many programs name only the grant holder; that is a fact about the record, not a defect to paper over."
          },
          "successor_count": {
            "type": "integer",
            "minimum": 1,
            "description": "Where one master taught a cohort. Use with successor_name omitted."
          },
          "practice": {
            "type": "string",
            "minLength": 2,
            "maxLength": 300,
            "description": "As the program names it, in the trade's own words. Do not translate into an occupation code."
          },
          "location": { "type": "string", "maxLength": 120 },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "The page or document where the program published this pairing. Every entry carries one; an entry nobody can check is not an entry."
          },
          "consent": {
            "type": "string",
            "enum": [
              "published_by_program",
              "participants_consented_to_register",
              "participant_declined",
              "unknown"
            ],
            "description": "published_by_program: the program published these names itself and is sharing what is already public. participants_consented_to_register: participants gave consent covering a third-party register. participant_declined: do not publish this entry; it is carried so the program's own totals reconcile. unknown: the program cannot say, which is common for older cohorts."
          },
          "notes": { "type": "string", "maxLength": 600 }
        }
      }
    }
  },

  "$comment": "Consent is the field that decides what happens to an entry. 'published_by_program' and 'participants_consented_to_register' publish. 'participant_declined' is never published and exists only so a program's counts reconcile. 'unknown' is held and published only as an aggregate count, never as a name, because a program cannot consent on a participant's behalf and neither can a register."
}
