> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asakiri.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reader and writer

> Assembling a course reader or writer, with versioning and what is still unspecified.

<Info>
  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.
</Info>

## The parsed course

A reader loads the files into one in-memory `Course` value, then hands it to a
[binding resolver](/build/resolving-and-rendering).

```ts theme={null}
interface Course {
  readonly project: CourseProject;
  readonly collections: readonly Collection[];
  readonly records: readonly ContentRecord[];
  readonly assets: readonly Asset[];
  readonly lessons: readonly Lesson[];
  readonly outline: readonly OutlineSection[];
}
```

## 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](/build/project-layout#loading-order)).
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.

```ts theme={null}
interface CourseSources {
  readonly project: string;
  readonly collections: Readonly<Record<string, string>>;
  readonly records: Readonly<Record<string, string>>;
  readonly assets: Readonly<Record<string, string>>;
  readonly lessons: Readonly<Record<string, string>>;
  readonly parts: Readonly<Record<string, string>>; // keyed by `${lessonId}::${partId}`
}
```

**Respect the [reference lifecycle](/data-structure/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](/build/file-formats#versioning-and-migration).

A writer has three obligations beyond a reader:

<Steps>
  <Step title="Stamp the envelope">
    Write `format` and `formatVersion` on every file you create or rewrite.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Write atomically">
    Write to a temporary file and rename, so a crash cannot leave a half-written record.
  </Step>
</Steps>

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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.