Skip to content

Interactive books are a determined application over vaults and the native APIs

State: open/accepted

  • The four augmented book apps and their four content formats are replaced by one set of projects inside solutions/determined/, with three deployables: the books product, an authoring CLI, and a static print site.
  • A book is an Obsidian vault. The reader, authoring and progress all run over determined’s native vault APIs and the frame. Books has no backend of its own, and it adds platform code only where a gap is generic.
  • Exercises are fenced blocks, vim-exercise and sql-exercise, rendered by a markdown plugin into runners that are plain Vue. The same runners mount as islands in the print site.
  • Learner state is notes in a learner vault, keyed by book id, with a learner_id on every note from the start.

Accepted. Sequenced by M-8L1Z. Supersedes the relocation plan in M-E1EU, which moved the four apps intact and kept every format; its six tasks are closed as superseded. B-0JM1-books-format-convergence is resolved by this decision: the vault is the one model.

The augmented family, adopted from sksizer/augmented_books in July and August 2026, carries four apps for one idea: vimit (Tauri and Astro, the vim curriculum), vimit_prototype (Nuxt, editor experiments), augmented-books (Tauri and Vue, a desktop reader with WASM SQLite runners) and learn_web (Nuxt, a web reader and editor). They hold four content formats: Astro collections, a JSON tree with referenced files, a +book.md hierarchy, and a Rust dialect of the hierarchy that misparses the others. The desktop app finds its content through an untracked symlink. Three of the four render markdown with their own stack.

Meanwhile the repo grew the pieces these apps reimplement. The frame and its module registry. markdown-ui over obsidian-md and obsidian-editor, with a plugin seam that routes a fence language to a component. code-editor with an extensions prop. The platform’s generic vault surface: read, write, create, rename with link rewriting, frontmatter writes under etag CAS, backlinks, search, the graph, a paged tree, and the roster. obsidian-astro for vault to static site. Fitness shows an experience with its own state over the frame alone, and agent-pants shows a solution mounted as an experience.

The owner’s targets are three: a desktop and hosted reader that records progress, downloadable books, and a printed static version. Authoring is a deployable of its own. Runners must cover vim and SQLite now and other kinds later.

  1. A determined application, not a solution. Books runs determined’s platform-server and reads the vault API through @determined/vault-platform-client, which is a build artifact of crates/determined-platform — the platform crate’s build.rs emits it. A consumer of a generated client cannot sit outside the solution that generates it, and books has no backend of its own to justify a solution under D-0001. So it lives inside solutions/determined/, beside the other applications on the same framework.

    ProjectPathWhat it is
    @determined/book-modelsolutions/determined/packages/book-model/Note kinds, exercise specs, contract corpus
    @determined/runnerssolutions/determined/packages/runners/Vim and sqlite runners, plain Vue
    @determined/exp-learningsolutions/determined/experiences/learning/The reader, author mode
    product-bookssolutions/determined/products/books/Thin product: frame, experience, ui
    Book vaultssolutions/determined/vaults/<book>/One vault per book

    The authoring CLI and the Astro print template are later phases and take determined’s usual homes: solutions/determined/apps/ and sites/.

  2. No backend of its own. The experience uses @determined/vault-platform-client’s generated transport and the frame. It declares no server route and no ontogen schema, and the product extends no shell. A platform addition is allowed only when it is generic and named here.

  3. Vault per book. A book is a folder that is a vault: book.md with the stable id, parts and modules as notes, glossary and card sets inside the book. Download is import into the roster. Search, backlinks and the graph come from the platform per vault.

  4. Exercises as fences. vim-exercise and sql-exercise fences with a YAML body validated by book-model. One markdown plugin maps lang:<fence> to a runner. Runners take a spec and emit attempts; they depend on neither Nuxt nor the transport. SQLite runs on sql.js in the browser; the wasmtime sandbox is retired.

  5. Learner state as notes. A learner vault holds, per book, a progress note, a card-state note and an append-only attempts note. Keys are the book id, never the roster’s vault id. Every note carries learner_id.

  6. Print via Astro. obsidian-astro gains a fence-to-component hook; the print site mounts the runners as islands. Progress is out of the static site.

  7. Promotions. @determined/code-editor and @sksizer/obsidian-astro move to packages/ts/, names unchanged: both are framework-general and depend on nothing in determined. @determined/vault-platform-client does not move. It is generated from crates/determined-platform, so it belongs beside the crate that writes it.

GapRuling
Import a vault from an archiveGeneric. Added to determined-platform in P5
Identity for a hosted instanceDeferred. Personal instance behind the platform token; learner_id on every note keeps it an auth change
A doc read with its embeds inlinedDeferred. P5 measures page loads over HTTP and decides
The suite-wide vault roster showing book vaults in the family workbenchOpen. Settled in P1
  • The ontogen crate a fitness-shaped app would carry does not exist. Aggregation over learner state is client-side over one note per book, which is adequate for a review scheduler and would not be for a fitness log.
  • The static site and the reader share runners but not chrome. A static Nuxt build of the reader over a static transport is a possible later spike, not this decision.
  • solutions/augmented/ is deleted in the last phase. augmented_web and augmented_dot_com move to sites/ as the superseded plan already specified.
  • Books shares determined’s dev-port band and its Tauri deep-link scheme, determined://, because it is one of determined’s products.
  • Relocate the four apps into solutions/augmented_learning/ (M-E1EU). Kept four formats and three render stacks. Rejected.
  • A fitness-shaped app with an ontogen schema and a domain crate. Proven path, but it adds core code for every learning entity and forgoes the plugin experiment. Rejected for now; the schema can be added later if learner state outgrows notes.
  • A separate solutions/learning/. Wanted by the three deployables, but the reader consumes a client generated by a determined crate, which would make the boundary a fiction on the first build. Rejected.
  • An app under solutions/determined/apps/learning/. Correct for a headless tool; wrong for a Nuxt reader that mounts the frame.
  • Exercises as embedded notes, drill. Reusable across books and printable as prose, but needs a resolver rule per kind. Fences first; embeds if reuse shows up.

← Back to Decisions