diff --git a/CHANGELOG.md b/CHANGELOG.md index d3c518d..d1fe928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,10 @@ A fifth language-learning exercise idea surfaced a gap the existing "extra text" fields do not cover: `hint` is shown on demand before/during answering, `examples` are worked examples shown before answering that must not spoil it, and neither fits "explain the French word-order rule AFTER the learner -answers". `Exercise` gains an optional `explanation` (Markdown, max 1000 -chars, mirrors `hint`'s shape) - not restricted to any exercise type, like -`hint` itself. +answers". `Exercise` gains an optional `explanation` (Markdown, max 2000 +chars - twice `hint`'s 1000, since Markdown structure eats characters +faster than a single-line nudge) - not restricted to any exercise type, +like `hint` itself. `x-schema-version` `1.12` -> `1.13` in both schemas (lesson + content-manifest move in lockstep, established convention). Additive: content without the new diff --git a/docs/lesson-format.md b/docs/lesson-format.md index 5956b1e..1d4079e 100644 --- a/docs/lesson-format.md +++ b/docs/lesson-format.md @@ -243,7 +243,7 @@ see [Stable identity](#stable-identity-stable_id)). ### Explanation (post-answer) -`explanation` (string \| null, max 1000 chars, schema v1.13) is Markdown +`explanation` (string \| null, max 2000 chars, schema v1.13) is Markdown explaining WHY the answer/grammar is what it is - a French word-order rule, a grammatical case, a spelling exception. Timing is what distinguishes it from the other three "extra text" fields: diff --git a/schema/lesson.schema.json b/schema/lesson.schema.json index 7951775..861b102 100644 --- a/schema/lesson.schema.json +++ b/schema/lesson.schema.json @@ -412,7 +412,7 @@ "explanation": { "anyOf": [ { - "maxLength": 1000, + "maxLength": 2000, "type": "string" }, { @@ -420,7 +420,7 @@ } ], "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). Not restricted to any exercise type. Additive; schema_version 1.13.", + "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": { diff --git a/src/__fixtures__/schema-baseline/lesson.schema.json b/src/__fixtures__/schema-baseline/lesson.schema.json index 7951775..861b102 100644 --- a/src/__fixtures__/schema-baseline/lesson.schema.json +++ b/src/__fixtures__/schema-baseline/lesson.schema.json @@ -412,7 +412,7 @@ "explanation": { "anyOf": [ { - "maxLength": 1000, + "maxLength": 2000, "type": "string" }, { @@ -420,7 +420,7 @@ } ], "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). Not restricted to any exercise type. Additive; schema_version 1.13.", + "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": { diff --git a/src/types/lesson-schema.generated.ts b/src/types/lesson-schema.generated.ts index 5c9f0c0..e0c0ac6 100644 --- a/src/types/lesson-schema.generated.ts +++ b/src/types/lesson-schema.generated.ts @@ -216,7 +216,7 @@ export type Distractors = string[]; */ export type Examples1 = InlineExample[] | null; /** - * 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). Not restricted to any exercise type. Additive; schema_version 1.13. + * 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. */ export type Explanation = string | null; /** diff --git a/src/validate.test.ts b/src/validate.test.ts index a58e8e1..18f7ec3 100644 --- a/src/validate.test.ts +++ b/src/validate.test.ts @@ -458,12 +458,12 @@ describe("schema 1.13 — explanation on exercises (idea 5: post-answer 'why')", expect(validateLesson(lessonWithExplanation(null)).valid).toBe(true); }); - it("boundary: rejects an explanation longer than 1000 characters", () => { - expect(validateLesson(lessonWithExplanation("x".repeat(1001))).valid).toBe(false); + it("boundary: rejects an explanation longer than 2000 characters", () => { + expect(validateLesson(lessonWithExplanation("x".repeat(2001))).valid).toBe(false); }); - it("boundary: accepts an explanation at exactly 1000 characters", () => { - expect(validateLesson(lessonWithExplanation("x".repeat(1000))).valid).toBe(true); + it("boundary: accepts an explanation at exactly 2000 characters", () => { + expect(validateLesson(lessonWithExplanation("x".repeat(2000))).valid).toBe(true); }); it("is not restricted to one exercise type: a matching exercise may carry it too", () => {