Skip to main content
Canonical format, version 1. A writer carries more responsibility than a reader: it must preserve keys it does not understand, keep unknown parts untouched, and stamp the format envelope on everything it writes.

The parsed course

A reader loads the files into one in-memory Course value, then hands it to a binding resolver.

Building a reader

  1. Read project.json. Reject an unknown format; branch on formatVersion.
  2. Load collections and their recordFiles, then assets, then lessons and their part files (see Project layout).
  3. Build the Course and a resolver.
  4. Render lessons part by part; grade exercises with their evaluation.
The reader is expected to be resilient. The current ProjectReader contract, for example, returns an explicit failure rather than throwing:
  • Manifest missing: fails as unavailable.
  • A referenced file is malformed JSON: fails as unavailable.
  • Manifest with no collections: succeeds with an empty list.
Mirror that spirit. A single bad file should degrade to a clear error or a missing resolution, not a crash.

Building a writer

A creator app reads, edits, and writes back. Two things make writes safe: Write back to the same file. Parsing retains the source path of every entity, so an edit updates the exact file it came from rather than rewriting the whole project.
Respect the reference lifecycle. References are live. Do not silently delete a referenced record, field, item, or asset; require the author to replace, detach, or knowingly break the reference. Detaching writes an inline literal snapshot. Detaching presentation from a record does not copy binary media. Validate before writing: check that every binding resolves, that required fields are present, and that referenced records and assets exist. The Studio repo validates its fixture in CI with pnpm check:example; do the same against your own courses.

Versioning and stability

Every file carries format and formatVersion; today "asakiri-course" and 1. Gate on both, refuse a higher version, and migrate a lower one. See Versioning and migration. A writer has three obligations beyond a reader:
1

Stamp the envelope

Write format and formatVersion on every file you create or rewrite.
2

Preserve what you did not parse

Read the existing file, merge your changes over it, and write the result. Unknown keys, unknown part types, and translations for locales you are not showing must survive the round trip. Studio never rewrites the body file of a part it could not parse.
3

Write atomically

Write to a temporary file and rename, so a crash cannot leave a half-written record.
The following are still expected to change, so keep them behind your own boundary:
  • schema-template format and custom-field constraints;
  • packaging rules for a distributable course.

Not specified yet

Some things a real learner app needs are intentionally not in the course format, because they are product decisions that have not been made. Building them today means inventing your own model that a future Asakiri spec may not match:
  • Learner state: progress, completion, and per-item history.
  • Review scheduling: any spaced-repetition or practice-ordering rules.
  • Packaging and distribution: how a course is bundled and delivered to a learner app.
If you need these now, keep them in a separate layer keyed by the stable IDs in the course, so you can re-map when an official model lands.