Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Changelog

All notable changes to the LC-JSON (Learning Content JSON) specification are documented in this file.

The format is based on Keep a Changelog, and this project adheres to the versioning policy described in NORMATIVE.md §8.

[1.0-rc.3] — 2026-06-13

Second publicly announced release candidate. Adds the localization model (LOCALIZATION.md) and a conformance-corpus expansion; removes two prototype-era sentenceTransformation fields from the schema (the change that requires a new immutable path — /1.0-rc.2/ cannot be mutated). Backwards-compatible with 1.0-rc.2: every rc.2-valid document remains valid under rc.3. Published at immutable /1.0-rc.3/ URLs; /1.0-rc.1/ and /1.0-rc.2/ stay served and frozen.

Removed

  • allowedFillerWords and prohibitExtraWordsBetweenChunks property declarations removed from sentence-transformation.schema.json. Completes the removal documented in the 2026-05-26 rc.1 doc revisions: the reference docs, VALIDATION.md §9.9, and the shipped fixtures dropped both fields then; the working schema was the last surface still declaring them. No additionalProperties: false added — 1.0-rc.2 documents carrying the fields continue to validate as unknown members per NORMATIVE.md §5.4 and are dropped on re-emit by conforming consumers. The frozen /1.0-rc.1/ and /1.0-rc.2/ schemas are unchanged.

Added

  • LOCALIZATION.md — language model. New normative-and-informative document specifying the three roles of language — language (delivery), lang/dir (language of parts, WCAG 3.1.2), and supportLanguage (optional pedagogical L1 layer) — and stating explicitly that LC-JSON 1.x is single-language-per-document (translations are separate documents, not localized field bundles). Bound by new NORMATIVE.md §13 (sections renumbered: Validation surface §13→§14, References §14→§15). GLOSSARY.md gains a “Language and localization” group.
  • Language tags are BCP 47. language, supportLanguage, and HTML lang accept BCP 47 tags; bare ISO 639-1 (en, es) is the common case, region/script subtags (pt-BR, zh-Hant) are permitted, and a consumer MAY act on only the primary subtag. The reference validator’s supportLanguage check was widened from “2-letter ISO 639-1” to a BCP 47 plausibility check (WARN), and the same check now also covers language. ACCESSIBILITY.md §6.1 wording aligned.
  • Screen-reader pronunciation expectations (informative). LOCALIZATION.md §7 and ACCESSIBILITY.md §6.3 state that lang is necessary but not sufficient for correct pronunciation: automatic language switching and installed voices vary across screen readers (NVDA, JAWS, Narrator, VoiceOver) and are outside the format’s control. lang remains required where parts differ — it is the floor, not optional.
  • GitHub issue and PR templates. bug_report.md and spec_change_proposal.md under .github/ISSUE_TEMPLATE/, plus pull_request_template.md mirroring CONTRIBUTING.md’s pre-merge checklist.
  • RATIONALE.md added to the published book. The positioning page was linked from the landing page but missing from the site navigation and sync set; it now ships as a chapter (“Rationale and positioning”).
  • globalId document-wide uniqueness made explicit and enforced. NORMATIVE.md §4.4: within a single document, globalId values MUST be unique across all entities (Units, Lessons, Items, and Questions share one namespace); comparison is case-insensitive. Previously implied by “identify the entity across re-imports” but stated nowhere and enforced by no published artifact. Reference validator gains a document-wide duplicate check (ERROR-tier) on both the course and questionSet paths; reference fields that point at a globalId (contentItemId, relatedItemIds) are exempt as non-declarations. New VALIDATION.md §12.7 catalog row. Fixture: tests/invalid/40-duplicate-global-id.json (case-varied duplicate).
  • Validator: WARN on course-root author (singular). Course author credits are carried by the authors array; a singular author at the course root is not declared by course.schema.json (it belongs to the QuestionSet artifact) and conforming consumers discard it. Tolerated as an unknown field per NORMATIVE.md §5.4; the reference validator now surfaces it as a likely authoring mistake. The QuestionSet artifact’s author field is unaffected.
  • Accessibility: base-vs-Profile authoring split made explicit. Base conformance is now stated as preservation only (NORMATIVE.md §12.1) — it never requires a producer to author alt text, captions, or transcripts, so a small or non-institutional producer is never non-conforming for omitting them (the validator surfaces omissions as non-blocking warnings). Authoring obligations are bound by the opt-in Accessibility Profile (§12.2): under a Profile claim, a producer MUST emit alt on every <img> (ACCESSIBILITY.md §2.1), captions and a transcript on prerecorded instructional video carrying speech, and a transcript on prerecorded audio-only instructional content (§3.1, WCAG 1.2.1 / 1.2.2 / 1.2.3). Transcripts were previously SHOULD; they are now MUST under the Profile. No schema change — normative prose only.
  • Conformance corpus expanded 38 → 64 fixtures. Per-type coverage for all 12 implemented question types: 12 new valid fixtures (valid/1425) including both matching sub-shapes (pairs, classification), ordering with scoringMode: "kendall", and a grading combination matrix (valid/25) exercising all four graded/ungraded × exercise/quiz combinations with a resolvable objectives pool. 14 new invalid fixtures (invalid/2740) pinning schema-tier rules (missing required fields, gap-marker pattern, matchingMode sub-shape selection and pairs/categories collision, items minItems, scoringMode enum, empty wordBank, question-level missing globalId) and domain-tier rules (non-boolean correctAnswer, comma in a multiGapCloze accepted answer, ContentSequence relatedItemIds referential integrity, duplicate globalId). tests/manifest.json updated; tools/run_corpus.py reports 64/64.

[1.0-rc.2] — 2026-05-30

First formally announced public release candidate. Corrects the prompt-field definition from the (never-announced) internal 1.0-rc.1 candidate. Backwards-compatible widening: every 1.0-rc.1-valid document remains valid under 1.0-rc.2. Published at immutable /1.0-rc.2/ URLs; /1.0-rc.1/ stays served and frozen.

Changed

  • prompt: minLength 10; defined as non-authoritative for the symbolic question types. Required on every question. Authoritative for trueFalseQuestion, multipleChoice, shortAnswer, and essay. Non-authoritative for the symbolic types (gap-fill family, sentenceTransformation, matching, ordering, placement): MAY be empty or carry a producer-derived summary; consumers MUST NOT rely on its content for scoring, rendering, equality, or deduplication. Affected: question-base.schema.json, question-types-reference.md, eight symbolic examples; new examples/01b-simple-gap-fill-readable-prompt.json. Object shape unchanged.

Added

  • Real-content empty-prompt domain rule. validate_course.py flags empty/whitespace prompt on trueFalseQuestion, multipleChoice, shortAnswer, and essay as an ERROR. Symbolic types pass; reserved types unconstrained (deferred to v1.1). Fixtures: tests/valid/13-symbolic-empty-prompt.json, tests/invalid/26-real-content-empty-prompt.json. Corpus: 38/38.
  • GOVERNANCE.md: masterdoc principle + Release Candidate Policy. Single-living-source model: published releases are immutable versioned artifacts; the git tag is the historical source for prior versions. RC policy: RC releases MAY introduce backwards-compatible corrections and clarify language; MUST NOT silently modify previously published artifacts; v1.0 final establishes the stable contract.

Documentation

  • NORMATIVE §5 (Consumer Conformance): preamble + forward-compatibility worked example. Preamble at top of §5: schema validation is necessary but not sufficient; cross-references §5.3–§5.6, §6, §10.3. New informative subsection at end of §5 (“Forward compatibility: three look-alike situations”) covers three JSON-layer cases governed by different consumer obligations: unknown top-level field (§5.4), extension-namespaced field (§7), unknown type discriminator value (§5.1 Exception + §6 fallback). Cites §6.1’s “reserved and unknown types are handled identically.”
  • NORMATIVE §5 case 3: design-choice note on points handling for unsupported types. Earned 0; possible points stay in the item total; item maximum is consumer-independent. Open question logged in the rc.2 release notes’ “Areas still under discussion.”
  • tests/README.md: Behavioral conformance (informative) section. Three-step round-trip self-test recipe (load → re-emit → diff) for the existing fixtures tagged in manifest.json as demonstrating §6.4, §7, and §12.1 preservation obligations. No new fixtures or runner.
  • Scope clarification. Release notes intro, src/index.md, and README-public.md now state that LC-JSON is a content-layer format complementary to LTI, OneRoster, xAPI, and SCORM, with a pointer to RATIONALE.md’s Scope and Limits.
  • README.md: count + listing corrections. Example count 3132; removed the directory-tree reference to non-existent examples/course-legacy-wrapped.json; added placement.schema.json to the Question Type Schemas list.
  • tests/manifest.json: two stale § references. valid/01-course-minimal.json §4.4 camelCase property naming§4.5. valid/06-html-with-video-track.json §10§11 (HTML Safety Profile). Sweep confirms no further stale NORMATIVE § refs in the manifest.
  • Accessibility severity alignment. ACCESSIBILITY.md §8 listed video-without-<track> as “informational note”; VALIDATION.md §12.2 and validate_course.py enforce it as WARN. ACCESSIBILITY.md is now aligned to WARN.
  • “rc.1 baseline” wording → “current baseline (established in rc.1)”. Affected: ACCESSIBILITY.md §8 heading, §11 heading, §11 inline; VALIDATION.md §12.2 + §14. The accessibility severity policy is unchanged; only the labeling is brought into the current package’s framing.
  • UUID prose: “RFC 4122 UUID” → “RFC 4122 UUID (any version; shape-only validation)”. Brings prose into line with the schema regex (which checks shape only, not version/variant bits). Affected: GLOSSARY.md (globalId entry), README.md (Common Validation Errors), VALIDATION.md (six catalog rows across §4, §5, §6, §7, §8, §10), question-types-reference.md (intro requirement, Common Properties table, Validation Rules). NORMATIVE.md §4.4 already carried “(any version)” and is unchanged. Open question for v1.0 (whether to enforce strict v4/variant) logged in the rc.2 release notes’ “Areas still under discussion.”
  • Reserved/unknown-type preservation language: “verbatim” / “byte-equivalent” → semantic preservation. NORMATIVE.md §6.2 and §6.4 now require preservation of every member, value, and nested structure across read/write cycles; key order within JSON objects is producer-discretion (SHOULD for authoring ergonomics, not MUST for consumers) per RFC 8259 §4. §5 forward-compatibility case 3 “preserved byte-equivalent” → “preserved with every member, value, and nested structure intact.” Tracked through GLOSSARY.md, VALIDATION.md (two catalog rows), HTML_SAFETY.md §9, ACCESSIBILITY.md, question-types-reference.md (8 reserved-type Status lines), and tests/manifest.json valid/05.
  • TrueFalseQuestion correctAnswer import normalization: explicitly labeled as pre-1.0 lenient migration affordance, not conforming behavior. question-types-reference.md previously documented value-coercion ("true", "correct", "tick", "✓", 1true; symmetric for false) without saying it was non-conforming. The schema requires a JSON boolean and NORMATIVE.md §5.1 binds consumers to reject schema-validation failures (strict mode). The section now states: conforming producers MUST emit a JSON boolean; conforming consumers in strict mode MUST reject non-boolean values; the coercion is a transitional ingestion aid for pre-1.0 documents that does not survive into a --strict-conforming re-export.
  • Release notes (“Areas still under discussion”) — three further open questions logged for 1.0 final: normative authority of the reference validator’s ERROR-tier domain rules; HTML processing-model split between strict-validation rejection and recovery-rendering sanitization; language-tag model (ISO 639-1 root vs. BCP 47 inline). None block rc.2.

Note

  • 1.0-rc.1 was never publicly announced; 1.0-rc.2 is the first announced prerelease. The /1.0-rc.1/ schema set remains served and byte-frozen at its immutable URL.

[1.0-rc.1 doc revisions] — 2026-05-27

Doc-only follow-up revisions to the rc.1 publication. No schema or wire-format changes; the rc.1 contract at /1.0-rc.1/ is unchanged.

Added

  • GOVERNANCE.md: canonical sources identified. New paragraph in the Trademark and naming section names github.com/lc-json/specification and lc-json.org as the canonical sources for the LC-JSON specification, distinguishing them from forks and mirrors (which remain permitted under Apache 2.0 but should be named as derivative works rather than as “LC-JSON” without qualification). README-public.md trademark paragraph extended with a one-line pointer.

[1.0-rc.1 doc revisions] — 2026-05-26

Doc-only follow-up revisions to the rc.1 publication. No schema or wire-format changes; the rc.1 contract at /1.0-rc.1/ is unchanged.

Removed

  • allowedFillerWords and prohibitExtraWordsBetweenChunks (sentenceTransformation) — prototype-era optionals, removed from the public spec surface. Both fields paired to permit specific words between chunks — a degree of leniency that practice never used. Every authored example emitted the empty default (allowedFillerWords: []) and the strict default (prohibitExtraWordsBetweenChunks: true); the off-stance for either was not a real authoring use case. The same intent — accepting a chunk with a permitted filler — is better expressed by adding the filler-bearing variant to the chunk’s acceptedChunks[N] array (rare edge case). The /1.0-rc.1/sentence-transformation.schema.json still declares both properties because rc.1 schemas are immutable; the properties have been dropped from the reference docs (question-types-reference.md JSON example and property table), from VALIDATION.md §9.9, and from the shipped fixtures (examples/09-sentence-transformation.json, tests/invalid/25-sentence-transformation-multiple-markers.json). The /1.0/ schema MUST omit both when finalized.

Fixed

  • Editorial polish pass on rc.1 documentation. Count consistency across question-types-reference.md (19 question types; reserved-types numbered 13–19), VALIDATION.md rule-catalog completeness rows for optional schema fields, vendor-neutral phrasing in ITEM_PATTERNS.md §3, US-English normalization gap closure, IMPLEMENTATIONS.md standard header block, internal-rationale generalization, elimination of redundant audience-targeted guidance in favor of single-source rules in Overview / Common Properties / Validation Rules, and markdown hygiene throughout. No normative changes; the wire format and JSON Schemas are unchanged.

1.0-rc.1 — 2026-05-25

Release-candidate hardening pass before 1.0 final. Closes contract-drift issues identified in the external evaluation while keeping the wire format additive (no breaking changes vs unreleased 1.0 baseline).

Added

  • placement question type — full schema, four examples, validator domain rules, conformance corpus. New top-level type with a placements: [{gap, item}] explicit-relationship shape (mirrors the matching-redesign principle). Three modes via placementUnit: sentence (inline gaps), paragraph (block-level gaps), and sectionLabel (label slots above sections — covers IELTS Matching Headings and analytical meta-labels). Decoy gaps (extra @@@N markers without placements[] entries) natively express TOEFL Sentence Insertion. Word-level placement is covered by wordBankCloze. Four canonical examples: 17a (Cambridge B2 sentence-mode missing-sentences), 17b (Cambridge C1 paragraph-mode reorder), 17c (IELTS Matching Headings + analytical meta-labels), 17d (TOEFL Sentence Insertion with 4 candidate positions). Validator: hard-error rules on placements[].gap references and duplicates; soft-warning rules on marker-placement convention violations per placementUnit and on non-sequential @@@N numbering. Conformance corpus extended with 4 valid + 6 invalid placement fixtures. Reference-runtime scoring: pointsEarned = pointsPossible × correctCount / gapCount under partial credit; strict zero-or-full otherwise. Decoy-gap rule: an item placed at an unlisted gap marks the question incorrect but does not subtract from credit earned on correctly-filled gaps when partial credit is allowed; under all-or-nothing it fails the strict check. Discriminator enum extended.
  • Ordering: Kendall tau partial-credit scoring. The reference runtime implements Kendall tau over the learner’s permutation; with N items and k inversions, pointsEarned = pointsPossible × (1 − k / (N × (N−1) / 2)). Distractors placed in the answer area drop the question to incorrect at the strict-mode level but partial credit is still awarded over the in-bounds correct indices. A new helper resolves the conditional default at scoring time.
  • Matching question — matchingMode discriminator with two sub-shapes. Replaces the legacy stems[] / targets[] parallel-array shape entirely. matchingMode: "pairs" selects pairs: [{ item, match }] for 1:1 matching; matchingMode: "classification" selects categories: [{ label, items }] for many-to-one classification. The discriminator is required (no default). Mixed shapes (both pairs and categories present, or matchingMode omitted) are rejected by the schema. Each pairs[i].{item,match} and categories[i].{label,items} is required and minLength: 1; categories[i].items has minItems: 1. New canonical example 15b-matching-classification.json (time expressions → tense). A one-off migration script rewrites legacy on-disk content; the new shape replaces the legacy one entirely with no back-compat path.
  • NORMATIVE §6 — Reserved and Unknown Types. Full fallback contract: consumers MUST preserve reserved/unknown question types verbatim across read/write cycles, MUST NOT silently drop, MUST treat earned points as 0, SHOULD render a non-interactive placeholder. Producers MUST satisfy question-base.schema.json if emitting reserved types and SHOULD NOT emit them in cross-implementation distribution. Closes the round-trip preservation gap that broke portability for tool-specific extensions.
  • NORMATIVE §7 — Extensions (namespaced x- members). Defines a forward-compatible, collision-free mechanism for tools to attach data outside the interchange contract (authoring provenance, internal identifiers, editor state). Extension members are keyed x-<namespace> (e.g. x-acme-reviewState), MAY appear on the document root and any Course/Unit/Lesson/Item/Question object, and are strictly additive — removing all x- members MUST leave a conforming document with equivalent learner-facing meaning. Consumers MUST NOT reject documents for carrying them, MUST NOT interpret members outside namespaces they own, and SHOULD preserve unrecognized members across a round trip (defining an extension-preserving consumer). This is the contract that lets a tool use LC-JSON as a faithful transfer/backup format for its own tool-specific state. IMPLEMENTATIONS.md registers the x-lessoncommons namespace (question lineage / authoring provenance). Sections §7–§11 (was §6–§10 after Reserved Types) renumbered to §8–§12.
  • NORMATIVE §11 + new sibling HTML_SAFETY.md — HTML safety profile. Normative allowlist for ContentItem.html and SignpostItem.customHtml: allowed elements (block, inline, media including <video>, <audio>, <source>, <track>, <h1><h6>, <div>, <blockquote>, <figure>); per-element attribute table; URL-scheme allowlist (http:, https:, mailto:, tel:, relative — javascript:/vbscript:/data: etc. forbidden); link normalization (target="_blank"rel="noopener noreferrer"); inline style CSS-property allowlist (sizing, spacing, borders, alignment); class attribute permitted unconstrained (author-defined CSS hooks); sanitization obligation; unknown-element strip-while-preserving-text per §6.2 (mirrors §6 reserved-types philosophy — degrade gracefully, never fail-closed); validator severity split (ERROR for XSS-class violations like <script> and on*, WARN for sanitizable cases). Closes the largest public-spec gap from the 2026-05-02 external evaluation.
  • unitLevel hint on ordering questions"word" (default), "sentence", "paragraph". Enables sentence-in-text and paragraph-in-text ordering tasks under the same discriminator. Existing ordering examples continue to validate (default = "word").
  • Two ordering examples: 16b-sentence-ordering.json (cellular respiration stages with scoringMode: "kendall") and 16c-paragraph-ordering.json (Enlightenment essay with scoringMode: "strict").
  • Authored-text line-break rendering convention — \n\n → paragraph, \n → line break. ITEM_PATTERNS.md §3 gains a non-normative subsection documenting the affordance for plain-text authored fields (description, prompt, passage, hint, feedback.correct, feedback.incorrect, etc.). Producers using this convention get portable rendering across consumers that align with the affordance; consumers may legitimately collapse all whitespace and treat the field as a single block. Lesson Commons Learn implements this convention as the reference behavior. Wire format unchanged — JSON strings continue to escape newlines per RFC 8259; no schema change. Trusted-HTML fields (ContentItem.html, SignpostItem.customHtml) keep their existing sanitizer pipeline and stay outside this convention.
  • NORMATIVE §5.6 — Randomization requirements for matching and placement. Consumers MUST present two surfaces in randomized order: (1) the choice pool (authored answer values + distractors) for both matching and placement, and (2) the row order in matching classification mode (where source order is grouped by category and would directly expose the answer). Source order is forbidden on these surfaces. The randomization algorithm and any seeding strategy are consumer-defined. The requirement does not apply to multipleChoice (where shuffleOptions per question still governs), to matching pairs-mode rows (each item has a distinct match, so source order doesn’t leak), or to ordering source tiles (already shuffled structurally). Schema descriptions on matching.distractors, matching.categories, and placement.distractors cross-reference §5.6.
  • tags arrays on Unit, Lesson, and Item. Five-tier tagging now uniform across Course, Unit, Lesson, Item, and Question.
  • ITEM_PATTERNS.md — informative authoring guide covering policy fields, common patterns, signposts + learning objectives, consumer-policy variance. §3 gains a tel: consumer-policy example showing how the same wire-level construct (a tel: link) is gated differently across consumer audiences.
  • Conformance test corpus expansion: valid/05-reserved-type-with-extensions.json (round-trip preservation case for §6.4), valid/06-html-with-video-track.json (HTML safety §7 media handling), invalid/12-unit-missing-global-id.json (§4.4 enforcement), invalid/13-html-with-script.json (HTML safety §2.4 + §3.5 — forbidden <script> element and onclick event handler).
  • Validator [NOTE] tier — informational lines for intentional weighted-points overrides, distinct from warnings.
  • validate_course.py --strict mode — public-conformance mode that rejects pre-1.0 document shapes (wrapped envelope {"course":{...}}, bare payload {"units":[...]} with no documentType) as fatal errors. Default mode remains lenient for legacy/pre-1.0 document ingestion during migration. NORMATIVE.md §10 conformance claims are evaluated in --strict mode.
  • tools/run_corpus.py — conformance corpus harness. Reads tests/manifest.json and asserts every valid fixture passes and every invalid fixture fails (the harness invokes validate_course.py --strict on every fixture internally). Wired into .github/workflows/publish.yml as a required job — a corpus regression blocks deployment. Closes the gap between CONTRIBUTING.md’s “CI runs the corpus” promise and the previous behavior (3 invalid fixtures silently passing).
  • ACCESSIBILITY.md (rc.1 release) — producer/consumer accessibility profile covering image alt text, video/audio (captions/transcripts/descriptions), keyboard alternatives for structured-task question types, feedback not conveyed by color alone, language and direction, and reserved-type placeholder accessibility. The rc.1 release (2026-05-23) carries per-section WCAG 2.1 AA SC cross-references, recommended ARIA patterns for the structured-task question types, the two-layer format/consumer duty framing, EN 301 549 + DOJ ADA Title II legal context, the ATAG vs WCAG split for producers vs consumers, the five claim-level gates for an AA claim, and selected WCAG 2.2 criteria designed-in (2.5.7 Dragging Movements, 2.5.8 Target Size). RTL producer wording precisely distinguishes RTL-primary documents from LTR documents with embedded RTL passages. Caption requirements split: SHOULD for generic video, MUST for prerecorded instructional video with speech when claiming Accessibility Profile conformance (WCAG 1.2.2). Additive deepenings (per-criterion normative cross-reference table, expanded ARIA patterns, screen-reader timing requirements, expanded accessibility conformance fixtures, --accessibility validator flag) land in 1.0 final on 2026-06-30 — every deferral is an explicit §-level callout. Closes the HTML_SAFETY.md dangling-reference issue flagged by the v2 audit.
  • NORMATIVE.md §12 — Accessibility Profile binding (hybrid: preservation in base conformance + opt-in claim for delivery). Splits the accessibility obligation into two layers per the 2026-05-23 accessibility audit Finding 1 and the consultant’s “must survive transformation” framing. §12.1 codifies the base-conformance accessibility-preservation floor (every conforming consumer MUST round-trip alt, <track>, lang, dir, language, supportLanguage, reserved-type accessibility metadata, and x--namespaced accessibility extensions). §12.2 defines the opt-in LC-JSON 1.0 Accessibility Profile claim binding all MUST-level items in ACCESSIBILITY.md §§2–8 (keyboard, ARIA, feedback, language-aware rendering, placeholder accessibility). §12.3 codifies the relationship to WCAG: LC-JSON enables WCAG 2.1 AA delivery via the affordances and consumer obligations; the WCAG claim itself remains the delivering consumer’s responsibility, not LC-JSON’s. Closes accessibility-audit Finding 1.
  • NORMATIVE.md §10 — Conformance Claims expanded with marketing wording. §10.1 lists base-conformance claim forms; §10.2 lists Accessibility Profile claim forms; §10.3 codifies three claim-accuracy rules (producer≠consumer; Accessibility Profile is fully bound; LC-JSON does not certify WCAG); §10.4 provides suggested badge/sentence/formal-claim wording for both tiers (Tier 1 badge: “LC-JSON 1.0 Compatible”; Tier 2 badge: “LC-JSON 1.0 Accessibility Profile”); §10.5 reaffirms the trademark stance. The marketing wording is informative and freely usable.
  • language is now a required root field on course.schema.json and question-set.schema.json (per accessibility-audit Finding 2 — the spec said language was schema-enforced but the schemas didn’t require it). All 31 existing root-level course/questionSet examples and conformance fixtures already carried language; no migration burden. New invalid fixture tests/invalid/20-missing-language.json pins the constraint.
  • Two new accessibility conformance fixtures. tests/valid/12-accessibility-round-trip.json demonstrates §12.1 base-conformance accessibility preservation: alt on <img>, <track> on <video>, lang/dir on inline spans, document-root language/supportLanguage, and x--namespaced accessibility metadata on a reserved-type question MUST all round-trip. Paired with the tests/invalid/20-missing-language.json invalid fixture from the previous entry, the two additions bring the conformance corpus toward its rc.1 total of 12 valid + 24 invalid = 36 fixtures; run_corpus.py reports 36/36 behave as expected.
  • VALIDATION.md — schema-vs-domain-vs-advisory rule catalog. New sibling document referenced from NORMATIVE.md §13 (“Validation surface”) and listed in README.md’s directory tree. Catalogs every documented validation rule across the 23 JSON Schemas, tools/validate_course.py (domain pass, ERROR/WARN/NOTE severities), and prose in NORMATIVE.md / HTML_SAFETY.md / ACCESSIBILITY.md / question-types-reference.md / ITEM_PATTERNS.md. Each rule is tagged with its enforcement tier and a precise citation (schemas/<file>.schema.json: <json-pointer> or validate_course.py: <function-name> + NORMATIVE §). Organized by document scope (root → course → unit → lesson → item-base → per item type → question-base → per question type → reserved types → question sets → cross-cutting). Cross-cutting section covers HTML safety severity table, accessibility preservation + validator severities, randomization (§5.6), extensions (x- namespacing), versioning / URL stability, and discriminator casing. Closes the external evaluation’s “validation appendix” gap (2026-05-02 §3) — consumers that only run JSON Schema validation can now see at a glance which rules they would otherwise miss. The inventory pass surfaced eight documented-but-unenforced rules; all eight were closed in the same rc.1-polish session by extending tools/validate_course.py (see next entry). The catalog reflects the rc.1 enforced state, not a gaps list. §14 “Forward-looking deepenings” tracks what’s scheduled for 1.0 final (the --accessibility validator flag, tag namespace conventions, and reserved-type per-type schemas).
  • Validator gap closures — 8 documented rules promoted from prose to enforced. Extends tools/validate_course.py with no schema changes — all rules were already documented in NORMATIVE.md / question-types-reference.md / README.md / ACCESSIBILITY.md but enforced nowhere. New domain-validator functions: validate_multiple_choice (KG-3: MCQ MUST have ≥1 positive optionsAndPoints value; KG-4: optionsAndPoints MUST cover every entry in options, with WARN for orphan entries); validate_word_bank_cloze and validate_multiple_choice_cloze (KG-1: passage @@@N marker set MUST equal the answer/option dictionary key set; KG-2: marker numbers SHOULD be sequential from 1; KG-5: correctAnswers[N] MUST be in bounds of gapOptions[N]; new ERROR for gapOptions / correctAnswers key-set mismatch); validate_essay (maxWords >= minWords when both > 0, WARN). Extended validate_multi_gap_cloze with the same KG-1/KG-2 checks via the new shared _check_cloze_gap_consistency helper. New _collect_objective_id_violations walker (KG-6: WARN if courseObjectiveIds[*] / unit.objectiveIds[*] / lesson.objectiveIds[*] reference an id not declared in course.objectives[] — closes the referential-integrity gap on objective references). validate_html_content gains a post-pass that scans for <video> blocks without a <track kind="captions"\|"subtitles"> child element (KG-8 — WARN at rc.1; ERROR-tier promotion under the --accessibility flag is targeted for 1.0 final). Internal: renamed _placement_marker_numbers_gap_marker_numbers since the helper is now shared across all cloze types. Four new invalid conformance fixtures pin the ERROR-tier checks (tests/invalid/21-mcq-no-correct-option.json, 22-mcq-options-points-missing-entry.json, 23-word-bank-cloze-gap-count-mismatch.json, 24-multiple-choice-cloze-index-out-of-bounds.json); tests/manifest.json updated; python tools/run_corpus.py reports 36/36 fixtures behave as expected (the harness invokes validate_course.py --strict on every fixture internally). KG-7 was a false alarm during inventory (validator and schema agree on relatedItemIds) and is dropped from the catalog. No new normative rules; no schema changes.
  • Language Policy (editorial). Specification text is written in US English as a single editorial register; example content should vary across English regional varieties (British, American, Australian, Indian, Irish, Canadian, South African, and others); non-English content in examples is reserved for demonstrating language-specific or multilingual capabilities (RTL rendering, non-Latin scripts, bilingual [L1:] tag rendering, language/dir behavior). The wire format is language-neutral — the policy governs only the editorial language of the specification and its examples.

Changed

  • Ordering: renamed unitLevelorderingUnit for teacher readability. Pre-publication rename, non-breaking. Schema, examples (16b, 16c), reference docs, and ITEM_PATTERNS.md all updated. Placement’s parallel placementUnit field-name reads naturally alongside.
  • Ordering: scoringMode default is now conditional on orderingUnit. Schema description prose now states: when scoringMode is omitted, consumers SHOULD default to "strict" for orderingUnit: "word" and "kendall" for "sentence" / "paragraph". The literal "default": "strict" keyword is removed from the schema property since JSON Schema draft-07 can’t cleanly express conditional defaults; the conditional guidance lives in the description, where consumers actually read it. Existing examples are unaffected: 16-ordering.json (word) carries no scoringMode and inherits strict; 16b and 16c set kendall explicitly.
  • globalId is now required on every Unit, Lesson, Item, and Question schema (was nullable + not-required on Unit/Lesson/Item, despite being normatively MUST under §4.4). Pattern unchanged (RFC 4122 any version).
  • $schema is now required at the document root (producer MUST emit; consumer SHOULD tolerate omission for re-import flexibility). Updated course.schema.json and question-set.schema.json required[].
  • scoringMode and orderingUnit (formerly unitLevel) remain optional on ordering.schema.jsonorderingUnit defaults to "word"; scoringMode’s default is the conditional rule above (description prose, no literal default keyword).
  • exercise/quiz decoupled from grading policy. quiz-item.schema.json no longer has "isGraded": {"const": true}. All four combinations of {exercise, quiz} × {graded, ungraded} are valid; new NORMATIVE §4.3 codifies this.
  • Brand consistency: schema descriptions now say “LC-JSON spec version” (was “LC.JSON”).
  • question-types-reference.md: matching reference entry rewritten as a real type (was labeled stub); points constraint corrected to ≥ 0 allowing null; reserved-type entries (11 total) reframed under §6 fallback contract; Phase 4: Advanced Types heading split into Structured & Reserved Types; casing references corrected to NORMATIVE §5.3.
  • README.md (specification): Reserved Types section now describes consumer obligations under §6 explicitly; example count updated to 30 across the file (placement adds 17a–17d to the example set).
  • ACCESSIBILITY.md: keyboard interaction obligations split per matching mode (pairs vs classification) and extended with a placement row; unitLevel reference updated to orderingUnit.
  • NORMATIVE §5.1 carve-out for §6 fallback. Resolved an internal contradiction between §5.1 (“consumer MUST reject documents that fail schema validation”), §5.2 (“consumer MUST accept any 1.x specVersion”), and §6 (“consumer MUST preserve unknown-type questions verbatim and apply fallback, do not reject”). Applied literally, the three together made the forward-compat case impossible: a 1.0-only consumer reading a 1.1 document with a future-minor question type was bound to both reject (§5.1) and preserve (§6). §5.1 now carries an explicit exception: schema-validation failures whose only cause is one or more type discriminator values not in the consumer’s implemented question-base.schema.json enum do not trigger rejection; the consumer applies §6 fallback to those questions and validates the rest of the document under §5.1. All other schema failures (missing required fields, type mismatches, pattern violations on known fields, additionalProperties violations) still trigger rejection. Doc-only NORMATIVE change; no schema change, no validator change — tools/run_corpus.py continues to report 36/36 fixtures behave as expected. Closes the consultant’s 2026-05-24 third-pass note on VALIDATION.md.
  • URL policy clarified — release candidates get their own immutable URL paths (NORMATIVE §3.1, §4.7, §8.1, §8.3). Each release candidate of an upcoming version X.Y is published at /X.Y-rc.N/, immutable once published. The /X.Y/ URL path is reserved for the accepted final X.Y release and MUST NOT be populated until that release is published. A document pinned via $schema to /1.0-rc.1/ continues to validate against rc.1 forever; adoption of /1.0/ final is an explicit re-export, not an automatic promotion. Producers MUST emit a $schema URL matching the spec version they conform to (/1.0-rc.1/ for an rc.1 producer, /1.0/ for a 1.0-final producer). This closes the audit’s “Release Candidate URL Immutability Wording” concern and preserves the §8.3 immutability guarantee across the rc.1 → 1.0 transition.

Deprecated

  • None.

Removed

  • course-walkthrough.json (was excluded from publication via build-time SKIP; deleted to remove residual maintenance noise).

Fixed

  • TrueFalseQuestion exports no longer leak choiceFeedback: {} (TF v2 architecture forbids the field; previous new() default emitted empty dict on every TF question).
  • Course/QuestionSet Version is now mandatory + numeric on producer side (^[0-9]+(\.[0-9]+){0,2}$); previously could ship as blank or free-form, breaking sortability across consumers.
  • Python authoring tools emit canonical camelCase question discriminators; prior PascalCase emissions are gone.
  • Cross-references in README-public.md, IMPLEMENTATIONS.md, and the test corpus updated for the §5.5 collapse + §6→§7 / §8→§9 renumber driven by the new §6 insertion.
  • Reference Runtime (consumer-side): bilingual tag rendering on question prompt is now applied uniformly across all 12 question types. Previously ShortAnswer, Essay, SentenceTransformation, WordBankCloze, and MultiGapCloze skipped the bilingual transform on prompt; all 12 types skipped it on hint. Adopting a unified line-break renderer (which composes the bilingual transform) closes the gap. No spec or schema change; behavior change in the reference runtime only.
  • HTML_SAFETY.md §8.1 / §2.4 alignment. The §8.1 “validator MUST reject” enumeration of forbidden elements had drifted from the §2.4 source list — six elements were missing (<select>, <textarea>, <applet>, <frame>, <frameset>, <noframes>). §8.1 now references §2.4 directly (“Any forbidden element listed in §2.4.”) so the two lists cannot drift in future revisions.
  • HTML_SAFETY.md §3.5 cleanup. Removed a misleading data: cross-listing in the forbidden-attributes section. The rule lives in §4.2’s URL-scheme allowlist; the §3.5 line carried an empty “except as permitted in §4.1” carve-out that didn’t reflect any actual permission (§4.1 permits no data: URLs). §3.5 now cross-references §4.2 instead of duplicating the rule.
  • ACCESSIBILITY.md §8 validator-severity table — root field name. The “Missing lang at document root” row swapped the LC-JSON root field (language, ISO 639-1, schema-enforced) for the HTML attribute (lang). Schemas enforce language; lang is the HTML attribute used inline for WCAG 3.1.2 Language of Parts. Table now reads “Missing language at document root.”

Notes

  • All 31 spec examples + 12 valid + 24 invalid conformance fixtures validate clean against the tightened schemas (run python LC.JSON/tools/run_corpus.py — expects “36/36 fixtures behaved as expected”).
  • Wire format is forward-compatible with 1.0 baseline: every change either softens a constraint, adds an optional field, or codifies behavior consumers were already expected to provide.

[1.0-internal] — 2026-04-29

Internal milestone — not publicly released. Captured here as the wire-format baseline that the 1.0-rc.x release-candidate line builds on. 1.0-rc.2 (2026-05-30) was the first publicly announced candidate; public consumers should target it or later (the earlier 1.0-rc.1 is served for transparency but was never announced). The 1.0-rc.1 schemas are published at lc-json.org/1.0-rc.1/; the /1.0/ URL space is reserved for the accepted final 1.0 release (target 2026-06-30) and does not resolve until then.

Added

  • Two artifact types under a common flat root: course (hierarchical) and questionSet (flat).
  • 11 user-facing question types: simpleGapFill, trueFalseQuestion, multipleChoice, wordBankCloze, multiGapCloze, multipleChoiceCloze, shortAnswer, essay, sentenceTransformation, matching, ordering. All schema-validated.
  • 7 reserved question types declared in the discriminator enum for forward compatibility, with full implementation targeted for 2027: association, hotspot, graphicGapMatch, graphicAssociate, graphicOrder, fileUpload, mediaPromptedEssay.
  • 22 JSON Schemas (Draft 7) covering every artifact, item type, and question type.
  • 25 example documents covering every artifact and per-type fragments.
  • Conformance test corpus under tests/ (4 valid + 10 invalid cases) with a machine-readable manifest.json mapping each file to the clause it tests.
  • NORMATIVE.md — RFC 2119 conformance requirements, producer/consumer roles, versioning policy, deprecation rules, conformance-claim language.
  • Reference Python tools: validate_course.py (validator). The fixture-scaffolding helper used during internal development was reclassified as internal authoring infrastructure for 1.0-rc.1 publication and is not shipped to the public repo.
  • Schema URL stability guarantee codified in NORMATIVE.md §8.3 (each published version path is immutable for the lifetime of the spec). Public publication of lc-json.org/1.0/*.schema.json is deferred to the accepted 1.0 final release; 1.0-rc.1 publishes at lc-json.org/1.0-rc.1/*.schema.json.
  • Apache License 2.0 throughout.

Notes

  • LC-JSON 1.0 distils approximately 12 months of internal format iteration. Earlier internal versions were never publicly released and are not part of this version history.