Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "quizbun-development",
"interface": {
"displayName": "Quizbun development"
},
"plugins": [
{
"name": "quizbun",
"source": {
"source": "local",
"path": "./plugins/quizbun"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
# out of the repository language statistics. Review the sources instead —
# scripts/create-quiz-validator*.ts and src/shared/lib/quiz/schema.ts.
skills/create-quiz/scripts/validate-quiz.mjs linguist-generated=true
skills/quizbun/scripts/open-quiz.mjs linguist-generated=true
skills/create-quiz/references/quiz.v1.schema.json linguist-generated=true
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Full walkthrough: https://quizbun.fyi/docs/contributing/
## Checklist

- [ ] The quiz validates on the [import page](https://quizbun.fyi/import/) and I completed a full run of it there.
- [ ] The quiz has at most 200 questions and 1 MB of JSON.
- [ ] `description` and `language` are set, and there is at least one tag.
- [ ] The file is `content/quizzes/{id}.json`, where `{id}` is the quiz's `id`.
- [ ] Every Explanation teaches the concept — none of them just restate the correct answer.
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ jobs:
- name: Check create-quiz skill drift
run: bun run skill:create-quiz:check

- name: Check quizbun skill drift
run: bun run skill:quizbun:check

- name: Build static site
run: bun run build

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ node_modules/
# local installation of the published create-quiz skill
/.agents/skills/create-quiz

# assembled ChatGPT plugin resources (copied from the standalone skills)
/plugins/quizbun/skills/create-quiz/references/
/plugins/quizbun/skills/create-quiz/scripts/
/plugins/quizbun/skills/create-quiz/package.json
/plugins/quizbun/LICENSE
/plugins/quizbun/assets/icon.png

# logs
npm-debug.log*
yarn-debug.log*
Expand Down
3 changes: 2 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"public/schema/**",
"content/quizzes/**",
"**/create-quiz/references/quiz.v1.schema.json",
"**/create-quiz/scripts/validate-quiz.mjs"
"**/create-quiz/scripts/validate-quiz.mjs",
"**/quizbun/scripts/open-quiz.mjs"
],
"overrides": [
{
Expand Down
1 change: 1 addition & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"$schema": "./node_modules/oxlint/configuration_schema.json",
"ignorePatterns": [
"**/create-quiz/scripts/validate-quiz.mjs",
"**/quizbun/scripts/open-quiz.mjs",
"**/*.d.ts",
".claude/**",
".agents/**"
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Docs are authoritative, in this order:

1. [CONTEXT.md](CONTEXT.md) — binding vocabulary; use these terms in code and prose.
2. [docs/description.md](docs/description.md) — product vision; wins on any conflict.
3. [SPEC.md](SPEC.md) — Standard, layer/slice inventory, routes, behavior, testing, deferred work.
3. [SPEC.md](SPEC.md) — standard, layer/slice inventory, routes, behavior, testing, deferred work.

## Commands

Expand All @@ -14,7 +14,7 @@ Bun; Node >= 22.12. Scripts live in `package.json` — `dev`, `build`, `check` (

- GitHub Pages serves the custom domain `https://quizbun.fyi/` at Astro base `/`. Use `withBase` for site and asset paths.
- The Zod schema is the single source of truth for the Quiz Object Standard; `public/schema/quiz.v1.json` is generated from it. Unknown fields are errors. Validation messages are a product feature: path-precise and pasteable into an AI chat.
- The Standard carries no presentation fields. Option identity is JSON order; shuffling, labeling, and page size are Renderer behavior.
- The standard carries no presentation fields. Option identity is JSON order; shuffling, labeling, and page size are renderer behavior.
- Markdown goes through [markdown.ts](src/shared/lib/render/markdown.ts) (`marked` + `sanitize-html`); raw HTML is always stripped.
- v1 ships no visual design — semantic HTML, keyboard operability, responsive structure only.
- Code layout follows FSD: `app → pages (routes) → _pages → features → entities → shared`. `src/pages/` holds Astro route files only; no `widgets/` layer. See the `fsd-quizbun` skill.
Expand Down
86 changes: 48 additions & 38 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -1,135 +1,145 @@
# Quizbun

A static, explanation-first quiz catalog built around a JSON quiz standard designed for AI generation. The Standard, site, and contribution workflow share this language.
A static, explanation-first quiz catalog built around a JSON quiz standard designed for AI generation. The standard, site, and contribution workflow share this language.

## Language

### The Standard
Use lowercase for project terms in prose, including quiz, question, explanation, catalog, library, and renderer. Capitalize them only at sentence starts or where ordinary English requires it. Keep proper names, such as Quizbun and Quiz Object Standard, unchanged. Preserve capitalization in code identifiers and exact quotations. Headings use sentence case.

### The standard

**Quiz**:
One self-contained learning unit with metadata and an ordered list of Questions, expressed as a single JSON object that follows the Quiz Object Standard.
One self-contained learning unit with metadata and an ordered list of questions, expressed as a single JSON object that follows the Quiz Object Standard.
_Avoid_: test, exam, deck

**Quiz Object Standard**, usually **the Standard**:
The versioned, strictly validated JSON format that defines what a Quiz is. Integer `schemaVersion`; unknown fields are errors.
**Quiz Object Standard**, usually **the standard**:
The versioned, strictly validated JSON format that defines what a quiz is. Integer `schemaVersion`; unknown fields are errors.
_Avoid_: format, spec (when referring to the artifact)

**Question**:
One prompt inside a Quiz that a Learner answers and receives an Explanation for. The title asks the Question, while the optional description adds context.
One prompt inside a quiz that a learner answers and receives an explanation for. The title asks the question, while the optional description adds context.
_Avoid_: item, task, exercise

**Option**:
One selectable entry in a choice Question. An Option has `text` and `isCorrect`, and its original JSON position is its identity.
One selectable entry in a choice question. An option has `text` and `isCorrect`, and its original JSON position is its identity.
_Avoid_: answer, choice, variant

**Accepted answer**:
One of the strings or numbers an `input` Question treats as correct, compared under the Question's validation mode (`text` or `numeric`).
_Avoid_: correct answer (ambiguous with Options)
One of the strings or numbers an `input` question treats as correct, compared under the question's validation mode (`text` or `numeric`).
_Avoid_: correct answer (ambiguous with options)

**Explanation**:
The teaching text shown after a Question is submitted, regardless of correctness. The core reward of the product.
The teaching text shown after a question is submitted, regardless of correctness. The core reward of the product.
_Avoid_: feedback, rationale, solution

**References**:
Optional Question source material shown after the Explanation. References use full Markdown for links, citations, and further reading.
_Avoid_: bibliography (unless a formal bibliography is meant), sources (when referring to the Standard field)
Optional question source material shown after the explanation. References use full Markdown for links, citations, and further reading.
_Avoid_: bibliography (unless a formal bibliography is meant), sources (when referring to the standard field)

**Image**:
A structured visual item on a Question with a source, required alt text, and optional caption and Placement.
A structured visual item on a question with a source, required alt text, and optional caption and placement.
_Avoid_: picture, photo, illustration

**Video**:
A structured YouTube reference on a Question with a video id, optional start time, and optional Placement.
A structured YouTube reference on a question with a video id, optional start time, and optional placement.
_Avoid_: embed, clip

**Placement**:
The part of a Question where an Image or Video belongs. `question` shows it with the prompt, while `explanation` reveals it with the Explanation.
The part of a question where an image or video belongs. `question` shows it with the prompt, while `explanation` reveals it with the explanation.
_Avoid_: position, slot

**Tag**:
A kebab-case keyword on a Quiz used for filtering and discovery. Tags are the only Quiz taxonomy.
A kebab-case keyword on a quiz used for filtering and discovery. Tags are the only quiz taxonomy.
_Avoid_: category, topic, label, hashtag

**Renderer**:
An application that displays Quizzes. Presentation choices belong to the Renderer, not the Standard.
An application that displays quizzes. Presentation choices belong to the renderer, not the standard.
_Avoid_: player, viewer, frontend (when referring to the role)

**Public catalog profile**:
The stricter rules applied to Catalog Quizzes during repository validation. It extends the Standard for contributions but is not another schema.
The stricter rules applied to catalog quizzes during repository validation. It extends the standard for contributions but is not another schema.
_Avoid_: public schema, extended schema

### The site

**Public catalog**, usually **Catalog**:
**Public catalog**, usually **catalog**:
The bundled, read-only set of quizzes that lives in the repository and ships with the site.
_Avoid_: store, gallery, public library

**Asset folder**:
The directory that holds one Catalog Quiz's vendored Image files.
The directory that holds one catalog quiz's vendored image files.
_Avoid_: uploads, media directory, attachments

**Library**:
A Learner's Quizzes stored on one device, separate from the Catalog.
A learner's quizzes stored on one device, separate from the catalog.
_Avoid_: my quizzes, collection, private catalog

**Import**:
Bringing Quiz JSON into the Library after it passes validation.
Bringing quiz JSON into the library after it passes validation.
_Avoid_: upload, load, add

**Import limits**:
The maximum quiz size Quizbun imports: 200 questions and 1 MB of JSON. A renderer limit, not part of the standard.
_Avoid_: schema limits, max length (when referring to the standard)

**Quiz link**:
A URL that carries one quiz in its fragment and opens it in import.
_Avoid_: share link, deep link (deep links already mean player routes)

**Export**:
Saving a Catalog or Library Quiz as JSON. Export never includes Progress.
Saving a catalog or library quiz as JSON. Export never includes progress.
_Avoid_: download, backup

### Learning

**Run**:
One pass by a Learner through a Quiz. A Run completes when every Question is submitted, and a Retake replaces it.
One pass by a learner through a quiz. A run completes when every question is submitted, and a retake replaces it.
_Avoid_: attempt, session, playthrough

**Page size**:
A Learner-owned Renderer setting that controls how many Questions appear on a page. Page size is never Quiz content.
A learner-owned renderer setting that controls how many questions appear on a page. Page size is never quiz content.
_Avoid_: questionsPerPage (the deleted quiz field from attempt #1)

**Voice**:
The Learner-selected on-device speech voice used by Read aloud. Voice is Renderer behavior, never Quiz content.
The learner-selected on-device speech voice used by read aloud. Voice is renderer behavior, never quiz content.
_Avoid_: narrator, TTS voice

**Read aloud**:
An opt-in Renderer feature that speaks a Question's Explanation with the selected on-device Voice.
An opt-in renderer feature that speaks a question's explanation with the selected on-device voice.
_Avoid_: text-to-speech, TTS, narrate

**Progress**:
The saved state of a Run, including which Questions were submitted and how.
_Avoid_: history, results (Progress is one Run, not an archive)
The saved state of a run, including which questions were submitted and how.
_Avoid_: history, results (progress is one run, not an archive)

**Content hash**:
The fingerprint of a Question's content that decides whether a saved answer is still valid. If the Question changed, its saved answer is discarded.
The fingerprint of a question's content that decides whether a saved answer is still valid. If the question changed, its saved answer is discarded.

**Retake**:
Starting a fresh Run on a finished (or reset) Quiz, replacing the previous Run.
Starting a fresh run on a finished (or reset) quiz, replacing the previous run.
_Avoid_: restart, replay

**Reset progress**:
Explicitly deleting the saved Run of a Quiz, mid-run or after completion. The same mechanism Retake uses, exposed as a button.
Explicitly deleting the saved run of a quiz, mid-run or after completion. The same mechanism retake uses, exposed as a button.
_Avoid_: clear, wipe

**Summary**:
The end-of-Run view with the result, links to each Explanation, and Run actions.
The end-of-run view with the result, links to each explanation, and run actions.
_Avoid_: results page, score screen

### People

**Learner**:
A person who takes a Quiz and reads its Explanations.
A person who takes a quiz and reads its explanations.
_Avoid_: test taker, student, user when the learning role matters

**Creator**:
A person who makes a Quiz, usually with AI, for private use or contribution.
_Avoid_: Author when referring to the person doing the work
A person who makes a quiz, usually with AI, for private use or contribution.
_Avoid_: author when referring to the person doing the work

**Contributor**:
Someone who submits a Quiz to the Catalog through a pull request.
Someone who submits a quiz to the catalog through a pull request.

**Author**:
The free-form string in a Quiz's optional `author` field. It is not an account or structured identity.
The free-form string in a quiz's optional `author` field. It is not an account or structured identity.
_Avoid_: creator, owner
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Contributing to Quizbun

The most valuable contribution is a quiz for the public Catalog. The complete walkthrough — generating a quiz with the published AI prompt, validating it locally, meeting the Public catalog profile, and reading CI feedback — lives in the published contributor guide:
The most valuable contribution is a quiz for the public catalog. The complete walkthrough — generating a quiz with the published AI prompt, validating it locally, meeting the public catalog profile, and reading CI feedback — lives in the published contributor guide:

**<https://quizbun.fyi/docs/contributing/>**

(The same text is in this repo at [docs/contributing.md](docs/contributing.md) — the site page is rendered from it.)

The short version:

1. Generate or write a quiz that satisfies [the Quiz Object Standard](https://quizbun.fyi/docs/standard/).
1. Generate or write a quiz that satisfies [the quiz Object standard](https://quizbun.fyi/docs/standard/).
2. Validate it by pasting the JSON into the [import page](https://quizbun.fyi/import/).
3. Add it as `content/quizzes/{id}.json` and open a pull request — one quiz (or one coherent cluster) per PR.

Expand Down
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Quizbun

Quizbun is a static, explanation-first quiz catalog built around the Quiz Object
Quizbun is a static, explanation-first quiz catalog built around the quiz Object
Standard.

[https://quizbun.fyi/](https://quizbun.fyi/)
Expand All @@ -15,24 +15,37 @@ npx skills add a-dev/quizbun --skill create-quiz

The installed skill includes the Quiz Object Standard authoring contract and a dependency-free Node.js validator. It does not require the Quizbun application or Bun.

## Check Explanations against marked answers
Add the `quizbun` skill to open a finished quiz in Quizbun. It writes a launcher file that loads the quiz on the import page, and it requires `create-quiz`:

Validation proves that a Question is well-formed, never that its Explanation
defends the Option marked `isCorrect`. A flag on the wrong Option passes every
automated gate, and a reviewer reading thirty Questions can miss it.
```sh
npx skills add a-dev/quizbun --skill create-quiz --skill quizbun
```

Quizbun imports quizzes of at most 200 questions and 1 MB of JSON. See [the quizbun skill](docs/quizbun-skill.md).

For ChatGPT, a separate prototype plugin bundles quiz generation and a
browser-assisted import workflow. Build it with `bun run plugin:chatgpt:package`;
see [installation and testing](docs/chatgpt-plugin.md). The standalone skills
remain available independently.

## Check explanations against marked answers

Validation proves that a question is well-formed, never that its explanation
defends the option marked `isCorrect`. A flag on the wrong option passes every
automated gate, and a reviewer reading thirty questions can miss it.

This check asks a [TypeSafe](https://typesafe.ai) judgment per choice Question
and reports the ones whose Explanation argues for a different answer:
This check asks a [TypeSafe](https://typesafe.ai) judgment per choice question
and reports the ones whose explanation argues for a different answer:

```sh
bun run quiz:explanations:check content/quizzes/{id}.json
```

Add `--all` to print every probability rather than only the flagged Questions.
Add `--all` to print every probability rather than only the flagged questions.
It needs `TYPESAFE_API_KEY` and network access, so it is advisory and stays out
of CI alongside the other optional analysers.

## Documentation

- Public quiz author and Catalog contributor docs live in [`docs/`](docs/).
- Public quiz author and catalog contributor docs live in [`docs/`](docs/).
- Maintainer product and architecture decisions live in [`SPEC.md`](SPEC.md).
Loading
Loading