Repository navigation
[QTI] Gap Match interaction #6202
Description
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
Add the Gap Match question type to the QTI editor. An author writes a passage, turns words into gaps (by highlighting them, or by inserting a blank gap and typing its answer into it), and adds optional distractors. Learners drag answers and distractors from one shared pool into the gaps (#5927).
Like inline choice (#6178), it is a passage-based type built on the TipTapEditor seams from #6176. Unlike inline choice, the item is one interaction with one response declaration: the passage sits inside <qti-gap-match-interaction>, and the correct response pairs each answer with its gap.
Scope
Included
- The gap match interaction plugin: its descriptor, parsing, XML assembly and validation.
- The gap node and Insert action in the passage's
TipTapEditor, the distractors list, and the editor UI. - An inline-only mode for
TipTapEditor, used for gap answers and distractors ([QTI] Add an inline-only mode to TipTapEditor for gap match choices #6193).
Excluded
- Answer-specific distractor pools (
match-group): every answer and distractor is a candidate for every gap. - Partial credit: an item is scored all-or-nothing.
- Image answers (
qti-gap-img) and graphic gap match. - Mixing gap match with other interaction types in one item.
- Learner preview inside Studio.
- Rendering in Kolibri's QTI viewer (kolibri#15213).
Strategy
- Order. [QTI] Add an inline-only mode to TipTapEditor for gap match choices #6193 (inline-only
TipTapEditor) starts once Bring back underline, strikethrough and align support, and remove RTE link support #6151 merges, and [QTI] Gap match interaction: descriptor, parsing, XML assembly and validation #6203 (descriptor, parsing, validation) can proceed in parallel with it. [QTI] Gap match interaction editor, with the gap node and Insert action #6204 (gap node, Insert, distractors and editor UI) depends on both, and can overlap with [QTI] Gap match interaction: descriptor, parsing, XML assembly and validation #6203 once the passage markup is agreed. - Shared with inline choice. The gap node follows the same pattern as the inline choice chip ([QTI] Inline choice chip and Insert action in the passage editor #6181): an inline node contributed through the [QTI] Update TipTapEditor to support custom insert node behavior defined on parent component #6176 seams, carrying its own data so undo and copy / paste keep it. Whichever lands first sets the pattern for the other.
- Scoring. All-or-nothing, through the standard
match_correcttemplate, which the core already writes for a single declaration with a correct response. No change to the editor core is needed. - Backend. No production change is needed: the server validates against the QTI 3.0 XSD, which includes gap match, and publishes
raw_dataas-is. - Kolibri. Studio authoring doesn't wait on kolibri#15213, but the end-to-end check below does.
Testing Requirements
Beyond each task's unit tests, the project is done when a channel with a gap match question (several gaps and distractors) publishes and, once kolibri#15213 lands, renders in Kolibri's QTI viewer, where filling every gap correctly produces SCORE 1 and any wrong gap produces 0.
AI usage
I used Claude Code to draft this tracking issue and its tasks. It read the QTI editor core, the inline choice issues, the QTI 3.0 schema's content model for gap match, and Kolibri's gap match issue to check feasibility. I decided the scope, the scoring and the split, and reviewed and edited the text.

Activity
sigilsystemsro-cmyk commentedon Oct 17, 2026
Phase 1: Core Parsing & XML Assembly (#6203)
Input Mapping: Implement the XML parser to ingest tags without supporting complex match-group distractor pools.
Validation Rule: Constrain validation strictly against the QTI 3.0 XSD schema; output raw_data as-is to the backend server.
Scoring Logic: Hardcode the evaluation template to all-or-nothing (match_correct): correct mapping yields score 1, any deviation yields 0.
Phase 2: Editor UI & Gap Node Integration (#6204)
TipTapEditor Binding: Leverage the existing inline-node pattern established in #6193 to inject gap choices directly into the passage text stream.
Insert Action: Configure the editor toolbar to expose the Insert action, allowing authors to drop blank gaps and associate distractors from the shared pool.
Constraint Enforcement: Block any attempt to mix gap match interactions with other question types within the same item container.
Immediate Execution Directive
Run the unit test suite on a newly published test channel to verify that rendering in the Kolibri QTI viewer correctly scores 1 on exact matches and 0 on partial or incorrect placements.