ascribe.toml format contract: what pages hold

The normative contract for the content model’s note types, phrases, glossary, images, and widgets.

For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.

Part of the ascribe.toml format contract, whose contents list every section. What these keys are for, with examples, is in the reference’s what pages hold.

6. [notes.<type>]

Note types (SPEC §4.5), the values of @note’s type attribute. Five are built in: note, tip, important, warning, and caution. A project adds types by declaring them, and can relabel a built-in type. Built-in types can’t be removed.

ascribe 0.4.4 on Linux
[notes.security]
label = "Security"

[notes.tip]
label = "Pro tip"

<type> is the note type (key rule).

Key Type Default Description
label string built-in types: Note, Tip, Important, Warning, Caution; new types: required The display label, used where a note’s type is shown as text, such as the plain-markdown output’s **Tip: …** (SPEC §9.4).

Under [notes], the inline form security = { label = "Security" } is equivalent.

Rules (§25.4): a new note type MUST set label (model-note-label-required).


7. [phrases]

The phrases registry (SPEC §5.1).

ascribe 0.4.4 on Linux
[phrases]
product = "Quill"
cloud = "Quill Cloud"
version = "3.4.1"
api = "https://api.quill.dev/v3/"
support-email = "support@quill.dev"
# The current line of [releases.self-managed]: "3.5".
release = { line = "self-managed" }

Every key is a phrase key (key rule, the same rule as SPEC Appendix A’s phrase), and every value is a TOML string (model-phrase-value-type). version = 3.4 is a float and an error; that’s the kind of silent type change TOML exists to prevent (SPEC §7.1). Values are inserted as literal text (SPEC §5.1).

A target’s line. A value can instead be a table with one key, line, naming a target whose lines [releases.<target>] (§12.1) lists: release = { line = "self-managed" }. The phrase is then a line as the list writes it: in the output of one line of a build the target versions ([builds.<name>] versions), that line, and everywhere else the target’s current line, its newest live one. So it changes when a line moves to live, and has no second home to keep. The target needs a live line (model-phrase-line), and the table takes no other key (model-unknown-key).

Phrases in frontmatter. SPEC §5.1 lets the content model decide which frontmatter fields accept phrases. A field accepts them when its table form sets phrases = true (§5.2):

[types.guide.frontmatter]
title = { type = "string", phrases = true }

Only string and list(string) fields can accept phrases (model-phrases-field-type). By default, no field does. The setting lives on the field, not in [phrases], so that a phrase key can never collide with a setting’s name.

Rules (§25.5): a phrase’s value MUST be a TOML string (model-phrase-value-type), or a table whose one key, line, names a target with a live line in [releases] (model-phrase-line). A field’s phrases = true MUST be on a string or list(string) field (model-phrases-field-type).


8. [glossary]

The glossary (SPEC §5.4): the pages that hold its terms, and how occurrences are matched. A term is a heading on a glossary page, marked @term, and its definition is the paragraph under it. Authors don’t mark terms where they’re used; processors link them (SPEC §9.2 step 7).

ascribe 0.4.4 on Linux
[glossary]
match = "first"
case-sensitive = false
pages = ["reference/glossary.md"]

8.1 Settings

Key Type Default Description
pages array of strings (paths) [] The glossary pages, each a path from the content root, written as in a link (SPEC §5.2; a leading / is allowed and means the same). Each MUST be a page, not a fragment. A term is linked to the first of its pages that a build publishes.
match string "first" Which occurrences are linked: "first", the first occurrence of each term on each page, or "every", every occurrence. “Page” means the resolved page for a build, after includes and build modes. A term can override it.
case-sensitive boolean false Whether occurrences must match a term’s case exactly. When false, matching ignores case. A term can override it.

There’s no terms table: a project that has one gets an error saying to write each term on a glossary page.

8.2 Terms

Each heading on a glossary page with @term directly under it, written on the page or in a fragment it includes, is a term:

## Rollout
@id: rollout
@term {aliases=rollouts}

A plan for turning a flag on for a growing share of users: by percentage, by segment, or on a schedule.
  • The term is the heading’s text, and its id is the heading’s id, which the site output’s data-ascribe-term names. An @id keeps the id when the heading’s text changes.
  • The definition is the paragraph that opens the section, after the heading’s directives, as plain text, with the build’s phrases. A paragraph a fragment brings in counts, and a section that opens with a @variant group has the paragraph each arm opens with, in the builds that keep the arm. The site output shows it as the link’s title, and the editor on hover; the JSON output lists it with each use. Keep it to a sentence or two, and put the rest in a second paragraph.
  • A term follows its section in each build. A build that removes the section, by @available or by a variant, has no such term, and a build that removes only its paragraph leaves it without a definition: either way, the build doesn’t link it.
  • @term’s attributes override the glossary’s settings for the term:
Attribute Type Default Description
aliases string none Other forms that count as occurrences, such as plurals, separated by |: aliases="API keys|keys".
case-sensitive boolean the [glossary] setting For terms like Go that collide with ordinary words.
match first, every, or marked the [glossary] setting A marked term isn’t linked automatically at all, for words like build that also have ordinary senses: link the occurrences that mean the term to its heading, as [build](glossary.md#build). marked is a term setting only; [glossary] doesn’t accept it.

An unused term (glossary-term-unused) is acknowledged with @intended among the directives under its heading.

A link to a term’s heading is a use of the term, wherever it’s written: it gets the term’s marker and its definition as its title, as an automatic link does, and it counts as a use. For a term with match = "first", it’s the page’s first link to the term, so no later occurrence is linked. A link to the glossary page, or to another heading in the term’s section, is an ordinary link.

Matching. A term MUST match only whole words, and where two terms overlap, only the longer (API key, not API). Only prose MUST be matched: never a heading, link text, code, raw HTML, or a directive’s primary identifier. A term MUST NOT be linked on a glossary page that shows it, nor in a build that doesn’t publish its heading or gives it no definition. SPEC §5.4 gives the rest: what a term is, its definition, and what an author’s link to its heading is.

Rules (§25.5): each path in pages MUST be a page under the content root, not a fragment, with its name’s case as on disk (model-glossary-page). Terms are read from pages, so the rest are checked with the project, not by the loader: two terms or aliases MUST NOT have the same text, compared ignoring case unless both terms are case-sensitive, nor two terms the same id, and the second of either isn’t a term (glossary-term-duplicate); a term’s section MUST open with a paragraph, its definition (glossary-definition-missing), of at most 300 characters in a build, or it’s advice (glossary-definition-long); and @term is a term only on a page pages lists, or in a fragment such a page includes (term-off-glossary-page), whose fix MUST NOT be applied by ascribe check --fix.


9. [images]

Image attributes (SPEC §5.3): which keys may appear in an attribute block after an image, and their types.

ascribe 0.4.4 on Linux
[images.attributes]
width = "number?"
height = "number?"
loading = { type = "enum(lazy, eager)", default = "lazy" }
theme = "set(enum(light, dark))?"
Key Type Default Description
attributes table of attribute types {} Accepted image attribute keys (key rule) and their types (§5). With no entries, an image accepts no attributes, and any attribute block after an image is an error (SPEC §8.2, “Unknown key”). Declaration order is canonical order (SPEC §8.3). A declared default reaches the output: in the site output, every image carries the defaults of attributes it doesn’t write.

Alt text and titles aren’t attributes; they use CommonMark’s syntax (SPEC §5.3). Presentation choices aren’t image attributes either. Keys HTML already uses on <img> (src, alt, title, and its global attributes) are rejected (model-attribute-reserved), since the site output writes image attributes onto the <img> element (SPEC §7.2).


10. [widgets.<name>]

Project widgets (SPEC §6): directives a documentation set defines. Each declaration is a directive schema with the same parts a built-in directive’s schema has (SPEC §3, §4).

ascribe 0.4.4 on Linux
[widgets.quill-labspace]
description = "An embedded, runnable Quill lab."
forms = ["line"]
primary = "none"
binding = "self"
title = "none"
groupable = false
plain-fallback = "Try this in the Quill lab at labs.quill.dev."

[widgets.quill-labspace.attributes]
lab = "string"
height = "number?"

<name> is the widget’s name (SPEC A widget-name: lowercase, with at least one hyphen). Names starting with ascribe- are reserved for Ascribe’s element library, and the names HTML reserves for itself (annotation-xml, color-profile, font-face, font-face-src, font-face-uri, font-face-format, font-face-name, missing-glyph) aren’t allowed, because the site output emits a widget as a custom element with the widget’s name (SPEC §9.4).

Key Type Default Description
forms array of strings required The permitted forms (SPEC §3.5): ["line"], ["container"], or both.
primary string "none" The primary (SPEC §3.4): "none"; "identifier" or "text", which are required; or "identifier?" or "text?", which are optional.
binding string required when forms includes "line"; not allowed otherwise What the line form applies to (SPEC §3.8), one of the values below.
title string "none" Whether the widget takes a title line (SPEC §3.7): "none", "accepted", or "required".
groupable boolean false Whether a run of the widget’s openers forms a group of arms (SPEC §3.6).
attributes table of attribute types {} The attribute schema: keys (key rule) and types (§5). Declaration order is canonical order (SPEC §8.3). The keys heading and primary, HTML’s global attributes (including title), aria- keys, and event-handler attributes such as onclick, are rejected (model-attribute-reserved): the site output writes these attributes onto the widget’s element (SPEC §7.2).
plain-fallback string none Plain-text fallback for the plain-markdown output (SPEC §6, §9.4): CommonMark text written in place of the widget. Phrases in it are substituted. It isn’t a template: attribute values aren’t inserted. Without it, the widget itself emits nothing.
plain-content string "keep" For a widget that wraps content, whether the plain-markdown output keeps that content ("keep") after the fallback, or drops it ("drop"). Allowed only when the widget wraps content: it has container form, or its binding is block or heading-or-block.
description string none Help text for the editor’s hover and completion.

Binding values:

Value SPEC §3.8 binding Behaves like
"self" Self: its own primary, or nothing @include
"heading" Preceding heading: at the top of its section @id
"block" Following block. If the primary is a text primary and it’s given, the primary is the content and nothing else is bound @steps; with primary = "text?", @note
"heading-or-block" At the top of a section, the section; anywhere else, the following block @available

Rules (§25.6): a widget with container form MUST NOT have a required primary, and a container-only widget’s primary MUST be "none" (model-widget-container-primary). A groupable widget MUST be container-only (model-widget-groupable-form). binding MUST be set with line form, and only then (model-widget-binding). The element a widget becomes in the site output, its tag name and attributes, is the element contract’s.