{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://lc-json.org/1.1-rc.1/course.schema.json",
  "title": "LC-JSON Course Document Schema",
  "description": "Top-level LC-JSON course document. The root carries $schema, documentType ('course'), and specVersion, with the course payload as a flat sibling of those fields (no enclosing envelope).",
  "type": "object",
  "allOf": [
    {
      "$ref": "publication-fields.schema.json"
    }
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "format": "uri",
      "description": "Canonical URL of this schema. Producers MUST emit this field pointing at the published canonical schema (e.g. https://lc-json.org/1.1-rc.1/course.schema.json). Consumers SHOULD tolerate documents that omit it (re-import scenarios where producers were lenient), but tooling support — IDE autocomplete, ad-hoc validation, schema dispatch — is best when the canonical URL is present."
    },
    "documentType": {
      "type": "string",
      "const": "course",
      "description": "Artifact discriminator. For a course document this is always the literal string \"course\" (lowercase). Per NORMATIVE.md §5.3, conforming consumers MUST reject other casings or separator variations. Tools MAY offer non-conforming legacy-migration modes outside the public conformance contract."
    },
    "specVersion": {
      "type": "string",
      "pattern": "^1\\.[0-9]+(\\.[0-9]+)?$",
      "description": "LC-JSON spec version. This document conforms to v1.x. Forward-compatible: any 1.x value is accepted by 1.0 importers; 2.x or higher is rejected cleanly."
    },
    "title": {
      "type": "string",
      "description": "Course title",
      "minLength": 1
    },
    "subtitle": {
      "type": "string",
      "description": "Course subtitle",
      "default": ""
    },
    "description": {
      "type": "string",
      "description": "Course description",
      "default": ""
    },
    "authors": {
      "type": "array",
      "description": "List of course authors",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "publisher": {
      "type": "string",
      "description": "Publisher name",
      "default": ""
    },
    "courseCode": {
      "type": "string",
      "description": "Course code/identifier (e.g., 'ENG-101')",
      "default": ""
    },
    "sourceCourseId": {
      "type": [
        "string",
        "null"
      ],
      "description": "Stable identifier for this course across versions, generated by the source authoring system. The same sourceCourseId across exports identifies them as successive versions of the same logical course. Does not identify a human author.",
      "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
      "default": null
    },
    "version": {
      "type": [
        "string",
        "null"
      ],
      "description": "Author-provided content version. Numeric, dotted, 1 to 3 segments (e.g., '1', '1.0', '1.2.3', '4', '12.3.4'). Sortable by segment-wise integer comparison. Independent of LC-JSON's own specVersion.",
      "pattern": "^[0-9]+(\\.[0-9]+){0,2}$",
      "default": null
    },
    "language": {
      "type": "string",
      "description": "ISO 639-1 code for the course delivery language (e.g., 'en', 'es')",
      "default": "en"
    },
    "supportLanguage": {
      "type": [
        "string",
        "null"
      ],
      "description": "ISO 639-1 code for the learner's native/support language (L1). Used to resolve generic [L1: ...] bilingual tags at render time. Null or absent means no support language configured.",
      "default": null
    },
    "coverImageUrl": {
      "type": "string",
      "description": "URL to course cover image",
      "default": ""
    },
    "estimatedDurationMinutes": {
      "type": "integer",
      "description": "Estimated completion time in minutes",
      "minimum": 0,
      "default": 0
    },
    "tags": {
      "type": "array",
      "description": "Course-level tags for categorization",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "objectives": {
      "type": "array",
      "description": "Learning objectives library for this course. Referenced by courseObjectiveIds and unit/lesson objectiveIds.",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Objective GUID identifier"
          },
          "text": {
            "type": "string",
            "description": "The learning objective statement (e.g., 'Identify key themes in narrative texts')"
          },
          "difficultyBand": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "Recall",
              "Understand",
              "Apply",
              "Analyze",
              null
            ],
            "description": "Bloom's taxonomy difficulty band: Recall (remember), Understand (explain), Apply (use), Analyze (higher-order)"
          },
          "tagIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tag IDs associated with this objective",
            "default": []
          }
        },
        "required": [
          "id",
          "text"
        ]
      },
      "default": []
    },
    "courseObjectiveIds": {
      "type": "array",
      "description": "Objective IDs assigned at course level (displayed in course intro/summary signposts)",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "units": {
      "type": "array",
      "description": "Array of Unit objects",
      "items": {
        "$ref": "unit.schema.json"
      },
      "default": []
    },
    "glossaryRefs": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "default": [],
      "description": "GlobalIds of glossary documents attached at this node. Placement encodes scope: a ref on the course root attaches course-wide, on a unit unit-wide, on a lesson lesson-wide (nearest attachment wins: lesson, then unit, then course). Plain id strings — refs resolve first against the course's root glossaries[] pool of carried copies, then against documents the consumer already holds. Junctions stop at Lesson: items and questions carry no glossaryRefs."
    },
    "glossaries": {
      "type": "array",
      "items": {
        "type": "object"
      },
      "default": [],
      "description": "Carried copies of the glossary documents this course's glossaryRefs reference — full glossary documents, identity (globalId, entry member ids) preserved verbatim, fields untouched. Transport for self-containment, not a fork: a consumer already holding a copied glossary links to its own and ignores the copy's content (NORMATIVE §5.7). One .json file therefore carries the whole course, glossaries included."
    }
  },
  "required": [
    "$schema",
    "documentType",
    "specVersion",
    "title",
    "language"
  ]
}
