Skip to content

Learning Block Specification

Course content is data. Every block uses this versioned envelope:

{
  id: string;              // stable within its topic or collection
  type: string;            // registered semantic block type
  version: number;         // positive schema version
  props: JsonObject;       // type-specific, serializable, no raw HTML
  meta?: {
    title?: string;
    tags?: string[];
    learningObjectives?: string[];
    estimatedMinutes?: number;
    masteryWeight?: number;
    sourceRefs?: SourceReference[];
  };
}

The registry entry is the executable contract. It must define parseProps, a renderer, capabilities, search-text extraction, and—where relevant—mastery weighting, references, and version migrations. Unregistered block types and invalid props render an explicit fallback.

Feeding the review queue

A block with the reviewable capability should also implement reviewItems, which lifts each thing the student can be asked about out of the block's props:

reviewItems: (props) => props.cards.map((card) => ({
  itemId: card.id,        // stable; it keys the item's scheduling history
  prompt: card.front,
  answer: card.back,      // optional — omit when the content states no answer
  group: card.group,      // optional
})),

/review drills these directly, so a student can rate an item without opening the block it came from. A reviewable block that does not implement reviewItems still records progress, but its items cannot be rated from the queue. itemId must match the itemId the block writes into its exercise.completed or flashcard.rated events, since that is how a rating is matched back to its scheduling history.

Registered version 1 blocks

Type Required prop shape Rendering rule Capabilities
core.theory paragraphs: string[] Reading prose searchable, printable
core.key-points title?, points[] Semantic key-point list searchable, printable
core.definition-list title?, entries[{term, definition, context?}] Definition list searchable, printable
core.table title?, columns[], rows[][] Accessible table searchable, printable
core.callout tone, title?, body Typed note/warning/success searchable, printable
media.gallery title?, images[{src, alt, caption?}] Alt-text-required gallery searchable, printable
math.formula-reference title?, formulas[] KaTeX formula reference searchable, printable
math.exercise Exercise Specification Math response and evaluator interactive, gradable, reviewable, printable
code.example languageId, code, caption? Read-only code example searchable, printable
code.exercise Exercise Specification + runtime Editable files and executor protocol interactive, executable, gradable, reviewable, stateful
diagram.mermaid source, caption? Mermaid diagram with failure fallback diagram, searchable, printable
resource.link title, href?, available, description?, provenance? External or internal resource link searchable, printable
resource.document title, href?, available, description?, provenance? Available/missing document record searchable, printable
assessment.question Exercise Specification One semantic assessment item interactive, gradable, reviewable, printable
assessment.flashcard-deck title, groups?, cards[{id, front, back, group?}] Filterable rated flashcards interactive, reviewable, stateful, searchable, printable
assessment.quiz id, title, description?, durationMinutes?, questions[] Auto/self-reviewed quiz interactive, gradable, reviewable, stateful, searchable, printable
assessment.practice-session id, title, description?, durationMinutes?, questions[] Filtered practice and review scheduling interactive, gradable, reviewable, stateful, searchable, printable, exportable
assessment.cloze title, intro?, items[{id, text, blanks[{key, accept[], hint?}], group?}] Typed fill-in-the-blank over authored sentences interactive, gradable, reviewable, stateful, searchable, printable
assessment.matching title, intro?, pairs[{id, term, definition, group?}], distractors? Select-a-term-then-a-definition pairing interactive, gradable, reviewable, stateful, searchable, printable
assessment.ordering title, intro?, sets[{id, prompt, steps[{id, label}], group?}] Reorder shuffled steps into the authored sequence interactive, gradable, reviewable, stateful, searchable, printable
tool.math-plotter title?, intro?, expressions[], xMin?, xMax? Mathematical function plotter using mathjs and recharts interactive, searchable
tool.number-drill title?, intro? 8-bit adder with carry trace, base converter, generated conversion drills interactive, searchable
tool.kmap-solver title?, intro? Four-variable Karnaugh map minimised to DNF or CNF interactive, searchable
tool.rtl-stepper learnIntro, practiceIntro, instructions[{id, label, cycles[]}] Cycle-by-cycle micro-code stepper plus a rebuild-the-sequence drill interactive, searchable
tool.staged-reveal method[], scenarios[{id, title, brief[], stages[]}] Scenario practice where each model-answer stage stays hidden until asked for interactive, searchable
reference.tabs tabs[{id, label, sections[], snippets[]}] Tabbed reference tables and code snippets interactive, searchable, printable
practice.exam-trainer questions[{number, theme, points, topicIds[], pitfalls[], links[]}] Per-exam-question cards with topic progress and drill links interactive, searchable

The last six — tool.number-drill, tool.kmap-solver, tool.rtl-stepper, tool.staged-reveal, reference.tabs, and practice.exam-trainer — take sections built from the same core.theory, core.key-points and core.table prop shapes listed above, so ported prose renders identically inside a tool or on its own.

The table is a quick authoring index. The executable source of truth is src/course-os/registry/blockRegistry.ts and the parseProps function in each registered definition. See Adding a course for a complete workflow.

Exercise and runtime contract

Exercises separate prompt, response, evaluation, hints, solution, and runtime. Responses can be single/multiple choice, text, numeric (with tolerance), symbolic, or editable files. A code runtime names a registered languageId, language version, and task mode (run, compile, test, or simulate). Execution always uses the normalized request/result protocol with denied network, resource limits, structured diagnostics, artifacts, and status values including compile error, runtime error, timeout, output limit, unavailable, and success.

Choice responses may include an options array of { value, label } entries. The shared assessment.question renderer presents those labels as selectable answers, evaluates supported answer keys and numeric tolerances in the browser, and records completed attempts in the review schedule. Written or symbolic responses without an automatic evaluator use an explicit self-review step against the authored model answer.

assessment.quiz and assessment.practice-session questions may add an evaluator with the same versioned { kind, version, config } boundary. Automatic written checking currently supports numeric-tolerance and symbolic-equivalence. A symbolic config states an expected result, answerKind, declared variables and constants, optional assumptions, tolerances, and an equivalence mode. The expected result is executable assessment data; modelAnswer remains explanatory learning content and is never parsed to discover the answer.

Supported final-answer kinds are scalar values, expressions, antiderivatives, linear equations, vectors, and finite complex sets. Students enter compact expression syntax such as sqrt(2), 2*x/(1+x^2), y+2=5/4*(x-1), or [1,-1,1]. Full LaTeX, assignments, property access, units, and undeclared symbols are rejected. Proofs, theorem explanations, domains, and geometric descriptions continue to use model-answer self-review.

Symbolic evaluation runs entirely in a restricted, timeout-bounded browser worker. It returns one of four states: correct, incorrect, invalid notation, or unverified. Invalid and unverified answers must not be recorded as incorrect. The UI keeps an incorrect answer editable, separates Check answer from Reveal model solution, and falls back to self-review whenever equivalence cannot be established safely. No answer text is sent to an API or external service.

tue-rtl and pp2-assembly run in a terminating browser worker: the block dispatches to the worker whenever the language's executor reports kind: "browser-worker", and posts to /api/runtime/execute otherwise. C, Java, Python, and future custom university languages plug into the same registry and sandbox boundary; course renderers never know which executor is used.

Exercises with a simulator-assertions evaluator forward address, value, description, initialStackPointer, and initialMemory from the evaluator config to the executor as publicTestConfig. The executor seeds machine state from those fields and returns the assertion as a normal tests entry, which the renderer shows as passed or failed.

Authoring workflow

  1. Add a data-only course manifest, topics, and collections under src/courses/<course-id>.
  2. Use registered block envelopes; never import a course-specific React renderer.
  3. Add the validated input to src/courses/index.ts.
  4. Run npm run validate:content, then npm run check.

The fixture course under src/courses/fixture is the smallest complete example. Adding it to the catalog would immediately provide overview, topics, resources, progress, review, exam preparation, search, graph, and shell navigation without adding a route file.