From 3946b2d687b087dd7862f29b6920138c79d9dc9c Mon Sep 17 00:00:00 2001 From: Asterios Raptis Date: Thu, 1 Oct 2026 14:03:20 +0200 Subject: [PATCH 1/2] chore(engine): pin 0.35.0 learn-content-engine 0.35.0 adds E-FREETEXT-DISJOINT (engine#237). No schema change; this repository's gate at 0.35.0: engine-validate: 36 lesson(s), 5 manifest(s) checked - 0 file(s) with errors Co-Authored-By: Claude Opus 5.5 (1M context) --- schema/engine-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/engine-version.txt b/schema/engine-version.txt index 85e60ed..7b52f5e 100644 --- a/schema/engine-version.txt +++ b/schema/engine-version.txt @@ -1 +1 @@ -0.34.0 +0.35.0 From 7b7f8995bc9639e85e530cecaef620dc7581ae0d Mon Sep 17 00:00:00 2001 From: Asterios Raptis Date: Thu, 1 Oct 2026 14:03:20 +0200 Subject: [PATCH 2/2] chore(tooling): the audit leaves the free_text overlap to the engine Template-owned files copied from adaptive-learner-content-template#96 (6fca411). Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/audit_content.py | 21 ++++----------------- tests/test_audit_content.py | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/scripts/audit_content.py b/scripts/audit_content.py index d697e71..514e36e 100644 --- a/scripts/audit_content.py +++ b/scripts/audit_content.py @@ -7,9 +7,6 @@ table so they can be fixed: * duplicate cards within a lesson (same front, or same front/back pair) - * a free_text answer that is also listed as a distractor - * matching pairs / free_text accepts that don't line up with any - card the exercise references (possible wrong "correct" answer) * empty / whitespace-only fields (card front/back, prompts, theory body, titles, matching pair sides): the schema's ``minLength`` lets a string of spaces through @@ -19,9 +16,10 @@ and one rule lives in one place: malformed answer sets (cloze markers and blanks, multiselect accept/distractors, word_tiles with fewer than two tiles, picture_choice without exactly one correct image, a repeated matching left -term), duplicate card, step and exercise ids (learn-content-engine#202), and -the quality minimums such as matching pairs and free_text accepts (#185, keyed -to a lesson's ``purpose``). The distractor requirement on free_text and +term, a free_text answer that is also a distractor (#237, 0.35.0)), +duplicate card, step and exercise ids (learn-content-engine#202), and the +quality minimums such as matching pairs and free_text accepts (#185, keyed to +a lesson's ``purpose``). The distractor requirement on free_text and picture_choice was dropped by the same decision. Exit code is always 0 - this is advisory. ``--strict`` makes it exit 1 @@ -92,17 +90,6 @@ def add(problem, fix): # against the card gloss - matching exercises legitimately pair # a word with its article / gender / category, not its dictionary # translation, so such a check is all false positives. - elif etype == "free_text": - accept = ex.get("accept") or [] - # An accepted answer appearing verbatim in distractors is - # contradictory. Compare case-SENSITIVELY: a distractor that - # differs only by capitalisation (e.g. testing that Spanish - # nationalities are lower-case) is a legitimate wrong answer. - overlap = {a.strip() for a in accept} & { - d.strip() for d in (ex.get("distractors") or [])} - if overlap: - add(f"free_text '{eid}' accept & distractors overlap: {sorted(overlap)}", - "remove the overlap") def main() -> int: diff --git a/tests/test_audit_content.py b/tests/test_audit_content.py index fc6574e..d6cf7e2 100644 --- a/tests/test_audit_content.py +++ b/tests/test_audit_content.py @@ -2,12 +2,12 @@ """Tests for scripts/audit_content.py. The audit is advisory and reports what no gate blocks: whitespace-only -fields, duplicate card fronts, a free_text answer that is also a -distractor, lessons missing from their set. What the engine gate blocks -(validate_with_engine.mjs) is not repeated here: a malformed cloze, a -multiselect overlap, too few word tiles, a picture choice without exactly -one correct image, a repeated matching left term. One rule, one place -(learn-content-engine's rule ownership). +fields, duplicate card fronts, lessons missing from their set. What the +engine gate blocks (validate_with_engine.mjs) is not repeated here: a +malformed cloze, a multiselect overlap, too few word tiles, a picture +choice without exactly one correct image, a repeated matching left term, +a free_text answer that is also a distractor (learn-content-engine 0.35.0). +One rule, one place (learn-content-engine's rule ownership). """ from __future__ import annotations @@ -69,6 +69,10 @@ def _audit(exercise: dict, cards: list[dict] | None = None) -> list[str]: "id": "m1", "type": "matching", "prompt": "Match.", "pairs": [{"left": "a", "right": "1"}, {"left": "A", "right": "2"}, {"left": "b", "right": "3"}], }, + "free_text answer that is also a distractor (E-FREETEXT-DISJOINT)": { + "id": "f1", "type": "free_text", "prompt": "?", + "accept": ["gracias", "Gracias"], "distractors": ["gracias"], + }, } @@ -90,13 +94,6 @@ def test_duplicate_card_front_is_reported() -> None: assert any("duplicate card front" in problem for problem in problems) -def test_free_text_answer_that_is_also_a_distractor_is_reported() -> None: - problems = _audit( - {"id": "f1", "type": "free_text", "prompt": "?", "accept": ["gracias", "Gracias"], "distractors": ["gracias"]} - ) - assert any("accept & distractors overlap" in problem for problem in problems) - - def test_a_clean_lesson_reports_nothing() -> None: cards = [{"id": "c1", "front": "Hund", "back": "dog"}] exercise = {"id": "f1", "type": "free_text", "prompt": "Translate.", "accept": ["dog", "Dog"], "distractors": ["cat"]}