Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion schema/content-manifest.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,5 +380,5 @@
],
"title": "ContentManifest",
"type": "object",
"x-schema-version": "1.11"
"x-schema-version": "1.13"
}
2 changes: 1 addition & 1 deletion schema/engine-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.20.0
0.23.0
55 changes: 54 additions & 1 deletion schema/lesson.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@
"default": null,
"description": "Optional placeholder text shown inside the input (``type`` mode) before the user starts typing.",
"title": "Placeholder"
},
"stable_id": {
"anyOf": [
{
"$ref": "#/$defs/SlugId"
},
{
"type": "null"
}
],
"default": null,
"title": "Stable Id",
"description": "engine#91 - schema 1.12 (additive). Element-level counterpart to the exercise/card stable_id (engine#90): identifies THIS blank for progress/SRS joins below the exercise level, so an answer-text correction (moving `accept[0]`) does not orphan its learner row. Once published it NEVER changes. Shares the SAME per-set stable_id namespace as card/exercise ids (checked the same way: collectStableIds set-wide, the schema's E-STABLE-ID-DUP rule per-document). Opaque mint-once value, NOT derived from content. Optional: content without it validates unchanged. Uses the strict $defs/SlugId shape (hyphens only) - unlike the card/exercise field, this is a brand-new field with no legacy underscore-bearing ids to grandfather."
}
},
"required": [
Expand Down Expand Up @@ -396,6 +409,20 @@
"description": "Optional inline worked examples shown BEFORE the answer controls, to help the learner understand the task (schema v1.5, additive). Each is plain text or a syntax-highlighted code snippet (see ``InlineExample.language``). Author responsibility not to spoil the answer. Independent of the per-type fields; absent on exercises that need no example.",
"title": "Examples"
},
"explanation": {
"anyOf": [
{
"maxLength": 2000,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional Markdown explanation of WHY the answer/grammar is what it is (e.g. word order, a grammatical case), shown AFTER the learner answers - regardless of correct or incorrect - unlike ``hint`` (on demand, before/during answering) and ``examples`` (worked examples shown before answering, must not spoil it). 2000 chars, twice ``hint``'s 1000: Markdown structure (a short list, bold terms) eats characters faster than a single-line nudge, and a real \"why\" often runs longer than a hint. Not restricted to any exercise type. Additive; schema_version 1.13.",
"title": "Explanation"
},
"ext_payload": {
"additionalProperties": true,
"description": "Opaque per-exercise payload for an ``ext:`` extension type. The core engine does not interpret it; the registered extension validator does. Absent on core exercises.",
Expand Down Expand Up @@ -899,6 +926,19 @@
"title": "Correct",
"type": "boolean"
},
"stable_id": {
"anyOf": [
{
"$ref": "#/$defs/SlugId"
},
{
"type": "null"
}
],
"default": null,
"title": "Stable Id",
"description": "engine#91 - schema 1.12 (additive). Element-level counterpart to the exercise/card stable_id (engine#90): identifies THIS option for progress/SRS joins below the exercise level. Once published it NEVER changes. Shares the SAME per-set stable_id namespace as card/exercise ids (checked the same way: collectStableIds set-wide, the schema's E-STABLE-ID-DUP rule per-document). Opaque mint-once value, NOT derived from content. Optional: content without it validates unchanged. Uses the strict $defs/SlugId shape (hyphens only) - unlike the card/exercise field, this is a brand-new field with no legacy underscore-bearing ids to grandfather."
},
"text": {
"description": "The option text shown to the learner. Unique within the exercise - the text IS the option, so a duplicate would be ambiguous.",
"maxLength": 500,
Expand Down Expand Up @@ -930,6 +970,19 @@
"minLength": 1,
"title": "Right",
"type": "string"
},
"stable_id": {
"anyOf": [
{
"$ref": "#/$defs/SlugId"
},
{
"type": "null"
}
],
"default": null,
"title": "Stable Id",
"description": "engine#91 - schema 1.12 (additive). Element-level counterpart to the exercise/card stable_id (engine#90): identifies THIS pair for progress/SRS joins below the exercise level, so an answer-text correction (moving `left`/`right`) does not orphan its learner row. Once published it NEVER changes. Shares the SAME per-set stable_id namespace as card/exercise ids (checked the same way: collectStableIds set-wide, the schema's E-STABLE-ID-DUP rule per-document). Opaque mint-once value, NOT derived from content. Optional: content without it validates unchanged. Uses the strict $defs/SlugId shape (hyphens only) - unlike the card/exercise field, this is a brand-new field with no legacy underscore-bearing ids to grandfather."
}
},
"required": [
Expand Down Expand Up @@ -1205,5 +1258,5 @@
],
"title": "Lesson",
"type": "object",
"x-schema-version": "1.11"
"x-schema-version": "1.13"
}
2 changes: 1 addition & 1 deletion schema/quality-rules.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema-version": "1.11",
"$schema-version": "1.13",
"_comment": "GENERATED from scripts/generate_lesson_schema.py (EXP-039). Do not edit. Shared quality minimums for the content quality gate (app + content repo).",
"rules": {
"minExerciseTypes": 2,
Expand Down
5 changes: 5 additions & 0 deletions scripts/validate_with_engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ const ADOPTED_EXTENSIONS = [
"ext:al-error-correction",
"ext:al-reading-comprehension",
"ext:al-graded-quiz",
"ext:al-dictation",
"ext:al-image-description",
"ext:al-speak-and-record",
"ext:al-audio-choice",
"ext:al-audio-tiles",
].map((type) => ({ type, major: 1, validate: () => [] }));

const withExtensions = { extensions: ADOPTED_EXTENSIONS };
Expand Down
Loading