# Documentation style guide (living)

This is the **living** home for documentation authoring conventions for the
RDBMS Playground website. It grows as we write.

- **Binding rules** come from
  [ADR-website-001 §7](../docs/website/adr/20260604-adr-website-001.md);
  this guide must not contradict them. If a convention is significant,
  durable, or contested, it is decided in an **ADR** (new or amended), and
  this guide references it. Finer, settled conventions live here directly.
- **Open decisions** (not yet settled) are tracked at the bottom so we
  decide them deliberately rather than re-deciding per page. When one
  settles, move it up into the body (and to an ADR if it's significant).

Status tags used below: **[DECIDED]** (binding or settled) ·
**[OPEN]** (to be decided — see the log).

---

## Terminology & wording  [DECIDED — ADR-website-001 §7]

- **No "DSL".** It is internal jargon. Use **simple mode** (the playground's
  keyword command language) and **advanced mode** (SQL).
- **No engine name.** Never name SQLite / STRICT / rusqlite / PRAGMA in
  user-facing copy. Say **"the database"** or **"the engine"**. (Continues
  the user-facing posture of ADR-0002.)
- Preferred terms (extend as we go): "command", "project", "table",
  "column", "relationship" (the user-facing word for a declared foreign-key
  link), "constraint", "index".
- Add new banned/preferred terms here as they come up, with a one-line
  reason.

## Voice & tone  [DECIDED, refine as needed]

- **Teaching-first.** Pedagogy wins ties (CLAUDE.md). Explain the *why*, not
  just the *how*; the audience spans beginners through learners ready for
  raw SQL.
- Second person ("you"), present tense. Imperative mood for step
  instructions ("Create a table…").
- Prefer short sentences and concrete examples over abstract prose.

## Structure  [DECIDED]

Pragmatic, Diátaxis-influenced split (five top-level sidebar sections,
autogenerated per directory under `src/content/docs/`):

- **Getting started** — install, first project, simple vs. advanced, the
  example database.
- **Using the playground** — *the application you drive*, kept distinct from
  the database-language Reference: command-line options, the assistive editor
  (completion / highlighting / `[ERR]`/`[WRN]` indicator / hints / in-line
  editing), the output pane (scrolling), projects (save/load/new/rebuild),
  undo & history, export & import, clipboard, getting help. (ADR-0003's
  "app-level commands" + ADR-0022/0027 typing assistance + the CLI.)
- **Guides** — task-oriented how-tos. *These are the most important didactic
  content and will be iterated for teaching quality before publication.*
- **Reference** — the exhaustive command/SQL/type surface. **Page
  granularity: one page per topic / command-family** (Tables, Columns,
  Relationships, Indexes, Constraints, Inserting & editing data, Querying,
  Types, …), each covering the simple-mode and advanced-mode forms where
  both apply. Hand-written now (the command surface is settled bar H1a
  output); small post-release adjustments are expected and fine.
- **Concepts** — the "why": projects & storage, undo & history, etc.

Ground every reference page in source — `parse.usage.*` and `help.*` in
`src/friendly/strings/en-US.yaml`, `src/dsl/command.rs`, `src/dsl/types.rs`
— never paraphrase grammar from memory.

## "Planned / not yet available" callouts  [DECIDED — ADR-website-001 §7]

Any capability that is not yet fully implemented is **omitted** or carries a
clear callout — never presented as shipped. Standard form: a Starlight aside

```md
:::caution[Planned]
This is planned and not yet available.
:::
```

## Examples & code  [DECIDED]

- **Shared example database: a small library** — `authors`, `books`,
  `members`, `loans` (see the canonical schema below). Reuse it across all
  pages so readers build familiarity; it models 1:n (an author has many
  books) and m:n (books ↔ members, through loans).
- Where both modes apply, show the **simple-mode** form and its
  **advanced-mode (SQL)** equivalent — the in-app teaching echo already
  pairs these, so docs mirror it.
- Prefer **worked examples** (a real command on the library schema) over
  abstract prose, and **always cross-link** the related reference/guide
  pages (use stubs so links resolve before a page is written).
- Code blocks for exact input/output; reserve casts for motion/flow.

### Code-block fences

- **Simple-mode commands → ` ```rdbms `** — custom highlight grammar in
  `src/grammars/rdbms.mjs`, registered with Expressive Code in
  `astro.config.mjs` (keywords + types coloured).
- **Advanced-mode SQL → ` ```sql `**; shell / install → ` ```sh `.
- A decorative `> ` prompt is prepended to `rdbms` lines via CSS
  (`src/styles/global.css`) — **do not type `>` in the fence**. It is
  copy-safe (Expressive Code's copy button uses `data-code`) and
  `user-select:none`.
- **One command per line** in an `rdbms` block; a multi-line single
  statement (e.g. advanced `CREATE TABLE`) belongs in a ` ```sql ` block,
  where no prompt is added.
- **Command output → a plain unlabelled ` ``` ` fence** placed immediately
  after the command block (no language, so no `> ` prompt and no
  highlighting). Output must be **captured from the real app**, never
  hand-drawn — box-drawing diagrams, query-plan trees, and data tables are
  copied verbatim (trailing padding spaces trimmed). The capture method: a
  throwaway in-crate test that builds the canonical library schema and prints
  the rendered output (`render_structure_with_diagrams`,
  `render_relationship_diagram`, `render_data_table`, `render_explain_plan`,
  `show_list`), removed once the output is pasted. Colour is lost in a static
  block (the structure still reads); the coloured/animated rendering is
  earmarked for casts.

### Canonical library schema (source of truth for examples)

Use these exact names/types in every example:

| Table     | Columns (playground types)                                                                 |
|-----------|---------------------------------------------------------------------------------------------|
| `authors` | `author_id` serial (pk) · `name` text (not null) · `birth_year` int                          |
| `books`   | `book_id` serial (pk) · `title` text (not null) · `author_id` int (→ authors) · `published` int · `isbn` text (unique) |
| `members` | `member_id` serial (pk) · `name` text (not null) · `joined` date                             |
| `loans`   | `loan_id` serial (pk) · `book_id` int (→ books) · `member_id` int (→ members) · `loaned_on` date · `returned_on` date |

Relationships: `books.author_id → authors.author_id` (1:n); `loans` joins
`books` and `members` (the m:n bridge). Show `shortid` on the Types page via
a small standalone example, not by complicating this schema.

## asciinema casts  [DECIDED]

- Casts show *flow/motion*; static code blocks show *exact input/output*.
  Prefer a code block when a still example suffices.
- Pair a hero/landing cast with a text transcript or the equivalent docs
  snippet (accessibility + SEO).

### Where to use a cast  [DECIDED 2026-06-10; stance revised 2026-06-12]

**Default to a cast; justify its _absence_, not its presence.** A cast at the
top of a page gives the reader the *shape* of what the page describes —
quickly and visually — with the detailed text below for when they need it. It
earns its place two ways, both observed in real use: it helps **visual
learners** grasp a flow at a glance, and it gives **advanced learners a fast
entry point and overview** — skim the motion, then drop into the prose only
where needed. So the question for each page is not "does this deserve a cast?"
but "is there a good reason *not* to have one?"

This is **not** a mandate for a cast on every page, still less several per
page: a page wants a cast only where the app *does something* worth seeing, and
one well-chosen cast is almost always enough. Concretely:

- **Expect a cast** on any page where the app *does or shows* something: the
  landing, **Getting started**, **Using the playground**, **Guides**, and the
  **Reference** command pages (a command being run, a diagram drawn, a query
  plan or a table of generated rows appearing).
- **Justify the absence** on the rest — pure-lookup or conceptual pages with
  nothing to animate (e.g. the Types table, prose Concepts pages). "Nothing
  moves here" is a fine reason to record on; "I didn't think about it" is not.
- Reference pages keep their **captured static output** regardless; a cast
  complements it, never replaces it.
- A cast is **selective**: it shows a chosen, representative slice of what the
  page documents — it need not exercise every command on the page.
- **Autoplay only the landing hero**; elsewhere casts are click-to-play (a
  poster frame) so they don't all clamor at once. All casts re-record in one
  `pnpm casts` run, so breadth stays maintainable.

### Cast pacing & clarity  [DECIDED 2026-06-11]

- **Give the viewer a beat before a command is submitted.** A command that is
  typed and `Enter`-ed in the same instant vanishes before the viewer can read
  it. Type, **pause**, then submit — especially for **modal confirmations**
  (e.g. the `save as` name prompt) and short commands (`new`): script the text
  and the `Enter` as *separate* steps with a pause between (`{ type: 'x',
  enter: false, after: … }` then `{ key: 'Enter', after: … }`).
- **Surface state where the schema sidebar would.** At the cast width (90 cols)
  the schema sidebar is hidden (ADR-0046 hides it at ≤90 cols), so the viewer
  can't see tables/relationships appear and disappear. Insert `show tables` /
  `show table <T>` / `show data <T>` at the points where state **changes**, so
  a sequence reads clearly (e.g. the projects cast shows `show tables` before
  saving, after `new`, and after loading — table present → none → present).
- **Driver: `autocast`** (ADR-website-001 §2; chosen by the 2026-06-10
  spike). `autocast`'s `!Interactive` feeds keys to the running TUI and
  captures the redraw — the correct model for a full-screen crossterm app.
  `asciinema-automation` was rejected: it assumes *shell* semantics (Enter as
  `\n`, and an `expect()` echo-check per keystroke), which a redrawing TUI
  breaks — it produced a garbled cast.
- **Authoring workflow.** Source command-lists live in `casts-src/casts.mjs`
  (human-readable steps: `type` / `wait` / `key`). `pnpm casts` runs
  `casts-src/generate.mjs`, which expands them to autocast YAML (one key per
  character; Enter = `^M`) and records to `public/casts/<name>.cast`. The
  command-lists are the durable source; **`.cast` files are regenerable** —
  re-run `pnpm casts` to re-record as the app evolves (a final sweep is due
  once the app is locked). Needs a `cargo build` binary at `../target/debug`.
- **Embedding.** `Demo.astro` (the WASM-swap seam, ADR-website-001 §3) wraps
  `Cast.astro` (asciinema-player island). Call sites use `Demo`.
- **Conventions.** Terminal geometry per cast (default 90×26); file name =
  cast `name`; player theme `asciinema`. **Cast theme — fixed dark for launch
  (DECIDED 2026-06-11).** Casts bake the app's dark theme into the recording as
  RGB SGR codes, so the player theme can't recolour them; true light/dark would
  require recording light-theme cast variants and swapping via the `Demo` seam.
  Deferred as not worth the doubled asset/recording cost for launch; revisit
  with the eventual full re-record.

## Formatting  [DECIDED, refine]

- Starlight asides for notes/tips/cautions; tables for reference matrices
  (types, constraints, referential actions).
- Keyboard keys rendered consistently (e.g. <kbd>Ctrl</kbd>+<kbd>Z</kbd>).
- Cross-link related pages; never expose ADR numbers or internal jargon to
  the reader (ADRs are for us, not the docs audience).

---

## Open decisions log

Decide these as we write; record the outcome (and escalate to an ADR if
significant).

**Resolved (2026-06-10):**
9. ~~Cast scripting toolchain~~ → **`autocast`**, proven by spike over
   `asciinema-automation` (which can't drive a full-screen TUI). Source
   command-lists in `casts-src/`, `pnpm casts` to record. See the *asciinema
   casts* section above. (Remaining sub-item: light/dark player theme sync —
   folded into #8 / Phase B.)

**Resolved (2026-06-05):**
1. ~~Depth / organising spine~~ → **Pragmatic** four-section split (see
   Structure above).
2. ~~Page granularity~~ → **one page per topic / command-family**, both
   modes per page (see Structure).
3. ~~Standard example dataset~~ → **the library** (schema above).
4. ~~Simple-vs-advanced pairing~~ → **show both where both apply** (see
   Examples).
5. ~~"Planned" callout~~ → standard `:::caution[Planned]` aside (see above).
6. ~~Reference generation vs hand-writing~~ → **hand-write now** (command
   surface is settled bar H1a output; small later adjustments expected).

**Resolved (2026-06-11):**
7. ~~Versioning~~ → **single-version for launch.** No version selector; one can
   be added at/after v1 if the docs ever need to diverge by version.
   Cast light/dark → **fixed dark for launch** (see *asciinema casts* above).
   `site` set to **`https://relplay.org`** (apex) — enables sitemap, canonical,
   and Open Graph URL resolution; a `www` host, if used, 301-redirects to apex.

**Still open:**
8. **SEO/meta — remaining.** Per-page titles/descriptions are set; canonical +
   basic Open Graph (`og:title`/`og:url`) now auto-generate from `site`. What
   remains: **Open Graph social-card images** (`og:image`) — needs a card
   design, so it travels with the branding work (logo + palette, TBD with the
   user; the existing favicon is *not* a brand guideline).
