medlab / case

medlab

what got hurt, and what now fails loudly

↑ sublayer

2026-11-02 · fixed: yes

A word is the first substring that matches it

Human ComprehensionInvariantsExample-Based TestsMetamorphic Testing

now fails loudly Cloze construction selects a whole source word by position, and a test requires that a target which is a substring of a longer word is refused rather than cut.

What broke

Wortling turns a sentence a learner captured into a flashcard: one word or expression is hidden, and the rest of the sentence is the context. German makes this harder than it looks, because the words a learner needs are often pieces of longer ones: a separable verb, a compound, an inflected form.

The card builder found the target word with a plain substring search. The first place the target appeared inside the sentence became the cloze boundary, whatever characters surrounded it. The target ein therefore matched inside eine, and the card hid ein and left a dangling e; the separable particle the learner had actually met was untouched.

The belief was that finding the characters of a word finds the word. It finds a sequence of characters that may be the middle of something else.

How it showed

The card that prompted the repair hid a fragment and left the rest of the word visible, which is a card a learner cannot answer: the missing piece is not a word, and the context still contains the letters that were removed.

The follow-up defect in the same code wore the same clothes in the other direction. Reading *, : and _ as word-internal made an ordinary colon refuse a surface that was already the whole word: "Sie sagte: Ich komme morgen." lost sagte, and the validator then told the model to quote the whole word it had just quoted, which is the one refusal a retry cannot repair. A separator now joins only when the character beyond it is alphanumeric, so Kurier*in holds and sagte: does not.

What was watching

The pipeline had tests for cloze construction, and they used targets that were whole words already: a substring search and a word search agree on those, so the tests could not tell the two implementations apart.

The browser and capture tests checked that a sentence arrived and that a card was produced, not that the hidden span was a word. The learner was the first thing to read the card as a card.

Where it sits

This is a comprehension failure over an invariants gap.

The family is comprehension because the defect is in what "the word" meant to the code. It is invariants because the card has a property, "the cloze hides a word", that a substring search does not establish.

Example-based tests were present and blind, for the reason above: every example used a target whose characters could not be found inside another word. Metamorphic testing is the sensor this needed: wrap a target in a longer word and the output must not change the way it did here, which is a relation the suite could have stated without anyone inventing the case.

What we did

Cloze construction selects a whole source word by position, so the target's boundaries come from the word rather than from where its characters first appear. The separator rule was tightened in the same series: a separator is word-internal only when the character beyond it is alphanumeric, and a run of separators is read through.

What now fails loudly

Tests in the pipeline suite cover a target that is a substring of a longer word and require the refusal rather than the cut, and the separator cases carry the colon sentence that used to lose its verb, alongside the gender-star and underscore forms that must still parse as one word.

What is not guarded is the vocabulary of word shapes itself: the cases are the ones someone thought of, and German compounds are inexhaustible. The word boundary now comes from the tokenizer rather than from a pattern, which is the change that makes a new shape a parser question rather than a new test.