Enumerate every possible sentence, review it, and lock it with a snapshot

"Wir wohnen bei der Schwester. / We live at the sister." was two bugs at
once, and both were the same shape: a template applied to a pairing nobody
had ever read.

Root cause: sentences are built combinatorially (frames x verbs x nouns x
subjects). The space is ~2900 sentences and only a sample had ever been
looked at, so every fix so far has been whack-a-mole on whichever bad one
happened to surface.

The fix is structural, not another special case:

- composeFrame() is now pure, and enumerateCorpus() walks every frame,
  verb, noun and case for every consumer (fill-the-article, ein-Woerter,
  possessive, Wechsel im Kontext, fused). src/corpus.txt is that full
  enumeration, checked in and reviewed.
- Two tests make it foolproof rather than decorative:
    * the snapshot test fails if generation drifts from corpus.txt, so no
      new sentence can reach a learner unreviewed. Regenerate with
      `npm run corpus` and the diff IS the review.
    * a coverage test runs the real generators over every sentence level
      and asserts each question it produces has a key in the corpus — so
      the file is provably the live output, not a parallel implementation.
  Two more assert no un-fused contraction and no German leaking into an
  English line.

Bugs the audit then found and fixed:

- zu/von/bei + a person means "at/to/from their PLACE". English needs the
  -'s ("at the doctor's"), and with a bare definite article German only
  allows roles you visit (beim Arzt), not family — "bei der Schwester" is
  wrong, "bei meiner Schwester" is right, so family frames are now
  possessive-only. Added Aerztin/Lehrerin/Freundin so the role case has a
  non-fusing feminine form.
- "zu dem Arzt", "in dem Haus": a fill-the-article sentence may no longer
  show a two-word form German obligatorily fuses. Those pairings belong to
  the Kontraktionen questions, which already teach them. "zu" therefore
  leaves the Dativ lesson entirely, the way "bis" already had.
- mein Mann / meine Frau mean my husband / my wife, not my man / my woman.
- "mein Maedchen" is not idiomatic and was reachable from the possessive
  templates; the coverage test caught it.
- You cannot drive into a kitchen, a cellar or a library: the "in" motion
  frame is split into drivable places and on-foot-only places.
- "stellt den Stuhl an die Wand" is "against the wall", not "to the wall";
  haengen/schreiben/kleben take "on"; wohnen am See is "by the lake".

Also: questions now carry the verb and case they were built from, the
article redraw is depth-bounded so a data mistake degrades to one odd
question instead of hanging, and level 3's advertised prep list matches
what it can actually generate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Uza6mhTpAvSwMQuFc4Cz8
This commit is contained in:
2026-09-18 07:24:29 +00:00
co-authored by Claude Opus 5
parent 1d5402edc7
commit 0dfc2955db
4 changed files with 3250 additions and 47 deletions
+2 -1
View File
@@ -7,7 +7,8 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview --host",
"test": "vitest run"
"test": "vitest run",
"corpus": "node -e \"import('./src/engine.js').then(({enumerateCorpus,corpusToText})=>require('fs').writeFileSync('src/corpus.txt',corpusToText(enumerateCorpus())))\""
},
"dependencies": {
"react": "^18.3.1",