ascribe.toml: how content varies
The content model’s dimensions, versions and releases, lifecycle states, and features: what content varies along, and how a page says where it applies.
For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.
Part of the ascribe.toml reference, whose contents list every section. The rules for a loader are in the contract’s how content varies.
11. [dimensions.<name>]
A dimension is an axis content varies along (SPEC §4.3), and whose values are availability targets (SPEC §4.4).
[dimensions.deployment]
label = "Deployment"
values = ["cloud", "self-managed"]
versionless = ["cloud"]
labels = { cloud = "Quill Cloud", self-managed = "Self-managed" }<name> is the dimension’s name (key rule, §1.2). It’s written as an attribute key in @variant, as a key in variant frontmatter and build selections, and as a target in availability specs, where it stands for all its values.
| Key | Type | Default | Description |
|---|---|---|---|
values |
array of strings | required | The dimension’s values (name-word), in display order. At least one; no duplicates. |
labels |
table of strings | {} |
Display labels for values, keyed by value (SPEC §9.4: “Labels for dimension values come from the content model’s display labels”). A value without a label is displayed as the value itself. Every key MUST be a declared value. |
label |
string | the dimension’s name | The display label for the dimension itself, used where a dimension name appears as an availability target or names a tab group. |
versionless |
array of strings | [] |
Values that are versionless (SPEC §4.4): availability for them takes a single state and no versions. Every entry MUST be a declared value. Values not listed are versioned. |
A target [releases.<target>] names, when no dimension has it, is a value of the implicit dimension release (§12.1).
Rules. A value can be in one dimension only (model-dimension-value-shared), since a bare target such as cloud in an availability spec has to mean one thing. Dimension names and values also take part in the one-role rule: a name can be a dimension name, a dimension value, a lifecycle state, or a feature key, but only one of them (model-name-multiple-roles), and none of them can be planned, the version word for a target’s planned line.
12. [versions]
How versions in availability specs are compared (SPEC §4.4). Which release is current is a target’s own, in [releases.<target>] (§12.1).
[versions]
scheme = "numeric"| Key | Type | Default | Description |
|---|---|---|---|
scheme |
string | "numeric" |
The version scheme. Spec 0.1 defines one: "numeric". |
The numeric scheme. A version is any string matching SPEC Appendix A’s version rule: numbers separated by dots (3, 3.4, 3.4.1). Versions compare component by component, numerically, from the left, with missing trailing components treated as 0: 3.4 equals 3.4.0, 3.10 is later than 3.9, and 4 is later than 3.99.1. Leading zeros don’t matter (3.04 equals 3.4). This is semantic versioning’s major.minor.patch ordering. It has no pre-release or build suffixes, because the spec’s grammar doesn’t allow them; express a pre-release with a lifecycle state (preview 3.4) instead.
No other scheme is defined. The table exists so a later spec version can add one without changing the file’s shape.
Rules. scheme is one of the schemes (model-invalid-value). current isn’t a key: a target’s current release is its newest live line in [releases.<target>], and a project that sets it gets an error that says so (model-versions-current).
12.1 [releases.<target>]
The release lines of a versioned target, by the target’s name. A line is a version as the list writes it, and holds every version that starts with its parts: 3.4 holds 3.4, 3.4.2, and 3.4.2.1. A project chooses how fine its lines are by how it writes them: ["3.4", "3.5"] for a line for each minor release, ["0.2.0"] for one for each patch release. The newest live line is the target’s current line; it isn’t written down, so it can’t disagree with the list.
[releases]
planned-label = "planned"
[releases.self-managed]
live = ["3.4", "3.5"]
planned = ["3.6"]
[releases.self-managed.ends]
"3.4" = 2027-01-31
[releases.self-managed.archived]
"3.3" = { url = "https://docs.example.com/archive/3.3/", ended = 2026-06-30 }
"3.2" = {}The target is a versioned value of a declared dimension, such as self-managed, or a name no dimension has. [releases.<target>] declares a name no dimension has as a value of the implicit dimension release, which exists only while it has a value. [releases] label is the dimension’s label, Release unless set, and [releases.<target>] label is the target’s, its name unless set:
[releases.ascribe]
label = "Ascribe"
live = ["0.4.1"]
Everything that names a dimension names release as it names a declared one: @variant {release=ascribe}, a switcher’s sync, a build’s variants, dimension: targets in ascribe refs, and the elements’ dimension attribute. It comes after the declared dimensions in their order.
| Key | Type | Default | Description |
|---|---|---|---|
live |
array of strings (versions) | [] |
The lines a reader can be on, in any order. A build versioned by the target gives one output for each. |
planned |
array of strings (versions) | [] |
Lines that haven’t shipped, each newer than every live line. A version in one is planned: its availability reads Self-managed (planned, 3.6) in every output, and stops being planned when its line moves to live, with no other edit. |
ends |
table of dates | none | The last day each live line is supported, by line: "3.4" = 2027-01-31, a TOML date, unquoted. |
archived |
table of tables | none | Lines that have left the window, each older than every live line, by line. Each takes url, where the line can still be read, and ended, the day its support ended, both optional: "3.2" = {} declares a line that’s readable nowhere. |
label |
string | the target’s name | The target’s display label, as [dimensions.<name>] labels gives a value’s, when the target is a value of the implicit release dimension: label = "Ascribe". A target a declared dimension has takes its label from that dimension’s labels instead. |
[releases] itself takes two keys beside the targets’ tables:
| Key | Type | Default | Description |
|---|---|---|---|
label |
string | "Release" |
The display label of the implicit release dimension, as [dimensions.<name>] label gives a declared dimension’s. The implicit dimension holds each target no declared dimension has, so [releases.ascribe] alone makes ascribe a value of release. |
planned-label |
string | "planned" |
The word a reader sees for a planned version, in place of its state: Self-managed (planned, 3.6). |
Rules. <target> is a declared dimension value that isn’t versionless, or a name (name-word) no dimension has, which makes it a value of the implicit dimension release. While a target is left to the implicit dimension, release is reserved: a declared [dimensions.release] is an error at each such target, naming both. A target a declared dimension has takes no label, since that dimension’s labels labels it (model-releases-target). The implicit dimension’s name and values take part in the one-role rule, as a declared dimension’s do (model-name-multiple-roles).
Each line is a version, quoted, listed once, and every line of one target, in every list, has the same number of parts (model-releases-line). A line is in one list, planned lines are newer than every live line, and archived lines older (model-releases-order). A line in ends is a live line (model-releases-ends), and a date is a TOML date (model-wrong-type). Any other key is an error (model-unknown-key).
What the list checks. For a target with a list, each version a mark, a feature, or a build’s filter names is placed among the declared lines:
| The version is | Reported |
|---|---|
| In a declared line | Nothing |
| Newer than every declared line | An error, available-version-ahead, with the newest line offered as a fix |
| In a line between two declared ones that isn’t declared | Advice, available-line-undeclared: declare the line, in archived with no address when nothing publishes it, or correct the number, with the declared lines either side offered as fixes |
| Older than every declared line | Nothing: it’s before the window, and a project doesn’t declare every line it ever shipped |
A mark’s planned is the target’s one planned line, so it’s placed as that line’s version; a target with no planned line, or more than one, makes it an error at the word, available-planned-line. ascribe versions release rewrites it to the version when the line ships.
A mark that does nothing. A mark does nothing only where no output shows it: when every build that publishes its content is versioned by the mark’s target or filtered for it, since a line’s output leaves out a version at or before the oldest live line. Then two content checks report a mark:
| Check | Reports |
|---|---|
available-same-in-every-line (advice) |
A version every live line has, so the mark can drop it and keep its state |
available-in-no-line (a warning) |
Content removed before the oldest live line with nothing after, which no output has |
A badge. A build that badges availability and isn’t versioned, such as one site, shows every version. So there:
ascribe 0.1.1still reads “since 0.1.1”, and content in no line is still published, soavailable-in-no-linestays quiet.- When every version a mark names is before the oldest live line and every live line has the content, though, that badge says only that the content is older than any reader’s line, so
available-same-in-every-linereports it too, saying the badge is shown, with an unsafe fix, since removing the versions removes the badge. - A project that keeps such badges, as “new in 0.2”, sets
available-same-in-every-line = "off"in[checks].
When the fix removes the mark. When the mark would then be its target alone, generally available, and the target is the only value of its dimension, the mark says nothing even as a badge (“Available: Ascribe (GA)” in a project with one product), so the fix removes the mark instead of rewriting it: the @available line, the page’s available key, or the row’s {available=…}. It does so only when the mark names nothing else and no build filters for another target, since content with no mark applies everywhere, and there that’s the one target. Content with no mark reads as the scope around it, though, so the fix rewrites the mark instead of removing it when it’s:
- Inside a mark that says something, such as a section marked
ascribe preview: the mark keeps its content generally available. - In a fragment, which reads as wherever it’s included.
What neither check reports.
- A version in the oldest live line still says what came with it, so
ascribe 0.4.0with 0.4.0 the only live line isn’t reported there. - A build filtered for the target at a version before the oldest live line still decides by the mark’s versions, so neither check reports a mark while one publishes it.
availability-left-behind compares a feature’s versions with each target’s current line, and a live line whose ends date has passed is advice, release-line-ended, as of the day the checks run as.
13. [lifecycle.<state>]
Lifecycle states (SPEC §4.4). Five are built in:
| State | Counts as available | Default label |
|---|---|---|
preview |
yes | preview |
beta |
yes | beta |
ga |
yes | GA |
deprecated |
yes | deprecated |
removed |
no | removed |
A project adds states by declaring them, and can change the available flag or label of a built-in state. Built-in states can’t be removed.
[lifecycle.sunset]
available = false
label = "sunset"
[lifecycle.beta]
label = "Beta"<state> is the state’s name (name-word).
| Key | Type | Default | Description |
|---|---|---|---|
available |
boolean | built-in states: as in the table above; new states: required | Whether content in this state counts as available (SPEC §4.4, §9.3). A new state must say so explicitly. |
label |
string | built-in states: as in the table above; new states: the state’s name | The display label used in availability annotations, such as “Available: Quill Cloud (GA); self-managed (preview, 3.4+)” (SPEC §9.4). |
Rules. ga always counts as available (model-lifecycle-ga-unavailable): content with no state is ga (SPEC §4.4), so otherwise unmarked content would be unavailable. A state you add says whether it counts as available (model-lifecycle-available-required). The built-in states take part in the one-role rule too: a dimension value named beta is an error.
14. [features.<key>]
The features registry (SPEC §4.4): named availability specs, so a feature going generally available takes one edit.
[features.streaming-sync]
name = "Streaming sync"
available = "cloud, self-managed preview 3.4"<key> is the feature key (name-word). Writing it as a whole @available primary, or as the whole available frontmatter value, stands for the spec.
| Key | Type | Default | Description |
|---|---|---|---|
name |
string | required | The feature’s display name, shown on hover and available to emitters. |
available |
string (availability spec) | required | The feature’s availability, in SPEC §4.4’s syntax. |
Rules. A feature’s spec is checked as it would be in a page: it has to parse (model-availability-syntax), name declared targets and states (model-availability-unknown-name), give no versions to a versionless target or a dimension name (model-availability-versionless), and list a history oldest first (model-availability-history-order). It can’t be another feature’s key (model-feature-nested), so features never refer to each other, and feature keys take part in the one-role rule. It names its versions: planned is for a page’s mark, and in a feature’s spec it’s model-availability-syntax.