LC-JSON 1.0-rc.2 — first announced release candidate
Pre-releaseLC-JSON v1.0-rc.2
Prepared: 2026-05-30 · Status: Public release candidate (pre-release), the first formally announced prerelease
Specification: https://lc-json.org · Repository: https://github.com/lc-json/specification
Schemas: https://lc-json.org/1.0-rc.2/ · License: Apache 2.0
This is the first publicly announced release candidate of LC-JSON (Learning Content JSON), an open specification for portable learning content, built around a simple premise:
Educational content should remain readable, structured, reusable, and under the control of the people who create it.
LC-JSON defines a JSON interchange format for courses, units, lessons, instructional content, exercises, quizzes, questions, learning objectives, and accessibility metadata — designed to move cleanly between authoring tools, learning-management systems, and delivery platforms rather than being locked inside any one of them.
LC-JSON is a content-layer format — complementary to, not competing with, the established LMS interop standards: LTI (tool launch and grade passback), OneRoster (rosters), xAPI (activity records), SCORM (runtime delivery). The RATIONALE.md document in the specification covers the full landscape and what LC-JSON is not.
What LC-JSON looks like
A minimal QuestionSet — one of the two artifact types (the other is a hierarchical Course):
{
"$schema": "https://lc-json.org/1.0-rc.2/question-set.schema.json",
"documentType": "questionSet",
"specVersion": "1.0",
"title": "Geography — Capitals",
"language": "en",
"questions": [
{
"type": "shortAnswer",
"globalId": "550e8400-e29b-41d4-a716-446655440001",
"prompt": "What is the capital of France?",
"acceptedAnswers": ["Paris"],
"points": 1.0,
"hint": "The city sits on the River Seine in northern France and has been the nation's political and cultural center for over a thousand years.",
"feedback": {
"correct": "Correct — Paris. Founded as the Roman town of Lutetia on the Île de la Cité, it has been France's capital since the late 10th century and is today the country's largest city and its cultural and economic heart.",
"incorrect": "Not quite — the answer is Paris, home to the Eiffel Tower, the Louvre, and Notre-Dame. As the line from the film Sabrina goes, 'Paris is always a good idea.'"
}
}
]
}The hint and feedback blocks are optional — shown here to illustrate what a fully-authored question carries. Omitted optional fields fall back to sensible defaults: caseSensitive → false (so Paris, paris, and PARIS all match), and difficulty → 5.0. Empty-optional fields like title and tags are simply left out rather than written as "" or [] — keeping documents legible and diffs clean.
shortAnswer is used here deliberately: it is a real-content type whose prompt legitimately is the question, so a single short headline reads cleanly. For fuller worked documents — a complete course, unit, lesson, and the exercise/quiz item shapes — see the spec's examples/ tree: course-minimal.json, unit-minimal.json, lesson-minimal.json, 11-exercise-item.json, 12a-graded-quiz-item.json, 12b-ungraded-quiz-item.json, and sample-course-with-questions.json at https://github.com/lc-json/specification/tree/main/examples.
A Course wraps these same question shapes inside exercise/quiz items, lessons, and units — identical document envelope, hierarchical body.
Changes from the internal rc.1 candidate
prompt:minLength1→0; non-authoritative for the eight symbolic question types.promptstays required on every question. An empty string""is valid.- For the four real-content types (true/false, multiple choice, short answer, essay)
promptis the question and remains authoritative; the reference validator flags an emptyprompton these as an ERROR. - For the eight symbolic types (gap-fill family, sentence transformation, matching, ordering, placement) the structured fields carry the question's meaning;
promptMAY be empty or MAY carry a brief producer-derived readable summary. Consumers MUST NOT rely on a symbolicprompt's content for scoring, rendering, equality, or deduplication.
- Reserved/unknown-type preservation language.
NORMATIVE.md§6.2 and §6.4 now require preservation of every member, value, and nested structure across read/write cycles, replacing the earlier "verbatim" / "byte-equivalent" wording. Key order within JSON objects is producer-discretion per RFC 8259 §4 (SHOULD for authoring ergonomics, not MUST for consumers). - TrueFalseQuestion
correctAnswerimport normalization. Explicitly labelled inquestion-types-reference.mdas a pre-1.0 lenient migration affordance, not conforming behavior. The schema requires a JSON boolean; conforming producers MUST emit a JSON boolean; conforming consumers in strict mode MUST reject non-boolean values per §5.1.
Backward compatibility: every document valid under the rc.1 schemas remains valid under rc.2. The prompt change is a non-breaking widening (minLength: 1 → 0 only relaxes the constraint; object shape unchanged). The preservation-language and TF-normalization changes tighten normative-prose precision; no schema changes, no wire-format changes.
What's in this release candidate
- Two artifact types: Course (course → units → lessons → items) and QuestionSet (flat list of questions)
- Five item types: content, exercise, quiz, contentsequence, signpost
- 12 question types with full per-type schemas — gap-fill, multiple choice, true/false, the cloze family, short answer, essay, matching, ordering, placement, sentence transformation; 7 further types reserved for a future minor version
- Learning objectives, metadata, and portable identifiers throughout the hierarchy
- Accessibility metadata preservation built into base conformance
- 23 JSON Schemas (Draft 7), a conformance test corpus, a reference validator, and worked examples
Try it
Every document declares its schema by URL, so any JSON Schema (Draft 7) validator works:
pip install jsonschema
python tools/validate_course.py --course-path your-document.jsonA document is conforming LC-JSON if it validates against the published schemas at its declared $schema URL and satisfies the producer/consumer requirements in NORMATIVE.md. A 38-fixture conformance corpus (13 valid, 25 invalid) plus the reference validator define the bright line.
- Specification + schemas: https://lc-json.org
- Example documents: https://github.com/lc-json/specification/tree/main/examples
- Conformance corpus: https://github.com/lc-json/specification/tree/main/tests
What's stable, what may still change
Stable — safe to build against:
- The document envelope (
$schema,documentType,specVersion) and flat-root shape - The two artifact types and five item types
- The 12 question-type shapes and their canonical camelCase discriminators
- The
/1.0-rc.2/schema URLs — immutable for the life of the specification
May still be refined before v1.0 final (target: 2026-06-30):
- Additional conformance fixtures and validation-tooling coverage
- Accessibility-profile deepening
- Minor cleanup of optional fields
- Documentation clarifications
No breaking changes to the wire format are expected. The /1.0/ URL space is reserved for the final release and will be a deliberate re-publication — a document pinned to /1.0-rc.2/ continues to validate against rc.2 indefinitely. The earlier internal /1.0-rc.1/ schema set remains served and frozen but was never publicly announced.
Areas still under discussion
Deferred to keep the foundational interchange model stable first: accessibility conformance profiles, packaging and distribution conventions, interactive-activity extensions, analytics and feedback metadata, and rich-media embedding conventions.
Two prompt-specific items are explicitly deferred, with no change to the rc.2 contract beyond minLength: 0:
- Future treatment of
promptfor symbolic types — whether a later version makes it optional/omittable rather than required-with-empty — left for post-1.0 review once there is real-world implementation experience. promptrules for the 7 reserved question types — deferred to v1.1, when those types are implemented and gain per-type schemas. Under rc.2 an emptyprompton a reserved type is permitted (no domain rule fires).
A separate design question — surfaced during the rc.2 cycle but not changed in this release — concerns grading semantics for unsupported question types:
- Grading semantics for unsupported question types. §6.2 currently keeps an unsupported question's possible points in the item's total while setting earned points to
0— the item maximum is not silently reduced. This preserves grade comparability across consumers but means a learner whose consumer cannot render a question has no path to those points (and the item percentage absorbs the gap). Open question for future versions: should consumers continue to leave unsupported items quietly non-performing — possibly under-counting against the author/instructor's grading intent — or should they apply maximum fairness to learners by preemptively setting the unsupported question's possible points to0? Feedback from implementers and instructional designers welcome via issues on the spec repository.
Two further questions surfaced during the rc.2 review and are explicitly deferred:
- Strict RFC 4122 UUID validation. The schema regex currently checks UUID shape only (8-4-4-4-12 hex), not RFC 4122 version or variant bits. The prose was aligned in rc.2 to reflect shape-only enforcement ("RFC 4122 UUID, any version; shape-only validation"). Open question for future versions: tighten the schema to enforce specific UUID versions (e.g. v4) and the RFC 4122 variant bits, or formally adopt shape-only as the contract. Feedback from implementers welcome via issues on the spec repository.
- Deterministic semantics for
points: null.question-base.schema.jsonpermitspoints: null, but for reserved and unknown question types §6.2 requires possible points to count toward the item's total —nullhas no deterministic value there. Open question for future versions: forbidnullfor scored questions, or define a deterministic default (e.g.null→1.0) that consumers MUST apply. Feedback from implementers and instructional designers welcome via issues on the spec repository.
Three further standards-surface questions surfaced during the rc.2 review cycle. None block rc.2, all are flagged here for resolution before or during 1.0 final:
- Normative authority of the reference validator.
tools/validate_course.pyenforces several domain rules (e.g. MCQ MUST have at least one option withpoints > 0;optionsAndPointsMUST cover every entry inoptions; cloze marker-set equality with answer-key set) as ERROR-tier. These rules are documented inVALIDATION.md(informative catalog) andquestion-types-reference.md(per-type guidance), but the binding RFC 2119 prose for them does not currently live inNORMATIVE.md. Open question for 1.0 final: promote every ERROR-tier domain-validator rule that conforming consumers MUST replicate into explicit normative prose inNORMATIVE.md, or formally state that the reference validator's domain pass is a reference-tool check (recommended but not normatively binding on third-party consumers). The corpus (run_corpus.py) currently encodes the former interpretation by pinning every ERROR-tier rule with an invalid fixture under--strict. - HTML processing model: strict-reject vs. recovery-sanitize.
HTML_SAFETY.mdsays forbidden elements (<script>,<iframe>, event handlers,javascript:/vbscript:URLs) MUST be stripped by the sanitizer;NORMATIVE.md§11 and the sameHTML_SAFETY.mdvalidator-severity table say consumers MUST reject documents containing those constructs as ERROR-tier violations. Both statements are true in different processing stages — validation/import rejects, rendering fallback (if a non-conforming document is rendered at all) strips — but the current prose does not separate the stages cleanly enough for a first-time reader. Open question for 1.0 final: explicitly split the processing model into "validation stage: reject" and "recovery rendering stage (out of strict conformance): sanitize," so implementers do not have to infer which MUST wins. - Language-tag model: ISO 639-1 root vs. BCP 47 inline. Root
languageandsupportLanguageare ISO 639-1 (en,es,pt, …); inline HTMLlangattributes insideContentItem.htmlfollow BCP 47 (en-US,pt-BR,zh-Hant,es-419, …). Courses targeting regional varieties (Brazilian vs. European Portuguese, Simplified vs. Traditional Chinese, US vs. UK English) cannot express that distinction at the document root in rc.2 — only inline. Open question for 1.0 final: keep rootlanguagedeliberately coarse (and document the limitation), widen it to BCP 47 (single model end-to-end), or add a sibling root field for regional variant. The accessibility profile's WCAG 3.1.1 obligation is satisfied either way; this is an authoring expressivity question.
Extensions & compatibility
LC-JSON is designed to grow without invalidating existing documents. Tools may add namespaced extensions (x-<namespace> members) for local metadata, provided base document validity is preserved and core structural semantics stay intact. Consumers SHOULD ignore unrecognized extension members while preserving them across read/write cycles. The specification distinguishes normative requirements (MUST/SHOULD/MAY) from implementation recommendations and ecosystem conventions.
Implementers are encouraged to preserve unknown fields, prefer graceful degradation over hard failure, avoid assumptions about property ordering, and keep content structure separate from presentation.
Feedback
This release opens the public review phase ahead of stable v1.0. Implementation reports, edge cases, and interoperability findings are especially welcome from LMS and EdTech developers, instructional designers, accessibility specialists, educational publishers, and open-educational-resource communities.
Open an issue or discussion at https://github.com/lc-json/specification.