Skip to content

feat(tutor): the exercise it plans is the card you get - #606

Merged
mrviduus merged 2 commits into
mainfrom
feat/tutor-exercise-shapes
Sep 11, 2026
Merged

feat(tutor): the exercise it plans is the card you get#606
mrviduus merged 2 commits into
mainfrom
feat/tutor-exercise-shapes

Conversation

@mrviduus

Copy link
Copy Markdown
Owner

The Tutor calibrates an exercise type for every word from that card's SRS stage — recognition for a new word, recall at stage 2, context for a well-known one with a sentence. It is computed on the server, deliberately not taken from the model's suggestion so the plan cannot schedule a jarring jump, carried to the client with everything needed to render it…

…and then thrown away. buildPlanCard hardcoded options: null and blankSentence: null, so all three types drew the same flashcard and the calibration reached the learner as a coloured badge.

The shape now follows the plan

type card
recognition four options, no sentence prompt — which word means this
recall no options. A flashcard the learner grades against themselves, which is the point of that stage; four options would be a recognition exercise wearing a recall label
context four options and the saved sentence with the word removed — the cloze, in the sentence the word was actually met in

One implementation of what the choices are

Options come from McOptions, extracted from ReviewCardBuilder — which carried two copies of the same forty lines, one for multiple_choice and one for context. The builder goes from 145 lines to 61, the rule has one home and three callers, and it finally has tests:

  • the answer is always among the four, and never also present as its own distractor (case-insensitively — the old pool branch compared case-sensitively and the LLM branch did not);
  • the cascade prefers the LLM's distractors → the learner's own vocabulary → a hardcoded list, so a card is never short of options (three choices would themselves tell the learner something);
  • malformed distractor JSON costs the card its distractors, never the review.

For the Tutor the pool is the other words in the same session — words the learner has actually met make a real choice rather than obvious filler.

Decisions worth noting

  • Clients pick the component from options, not from the type string, so a card can never claim options it does not carry. A correctOptionIndex arriving without options is ignored rather than indexed into undefined.
  • reviewMode stays vestigial and unread. Its comment in the shared DTO explicitly says not to build on it, so nothing here does; the comment is updated where it described the old behaviour.
  • context with no sentence is pinned even though it is unreachable (CalibrateForStage downgrades it to recall first) — an empty cloze is the one outcome worse than a plain card.
  • This is the one place where the card shape is chosen by the server rather than by the client's Blitz/Flashcards preference, and CLAUDE.md now records the exception rather than leaving the two statements to contradict each other.

Verification

  • dotnet test tests/TextStack.UnitTests — 1251 pass (6 new McOptions, 5 new exercise-shape)
  • web 713, mobile 369 — pass; tsc --noEmit on both — clean; vite build — clean
  • dotnet format textstack.sln --verify-no-changes — clean

Not verified in a browser: a Tutor session needs an account with vocabulary and an LLM call, and there is no OpenAI key in the local stack. The projection and the option builder are unit-tested; the two screens are not.

🤖 Generated with Claude Code

https://claude.ai/code/session_011rgEMvYYi4Egj99dVtvm6E

mrviduus and others added 2 commits September 11, 2026 00:53
The Tutor calibrates an exercise type per word from that card's SRS stage —
deliberately not from the model's suggestion, so the plan cannot schedule a
jarring jump — carries it to the client with everything needed to render it, and
then threw it away: buildPlanCard hardcoded options:null and blankSentence:null,
so all three types drew the same flashcard and the calibration arrived as a badge.

  recognition  four options, no sentence prompt
  recall       no options — a flashcard the learner grades themselves, which is
               the point of the stage
  context      four options AND the saved sentence with the word blanked

One implementation of what the choices are: McOptions, extracted from
ReviewCardBuilder, which carried TWO copies of the same forty lines (one for
multiple_choice, one for context). The builder drops from 145 lines to 61, and
the rule finally has tests — the answer is always among the four and never also
a distractor, and the cascade prefers the LLM's distractors, then the learner's
own vocabulary, then a hardcoded list so a card is never short of options.

The tutor's distractor pool is the other words in the same session: words the
learner has met make a real choice rather than obvious filler.

Clients pick the component from the payload (options), never from the type
string, so a card cannot claim options it does not carry. reviewMode stays
vestigial and unread; the shared DTO's comment about it stays true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011rgEMvYYi4Egj99dVtvm6E
@mrviduus
mrviduus merged commit 50a1a98 into main Sep 11, 2026
10 checks passed
@mrviduus
mrviduus deleted the feat/tutor-exercise-shapes branch September 11, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant