Follow-up to the previous commit: testing further showed a missing
initial capital also fails validation, which raised the real
question (CLAUDE.md: ask rather than decide alone on an ambiguous
pedagogical point) — is sentence-initial capitalization and final
punctuation something the audio should convey, or a general writing
convention the child applies regardless of what they hear?
Decided: a general convention, like in a real classroom dictée. So
both stay required, and the terminal-punctuation tolerance added in
the previous commit is reverted (DictationAnalyzer back to comparing
the text as-is).
Since the audio still can't vocalize either rule, the child needs to
be told some other way: added a permanent reminder label to the
dictation scene ("N'oublie pas : une majuscule au début de la phrase
et un point à la fin !"), sourced from data/localization/fr.json.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Manual test: the Piper voice (previous commit) is now intelligible,
but typing exactly what's heard ("Les petits chats dorment", no
period) was rejected. The provisional audio never vocalizes
punctuation, so nothing tells the child to add it — penalizing its
absence punishes information they were never given.
DictationAnalyzer now strips terminal punctuation (. ! ? …) from both
sides before comparing, in the exact-match/variant check and before
word alignment, without touching punctuation elsewhere in the
sentence. A real error on the last word is still caught (added test).
A scene-level test reproducing the exact reported interaction
(tests/exercises/test_exercise_host_dictation.gd) failed before the
fix and passes after.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AnswerValidator.validate_sentence_correction compared the typed
correction to the expected word with raw string equality, with none
of the tolerance already built for dictations: no case folding, no
typographic-apostrophe or whitespace normalization, no decomposed-
accent handling. Tapping "petit" and typing "petits" could fail for
reasons having nothing to do with the grammar point being tested
(e.g. a stray capital from autocapitalization), which violates the
"an ambiguous answer is never auto-marked wrong" invariant.
Found this while chasing a manual bug report: an automated test
reproducing the user's exact click sequence (tap once, type, submit)
did NOT reproduce the failure, which actually pointed at the real
gap -- the logic worked for byte-identical input, but had zero
tolerance for anything else. Now reuses TextNormalizer (built for
dictation analysis) before comparing, case-insensitively, while still
showing the child's raw typed text in the feedback. Three new tests
cover case, incidental whitespace, and decomposed accents.
Rebuilt both exports so the shipped binaries include the fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scripts/correction/: AnswerValidator (multiple choice, sentence
correction via tap-the-faulty-word + retype) and DictationAnalyzer
build the CorrectionResult structure from cahier des charges §6.
Dictation analysis aligns words with WordAligner (Wagner-Fischer edit
distance) before any character comparison, so an omitted or added
word doesn't cascade into false errors on every following word.
TextNormalizer handles non-breaking spaces, typographic apostrophes,
and decomposed-vs-composed French accented characters via an
explicit table (GDScript has no built-in NFC/NFD normalization).
Error classification follows the data annotations only (rule_id
present -> grammatical, absent -> unclassified), never a runtime
linguistic guess.
scripts/pedagogy/: DifficultyManager (per-skill difficulty from
recent results), AttemptFlow (3-attempt graduated aid: hint, then
explanation, then reveal, matching exercise-lifecycle.md's second
gap), and ReviewScheduler (the five mastery states from §9, with
session-counted spaced review intervals [1, 3, 6, 12] rather than
wall-clock dates, since a family device's clock isn't trustworthy).
All of it is pure: no scene tree, no clock or RNG read directly,
every effectful input (session number, attempt outcome) is a
parameter. 65 headless tests across both layers, including the
word-omission-doesn't-cascade regression and the full
decouverte -> acquise -> a_reviser -> acquise path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>