ascribe.toml format contract: checks and the editor

The normative contract for the content model’s checks, the editor’s build, and defaults.

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 checks and the editor.

22. [checks]

[checks] holds settings for Ascribe’s own checks and tools, the processor settings SPEC §7.2 leaves to a processor: SPEC defines none of these checks, their names, or their levels.

Each key of [checks] other than vale and links is a check’s slug, as the diagnostics reference lists it, and its value is a level, "off", "advice", "warning", or "error", or a table, [checks.<check>], with these keys and the check’s own settings:

Key Type Default Description
level string the check’s own level The check’s level: "off", "advice", "warning", or "error". Without it, the check keeps its own.

A check’s own settings are listed with the check in the diagnostics reference. page-size and image-large take limit, and so does memo-long from ascribe 0.4.0.

summary-long takes limit too.

A level applies wherever the diagnostic is reported: by ascribe check, by ascribe build, and in the editor. "off" MUST NOT be reported, and advice MUST NOT fail ascribe check, with --deny-warnings or without. A check the diagnostics registry marks off by default (off-by-default) MUST NOT be reported unless [checks] names it: named with a level, it’s reported at that level, and named as a table without level, at its registry severity.

Rules (§25.9): a key MUST be a check’s slug, or vale or links, and an unknown one is reported with a did-you-mean suggestion (model-unknown-key). The check MUST be one the diagnostics registry marks configurable (model-check-not-configurable), and a level one of the four (model-invalid-value). A value MUST be a string or a table, and a table MUST have only level and the check’s settings (model-unknown-key). page-size’s, memo-long’s, and summary-long’s limit MUST be an integer above 0, and image-large’s an integer number of bytes or a string of a number and B, KB, or MB, with or without a space between, where a kilobyte is 1,000 bytes and a megabyte 1,000,000 (model-wrong-type).

22.1 [[intended]]

Each [[intended]] table acknowledges one review check’s problem with one content model entry or image (SPEC §4.9). The problem MUST NOT be reported, and MUST be counted and listed with the reason instead, in every build.

Key Type Default Description
check string required The check, by its name. It must be a review check that reports its problems at an entry, as the diagnostics reference says.
reason string required Why the problem is intended. Not empty.
phrase string required: exactly one of phrase, feature, and image The entry, when it’s a phrase: its key.
feature string required: exactly one of phrase, feature, and image The entry, when it’s a feature: its key.
image string required: exactly one of phrase, feature, and image The entry, when it’s an image: its path under the content root.

An acknowledgement that covers nothing when every build is checked is advice (intended-unused), at its [[intended]] header, with a fix that removes the table. A glossary term is acknowledged with @intended in its section, and term here is model-unknown-key.

Rules (§25.9): check MUST name a check that exists, with a did-you-mean suggestion when it doesn’t, whose registry entry has next = "review" and place = "entry" (model-intended-check). The table MUST name exactly one declared phrase, feature, or image (model-intended-entry). check and reason MUST be present (model-missing-key), reason MUST NOT be empty (model-empty-text), and the table MUST have no other key (model-unknown-key).

22.2 [checks.vale]

How the prose is checked with Vale, which the project installs and Ascribe runs, never links or downloads.

Key Type Default Description
preset string Ascribe’s own selection of Vale rules: quiet, rules that are almost never wrong. Nothing is downloaded: the preset is written under .ascribe/vale/.
config string Instead of a preset, the path of the project’s own .vale.ini, from the project root.
off array of strings [] Rules of the preset to turn off, by the names alerts give them, such as Ascribe.Repeated. Only with preset.
command string "vale" The command that runs Vale. A path with a separator is from the project root; a bare name is looked up as the shell would. A path is a program in the project: the editor asks before it runs one, and the agents’ hooks don’t run one (Prose, through Vale).
in-check boolean false Whether ascribe check, and the agents’ hook, check the prose without --vale. It also says the project wants its prose checked everywhere: without it, the editor doesn’t report that Vale couldn’t be run (prose-not-checked), since a machine without Vale isn’t one the project asked to check.
max-level string "error" The most an alert can be: "advice", "warning", or "error". Vale’s suggestion, warning, and error are advice, a warning, and an error up to it; with "advice", Vale’s alerts never fail --deny-warnings.

A preset’s name MUST NOT change once released, and a rule added to a preset is a Behavior change in the changelog. A command that’s a relative path, or an absolute one into the project’s folder, is a program in the project: the language server MUST run it only once the person allows it, and the agents’ hook MUST NOT run it.

Ascribe MUST write nothing for Vale outside .ascribe/vale/ in the project root, except by ascribe vale eject: there it writes a preset’s configuration and styles, and the vocabulary Ascribe of the project’s own words (phrase values, glossary terms and aliases, dimension, value, and feature labels), one escaped regular expression to a line.

Each alert is a prose diagnostic (ASC165) on the source file, with the rule as its JSON rule, and at the phrase when it’s inside a phrase’s text. A Vale that can’t be run, fails, runs out of time, or isn’t started by its caller is one prose-not-checked advice (ASC166) at [checks.vale], and never fails the check. It’s reported only where the project asked for its prose checked everywhere: ascribe check with --vale, or anything that checks with in-check = true. The language server, which runs Vale whenever there’s [checks.vale], MUST NOT report it without in-check. The MCP server never starts Vale. Both diagnostics are configurable in [checks], whose level applies after max-level caps Vale’s.

Rules (§25.9): [checks.vale] MUST have exactly one of preset and config, and off MUST appear only with preset and name only its rules (model-checks-vale). preset and max-level MUST take one of their values (model-invalid-value), and the table no other key (model-unknown-key).

How ascribe report links checks external links: with lychee, which the project installs, run in the project root with the addresses on its standard input. ascribe check MUST NOT reach the network.

Key Type Default Description
command string "lychee" The command that runs lychee. A path with a separator is from the project root; a bare name is looked up as the shell would.
ignore array of strings [] The hosts whose links aren’t checked: a host name, such as example.com, or *. and a host name, which covers every host under it but not the name itself. Case doesn’t matter.
reuse-days whole number 14 How many days an address that answered isn’t asked about again. Each address’s own limit falls between half of this and all of it, so addresses first checked together don’t all come due on one day. 0 asks about every address on every run.
confirm-days whole number 3 How many days a link that answered before and fails now is listed as unconfirmed, and not reported, before it’s reported. A 404 or a 410 is reported at once. 0 reports every failure at once.

The links checked are those whose destination is an http or https URL, in every source file, each address once, except a host ignore matches, compared without case after a trailing . is dropped. An address that answers with a 5xx status is asked about again, once, after a wait, and only the second answer counts; a link whose address fails again with a 5xx is reported with the message variant that says the server failed, not that the link is broken for readers. A link whose address answers with an error, doesn’t answer in time, or can’t be reached is a link-external-broken advice (ASC168, next = "review", place = "block"), and one whose redirects begin with a 301 or 308 a link-external-moved advice (ASC169, next = "choose"), with an unsafe fix that writes the address the permanent redirects end at. Both are at the link, and configurable in [checks]. An @intended {check=link-external-broken} that covers no broken link is intended-unused only when ascribe report links ran.

The cache is .ascribe/cache/links/ in the project’s folder: one file an address, keyed by the address exactly as lychee is given it and a hash of the project’s lychee.toml. Only an answer with nothing to report is kept, so every diagnostic in a report comes from an address asked about in that run. An address’s answer is reused while it’s younger than its own limit: a number of days from half of reuse-days, rounded up, through all of it, fixed by a hash of the address. A file that can’t be read, has another version, or holds another key counts as no answer.

The history is .ascribe/history/links.json in the project’s folder: one file, version 1, sorted, written whole through a rename after each run, holding for each address the pages link to the last day it answered, the day it started failing, and whether the run reported it. An address no longer linked to is dropped. A run that reads the file again before writing it and finds another run’s writing keeps, for each address, the record that confirms its failure later. A link that fails, other than with 404 or 410, whose address the history records answering before, is unconfirmed, not a diagnostic, until it has failed on every run for confirm-days days from its first failure since that answer. A file that can’t be read or has another version is no history, and with none, or with --no-history, every failure is a diagnostic. A permanent redirect is an answer. An @intended {check=link-external-broken} covers an unconfirmed link as it covers a reported one.

ascribe check, build, diff, drift, fmt, the language server, and the agents’ hook MUST NOT read or write the cache or the history.

Rules (§25.9): each ignore entry MUST be a host name, or *. and one (model-wrong-type); command MUST be a string, and reuse-days and confirm-days integers of 0 or more (model-wrong-type); the table MUST have no other key (model-unknown-key).


23. [editor]

Settings for the authoring environment (SPEC §10).

ascribe 0.4.4 on Linux
[editor]
build = "site"
Key Type Default Description
build string the only build, else site The build whose page-level diagnostics the language server reports by default (SPEC §8.1 checks pages once per build). It MUST name a declared build.

Default. If only one build exists, that build. Otherwise, the build named site, if there is one. Otherwise the key is required (model-editor-build-required).


24. Defaults: what an absent section means

A file containing only spec = "0.1" is valid. It means:

Section When absent
[project] content-root = "docs", output-dir = ".ascribe/build"
[types] One implicit default page type, named page, with frontmatter title = "string" and nothing else. If [types] declares any type, there’s no implicit type.
[fragments] Only paths with a _ segment are fragments. The fragment schema has no fields, so a fragment’s frontmatter can’t have any keys.
[notes] The five built-in note types.
[phrases] No phrases. Every {…} is literal text.
[glossary] No glossary.
[images] No image attributes.
[widgets] No project widgets.
[dimensions] No dimensions. Any @variant arm with attributes, or variant frontmatter, is an error.
[versions] scheme = "numeric"
[lifecycle] The five built-in states.
[features] No features.
[consumer] profile = "astro" with its defaults.
[builds] One implicit build, site, with variants = "switch" and availability = "badge". If [builds] declares any build, there’s no implicit one.
[listing] No rules. A page that doesn’t set listing is listed.
[crawlers] No lines.
[permalinks] No retired permalinks and no aliases.
[sources] No sources. Nothing outside the project’s folder can be read.
[checks] Every check at its own level.
[editor] build as in §23.