ascribe.toml reference

The content model: what pages are and hold, how content varies, where pages are published, what they draw from, and the checks.

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

ascribe.toml is a project’s content model: its schema (SPEC §7). It sits at the project root and declares the content types and their frontmatter, the dimensions content varies along, phrases, the glossary, project widgets, how the site output fits Astro, and the builds. The editor, ascribe check, and ascribe build all read it, so they can’t disagree about what’s valid.

A file containing only spec = "0.1" is valid; every section has a default (§24). Example files:

Every problem the loader reports is in the diagnostics reference, on Diagnostics: the content model and Diagnostics: the content model’s builds and site, with its fix. References to “SPEC” are to the Ascribe specification. The rules a loader enforces, stated for someone writing one, are in the ascribe.toml format contract, with the same section numbers and key tables.

In VS Code, Project Settings shows a project’s settings beside its ascribe.toml, each with its value, its default, and its line, and changes a value the file sets in place.

Contents

The sections are grouped by what they’re for, a page each.

What pages are, on this page:

  1. Conventions
  2. Top level: spec, and taking from another project
  3. [project]
  4. [types.<name>] and [fragments]
  5. Field and attribute types

What pages hold:

  1. [notes.<type>]
  2. [phrases]
  3. [glossary]
  4. [images]
  5. [widgets.<name>]

How content varies:

  1. [dimensions.<name>]
  2. [versions], and [releases.<target>]
  3. [lifecycle.<state>]
  4. [features.<key>]

Where pages are published:

  1. [consumer]
  2. [builds.<name>]
  3. [listing]
  4. [crawlers]
  5. [permalinks]

What pages draw from:

  1. [sources.<name>]
  2. [apis.<name>]

Checks and the editor:

  1. [checks]
  2. [editor]
  3. Defaults: what an absent section means
  4. Validation

1. Conventions

1.1 The file

  • The content model is a TOML 1.0 file named ascribe.toml. The directory that contains it is the project root. Every path in the file is relative to the project root unless a key says otherwise.

  • The file is UTF-8.

  • TOML’s equivalent spellings are all accepted: a table can be written as a [header] section, as an inline table (key = { … }), or with dotted keys. This reference shows the most readable form for each section. For example, these are the same:

    [dimensions.pm]
    values = ["npm", "pnpm", "yarn"]
    
    # same as
    dimensions.pm.values = ["npm", "pnpm", "yarn"]
  • Keys are kebab-case (content-root, trailing-slash), matching Ascribe’s attribute keys. TOML allows hyphens in bare keys, so they need no quotes.

  • Unknown keys are errors, everywhere, with a did-you-mean suggestion. A misspelled key is otherwise a silently ignored setting. The exceptions are the tables whose keys are names the project chooses ([phrases], [types], [dimensions], and so on); their keys are validated as names instead.

  • Declaration order is kept where it matters. Attribute keys keep the order they’re declared in ([images.attributes], [widgets.<name>.attributes]), because canonical form writes attributes in declared order (SPEC §8.3). The [dimensions] tables keep theirs too: it’s the canonical order of @variant attributes, and decides which dimension a tab group syncs on (the element contract, §3). Order is kept elsewhere for stable output (for example, the order of dimension values in a tab switcher is the order of values). Nothing else in the model depends on order.

1.2 Names

Several kinds of name appear in the file. Each has a grammar from SPEC Appendix A or from this reference:

Name Grammar Rule Used for
name-word SPEC A name-word A letter, then letters, digits, _, or - Dimension values, lifecycle states, feature keys, frontmatter field names
key SPEC A key A lowercase letter, then lowercase letters, digits, or - Dimension names, phrase keys, note types, attribute keys, content type names, source names
widget name SPEC A widget-name Lowercase words of letters and digits joined by single hyphens, with at least one hyphen; starting with a letter Widget names
build name This reference A letter, then letters, digits, _, -, or . Build names

Dimension names follow the stricter key rule, not just name-word, because they’re also written as attribute keys in @variant {deployment=cloud} (SPEC §3.3, §4.3).

Build names allow . so that names like self-managed-3.3 work. A name containing . must be quoted in a TOML header: [builds."self-managed-3.3"].

Names are case-sensitive and spelled exactly as declared (SPEC §4.4).

1.3 Paths and patterns

  • Paths use / as the separator on every platform.

  • Filesystem paths in [project] and [sources] are relative to the project root. They can’t be absolute, and they can use ...

  • A path in [glossary] pages (§8) is written like a link destination in a document (SPEC §5.2), relative to the content root.

  • Patterns (globs) are matched against a source file’s path relative to the content root, including its extension, with / separators. They are case-sensitive. The syntax:

    Syntax Matches
    * Any run of characters within one path segment, not including /
    ** Any number of whole segments, including none. It’s only ever a whole segment (a/**/b, a/**, **/b)
    ? Any one character except /
    {a,b} Either alternative. Alternatives don’t nest
    \ Escapes the next character

    Every other character matches itself. A pattern doesn’t start with /, and has no .. segment. Patterns only ever apply to Markdown source files (*.md); other files under the content root are assets. The exception is a source’s include and ignore (§20), which are matched against any file’s path relative to the source’s folder.

1.4 How this reference describes keys

Each section has a table of keys with these columns:

  • Type: the TOML type. “Field type”, “attribute type”, and “availability spec” are strings with the grammars in §5 and SPEC §4.4.
  • Default: the value when the key is absent, or required.
  • Description.

2. Top level: spec

spec = "0.1"
Key Type Default Description
spec string required The version of the Ascribe specification this project targets (SPEC §11). It MUST be quoted: spec = 0.1 is a TOML float and is an error. A processor accepts only the spec versions it implements, compared as exact strings; this reference defines "0.1".

The only other top-level keys are [from] (§2.1) and the tables in §3–§23, [releases] (§12.1) and [[intended]] (§22.1) among them. Anything else is an unknown key.

2.1 Taking from another project

Two projects beside each other, such as a product’s docs and its landing site, often share facts: the releases, the site’s address, a phrase or two. One of them writes each, and the other takes it with [from], so it’s written once, and a change to it reaches both. Ascribe’s own landing site takes its releases, its address, and the source its examples come from from the docs’ ascribe.toml.

[from."../../docs/ascribe.toml"]
take = ["releases", "dimensions.pm", "phrases.version", "consumer.site"]

The key after from is the other file’s path, relative to the folder ascribe.toml is in. A project can take from more than one file, each a table of its own.

Key Type Default Description
take array of strings required What’s taken from the file, each a table or a key as a dotted path: releases takes the whole table, consumer.site one key of [consumer]. It’s read as if written here, and can’t be written here too.

What’s taken is read from the other file as if written in this one, and every rule of this reference holds it there. A source’s path is the one exception: it’s relative to the folder of the file it’s written in, so a taken source is the same folder from either project. The other file’s own [from] isn’t followed: what’s taken is always written in the file named.

spec, [project], and [from] itself can’t be taken (model-from-take): they’re what makes a project its own. Name each table or key once, and not a table and a key in it. Nothing taken can also be written here (model-from-written): to change a taken value for this project, remove it from take and write it here.

Rules. The file must be there and be TOML (model-from-file), inside the git repository the project is in, as a source’s folder must, and what take names must be in it (model-from-take). With no repository, the editor and ascribe mcp read it only inside the folder they were opened on.

Editing. A taken key is changed in the file that writes it, wherever the edit starts:

  • The editor’s actions, Project Settings, and a rename write a taken key in the other file, at the same path, and say which file they changed; renaming a key take names, such as a phrase, renames it in take too. A rename of a phrase or a value the other file writes is made in that project’s pages as well, so both projects stay as they were with the new name; when it can’t be made there, as when that project has the new name already, the rename is refused, naming the file.
  • ascribe versions release and ascribe versions retire run in a project that takes the releases are made in the project that writes them: its ascribe.toml and its pages’ marks change, and the text says so. One release moves the lines of every project that takes them.
  • ascribe templates eject and ascribe vale eject refuse a taken table, naming its file, since the files they write beside this ascribe.toml couldn’t be named from there; run them in the other project, or stop taking the table and eject then.
  • In the editor, the hover on a name in take says where it’s written, Go to Definition opens it there, and a change to the file reaches the project at once.

3. [project]

Where the source lives and where builds write.

ascribe 0.4.4 on Linux
[project]
content-root = "docs"
output-dir = ".ascribe/build"
Key Type Default Description
content-root string (path) "docs" The content root (SPEC §2.2): the directory holding every source file. Paths in links and includes that begin with / are relative to it. It must exist and be a directory.
output-dir string (path) ".ascribe/build" Where ascribe build writes output. Each build and emitter writes under <output-dir>/<build>/<emitter>/. It need not exist. It may be outside the project’s folder, with .., and a build then writes there. A build never writes or removes through a symbolic link on the way to it, or in it (output-layout contract).

Rules: both paths are relative to the project root (model-path-absolute). Neither folder can be inside the other, and they can’t be one folder (model-output-overlaps-content): a build would read its own output as source, or delete source as stale output. Ascribe tells them apart after resolving ., .., and, for folders that are there, symbolic links, so a link can’t hide one inside the other.

The defaults: ".ascribe/build" keeps generated output out of the way of both the source and a consumer’s own dist/.


4. [types.<name>] and [fragments]

4.1 Page types

A content type is a frontmatter schema for a kind of page. A page names its type in its frontmatter, with type.

[frontmatter]
description = "string?"

[types.guide]

[types.reference.frontmatter]
api-version = "string"

<name> is the type’s name (key rule). It appears in diagnostics, in generated code (the Zod schema’s name), and on each page of the type, as the value of type.

Frontmatter every type shares. [frontmatter] declares the fields every page type has, written as a type’s own are, so a field every page has is written once. A type’s own frontmatter adds fields to them. A type that declares a field [frontmatter] has replaces it whole, for that type: an index page’s type can make a shared description optional, or give it a default. The two aren’t merged key by key, so a field reads as one declaration, wherever it’s written. The implicit page type of a project that declares no types has them too.

Where a field is declared. In VS Code, hover a field’s key in ascribe.toml, or a frontmatter key in a page, to see whether the field is [frontmatter]’s, the type’s own, or the title every type has; on a field of [frontmatter], the hover names the types that replace it. ascribe model --section types says the same after each field’s type, and its JSON in each field’s declared: frontmatter, type, or implicit. A type’s field that’s the same as [frontmatter]’s, key for key, changes nothing, and is reported as advice at the type’s field (frontmatter-field-repeated), with a fix that deletes it; written in another form, as { type = "string" } for "string", it’s still the same field.

Key Type Default Description
frontmatter table of fields {} (no fields) The fields every page type has, as a type’s own frontmatter declares them (below), so a field every page has is written once. A type’s own fields add to them, and one of the same name replaces the shared field whole, for that type. The implicit page type of a project with no types has them too.

Every page type has a required string title, whether it’s declared or not (below), so a type with nothing else of its own needs no keys: [types.guide] above is a type a page can name, with title and the shared fields.

Key Type Default Description
frontmatter table of fields {} (no fields of its own) The type’s own frontmatter schema: one entry per field, keyed by field name (name-word). Values are field types (§5). Every type has a required string title, declared or not (below). A type can’t declare a field type, which is reserved (model-field-reserved).
purpose string none What a page of the type is for, in a few words. The rules ascribe agents sync writes say it on the type’s line, and ascribe model shows it. A type copied from the starter set says its summary there without one (below).
template string (path) none The page a page of the type starts from: a file from the folder ascribe.toml is in, outside the content root (below). A type with no template and no guide takes both from templates/templates.lock, when the lock ascribe templates eject wrote records the type.
guide array of paths [] What to read before writing a page of the type, in the order to read it, such as a guide and a project’s own notes on it: files from the folder ascribe.toml is in, outside the content root (below). Without it or template, from templates/templates.lock, as template is.

Which type a page is. A page says what type it is with type in its frontmatter, the first key by convention, wherever its file is: a folder can hold pages of every type.

---
type: reference
title: Commands
api-version: "3"
---
  • A project that declares no types has one, the implicit page, whose fields are a required string title and [frontmatter]’s. Its pages need no type, and a page that has one is an error (content-type-unknown), with a fix that removes it.
  • A project that declares types has no implicit one. A page that names no type is an error on the page (content-type-missing), with a fix for each declared type that adds type: to the page’s frontmatter. A page whose type names a type that isn’t declared is an error at the value (content-type-unknown), with a fix to the closest name when one is close.
  • Fragments have no type (§4.3), and can’t use type (frontmatter-reserved-in-fragment).

A type’s table has no keys that say which pages it applies to: files and default are unknown keys (model-unknown-key), whose message says to name the type on its pages instead.

A type’s template and guide. Both are paths from the folder ascribe.toml is in, and both name files that aren’t pages:

[types.how-to]
template = "templates/how-to.md"
guide = ["templates/how-to.guide.md", "templates/how-to.ascribe.md"]

Each file must be there, with its name’s case as it is on disk, inside the folder ascribe.toml is in, and outside the content root, where it would be read as a page, and the output directory, which a build writes and removes. A symbolic link is followed to where it leads (model-type-file). guide is always a list, even with one file in it (model-wrong-type). The template is read when the content model loads, and must read as a page: valid YAML between its --- lines, and no error in its Ascribe syntax (model-type-template). Either is a problem with the content model, reported at the path, once, rather than on every page made from the template.

A template from the lock. A type with neither key takes its template and guide from templates/templates.lock, the file ascribe templates eject writes, when the lock records a template under the type’s name: its template, then their guide and Ascribe’s notes on the type, in the order to read them. That’s how a type eject added or kept has its template, with nothing about it in ascribe.toml. A type that names either key keeps what it names and takes nothing from the lock, so a project without one, or a template written by hand, names its files as above.

The files the lock gives are held to the same rules, and a problem with one is the type’s (model-type-template), reported at the type’s [types.<name>], since ascribe.toml has no path to point at. A lock that isn’t there, or isn’t one eject wrote, gives nothing; ascribe templates status says what’s wrong with it. In VS Code, the hover on a type’s name in ascribe.toml says where its template comes from.

What a type is for. purpose is one string, for a project’s own types, and for a team that wants to say what one of the starter set’s types means to it:

[types.how-to]
purpose = "One task, done step by step, for someone who knows why they're doing it."

The rules for agents say it at the end of the type’s line. A type ascribe templates eject added and that has no purpose says their index’s summary instead, from the templates.lock beside its template. When the rules don’t fit in their 4,000 characters, a type’s line keeps its name and what it’s for after the rest of it is cut, and the page types are the last thing cut, with ascribe model --section types named for the rest.

A page that still has its template’s text. On a page of a type with a template, ascribe check reports each of the template’s placeholders the page still has word for word, and a frontmatter field with the template’s value: as advice in a draft (template-placeholder-left), and as an error in a page that publishes (template-placeholder-published). The guide to page templates says what it finds and what it can’t.

A section the template leaves to the page. A section the template gives nothing under its heading but memos, and that a page of the type still leaves empty, builds as a heading with nothing under it; ascribe check reports it as advice at its heading (template-section-empty). The guide to page templates says what counts as empty.

The page title. Every page type has title, a required string field, whether it declares it or not. It’s the page’s title everywhere Ascribe needs one: an empty link to the page shows it as its text, for one (SPEC §5.2). Declaring it, in [frontmatter] or in a type’s own frontmatter, changes only how it’s read: it can accept phrases (§11), and code spans (§5.3). A declared title that’s optional, has a default, or isn’t a string is an error at the field (model-field-reserved).

Reserved keys. These are reserved frontmatter keys. Any page can use them, whatever its type says, and they mean what SPEC says; a type, or [frontmatter], that declares one is an error (model-field-reserved). Generated consumer schemas include them automatically, except the three that reach a site another way.

Key What it is In generated consumer schemas
type The page’s content type Included
available SPEC §4.4 Included
variant SPEC §4.3 Included
permalink SPEC §2.1, a page’s permalink Included
listing Listing a page, below Left out: it reaches a site in the page’s record (output-layout contract §1.3)
replaces SPEC §2.1, the page a page replaces Left out: it reaches a site in the versions file (output-layout contract §1.5)
nav-label SPEC §2.1, a page’s name in the sidebar and the previous and next links Left out: it reaches a site in the navigation (output-layout contract §1.6)

intended (SPEC §4.9) is reserved too, and accepted by every page and every fragment: it acknowledges a check’s problem, and since no output has it, generated consumer schemas leave it out. With the astro profile, a type can’t declare slug either (model-field-reserved): Astro would take a page’s slug as its entry id instead of its path, and publish the page at an address Ascribe never worked out.

formatted is reserved on page types too: the site output writes the formatted form of fields that set inline under it (§5.3), so a type must not declare it (model-field-reserved).

source is reserved on page types too: the site output writes each page’s path from the content root under it, for a layout’s edit link, so a type must not declare it (model-field-reserved).

Listing a page

A page says whether readers that aren’t people are offered it with listing (SPEC §2.1), one of three values, from open to closed: listed, the default when a page doesn’t set it, unquoted, and unlisted. What each tells search engines and agents is in Publishing. Any other value, or one that isn’t a string, is an error (frontmatter-type-mismatch).

---
title: Migrating from 3.x
listing: unlisted
---

Each build writes every page’s record, with its listing, to _ascribe/pages.json in the site output. The site output’s frontmatter doesn’t carry listing, since the record does.

A project can set listing for the whole site, a folder, or a content type, in [listing]. A page under a rule can set the same value or a stricter one, never a looser one (listing-loosened).

Unknown frontmatter keys. A page whose frontmatter has a key its type doesn’t declare (and that isn’t reserved) is an error on the page.

4.2 Frontmatter values

Frontmatter is YAML. For type checking, processors parse it with the YAML 1.2 core schema: true and false are booleans (yes, no, on, and off are strings), and 3.10 is the number 3.1. So a string field whose value is 3.10 unquoted is a type error, and the message suggests quoting it.

4.3 Fragments

ascribe 0.4.4 on Linux
[fragments]
patterns = ["includes/**", "**/*.partial.md"]

[fragments.frontmatter]
owner = "string?"
Key Type Default Description
patterns array of patterns [] Additional fragment patterns (SPEC §2.2). A file is a fragment if any segment of its path begins with _, or its path matches one of these patterns.
frontmatter table of fields {} (no fields) The fragment schema (SPEC §2.2): the frontmatter every fragment is validated against. Fields as in §5. title is not required.

Content types never apply to fragments: a fragment names no type, and type in a fragment’s frontmatter is an error (frontmatter-reserved-in-fragment).

Reserved keys on fragments. type, available, variant, permalink, listing, replaces, and nav-label are a page’s, so a fragment that has one in its frontmatter gets an error, and [fragments.frontmatter] can’t declare them (model-field-reserved). To mark what a fragment contributes, put @available inside it. A fragment can use intended, which [fragments.frontmatter] can’t declare either.


5. Field and attribute types

Two related type languages:

  • Field types describe frontmatter fields ([types.<name>.frontmatter], [fragments.frontmatter]). They cover the SPEC §7.2 set: string, number, boolean, date, enumeration, list, and object, each optional or with a default.
  • Attribute types describe attributes on images and widgets ([images.attributes], [widgets.<name>.attributes]). They cover SPEC §3.3’s set: string, enumeration, boolean, and number, plus set-valued keys.

Each entry is written either in short form, a string, or in table form, when it needs a default, a description, phrases, enumeration values that aren’t simple words, or nested fields.

[types.guide.frontmatter]
title = "string"                                   # required string
description = "string?"                            # optional string
level = "enum(beginner, intermediate, advanced)"   # required, one of three
tags = "list(string)?"                             # optional list of strings
updated = "date?"                                  # optional date
status = { type = "enum(draft, published)", default = "published" }
author = { type = "object?", fields = { name = "string", url = "string?" } }

5.1 Short form

A field is required unless its type ends in ? or it has a default. The same holds for attributes: lab = "string" means every use of the widget must give lab.

Short form Field types Attribute types Accepts
string yes yes Frontmatter: a YAML string. Attribute: a token or quoted string.
number yes yes Frontmatter: a YAML integer or float. Attribute: a token matching ["-"] 1*DIGIT ["." 1*DIGIT] (600, -2, 1.5; not 600px).
boolean yes yes Frontmatter: YAML true or false. Attribute: the token true or false (SPEC §3.3).
date yes no A calendar date written YYYY-MM-DD that exists (2026-02-30 is an error), quoted or not. Times aren’t accepted.
enum(a, b, …) yes yes One of the listed values, compared exactly. Frontmatter: a YAML string. Attribute: a token or quoted string.
list(T) yes no A YAML sequence whose items are all of type T, where T is string, number, boolean, date, enum(…), or (table form only) object. An empty sequence is allowed.
set(T) no yes A value set (SPEC §3.3), such as platform=cloud|on-prem, or a single token. T is string or enum(…). Members are tokens. Only keys typed set(…) accept |.
object table form only no A YAML mapping whose keys are the object’s fields (nested field types). Unknown keys are errors.
any of the above + ? yes yes The same type, optional.

Grammar (ABNF, with the rules of SPEC Appendix A):

field-type      = field-base [ "?" ]
field-base      = scalar / enum / list-type / "object"    ; "object" in table form only
scalar          = "string" / "number" / "boolean" / "date"
list-type       = "list" "(" OWS ( scalar / enum / "object" ) OWS ")"
                                        ; "list(object)" in table form only

attribute-type  = attribute-base [ "?" ]
attribute-base  = "string" / "number" / "boolean" / enum / set-type
set-type        = "set" "(" OWS ( "string" / enum ) OWS ")"

enum            = "enum" [ "(" OWS enum-value *( OWS "," OWS enum-value ) OWS ")" ]
                                        ; bare "enum" in table form only, with "values"
enum-value      = 1*( ALPHA / DIGIT / "-" / "_" / "." )

Spaces are allowed only where OWS appears. The canonical spelling has no spaces except one after each comma: enum(a, b).

5.2 Table form

Key Type Default Description
type string required A field type or attribute type (§5.1). In table form it may also be object, list(object), or bare enum, with the keys below.
fields table of fields required when type is object, object?, list(object), or list(object)?; not allowed otherwise The nested fields of an object, in the same syntax (short or table form). Field types only.
values array of strings required when type uses bare enum; not allowed otherwise The enumeration’s values, for values that aren’t simple words, such as ["Getting started", "How-to"]. Values must be distinct. Values of a set(enum) attribute must be tokens.
default any TOML value none The value used when the field or attribute is absent. It must have the declared type (a TOML string for string and enum, integer or float for number, boolean for boolean, local date for date, array for list, inline table for object, a string or array of strings for set). A field with a default is optional; adding ? as well is allowed and changes nothing.
phrases boolean false Whether phrases (SPEC §5.1) are substituted in this field’s value. Field types only; allowed only on string and list(string) fields (and their optional forms), including fields nested in objects. See §11.
inline string none "code" reads the value’s code spans. Allowed only on a page type’s top-level string fields (and string?), its own or [frontmatter]’s. See §5.3.
description string none Help text shown by the editor (hover and completion) and emitted into generated schemas as documentation.
role string none What the field is for, which a check reads: "description", "recheck-date" (or "review-date", its old name, for a release), or "owner". Allowed only on a page type’s top-level fields, its own or [frontmatter]’s. A field named for a role has it without role, unless the type gives it to another field. See §5.4.

Nesting beyond one level of fields is allowed but discouraged; keep frontmatter flat.

5.3 Code in a field

A backtick in a string field is just a backtick. To show code in a page title, set inline = "code" on the field:

[types.reference.frontmatter]
title = { type = "string", inline = "code" }
---
title: "`ascribe.toml` reference"
---

The value is then read as Markdown that knows only code spans. A run of backticks opens a code span, and the next run of the same length closes it. Outside code spans, a backslash escapes punctuation, so \` is a literal backtick. Emphasis, links, and HTML stay literal text, since a title also becomes the text of other links. With phrases = true as well, phrases are substituted outside code spans, never inside them.

Each output has the title both ways: as plain text, ascribe.toml reference, and formatted, with the code spans kept.

  • Empty link text and the plain-markdown output’s heading keep the code spans: [`ascribe.toml` reference](…).
  • The site output writes the field as plain text, for a layout’s <title>, search, and sorting, and its formatted form as HTML under formatted, for the page’s heading and navigation. The generated schema types both, so a layout uses entry.data.formatted.title with set:html. See Astro.
  • The JSON output has the plain text in title and frontmatter, and the formatted form in formatted.title, a list of text and code pieces.
  • Review shows the formatted title where a page’s title is a heading or a list entry: the heading of VS Code’s page preview, the HTML preview’s list of pages and its page headings, and the list of comments in the editor and the site preview. Ascribe: Changed Pages, which can show only text, writes the code spans between backticks. Tooltips, buttons, and search keep the plain text.

Only a page type’s top-level string fields can set it, its own or [frontmatter]’s, not fields in objects or the fragment schema, and "code" is its only value (model-inline-field).

5.4 Field roles

A check that needs a page’s description or the date it’s next due to be checked, or a report that counts pages by owner, reads the field with that role.

A field named for a role has it, when its type is one the role takes:

[frontmatter]
description = "string?"
recheck-date = "date?"
owner = "enum(docs, platform)"

A field’s name is yours to choose, so a field of another name takes a role with role, and the field named for it then has none:

[types.guide.frontmatter]
summary = { type = "string?", role = "description" }
review-by = { type = "date?", role = "recheck-date" }
team = { type = "enum(docs, platform)", role = "owner" }
Role The field’s type What reads it
description string, optional or not page-description-missing reports a page of the type without one. A required field, or one with a default, always has one, so the check has nothing to report: a page that leaves a field with a default out, or empty, has the default as its description, in llms.txt and the page records too. description-link reports a Markdown link in one: a description is plain text, so the link is shown as written.
recheck-date date, optional or not recheck-overdue reports a page whose date has passed. review-date, the role’s old name, still works, as a value of role and as a field’s name, and so does the check’s, review-overdue, in [checks] and in an acknowledgement, until the next release.
owner string or enum, optional or not ascribe report inventory counts pages by owner.

Without a field with the role, those checks don’t look at the type’s pages, and the report counts its pages as having no owner. A role changes nothing in the outputs.

In VS Code, the sidebar’s This page shows a page’s description against its limit, and when it’s due for a recheck, from the fields with these roles.

Rules. A role is one of the three (model-field-role), on a field of its type, in a page type’s top-level fields, its own or [frontmatter]’s: not a field in an object, nor in the fragment schema. Two fields of one table can’t have the same role. A role a type gives one of its own fields with role is that field’s, and not a field’s in [frontmatter]. A field named for a role has it only when no other field of the type is given it, and its type is one the role takes: a description that’s a number is a field like any other.