Use this reference when adapting quiz-template.html. Read the design guide for layout and interaction requirements. The template needs no build step, network service, or API key. It keeps responses in memory while the page is open; it does not save them across reloads.
CONTRIBUTING.md.script#quiz-data[type="application/json"] with the new material, questions,
answer keys, explanations, source locators, and prepared retry variants.Do not render supplied material as executable HTML. When serializing the data for
the script element, escape every literal < as the JSON escape \u003c. JSON
encoding alone does not prevent a source passage containing </script> from
terminating an HTML script element. For example, in JavaScript:
const embedded = JSON.stringify(quizData, null, 2).replace(/</g, '\\u003c');
Insert that serialized string into the data element, rather than concatenating raw question or source text into HTML. Preserve JSON escaping for quotes, newlines, and backslashes as well.
The root is one JSON object:
| Field | Contract |
|---|---|
title |
Required nonempty quiz title. |
description |
Optional introductory text. |
source |
Required object with nonempty name and scope. This is the overview; individual questions carry their own evidence. |
questions |
Required nonempty array of question objects. |
lang |
Optional document language tag. A tag whose primary subtag is ko (case-insensitive, such as ko or ko-KR) selects built-in Korean UI; otherwise the UI defaults to English. It does not translate question content or static HTML. |
labels |
Optional UI string overrides applied over the selected built-in UI. All values must be strings. Translate the complete set when another language is needed. |
Each question has these fields:
| Field | Contract |
|---|---|
id |
Required nonempty string, unique across all initial and retry questions. |
concept |
Required nonempty concept name used to group review needs. |
type |
Required: choice, ox, or short. |
prompt |
Required nonempty question text. State the expected response scope for short answers. |
options |
For choice, at least two objects with distinct nonempty id values and nonempty text values. Omit for ox and short. |
answer |
For choice, exactly one option’s ID. For ox, the string "true" or "false", not a JSON boolean. Omit for short. |
referenceAnswer |
Required nonempty reference answer for short; used for comparison, not semantic automatic grading. |
essentialPoints |
Optional array of nonempty strings for short-answer self-checking. |
explanation |
Required nonempty explanation grounded in the selected source. |
hint |
Optional assistance for this question. |
source |
Required object with nonempty document and section, optional excerpt, and optional url. Use only real locators. URLs must use HTTP(S); omit unavailable links rather than inventing them. |
retry |
Optional single follow-up question object, not an array. Its prompt, explanation, and type-specific answer fields are required. It inherits the original type, concept, and source unless supplied, and its ID defaults to the original ID plus --retry. Nested retries are not supported. |
Prepare retry variants when generating the initial artifact: the offline page cannot invent new questions. Keep a retry on the same concept and within the original source scope. A concept name or source inherited by the engine is not proof that a newly written variant is actually grounded; review its content.
This minimal example uses the repository’s actual language policy and demonstrates a follow-up question of a different type:
{
"title": "Contributing to plz",
"description": "Practice the repository's language policy.",
"source": { "name": "CONTRIBUTING.md", "scope": "Language policy" },
"lang": "en",
"labels": {},
"questions": [
{
"id": "skill-language",
"concept": "Language of skill instructions",
"type": "choice",
"prompt": "Which language is required for SKILL.md and references?",
"options": [
{ "id": "english", "text": "English" },
{ "id": "korean", "text": "Korean" }
],
"answer": "english",
"explanation": "The language policy specifies English for SKILL.md and references.",
"source": {
"document": "CONTRIBUTING.md",
"section": "Language policy",
"excerpt": "SKILL.md and everything in references/ are written in English."
},
"retry": {
"type": "short",
"prompt": "What language should a new reference guide use?",
"referenceAnswer": "English.",
"essentialPoints": ["English"],
"explanation": "A reference guide follows the same English-language policy."
}
}
]
}
Translate question content, source descriptions where appropriate, UI labels, and
the static <html lang> and noscript message. The engine sets the page title
from title, the meta description from description (or title), and the skip
link from the skipLink label; the static values only show before the script runs.
English and Korean UI labels are bundled; labels overrides the selected defaults.
For another language, use DEFAULT_LABELS in the template as the complete key
inventory. Preserve each label’s existing interpolation tokens when translating
the surrounding text: {count}, {number}, {answered}, {total}, {correct},
{unanswered}, {assisted}, {short}, {incorrect}, {skipped}, {checked},
{pending}, {status}, and {type}. listSeparator joins lists such as the
type breakdown and answer-sheet cell names; translate it too (for example, “、” in
Japanese). Override keys that the template no longer uses are ignored, so older
labels objects keep working. Changing lang alone does not translate questions,
static text, or UI into an unbundled language.
The template runs learning mode one question at a time and check mode as a batch. Check-mode inputs remain drafts until final submission. Feedback is withheld until the appropriate stage; explicit assistance affects only the requested question. The answer key is embedded in the local file, so this is a learning interaction, not a secure examination system.
Choice and OX items use automatic scoring. Short answers use a labeled reference comparison and learner self-check; keep them separate from the objective subtotal. Do not replace this with keyword or exact-string matching and call it AI grading. Assisted, skipped, unanswered, and self-assessed outcomes stay distinct. Follow-up practice uses prepared variants and preserves the initial attempt’s results.
For browser QA, window.__quiz.snapshot() returns copies of session state and
summaries without answer keys. It is read-only and does not replace observing the
rendered controls, feedback timing, or keyboard behavior.
From the repository root, run the browser regression suite against the bundled template after changing its data or behavior:
node --test scripts/check-quiz-template.test.mjs
For a generated file that preserves the template’s data and engine contract, point the same suite at that file:
QUIZ_TEMPLATE_PATH=/absolute/path/to/quiz.html node --test scripts/check-quiz-template.test.mjs
These checks require the repository’s existing Playwright development dependency and Chromium. They validate startup using the file’s actual data, then use a controlled fixture for behavior regressions. They do not verify the factual quality of every generated question or replace inspection of the finished UI.
Review every answer and source locator. Then follow the design guide’s browser QA for both modes, partial submission, honest short-answer self-checking, assistance, separate retries, keyboard use, responsive layout, and console errors. Report unexecuted checks; a passing fixture test does not prove that a customized artifact has been visually or factually reviewed.