The on-disk format is canonical:
"format": "asakiri-course", "formatVersion": 1.
Validate against the published
JSON Schemas
rather than reimplementing the parser, and read
Versioning and migration before you ship.Two targets
Learner app (read-only)
Load a course, resolve bindings, render fragments, play media, and grade responses.
Creator app (read + write)
Everything a reader does, plus editing files, enforcing the reference lifecycle, and
validating before write.
What to read, in order
1
Project layout
The directory tree and manifest: how a course project is
arranged and how
project.json points at everything.2
File formats
The JSON for each entity: collections, records, assets, lessons,
parts, compositions, and exercises.
3
Rich text
The Tiptap document format, including the custom nodes that embed
reusable content and media.
4
Resolving and rendering
The learner-app contract: turning bindings into
values and grading each response.
5
Reader and writer
How to assemble a reader or a writer, plus versioning and
what is still unspecified.
What the format guarantees you
- Every file is versioned.
formatandformatVersionappear on every file, not just the manifest, so a file copied between courses still says what it is. - Identity is stable and separate from order. Reordering a course rewrites lists of IDs and never changes the IDs themselves, so progress keyed on them survives.
- Unknown content is preserved, not rejected. A type you do not recognize is kept verbatim, so an older reader can open a newer course.
- Answers never reference display text. Grading refers to authored IDs or accepted-value bindings.
Source of truth
Every page here tracks real code in the Studio repo: theexamples/courses/japanese-starter
fixture, src/core/course (types, resolve-binding.ts, parse-course.ts), and
src/platform/project-reading and project-writing (the directory layout). When the code
changes, these pages should change with it.