Write the question. Then see it marked.
The editor's job is not to collect an answer key. It is to let the author state exactly what counts as right — and then show them, before anyone sits the paper, what the marker will actually accept.
Marked text, end to end
The question, every accepted answer and the candidate's response are the same kind of object: a document of blocks and runs carrying marks. That is what makes formatting markable. A chemistry author can require the subscripts in H₂SO₄; a physics author marking the same string as prose can ignore them; a language author can require an accent and forgive a capital.
Because it is one model, the tester sits beside the editor and takes a trial answer through the real engine — not an approximation of it — and reports which synonym matched, which stage of the tolerance pipeline let it through, and what the mark would be.
Leniency is one control
Behind the slider sit the stages an answer passes through. Moving the control moves them together, in an order chosen so that the result is always defensible:
| Stage | Strict | Moderate | Lenient |
|---|---|---|---|
| Whitespace | collapsed | collapsed | collapsed |
| Case | significant | ignored | ignored |
| Punctuation | significant | ignored at edges | ignored |
| Spelling variants | declared only | declared + list | declared + list |
| Typo distance | none | 1 edit on long words | proportional to length |
| Formatting marks | as declared | as declared | as declared |
Formatting never moves with the slider. It is the author's explicit declaration, and a leniency control that quietly discarded it would be marking a different question.
The grading grammar
An accepted answer is an expression, not a list. That is what lets a single item say "either name, both parts, in any order, with at least three of the five reasons".
# "name the acid and give its formula, either order" ALL( ANY( "sulfuric acid", "sulphuric acid" ), ANY( "H2SO4", "H₂SO₄" [require: subscript] ) ) # "any three of the five, one mark each" COUNT(3 of 5) OF( "conduction", "convection", "radiation", "evaporation", "condensation" )
Each operator carries its own scoring behaviour, so partial credit falls out of the structure instead of being configured separately and drifting away from it.
The on-screen keyboard
A paper in Bangla, IPA or mathematical notation is unanswerable on a device with no matching input method — which in practice means the exam hall's machines, or the candidate's own phone. So the keyboard travels inside the pack.
- A key emits a string, not a key code. One key can type a combining mark, a ligature or a full cluster such as a conjunct consonant.
- Levels are named rather than numbered, so a layout can say "superscript" instead of "level 3".
- Direction is a property of the layout, so right-to-left scripts need no separate component.
- Four layouts are built in — Latin, maths and chemistry, IPA, Bangla — so nothing has to be configured to start using it.
- A custom layout is JSON and ships inside the
.mcqpack that needs it.
Question sets
A paper is assembled from named sets, each with its own selection and order. That is how one bank serves a mock, a resit and a practice sheet without three copies of the same item drifting apart — and how randomisation stays reproducible, because the selection is seeded and the seed is recorded with the attempt.
Rules, and their limits
Items can branch, raise flags, show a message or media, navigate, and call a webhook that the pack declared up front. That is the whole action set. It is closed on purpose: see Security for why an exam file that can start a process is a remote-code-execution primitive distributed as courseware.
Getting a bank in
- QTI 3.0 import, with a note attached to every item whose construct did not map cleanly.
- RTF paste from the word processor the bank has been living in, formatting preserved as marks.
- .mcq packs from a colleague, complete with media and keyboard layout — and the
.mcqxpacks and legacy MCQPro banks that came before them.
Write one question. Mark it four ways.
Install the desktop build, open the browser app, or scan into the Android shell. They are the same library, the same marks and the same rules — with or without a network.