> For the complete documentation index, see [llms.txt](https://ascribed-dev.com/docs/llms.txt). This documentation describes Ascribe 0.4.4. Anything marked in main, not yet released isn't in that release yet.

# `ascribe.toml`: how content varies

> Reference · The content model's dimensions, versions and releases, lifecycle states, and features: what content varies along, and how a page says where it applies.

Part of the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model.md), whose [contents](https://ascribed-dev.com/docs/content-model.md#contents) list every section. The rules for a loader are in the contract's [how content varies](https://ascribed-dev.com/docs/contracts/content-model/variation.md).

## 11\. `[dimensions.<name>]`

A **[dimension](https://ascribed-dev.com/docs/glossary.md#dimension)** is an axis content varies along ([SPEC §4.3](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#43-variant)), and whose values are [availability](https://ascribed-dev.com/docs/glossary.md#availability) targets ([SPEC §4.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)).

```toml
[dimensions.deployment]
label = "Deployment"
values = ["cloud", "self-managed"]
versionless = ["cloud"]
labels = { cloud = "Quill Cloud", self-managed = "Self-managed" }
```

Tested with ascribe 0.4.4 on Linux.

`<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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#94-outputs): "Labels for dimension values come from the [content model](https://ascribed-dev.com/docs/glossary.md#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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)): 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](https://ascribed-dev.com/docs/content-model/variation.md#121-releasestarget)).

**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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)). Which release is current is a target's own, in `[releases.<target>]` (§12.1).

```toml
[versions]
scheme = "numeric"
```

Tested with ascribe 0.4.4 on Linux.

| 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>]`](https://ascribed-dev.com/docs/content-model/variation.md#121-releasestarget), and a project that sets it gets an error that says so (`model-versions-current`).

### 12\.1 `[releases.<target>]`

The [release lines](https://ascribed-dev.com/docs/glossary.md#release-line) 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.

```toml
[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" = {}
```

Tested with ascribe 0.4.4 on Linux.

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:

```toml
[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](https://ascribed-dev.com/docs/content-model/output.md#16-buildsname) 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`](https://ascribed-dev.com/docs/writing/directives.md#the-planned-line) 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`](https://ascribed-dev.com/docs/versions/release.md#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.1` still reads "since 0.1.1", and content in no line is still published, so `available-in-no-line` stays 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-line` reports 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](https://ascribed-dev.com/docs/glossary.md#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.0` with 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`](https://ascribed-dev.com/docs/checks/diagnostics/content-checks.md) 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](https://ascribed-dev.com/docs/checks/check.md#checking-as-of-another-day).

---

## 13\. `[lifecycle.<state>]`

Lifecycle states ([SPEC §4.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)). 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.

```toml
[lifecycle.sunset]
available = false
label = "sunset"

[lifecycle.beta]
label = "Beta"
```

Tested with ascribe 0.4.4 on Linux.

`<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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available), §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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#94-outputs)). |

**Rules.** `ga` always counts as available (`model-lifecycle-ga-unavailable`): content with no state is `ga` ([SPEC §4.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)), 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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)): named availability specs, so a feature going generally available takes one edit.

```toml
[features.streaming-sync]
name = "Streaming sync"
available = "cloud, self-managed preview 3.4"
```

Tested with ascribe 0.4.4 on Linux.

`<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](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#44-available)'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`](https://ascribed-dev.com/docs/writing/directives.md#the-planned-line) is for a page's mark, and in a feature's spec it's `model-availability-syntax`.
