# Changelog

Every Ascribe release: the `ascribe` binary, the npm packages (`@ascribed/cli`, `@ascribed/astro`, `@ascribed/elements`, `@ascribed/review`), and the VS Code extension share one version. Versions follow [semantic versioning](https://semver.org/); while the major version is 0, a minor version may change behavior.

## <a id="unreleased"></a>Unreleased

In `main`, not yet released.

### <a id="unreleased-the-language"></a>The language

- **An API's `reference` that isn't Redoc's is called an address pattern.** `link-operation-form-unsupported` and `model-api-reference` say so in their messages, so they no longer read like a content type's template. See [Link to your API reference](https://ascribed-dev.com/docs/openapi/).
- **Behavior change: the OpenAPI example checks are `api-example-*`.** They were `example-schema-mismatch`, `example-not-json`, `example-body-missing`, and `example-schema-unsupported`, and keep their codes, ASC277 to ASC280, which `ascribe explain` still finds them by ([Check a JSON example against its schema](https://ascribed-dev.com/docs/openapi/examples/)).
- **Attributes written without braces are one error, with a fix that adds the braces.** `@note type=warning: text` no longer also reports the heading below it, and `ascribe check --fix` braces it. See [Directive lines](https://ascribed-dev.com/docs/writing/syntax/#directive-lines).
- **A build filtered for a version older than every declared line is advice.** `build-filter-line-undeclared` reports a filter whose version is in no live, planned, or archived line: declare the line in `archived`, or use `versions =`. See [`[builds.<name>]`](https://ascribed-dev.com/docs/content-model/output/).
- **A missing include or snippet file is offered the path that names it.** And `@variant` in a project with no dimensions gives the `[dimensions.<name>]` table to add. See [`@include`](https://ascribed-dev.com/docs/writing/directives/).
- **`draft: true` keeps a page out of every build until it's ready.** `ascribe check` still checks a draft, and a link to one from a published page is `link-page-dropped`. See [Frontmatter](https://ascribed-dev.com/docs/writing/frontmatter/#a-draft).
- **Behavior change: `example-stale` is a warning, which `--deny-warnings` fails on.** A tested example changed after its last test run now fails CI's check; set `example-stale = "advice"` in `[checks]` to let it pass ([Checks in CI](https://ascribed-dev.com/docs/checks/ci/#keep-tested-examples-tested)).
- **`ascribe check` reports an `ascribe.examples.toml` that no source reads.** One below a source's root was ignored without a word; now `check`, the editor, and `ascribe observe` say so, and how to fix it. See [tested examples](https://ascribed-dev.com/docs/examples/tested/#1-say-which-files-are-examples).
- **A GitHub alert, such as `> [!NOTE]`, is a warning, with a fix that writes the `@note`.** Ascribe showed it as a plain quote. The fix keeps the quote's content and the alert's type. See [`@note`](https://ascribed-dev.com/docs/writing/directives/#note).
- **A code block's info-string word that looks like a pair but isn't one is a warning.** `phrases = true`, `Title=x`, an unclosed quote, and a quote escaped once each get `info-pair-malformed`, with a fix where the pair is clear. See [Code block info strings](https://ascribed-dev.com/docs/writing/syntax/#code-block-info-strings).
- **Behavior change: a code block's info string has a grammar, and the outputs drop the checker's pairs.** Pairs such as `title` and `tested` are read as §3.3.1 says, and the site and plain outputs leave out `request` and `response`. See [SPEC.md](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md).
- **`@Note:` draws `directive-unknown`, suggesting `@note`.** A line shaped like a directive whose name is a keyword in another case is still text, and is now warned about as a misspelling is. See [Syntax](https://ascribed-dev.com/docs/writing/syntax/).
- **A broken link's message names the target it most likely meant.** A heading whose id starts with the written id's words ranks first, and when one target clearly wins, the message ends with "did you mean" it. See [`ascribe link`](https://ascribed-dev.com/docs/command/questions/#cli-link).
- **Behavior change: a memo file's `about` has a grammar of its own in SPEC.md.** A path is percent-decoded as a link's is, and a malformed one is now `memo-invalid`; the editor and `ascribe rename` write it the same way. See [Memo files](https://ascribed-dev.com/docs/writing/memo-files/).
- **A draft isn't reported as left out of `ascribe.nav`.** `nav-page-unplaced` passes over a page with `draft: true`, which no sidebar shows yet. See [A draft](https://ascribed-dev.com/docs/writing/frontmatter/).
- **Behavior change: retired and aliased permalinks are kept in `ascribe.addresses`, not `ascribe.toml`.** `[permalinks]` in `ascribe.toml` is an error, which `ascribe addresses init` moves, and `permalink-removed` is now `permalink-lost`. See [Permalinks](https://ascribed-dev.com/docs/permalinks/).
- **`ascribe check` reports a phrase's value written out where the phrase could be.** A version or a URL typed out goes stale at the next release; the fix writes the phrase. See [Phrases](https://ascribed-dev.com/docs/writing/inline/).
- **A template's placeholder in a page that publishes is an error.** `template-placeholder-published` fails `check` and `build`; in a draft, `template-placeholder-left` stays advice. See [What's left to fill in](https://ascribed-dev.com/docs/templates/).
- **Behavior change: a page's review date is its recheck date.** `review-overdue` is `recheck-overdue`, keeping ASC153, and the role `review-date` is `recheck-date`; for a release, both old names still work in `ascribe.toml`, acknowledgements, frontmatter, and `ascribe explain` ([Field roles](https://ascribed-dev.com/docs/content-model/#54-field-roles)).
- **A link to a renamed `operationId` is offered the operation now at its method and path.** `ascribe check` reads the base's spec for it, and `--fix` applies it. See [A renamed id](https://ascribed-dev.com/docs/openapi/#a-renamed-id).
- **Snippets take regions from `Dockerfile`, `Makefile`, `.env`, CSS, SCSS, Less, and GraphQL files.** A file is matched by its name before its extension, and CSS takes tags in `/* */` comments. See [Snippets](https://ascribed-dev.com/docs/examples/snippets/).
- **The specification says it's a draft until 1.0.** Its grammar and built-in vocabulary may grow until then, and `spec = "0.1"` names the draft, not a frozen version. See [SPEC.md §11](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#11-versioning).
- **The specification tells its own errors from a content check a project raises to an error.** A raised check fails the build as an error does, but the page still conforms, and a raised review check can still be acknowledged: [Quieting a problem](https://ascribed-dev.com/docs/checks/check/).
- **The specification describes the implicit `release` dimension.** Ascribe already worked this way; the spec now says so, in §4.4 and §7.2. See [SPEC.md](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#72-declarations).
- **The specification's principles and binding rules cover the newer features.** Principle 3 says what's derived and from where, binding names `@memo`'s and `@summary`'s exceptions, and §1.3 defines advice. Nothing behaves differently. See [SPEC.md §1.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#12-design-principles).
- **The specification says which settings and checks are Ascribe's own.** Nothing Ascribe reports changes, and the diagnostics reference marks the checks the specification doesn't define. See [SPEC.md §7.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#72-declarations) and [the diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/).
- **SPEC.md's builds example, site output, and editor sections say what the toolchain does.** §7.3, §9, and §10 now match the toolchain. See [SPEC.md](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#9-compilation).
- **Behavior change: `summary-missing` and `summary-long` are off unless `[checks]` turns them on.** Only a project that shows its docs in a product needs summaries, and it turns them on. See [Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/).
- **A page with no `title` can take its opening heading as one.** `frontmatter-missing-field` offers the fix, safe while nothing links to the heading, so `ascribe check --fix` titles a folder of pages at once. See [Bring existing docs to Ascribe](https://ascribed-dev.com/docs/existing-docs/#fix-what-the-check-finds).

### <a id="unreleased-the-ascribe-command"></a>The `ascribe` command

- **Behavior change: `ascribe build` shows errors in full, counts the rest, and prints one line per build.** Warnings and advice are one summary line that points to `ascribe check`; `--format json` is unchanged. See [ascribe build](https://ascribed-dev.com/docs/checks/check/#cli-build).
- **`ascribe build` and `ascribe report` hold back what `ascribe.baseline` lists, as `ascribe check` does.** Both count what they held back, and `--no-baseline` reports everything. See [The baseline](https://ascribed-dev.com/docs/checks/baseline/).
- **`ascribe check --exit-code=warning` fails on warnings.** `--deny-warnings` stays, as its alias, so the command takes the option the conventions name. See [`ascribe check`](https://ascribed-dev.com/docs/checks/check/).
- **`ascribe check --format github` shows each problem on the pull request's lines in GitHub Actions.** `ascribe fmt --check` takes it too, and the CI recipe uses both. See [Output for GitHub Actions](https://ascribed-dev.com/docs/checks/check/).
- **`ascribe check` prints a code's shared help once.** In text output, later diagnostics with the same help point back to the first, and other formats are unchanged. See [Text output](https://ascribed-dev.com/docs/checks/check/#text-output).
- **`ascribe init` sends you to a first page, and `ascribe check` says when there are none yet.** In a project with no pages, `ascribe check` says `No pages in docs/ yet.` and exits with 0. See [`ascribe init`](https://ascribed-dev.com/docs/install/).
- **`ascribe check`'s summary line says how many files have problems.** It reads `checked 9 files for every build, 1 with problems: …`, and names the files the paths cover only when paths are given. See [`ascribe check`](https://ascribed-dev.com/docs/checks/check/).
- **Behavior change: `--check` writes a unified diff in `permalinks sync`, `agents sync`, and `observe`.** Each still exits with `1` when something would change. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **`ascribe check --format concise` keeps the first fix.** A problem with one ends its line with `→` and what the fix does, such as `→ Link to setup.md`. See [Output for agents](https://ascribed-dev.com/docs/checks/check/).
- **`ascribe diff main` and `ascribe drift HEAD` name the `--base` option.** A revision given where a page goes, as `git diff` takes one, gets an error that says to write `--base main`. See [`ascribe diff`](https://ascribed-dev.com/docs/review/diff/).
- **Behavior change: `ascribe fmt --check` lists each change, and `--diff` writes a unified diff.** Each change is its `file:line`, then the lines as they are and as they'd be, instead of the file alone. See [`ascribe fmt`](https://ascribed-dev.com/docs/checks/check/).
- **Behavior change: `ascribe fmt --check` writes a unified diff.** `--dry-run` lists each change, as `--check` did, and `--diff` is `--check`'s old spelling. See [`ascribe fmt`](https://ascribed-dev.com/docs/checks/check/).
- **Every command that takes `--format text` takes `concise` too.** A command with no one-line form writes its text, so an agent can pass `--format concise` to any of them. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **`sources fetch`, `templates list`, `vale eject`, `agents sync`, and `agents prompt` take `--format json`.** Each writes one document, and takes `concise` as text. See [JSON report contract](https://ascribed-dev.com/docs/contracts/json-reports/).
- **Behavior change: `agents sync`, `agents index`, and `observe` refuse files git couldn't put back.** Each takes `--dry-run`, and `--force` writes a file with changes not committed. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **Behavior change: `init`, `sources`, `templates eject`, and `vale eject` refuse uncommitted files.** `sources fetch`, `sources update`, and the others each take `--dry-run`, and `--force` writes a file git couldn't put back. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **Behavior change: `fmt`, `check --fix`, and `rename` refuse pages git couldn't put back.** Each takes `--dry-run`, and `--force` writes a page with changes not committed. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **Behavior change: `baseline`, `permalinks`, and `addresses` refuse files git couldn't put back.** Each that writes takes `--dry-run`, and `--force` writes a file with changes not committed. See [The `ascribe` command](https://ascribed-dev.com/docs/command/).
- **`ascribe --help` lists the commands in groups, with short descriptions.** A command's own `--help` says the rest, and the note for agents moved to `ascribe agents --help`. See [the command reference](https://ascribed-dev.com/docs/command/).
- **A diagnostic's `help` links to the docs' permalinks.** The JSON of `ascribe check` and `ascribe explain`, and the editor's, has addresses an agent can follow, such as `https://ascribed-dev.com/go/22-checks/`, in place of links relative to the docs. See [`ascribe check`](https://ascribed-dev.com/docs/checks/check/).
- **`ascribe init` writes `ascribe.addresses`, so a new project keeps the addresses it publishes.** A page moved with `git mv` is then `address-lost` on the next check against the base, and its fix records the move. See [`ascribe init`](https://ascribed-dev.com/docs/install/).
- **`ascribe init` adopts a folder of Markdown already there.** It declares no page types, so no page needs `type:`, then checks the pages and says what to run next; `--content-root` names their folder. See [Bring existing docs to Ascribe](https://ascribed-dev.com/docs/existing-docs/).
- **`ascribe model` and `ascribe_model` show every table of `ascribe.toml`, `[apis]` among them.** New sections: `frontmatter`, `fragments`, `lifecycle`, `notes`, `images`, `apis`, `from`, `intended`, and `versions`, so an agent without a shell can read each API's spec and where its reference is. See [`ascribe model`](https://ascribed-dev.com/docs/command/questions/).
- **`ascribe new` starts a page from its type's template, as a draft.** It sets the page's `type` and `title`, adds `draft: true`, as the editor's New Page now does too, and never writes over a file. See [Start a page](https://ascribed-dev.com/docs/templates/).
- **Behavior change: `ascribe observe` without a terminal runs nothing unless given `--yes`.** In CI, a hook, or an agent's shell, it lists the config's commands and exits 2, naming `--yes`. See [`ascribe observe`](https://ascribed-dev.com/docs/examples/sources/).
- **`ascribe preview` shows a build's pages as a site, with no Node, editor, or Astro.** It prints the check's problems, serves the pages at `127.0.0.1`, and renders them again as you edit: [`ascribe preview`](https://ascribed-dev.com/docs/checks/check/).
- **`ascribe report delivery` names the links `llms-txt-links-resolve` didn't reach.** `delivery-output` (`ASC171`) lists each address with its status or why it couldn't be fetched. See [What to measure](https://ascribed-dev.com/docs/agents/measuring/#check-what-the-site-delivers-to-agents).
- **Behavior change: `ascribe report builds` lists each mark once.** A section's `@available`, a variant arm, or a page's frontmatter is one entry at its line, under its heading, and the JSON is at `schema_version` 3 ([the project report](https://ascribed-dev.com/docs/checks/report/#builds)).
- **Behavior change: `ascribe report agents` is `ascribe report delivery`.** The section is named for the Web Documentation Delivery Spec it checks: its JSON key is `delivery`, at `schema_version` 2, and `agents` still runs it for now ([the project report](https://ascribed-dev.com/docs/checks/report/#the-site-and-agents)).
- **`ascribe versions retire` needs no `--force` for its target's part of other builds' availability.** It lists those pages apart, as `expected` in the JSON. See [Archive and retire a line](https://ascribed-dev.com/docs/versions/archive/).
- **`ascribe versions retire` records the line's addresses in `ascribe.addresses`.** Each address that goes to a page in the current line is recorded in `[moved]`, so the build writes its redirect. See [Archive and retire a line](https://ascribed-dev.com/docs/versions/archive/).
- **`ascribe search` cuts a long block to its match, in JSON and over MCP too.** A place's `text` is at most about 240 characters around the match, so a large block no longer comes back whole. See [`ascribe search`](https://ascribed-dev.com/docs/command/questions/).
- **`ascribe search` lists partial matches when no block has every word, and skips common words.** The closest places come with the words each lacks, as `partial_places` in JSON; `how do I publish llms.txt` finds what `publish llms.txt` does. See [the search reference](https://ascribed-dev.com/docs/command/questions/).
- **`sources fetch` and `sources update` reach each repository once.** They plan their changes, ask the git guard about them, and then make that plan, in place of fetching a second time to write; with `--force`, they write as they go. See [`ascribe sources` and `observe`](https://ascribed-dev.com/docs/examples/sources/).
- **Help printed in a terminal has no Markdown links.** `ascribe check` and `ascribe explain` write a link in a diagnostic's help as its text; JSON keeps the Markdown. See [`ascribe explain`](https://ascribed-dev.com/docs/command/questions/).
- **`ascribe versions archive` is now `ascribe versions export`.** It writes a copy of a line and doesn't archive it, which `retire` does. The old name still works, with a warning, for one more release. See [`ascribe versions export`](https://ascribed-dev.com/docs/versions/release/#cli-versions-export).

### <a id="unreleased-the-editor"></a>The editor

- **The Project view's Health shows only the counts that need doing.** Counts of none are behind a **Nothing else to do** line, and a group with nothing to do is its head alone. See [the Project view](https://ascribed-dev.com/docs/editor/sidebar/).
- **This page says each thing once, with every warning under Health.** About says what the page is, and the rows that can warn move to Health, whose header counts them. See [The sidebar](https://ascribed-dev.com/docs/editor/sidebar/#this-page).

### <a id="unreleased-the-language-server"></a>The language server

- **`@operation`'s link completes, hovers, and opens as a link to an operation does.** It gets the safe fix when the spec renames the operation, too. See [Show an operation in a page](https://ascribed-dev.com/docs/openapi/operation/#operation-editor).

### <a id="unreleased-astro-and-the-elements"></a>Astro and the elements

- **Code blocks have a copy button.** `@ascribed/elements` adds one to each code block, which a site can restyle or turn off in CSS. See [the element library reference](https://ascribed-dev.com/docs/astro/elements/#copy-buttons).
- **`init` writes a stylesheet for the starter's layout.** `src/styles/docs.css` gives the layout a readable column, a sidebar (a menu on phones), a side table of contents, and both color schemes, from the element library's `--ascribe-*` properties ([Set up a site](https://ascribed-dev.com/docs/astro/#the-stylesheet)).
- **`MissingPermalink` no longer offers search by default.** Its last paragraph offers the home page alone, since a site may have no search; a site with search passes its own paragraph ([the reference](https://ascribed-dev.com/docs/astro/reference/)).
- **Tabs' labels are text in the page, and code blocks name their language in a class.** A tool that converts a page's HTML to text or Markdown for an agent then reads which tab is which, and each block's language. See [the element library](https://ascribed-dev.com/docs/astro/elements/#generated-text).

### <a id="unreleased-agents"></a>Agents

- **The edit hook names the placeholders a page's template left.** After an edit, the agent hears each `template-placeholder-left` and `template-section-empty` in the files it wrote, so it fills them in while the page is its own. See [Hooks](https://ascribed-dev.com/docs/agents/hooks/).
- **Links in the Markdown published for agents lead to the other pages' Markdown.** A link to a heading lands on that heading in the `.md`, so an agent that follows a link stays in Markdown ([Publish for AI agents](https://ascribed-dev.com/docs/astro/agents/#publish-for-ai-agents)).
- **Agents' Markdown says what a page is for and which release it describes.** After the title, each `.md` gives the page's type and description, and with `[releases]`, its first line and `llms.txt` name the release ([Publish for AI agents](https://ascribed-dev.com/docs/astro/agents/#publish-for-ai-agents)).
- **The MCP server reads only the files that changed since its last call.** Before, any change to a page loaded the whole project again, which takes seconds on a large one ([ascribe mcp](https://ascribed-dev.com/docs/agents/commands/#cli-mcp)).
- **The MCP server's `ascribe_rename` gives what `ascribe rename` changes, for an agent with no shell.** It returns each changed file's text, the page that moves, and the entries recorded in `ascribe.addresses`, writing nothing; `ascribe rename --format json` writes the same ([ascribe rename](https://ascribed-dev.com/docs/command/questions/#cli-rename)).
- **The MCP tools `ascribe_format` and `ascribe_changes` are now `ascribe_fmt` and `ascribe_diff`.** They're named for their commands; the old names still work for one more release ([ascribe mcp](https://ascribed-dev.com/docs/agents/commands/#cli-mcp)).
- **The MCP tools give no output schema, and `tools/list` is a fifth of its size.** It's 17 KB, from 92 KB, and the JSON report contract still has each schema. See [the MCP tools](https://ascribed-dev.com/docs/agents/commands/).
- **A permalink's address gives an agent its section in one fetch.** Its section is at `go/<name>.md`, and with `host = "netlify"` the address answers `301`. See [Permalinks](https://ascribed-dev.com/docs/permalinks/).
- **`ascribe agents sync` names the command the project runs Ascribe with.** `AGENTS.md`, the skill, and the hooks say `npx ascribe`, `pnpm exec ascribe`, or `yarn ascribe` when a `package.json` installs it, and `--command` names another. See [the command they name](https://ascribed-dev.com/docs/agents/instructions/).
- **`ascribe_resolve` says to leave out `project` when `page` is a path.** The page's project is found from it, and the error gives the call to make instead. See [the MCP tools](https://ascribed-dev.com/docs/agents/commands/).
- **Long agent rules keep every page type, with what it's for.** Over 4,000 characters, each directive's syntax and the text the skill repeats go first, and the page types are cut last. See [Write the agent files](https://ascribed-dev.com/docs/agents/instructions/).
- **The rules give a dimension's values, and an `@available` example from the project's own targets.** The check line loses its "Programmatic check:" label, and `new-page` asks to fill in empty fields only when there are some. See [Write the agent files](https://ascribed-dev.com/docs/agents/instructions/).
- **The skill's commands work from the root when the only project is in a folder.** `ascribe agents sync` gives the commands that check or read the whole project `--config` with the project's folder. See [the skill](https://ascribed-dev.com/docs/agents/instructions/#the-skill).

### <a id="unreleased-review"></a>Review

- **`ascribe diff` lists the pages an API spec's change reaches.** A page whose `@operation` or operation link writes something new is listed through the operation, `api/openapi.yaml#createWidget`, in the text, JSON, prompt, and review. See [When the spec changes](https://ascribed-dev.com/docs/openapi/).
- **`ascribe diff` says a change once when it's the same in every build.** Such a page is listed first, `in every build`, and JSON and HTML are unchanged. See [ascribe diff](https://ascribed-dev.com/docs/review/diff/#cli-diff).
- **The review report is now the HTML preview.** `ascribe diff --format html` is named beside the page preview and the site preview, so "report" means `ascribe report` alone ([Attach the HTML preview to every pull request](https://ascribed-dev.com/docs/review/ci-report/)).
- **`ascribe diff` and review show a moved page once, as moved.** A page git sees renamed, or whose move `ascribe.addresses` records, is listed where it is now with what it moved from, and the JSON carries `moved_from`. See [`ascribe diff`](https://ascribed-dev.com/docs/review/diff/).
- **The page preview's toolbar and threads say less.** The build's settings move to the Build picker's tooltip, and a thread carries one label for where it stands. See [how review works](https://ascribed-dev.com/docs/review/how-it-works/).
- **Review marks the changed words in a one-line note and in a short sentence that grew.** A sentence that keeps its old words at one end is a change, not a removal and an addition. See [`ascribe diff`](https://ascribed-dev.com/docs/review/diff/).

### <a id="unreleased-the-docs-and-the-site"></a>The docs and the site

- **The docs' home page opens with a path for engineers, writers, and agent builders.** Each is a few pages in order after Getting started, and the contracts are under For implementers, at the sidebar's end. See [the docs](https://ascribed-dev.com/docs/).
- **The changelog is published as Markdown, for agents, at `/changelog.md`.** The page links to it, asking for `/changelog/` as Markdown answers with it, and the docs' `llms.txt` lists it under Optional. See [the site's README](https://github.com/ascribed-dev/ascribe/blob/main/site/README.md#the-changelog-for-agents).
- **The CI recipe runs the `ascribe` your project pins.** Its workflow runs `npm ci` and `npx ascribe`, so CI checks with the editor's version; without Node, it installs a named release. See [Checks in CI](https://ascribed-dev.com/docs/checks/ci/).
- **The `ascribe.toml` reference is grouped by what each section is for.** A page each for what pages hold, how content varies, where they're published, what they draw from, and the checks. See [the contents](https://ascribed-dev.com/docs/content-model/#contents).
- **Each diagnostic's header line links its words to what they mean.** The severity, level, next step, "configurable", and "acknowledged" link to their definitions, and three long diagnostics pages are split to fit. See [Diagnostics](https://ascribed-dev.com/docs/checks/diagnostics/).
- **Getting started's dimension has `labels`.** Its example gives each package manager the name readers see on the site's tabs, and says what `label` and `labels` are for. See [Getting started](https://ascribed-dev.com/docs/getting-started/).
- **The glossary defines thirteen more of Ascribe's concepts, each with one meaning.** Each says where the word is also used another way, and the diagnostics reference explains severity and level. See [the glossary](https://ascribed-dev.com/docs/glossary/).
- **The docs index, the command reference, and Files Ascribe writes list every feature again.** A test now fails when one of them falls behind. See the [docs index](https://ascribed-dev.com/docs/).
- **The docs no longer badge features with the release that added them.** Every reader is on the live release, so only what's in `main` but not yet released, or a lifecycle state such as deprecated, is marked. See [`@available`](https://ascribed-dev.com/docs/writing/directives/#available).
- **The changelog is a line per entry, with the detail on the page each entry links to.** 0.4.4 and the entries after it are written this way. See [the changelog's entries](https://github.com/ascribed-dev/ascribe/blob/main/changes/README.md).
- **The glossary tells the four previews apart, and defines the recheck date.** The page preview, the site preview, the HTML preview, and `ascribe preview` each have one name ([Preview](https://ascribed-dev.com/docs/glossary/#preview)).
- **The reference pages' longest paragraphs are lists, tables, and shorter paragraphs.** None outside the contracts runs over 150 words, and a test keeps it so. See [when a mark does nothing](https://ascribed-dev.com/docs/content-model/variation/#121-releasestarget).
- **The docs site links each page to the ones before and after it.** On this page marks the section in view, and the phone menu dims the page behind it. See [Navigation](https://ascribed-dev.com/docs/navigation/).
- **The docs' tested command output says which ascribe printed it.** "Tested with ascribe 0.4.4 on Linux", where it said the Rust that built the binary, which nothing in the docs asks a reader to install. See [Show what an example was tested with](https://ascribed-dev.com/docs/examples/tested/).

### <a id="unreleased-fixes"></a>Fixes

- **Fixed:** the docs now say a retired address gets a page saying why it was removed. Files Ascribe writes lists the redirects and pages `@ascribed/astro` writes from `ascribe.addresses`. See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).
- **Fixed:** An archive's pages declare their language. Each starts with a "Skip to content" link past the sidebar too, for a keyboard at a phone's width. See the [archive contract](https://ascribed-dev.com/docs/contracts/archive/).
- **Fixed:** `ascribe agents sync` no longer imports `AGENTS.md` twice in a `CLAUDE.md` that imports it already. It leaves such a file as it is, and removes a block it added there before. See [Claude Code](https://ascribed-dev.com/docs/agents/claude-code/).
- **Fixed:** a code span that runs from an indented line onto the next ends where its text does. The text after it overlapped it.
- **Fixed:** With `--config <folder>`, `ascribe addresses move` and `retire` read a page's path from here. Before, `retire` recorded that path as an address no page had; `rename`, `diff`, and `permalinks` read it the same way now.
- **Fixed:** A wide table on the docs site scrolls with the keyboard. Each docs page's lead, under its title, shows its code and curly quotes as the page does.
- **Fixed:** `ascribe fmt` keeps a directive in the list item it's in when the item follows `>` with no space. It took a space away, which moved the directive out of the item.
- **Fixed:** the `@intended` example on the directives page names a check that exists. It said `check=link-dead`, which `ascribe check` rejects; it's `check=link-external-broken`, the external link check, as on [the external links page](https://ascribed-dev.com/docs/checks/external-links/).
- **Fixed:** a lone carriage return in frontmatter no longer shifts what `ascribe fmt` edits. The positions after it were a byte early, so a space went before a directive's colon, as they were after one ending a link reference definition.
- **Fixed:** a paragraph that a table interrupts after a `\r\n` line ending no longer loses its position. Its span was empty, so what pointed at it pointed nowhere.
- **Fixed:** `ascribe report delivery` names the checks new since the last report next to that count. They came after "no longer fails", which read as if those checks had stopped failing.
- **Fixed:** `ascribe report` names the line a versioned build's content isn't available on. Content marked for a later line read "not ;"; it now reads "not self-managed 3.4", as the editor's build lens does.
- **Fixed:** In the editor, a search match in a code file a `@snippet` shows gets that file's lines. After an edit, it could get another code file's lines or text.
- **Fixed:** a table row under an indented header line has the position of its own text. Each cell's span was shifted by the header's indent, so what pointed at it missed by that many characters.

## <a id="v0.4.4"></a>0.4.4 (2026-10-26)

### <a id="v0.4.4-the-language"></a>The language

- **`ascribe check` says which OpenAPI operations no page links to.** It's advice, `api-operation-unreferenced`, and `ascribe report inventory` counts each API's operations, linked and checked. See [Find the operations no guide links to](https://ascribed-dev.com/docs/openapi/coverage/).
- **`@operation` puts an API operation's method, path, and summary in a page.** It's linked to the operation in the published reference, and its link is checked. See [Show an operation in a page](https://ascribed-dev.com/docs/openapi/operation/).
- **Check a JSON example in a guide against its operation's schema in your OpenAPI spec.** Mark a `json` block with `request=` or `response=`, and `ascribe check` reports each disagreement at its place. See [Check a JSON example against its schema](https://ascribed-dev.com/docs/openapi/examples/).
- **Link a guide to an operation in your OpenAPI spec, checked against the spec.** Declare the spec in `[apis]`, and a link like `openapi.yaml#getUser` is checked and written as the operation's address in the reference. See [Link to your API reference](https://ascribed-dev.com/docs/openapi/).
- **Link to an operation your OpenAPI spec gives no `operationId`, by its method and path.** `openapi.yaml#delete/users/{id}` is checked against the spec, and needs Redoc 3. See [Link to an operation with no id](https://ascribed-dev.com/docs/openapi/no-id/).
- **Every lost address has its fixes, and `ascribe.addresses`' entries are checked.** `address-lost` offers recording a rename, forwarding, or retiring, and nine checks report an entry that no longer works. See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).
- **A routing setting changed is one warning, and its fix forwards every address it moved.** That's `base-path`, `permalink-path`, `agents`, or a build's name. See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).
- **Keep the addresses you've published: a moved page records itself, and the build redirects it.** With `ascribe.addresses`, `ascribe check` warns about an address nothing accounts for (`address-lost`). See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).
- **Behavior change: `[consumer] host = "netlify"` replaces `headers`, and writes `_redirects` too.** `headers` is now an unknown key. See [the host's files](https://ascribed-dev.com/docs/publishing/site/).

### <a id="v0.4.4-the-ascribe-command"></a>The `ascribe` command

- **`ascribe resolve --emit site` writes a page as the site output does.** `--frontmatter` adds the site output's frontmatter, without building the project. See [`ascribe resolve`](https://ascribed-dev.com/docs/command/questions/).

### <a id="v0.4.4-the-editor"></a>The editor

- **The editor completes, shows, and opens a link's OpenAPI operation, and fixes one that was renamed.** A link to a deprecated operation is advice, `link-operation-deprecated`. See [Link to operations in the editor](https://ascribed-dev.com/docs/openapi/editor/).
- **The editor shows lost addresses between checks, and the old addresses that lead to a page.** A deleted page's `address-lost` is in the Problems panel within seconds of a save. See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).

### <a id="v0.4.4-the-language-server"></a>The language server

- **`ascribe/addresses` says what comparing the addresses with the base's found.** It's sent after a load, a save, or a change on disk, and `ascribe/page` lists a page's old addresses. See [Keep the addresses you've published](https://ascribed-dev.com/docs/publishing/redirects/).

### <a id="v0.4.4-agents"></a>Agents

- **`ascribe link` and the MCP tool `ascribe_link` answer a link to an OpenAPI operation with the operation.** Its `kind` is `operation`, with the operation's method, path, summary, and address. See [`ascribe link`](https://ascribed-dev.com/docs/command/questions/).
- **Agents are told how to move pages without losing their addresses.** `AGENTS.md` and the skill say to use `ascribe rename`, and the MCP server can record a move or a retirement. See [Agent instructions](https://ascribed-dev.com/docs/agents/instructions/).
- **`ascribe_resolve` takes `emit`, and the agents' rules say when to ask for the site form.** `emit: "site"` returns a page as a site gets it. See [`ascribe mcp`](https://ascribed-dev.com/docs/agents/commands/).

### <a id="v0.4.4-fixes"></a>Fixes

- **Fixed:** a page or fragment can be renamed by its case alone on macOS and Windows. `ascribe rename` and the editor rename it and change the links to it ([`ascribe rename`](https://ascribed-dev.com/docs/command/questions/)).
- **Fixed:** with `trailing-slash = "never"`, an Astro site serves each page at its address, not a redirect. `@ascribed/astro` sets Astro's `build.format` to `"file"` ([the Astro reference](https://ascribed-dev.com/docs/astro/reference/)).

## <a id="v0.4.3"></a>0.4.3 (2026-10-24)

### <a id="v0.4.3-the-language"></a>The language

- **Every build writes each permalink's section to `_ascribe/help.json`, for a product to show.** The site output has an entry for each permalink and each alias of one: its title, its summary, from the section's `@summary`, its section as the plain output writes it, with images as their alt text, and its address. A product bundles the file from its own line's build and shows a section in a tooltip or beside an error, with "Read more" opening the permalink ([Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/), output-layout contract §1.7).
- **An edit of a key `[from]` takes is made in the file that writes it.** The editor's actions, Project Settings, renames, and `ascribe permalinks retire` change a taken key in the other `ascribe.toml`, at the same path, and say which file they changed; renaming a key `take` names renames it in `take` too, and a rename of a phrase or a value the other file writes renames it in that project's pages as well, when it's a project with pages, or is refused, naming the file, when it can't be made there. `ascribe versions release` and `ascribe versions retire` run in a project that takes the releases are made in the project that writes them, and say so. `ascribe templates eject` and `ascribe vale eject` refuse a taken table, naming its file. See [the `ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/).
- **A problem in a generated file points at what generates it.** A top-level `@memo` that begins `Generated from` or `Generated by` says the file is generated: each problem in the file names the memo as a related place, with its text; `ascribe check --format prompt` tells the agent to change what the file is generated from and run the command again, not to edit the file; and no fix edits the file, in the editor or with `ascribe check --fix`. See [Memos](https://ascribed-dev.com/docs/writing/directives/).
- **`help.json` has a section's text with each arm of its switcher chosen, in `choices`.** An entry for a section with a switcher has `choices`, keyed by dimension and then value, each the section as a build that selects that value writes it, so a product that knows its reader uses pnpm shows `choices.pm.pnpm` ([Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/#choosing-an-arm), output-layout contract §1.7).
- **`page-size` names the page's largest sections and includes.** The problem lists up to five of the page's parts as related information, largest first, each with its size in characters: its sections, at their headings, wherever they're written, and its includes, at their `@include` lines. A page that's large because of one included file says which. See [the checks](https://ascribed-dev.com/docs/content-model/checks/).
- **`available-same-in-every-line` removes a mark that would say nothing.** When the mark without its versions would be its target alone, generally available, and the target is the only value of its dimension, as `ascribe 0.4.0` in a project with one product, the fix removes the `@available` line, the page's `available` key, or the row's `{available=…}` instead of writing `ascribe`, which a badge build showed as "Available: Ascribe (GA)". A project with another value in the dimension, a build filtered for another target, a mark inside one that says something, and a fragment's mark keep the rewrite. See [the release targets](https://ascribed-dev.com/docs/content-model/content/).
- **`@summary` gives a section a summary, which embedded help shows.** `@summary: text` under a heading gives the section its own summary, which the page doesn't show; `@summary` on the line above a paragraph makes that paragraph the summary, shown where it is. A section has one, a summary in a fragment is the summary of the section it lands in, and one inside a `@variant` arm is an error. `_ascribe/help.json`'s `summary` is the section's `@summary`, and a section without one has none; the JSON output gives a heading its section's `summary`. Each diagnostic in the diagnostics reference has a summary, the sentence saying when it's reported, so a product showing help for a diagnostic shows that. See [`@summary`](https://ascribed-dev.com/docs/writing/directives/) and [Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/).
- **`ascribe check` reports a permalink with no summary, and a summary too long to show.** `summary-missing` is advice at the `@id` line of a heading whose `@id` marks it a permalink and whose section has no `@summary`; a project that ships embedded help raises it to a warning in `[checks]`, so every permalink needs one. `summary-long` is advice at a `@summary` whose text is 300 characters or more, as plain text on one line; `[checks.summary-long] limit` sets another length. A permalink or a summary in a fragment is reported once, in the fragment. A page's own permalink has its description, which `page-description-missing` already reports. See [Write the summaries](https://ascribed-dev.com/docs/permalinks/in-your-product/).
- **An unknown key in `ascribe.toml` names the Ascribe that read it, which may be older than the project.** The message of `model-unknown-key` (ASC061) ends with the version, such as "unknown key `from` in `[ascribe.toml]`; this is ascribe 0.4.0, and the project may need a newer one", in `ascribe check`, the language server, and the MCP tools alike, and a command or tool that stops because `ascribe.toml` has errors quotes the first unknown key's message. Which keys a later version added can't be known, so the note is on every unknown key but those whose message already says where the setting went: a type's `files` and `default`, `[glossary] terms`, and `[[intended]] term`. See `docs/content/contracts/content-model/validation.md` and [decision #849](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/849-unknown-key-version.md).

### <a id="v0.4.3-the-ascribe-command"></a>The `ascribe` command

- **`ascribe check --format concise` takes `--limit`, and its `and N more:` command lists the rest.** The command after a cut list named the first file left out, so it often showed one problem of the rest; it's now the same check with `--limit all`, which lists every one. `--limit <n>` sets how many the list shows, 50 by default ([`ascribe check`](https://ascribed-dev.com/docs/checks/check/)).
- **`ascribe refs release:<target>@<version>` lists the marks that name a version in one release line.** `ascribe refs release:ascribe@0.4.0` finds each availability spec that names a version in that line of `[releases.ascribe]`, a history's steps and `planned` included, where a text search or `dimension:release` couldn't say which release a mark was for. Every `availability` place `refs` lists now shows its spec as written, after the use in text and as `spec` in JSON, which is null for every other use. The `ascribe_refs` tool takes the same target. See [`ascribe refs`](https://ascribed-dev.com/docs/command/questions/).
- **`ascribe versions release` removes the marks of the line it archives.** Where no build is versioned by the target, the release moves the old current line to `archived`, and each mark naming a version in it then says nothing: every line left has what it marks. The release now removes or rewrites each one `available-same-in-every-line` would report, as its fix does, along with a feature's spec, and lists them; `--dry-run` lists them without writing. The JSON lists them as `cleared` and `cleared_features` ([`ascribe versions release`](https://ascribed-dev.com/docs/versions/release/)).
- **`ascribe resolve go:<name>` shows the help a product shows for a permalink.** It prints the permalink's title, summary, text, and address, as the build's `_ascribe/help.json` has them, and `--format json` prints the entry as the file writes it, with `version`, `build`, and `line`. A name the build doesn't publish exits with `1` and names the closest; a retired one gives its reason. The `ascribe_resolve` tool takes the same `go:<name>`, and the rules `ascribe agents sync` writes say an agent can read a permalink's help with it. See [`ascribe resolve`](https://ascribed-dev.com/docs/command/questions/) and [Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/).

### <a id="v0.4.3-the-editor"></a>The editor

- **A problem's source names its project when the workspace has several.** The Problems panel shows `ascribe · handbook` where it showed `ascribe`, so you can tell which project's server found it ([Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/)).
- **The editor shows a section's summary on its heading, and on its Permalink row.** The hover on a heading, or on its `@id` line, shows the section's `@summary` in the editor's build, as `_ascribe/help.json` writes it, and This page's Permalink row has it in its tooltip, or says the section has none. The language server's `ascribe/page` gives each permalink its `summary`. See [Writing in the editor](https://ascribed-dev.com/docs/editor/writing/) and [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/).
- **Behavior change: a change on disk no longer starts a project's language server.** With `ascribe.startServers` at its default, `onDemand`, a server starts when one of the project's files is opened, or when something asks for the project: **Switch Project**, the Project view, a command run on it, or **Check Every Build**. Before, a branch switch, a pull, a test run that rewrites fixtures, or an agent writing files could start a server for every project it touched, each running until the window closed. A project that hasn't started still shows no problems and "not checked yet"; a server already running still follows changes on disk. See [When servers start](https://ascribed-dev.com/docs/editor/settings/#when-servers-start).

### <a id="v0.4.3-the-language-server"></a>The language server

- **The language server and the MCP server give the version `ascribe --version` prints.** A build that isn't a release says `0.4.1+dev (7ae1340440fc)`, with its commit, in the language server's `serverInfo` and its first log line, `ascribe-lsp 0.4.1+dev (7ae1340440fc): using the project at …`, and in the MCP server's `serverInfo`, where each said `0.4.1` before. Which binary is answering an editor or an agent is there to read, without `ps` or `lsof`.

### <a id="v0.4.3-astro-and-the-elements"></a>Astro and the elements

- **`@ascribed/astro` serves each permalink's help entry as JSON, for each live line.** A product fetches its own line's at `<permalink-path><name>.json`, such as `/docs/go/connect.json`, or `/docs/3.4/go/connect.json` for an older line, and falls back to the copy of `_ascribe/help.json` it bundled. The build's `_ascribe/headers.json` gives each address `Access-Control-Allow-Origin: *`, so a product on another origin can read it ([Show the docs in your product](https://ascribed-dev.com/docs/permalinks/in-your-product/)).

### <a id="v0.4.3-agents"></a>Agents

- **The plugin runs the project's `ascribe` binary, as the VS Code extension does.** Its language server, stop hook, and MCP server used to run `ascribe` from the path, so a session got whatever was installed globally, not the version the project pins. They now run, in order, the binary `ASCRIBE_PATH` names, the project's `node_modules/.bin/ascribe`, and then `ascribe` on the path, through a launcher the plugin ships. See [Claude Code](https://ascribed-dev.com/docs/agents/claude-code/).
- **Agents are told to give a heading with a permalink a summary.** In a project whose headings declare permalinks, the rules `ascribe agents sync` writes say to give each section a `@summary`, the sentence a product shows with its help, and how to choose between `@summary` above a paragraph and `@summary: <text>`; the skill says so too. `ascribe check --format prompt` quotes the text of each section with no summary under its problem, so an agent can write the summary from the prompt. See [What the instructions say](https://ascribed-dev.com/docs/agents/instructions/).

### <a id="v0.4.3-the-docs-and-the-site"></a>The docs and the site

- **The content checks' diagnostics are on two pages.** The checks of outputs, prose, external links, the published site, and tested examples moved from [Diagnostics: content checks](https://ascribed-dev.com/docs/checks/diagnostics/content-checks/) to [Diagnostics: content checks of outputs and the site](https://ascribed-dev.com/docs/checks/diagnostics/output-checks/), since with them the page was over `page-size`'s 50,000 characters. Their permalinks lead to the new page. See [decision #876](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/876-content-checks-split.md).
- **The content model's diagnostics are on two pages.** The diagnostics about the consumer, builds, the editor, `[permalinks]`, `[crawlers]`, `[listing]`, and `[checks]` moved from [Diagnostics: the content model](https://ascribed-dev.com/docs/checks/diagnostics/content-model/) to [Diagnostics: the content model's builds and site](https://ascribed-dev.com/docs/checks/diagnostics/content-model-builds/), since with them the page was over `page-size`'s 50,000 characters. Their permalinks lead to the new page. See [decision #896](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/896-content-model-split.md).
- **The schema of `ascribe model`'s JSON is on a page of its own.** It moved from [the questions page of the JSON report contract](https://ascribed-dev.com/docs/contracts/json-reports/questions/) to [`docs/content/contracts/json-reports/model.md`](https://ascribed-dev.com/docs/contracts/json-reports/model/), since with it the questions page was over `page-size`'s 50,000 characters. The schema itself is unchanged. See [decision #845](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/845-model-json-page.md).

### <a id="v0.4.3-fixes"></a>Fixes

- **Fixed:** `ascribe build` takes about a third less memory at its peak again. Since 0.4.1, it kept the index the checks had made of the project while it indexed the project again to write the outputs; it now lets the first go. On the 3,000-page benchmark project, the peak is back from 392 MB to 275 MB.
- **Fixed: the extension's Marketplace changelog no longer opens with an empty Unreleased section.** It read as if the release it describes weren't out yet. The extension's package now carries the changelog without an unreleased section that has nothing in it.
- **Fixed: `ascribe --help` describes `baseline` and `build` each as itself.** `baseline` read "Build the documentation set's outputs", and `build` read "Arguments of `ascribe build`". A test now holds every command in `--help` to a description of its own.
- **Fixed:** the language server answers a keystroke in a large project faster again. Since 0.4.0, resolving each page an edit reaches looked through every file of the project for glossary terms no glossary page shows; and since 0.4.2, each round of checks matched every file's problems against `ascribe.baseline` even when the project has none. On the 3,000-page benchmark project, a keystroke in a fragment 30 pages include went from about 20 ms to 15 ms, and one in a page from 5 ms to 3.6 ms.
- **Fixed:** A model that stops loading clears every page's problems until it loads again. The language server put `ascribe.toml`'s problems in the Problems panel and left every page's last problems there, found with the model that no longer loads; now they're cleared, and published again once `ascribe.toml` loads ([Problems and fixes](https://ascribed-dev.com/docs/editor/problems/)).
- **Fixed:** the language server no longer uses a third of a core while a build writes in the project. Each file the editor's watcher reported, such as a build's output, a dev server's cache, or an image, ran the checks across the project again, about 37 ms on a project of 240 pages. They now run again only when the change reaches what the project reads: a page, a fragment, a file a page uses that appears or goes, or a memo.
- **Fixed:** A project whose folder is deleted takes its server and problems with it. Deleting a folder, as removing a git worktree does, is reported as the folder alone, so the extension missed the `ascribe.toml` inside it, and kept that project's server running and its problems in the Problems panel; now it stops the server, which clears them ([Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/)).
- **Fixed:** A language server that doesn't stop when asked is killed, with any process it started. The editor left it running after "Stopping server timed out", and a server whose window had closed could run on for days; now it's killed when its stop times out, and every server still running is killed as the window closes ([Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/)).

## <a id="v0.4.2"></a>0.4.2 (2026-10-22)

### <a id="v0.4.2-the-language"></a>The language

- **Behavior change: a project with a `site` publishes `llms.txt` and each page's Markdown.** `[consumer] agents` is `true` when `site` is set and `false` when it isn't, so a site with an address publishes what agents read unless it says `agents = false`. Only an explicit `agents = true` without `site` is still `model-consumer-agents-site`. `@ascribed/astro` reads it the same way, and publishes them with the site. See [The outputs for agents](https://ascribed-dev.com/docs/content-model/output/#consumer).
- **Every source leaves out `node_modules` and `.git` folders.** Whatever its `ignore` says, no file in a `node_modules` or `.git` folder, at any depth, is readable from a source, so a source over a JavaScript project needs no `ignore = ["**/node_modules/**"]`. See [`[sources.<name>]`](https://ascribed-dev.com/docs/content-model/sources/#20-sourcesname).
- **`prose-not-checked` is reported only where the project asked for prose checks everywhere.** With `[checks.vale] in-check = true`, or `ascribe check --vale`, a Vale that can't be run is still one advice; without either, the editor checks the prose wherever Vale runs and says nothing where it doesn't. See [Prose, through Vale](https://ascribed-dev.com/docs/checks/vale/#where-vale-runs).
- **`[from]` takes tables and keys from another project's `ascribe.toml`.** `[from."../../docs/ascribe.toml"] take = ["releases", "phrases.version", "consumer.site"]` reads what it names as if it were written here, so two projects in one repository keep one copy of what they share. The other file must be in the same repository; its own `[from]` isn't followed; a key both taken and written is an error (`model-from-written`); and `spec`, `project`, and `from` can't be taken. The editor watches the other file, hovers a taken key with where it's written, and goes to it; edit commands and `ascribe versions release` stop with an error naming the file that holds a taken key; and `@ascribed/astro` and the extension ask the binary for what's taken. See [the `ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/).
- **The editor and `ascribe model` say where a field is declared, and a repeated field is advice.** Hovering a field's key in `ascribe.toml`, or a frontmatter key in a page, says whether the field is `[frontmatter]`'s, the type's own, or the implicit `title`; on a shared field it names the types that replace it. `ascribe model` says the same for each field, in its text and in a new `declared` field of its JSON. A type's field that's the same as `[frontmatter]`'s, key for key, is reported as advice at the type's field (`frontmatter-field-repeated`), with a safe fix that deletes it, so a copy that would drift from the shared one is cleaned up. See [the content model reference](https://ascribed-dev.com/docs/content-model/#41-page-types).
- **`planned` is a version word in an availability spec.** `@available: ascribe planned`, or `cloud, self-managed preview planned`, marks content for its target's planned line without writing the line's number, and reads, checks, filters, and shows as that line's version does. Each target says its own, so a spec can name two targets that each have a planned line, and a history can end in one: `self-managed (preview 3.5, ga planned)`. A target with no planned line, or more than one, is an error at the word, `available-planned-line`. `planned` isn't a feature key, a feature's spec can't use it, and no lifecycle state, dimension, value, or feature key can be named `planned`. The editor offers `planned` after a target and after a state, and its hover names the line. See `docs/content/writing/directives.md` and `docs/content/versions/lines.md`.
- **`[releases.<target>]` declares its target, so a product needs no dimension of its own.** A target no declared dimension has is a value of an implicit dimension, `release`, which `@variant {release=…}`, builds' `variants`, and everything else that names a dimension can name. `[releases] label` labels the dimension, `Release` unless set, and `[releases.<target>] label` the target. A project that declares its own `release` dimension while a target is left to the implicit one gets `model-releases-target`. See [the `[releases]` reference](https://ascribed-dev.com/docs/content-model/content/).
- **Behavior change: `[frontmatter]` declares fields every page type has, and `title` is implicit.** A type's own field of the same name replaces a shared one whole, for that type, and a type can have no keys at all, as `[types.index]`. Every type has a required string `title` whether it declares it or not, so `model-type-title` is gone; a declared `title` that's optional or not a string is `model-field-reserved`. A field named `description`, `review-date`, or `owner` now has that role without `role`, unless the type gives the role to another field, so `page-description-missing` and `review-overdue` can report pages they didn't before. Project Settings shows `[frontmatter]` as a card under **Content types**, and `ascribe templates eject` leaves out of a type it adds the fields every type has already. See [the content model reference](https://ascribed-dev.com/docs/content-model/#4-typesname-and-fragments).
- **A type with no `template` or `guide` takes them from `templates/templates.lock`.** When the lock `ascribe templates eject` writes records a template under the type's name, the type has that template and its guide, with nothing about them in `ascribe.toml`; a type that names either keeps what it names. A file the lock gives that isn't there is the type's error, `model-type-template`, at its `[types.<name>]`. `ascribe templates eject` into `templates/` no longer writes the two keys, and with `--keep-type` leaves `ascribe.toml` untouched; with `--to` another folder it still names them. In the editor, the hover on a type's name in `ascribe.toml` says where its template comes from. The docs' own five set types drop their `template` and `guide` lines, with the same outputs ([page types](https://ascribed-dev.com/docs/content-model/), [page templates](https://ascribed-dev.com/docs/templates/)).

### <a id="v0.4.2-the-ascribe-command"></a>The `ascribe` command

- **`ascribe.baseline` puts a project's existing advice off, so `ascribe check` reports only what's new.** `ascribe baseline write` lists every advice the project has in `ascribe.baseline`, beside `ascribe.toml`, to commit; from then on `ascribe check` holds those back and counts them (`2 advice, 3412 in the baseline`, and `summary.baselined` in its JSON), and reports an entry that matches nothing as `baseline-entry-unmatched`, which `ascribe baseline prune` removes. Only advice is ever held back, and `--no-baseline` reports everything. [The baseline](https://ascribed-dev.com/docs/checks/baseline/) has the commands and the file's syntax.
- **`ascribe --help` opens with what Ascribe is: a toolchain for documentation written as code.** It opened with a list of commands, which the help gives below it anyway.
- **Behavior change: `ascribe versions release` rewrites `planned` and moves no features.** Releasing a target's planned line rewrites each mark's `planned` for that target to the line's version, as `ascribe planned` becomes `ascribe 0.4.2`, where it rewrote marks that named a feature before. A feature's spec names its versions, so it's left as it is, `--next` only adds the line planned after the release, and a release no longer stops for a feature with no line to move to. The JSON's schema version is 2: `features` is gone, and each mark's `from` is its spec as written. See `docs/content/versions/release.md`.
- **`ascribe report agents` says which checks are failing since the last report.** From its second run on the same site, it says "23 checks: 21 passing, 2 failing, 1 of them failing since 9 October" and names the new ones; its JSON's `agents` gains `previous_run`, `new`, `resolved`, and `new_checks`, `null` or empty on a first run, on another site, and with `--no-history`. Every check still runs every time: the report only remembers how each came out, in `.ascribe/history/report.json`. [The project report](https://ascribed-dev.com/docs/checks/report/#the-site-and-agents) says what's new means.
- **`ascribe report` says which problems are new since the last report, and how many are gone.** From its second run, `problems` says "14 problems, 2 of them new since 9 October; 5 the last report listed are gone" and lists the new ones; its JSON gains `previous_run`, `new`, `resolved`, and `new_items`, `null` or empty on a first run and with `--no-history`, which neither reads nor writes the history. A problem keeps its identity when lines are added above it and is new when the text it's about is edited. The last run's problems are kept in `.ascribe/history/report.json`. [The project report](https://ascribed-dev.com/docs/checks/report/) says how a problem is told from another.

### <a id="v0.4.2-the-editor"></a>The editor

- **The editor leaves folders git ignores, and build and dependency folders, out of its projects.** An `ascribe.toml` that git ignores, one in a folder such as `target`, `dist`, `vendor`, or `.venv`, and one in a source's copies (`sources/<name>/`) is no longer a project: it isn't listed in **Switch Project** and starts no language server, as when `cargo test` writes projects into `target/`. To work on one, open its folder as a workspace folder of its own. [Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/) lists what's left out.
- **The Project view remembers the last Check Every Build between sessions.** When VS Code opens and nothing in the project changed since the check started, its counts are current, saying when ("checked yesterday at 3:30 PM"), and its problems are back in the Problems panel. A file of the project modified, added, or removed since, as a branch switch does, leaves them out of date, with **Check Every Build**. A check from another version of the extension isn't read. See [The Project view](https://ascribed-dev.com/docs/editor/sidebar/#the-project-view).
- **Ascribe has a new mark.** The extension's Marketplace and activity bar icons, the docs site's logo and favicon, and the social cards are redrawn: a pixel a with the change marks of a gutter beside it, in deep blue.
- **This page and the Project view count problems as icons with counts, as the status bar does.** An error, warning, or info icon for advice, each with its count, leaving out a severity there's none of; the tooltip and a screen reader say them in words, such as "2 errors, 1 warning, and advice on 3 lines", and so does Check Every Build's notification. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/).
- **The Project view's Health groups its counts by when each is from, under Actions.** Every build, As you type, and At the last save each say once when their counts are from, with **Check Every Build** or **Check Agent Files** at the group's head while they aren't current, and each count is one line, its label then its value. Health's "to do" counts only the current counts above zero. Actions come first, so they stay put as the counts change. See [The Project view](https://ascribed-dev.com/docs/editor/sidebar/#the-project-view).
- **The preview and a page's problems follow an edit to the code a `@snippet` shows.** When a code file a snippet shows is saved, created, or deleted, by you or by a branch switch, every page that shows it, directly or through a fragment, is checked again, and the preview of one redraws, its tested line included; a burst of changes checks each page once. An unsaved edit to the code file still waits for its save. The language server tells the client with a notification of its own, `ascribe/preview/changed`. See [the preview](https://ascribed-dev.com/docs/editor/preview/).
- **A code file's tag problems are shown in the code file, at the tag.** A `snippet-tags` problem, a region's tag never closed, an `-end` with nothing to close, a region named twice, or a reserved tag, is shown in the code file too while a page shows it, with the page's message and severity, once however many `@snippet`s show the file, naming each. The page keeps its own; saving the fixed file clears both. `ascribe check` and its JSON don't change. See [writing in the editor](https://ascribed-dev.com/docs/editor/writing/#code-files-a-page-shows).
- **This page's Address and Markdown rows open the page on the live site.** When the project names its `[consumer] site`, choosing either opens the site's origin and the address in your browser, through VS Code, which asks first about a domain it doesn't trust. It's the published site, so a page that's new or moved on your branch isn't there yet; the tooltip says so. Without a `site`, for a fragment, and where the build the status bar shows leaves the page out, they stay text ([the sidebar](https://ascribed-dev.com/docs/editor/sidebar/)).

### <a id="v0.4.2-the-language-server"></a>The language server

- **The editor reads `ascribe.baseline`: what it holds back isn't shown, and a stale entry is.** The language server leaves out the advice the baseline lists, as `ascribe check` does, and follows the file as it's typed and on disk. The hover on a held-back finding's line says the baseline holds it, and its quick fix, **Move out of the baseline, mark as intended, and write why**, takes its entry out of the file and writes an `@intended` where the check's place says. An entry that matches nothing is shown on `ascribe.baseline` as `baseline-entry-unmatched`, when the editor's build is the project's every build. See [The baseline](https://ascribed-dev.com/docs/checks/baseline/#in-the-editor).
- **Rename a release target from its `[releases.<target>]` header.** `F2` on the target in the header of a target the implicit `release` dimension has renames it everywhere a rename from a page does. See [Refactoring](https://ascribed-dev.com/docs/editor/writing/#refactoring).
- **Go to Definition on a `@snippet`'s address opens its code, and its hover shows the code.** Go to Definition opens the code file at the region's start tag, or at its first line for a snippet of a whole file; a missing file or region goes nowhere, as a broken link does. The hover adds the code after what it said before, as the page shows it: the region, with its `remove` tags applied, in a fenced block in the snippet's language, cut at 20 lines with how many more there are ([writing in the editor](https://ascribed-dev.com/docs/editor/writing/)).

### <a id="v0.4.2-astro-and-the-elements"></a>Astro and the elements

- **Behavior change: the elements and review take Deep River and Shallows, Ascribe's new colors.** The element library, review's marks and comment threads, the review panel in Astro's dev toolbar, the HTML report, and an archive's frame move from the indigo palette to the mark's: deep blue-gray neutrals, river blue for links, notes, steps, and what changed, moss for a tip and what was added, and amber for focus rings. Review's links and buttons are river, and a review thread and a hovered tab sit on a tinted panel that shows on a white page. Important notes and moved blocks are violet, warnings yellow, and cautions and removed blocks crimson, kept apart from moss and river for readers with red-green color blindness. Every text color passes 4.5:1 on its background, in light and in dark. The HTML report's headings are set in the system's serif. A site that doesn't set the `--ascribe-*` and `--ascribe-review-*` custom properties looks different; their names are unchanged. To keep the previous look, add this to the site's stylesheet, after the element library's and review's:

  <details>
  <summary>The previous default colors</summary>

  ```css
  /* The element library */
  @supports (color: light-dark(#000, #fff)) {
    :root {
      --ascribe-border-color: light-dark(#e3e6ec, #393d46);
      --ascribe-muted-color: light-dark(#676c75, #a5a9b2);
      --ascribe-note-color: light-dark(#474dc6, #91a1fe);
      --ascribe-note-background: light-dark(#f5f6fe, #161b3a);
      --ascribe-tip-color: light-dark(#057256, #22c598);
      --ascribe-tip-background: light-dark(#e1fff2, #02251a);
      --ascribe-important-color: light-dark(#932f8e, #ffc8f9);
      --ascribe-important-background: light-dark(#fff2fd, #2d132b);
      --ascribe-warning-color: light-dark(#845502, #e0992e);
      --ascribe-warning-background: light-dark(#fef5eb, #2b1a03);
      --ascribe-caution-color: light-dark(#a73303, #fe7f57);
      --ascribe-caution-background: light-dark(#fef4f1, #341308);
      --ascribe-steps-color: light-dark(#5962e8, #91a1fe);
      --ascribe-steps-marker-text-color: light-dark(#ffffff, #0c0f16);
      --ascribe-tab-color: light-dark(#676c75, #a5a9b2);
      --ascribe-tab-active-color: light-dark(#5962e8, #91a1fe);
      --ascribe-tab-focus-color: light-dark(#5962e8, #b3c0ff);
      --ascribe-tab-hover-background: light-dark(#f7f9fd, #13171f);
      --ascribe-state-color: light-dark(#676c75, #a5a9b2);
      --ascribe-state-background: light-dark(#f0f3f7, #22272e);
      --ascribe-state-ga-color: light-dark(#057256, #22c598);
      --ascribe-state-ga-background: light-dark(#e1fff2, #02251a);
      --ascribe-state-preview-color: light-dark(#932f8e, #ffc8f9);
      --ascribe-state-preview-background: light-dark(#fff2fd, #2d132b);
      --ascribe-state-beta-color: light-dark(#474dc6, #91a1fe);
      --ascribe-state-beta-background: light-dark(#f5f6fe, #161b3a);
      --ascribe-state-deprecated-color: light-dark(#845502, #e0992e);
      --ascribe-state-deprecated-background: light-dark(#fef5eb, #2b1a03);
      --ascribe-state-removed-color: light-dark(#a73303, #fe7f57);
      --ascribe-state-removed-background: light-dark(#fef4f1, #341308);
    }
  }

  /* Review's marks and overlay */
  :root {
    --ascribe-review-added: #057256;
    --ascribe-review-added-background: #c7fae5;
    --ascribe-review-changed: #845502;
    --ascribe-review-changed-background: #fee9d0;
    --ascribe-review-removed: #ca3f02;
    --ascribe-review-removed-background: #fef4f1;
    --ascribe-review-moved: #932f8e;
    --ascribe-review-moved-background: #fff2fd;
    --ascribe-review-muted: #676c75;
    --ascribe-review-surface: #ffffff;
    --ascribe-review-flash: #fee9d0;
    --ascribe-review-focus: #5962e8;
    --ascribe-review-text: #22272e;
    --ascribe-review-border: #e3e6ec;
    --ascribe-review-thread: #f7f9fd;
    --ascribe-review-input: #ffffff;
    --ascribe-review-accent-text: #ffffff;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-ascribe-scheme="light"]) {
      --ascribe-review-added: #22c598;
      --ascribe-review-added-background: #003929;
      --ascribe-review-changed: #e0992e;
      --ascribe-review-changed-background: #432900;
      --ascribe-review-removed: #fe7f57;
      --ascribe-review-removed-background: #511b0a;
      --ascribe-review-moved: #ffc8f9;
      --ascribe-review-moved-background: #461c43;
      --ascribe-review-muted: #a5a9b2;
      --ascribe-review-surface: #0c0f16;
      --ascribe-review-flash: #432900;
      --ascribe-review-focus: #b3c0ff;
      --ascribe-review-text: #f0f3f7;
      --ascribe-review-border: #393d46;
      --ascribe-review-thread: #13171f;
      --ascribe-review-input: #0c0f16;
      --ascribe-review-accent-text: #0c0f16;
    }
  }

  :root[data-ascribe-scheme="dark"] {
    --ascribe-review-added: #22c598;
    --ascribe-review-added-background: #003929;
    --ascribe-review-changed: #e0992e;
    --ascribe-review-changed-background: #432900;
    --ascribe-review-removed: #fe7f57;
    --ascribe-review-removed-background: #511b0a;
    --ascribe-review-moved: #ffc8f9;
    --ascribe-review-moved-background: #461c43;
    --ascribe-review-muted: #a5a9b2;
    --ascribe-review-surface: #0c0f16;
    --ascribe-review-flash: #432900;
    --ascribe-review-focus: #b3c0ff;
    --ascribe-review-text: #f0f3f7;
    --ascribe-review-border: #393d46;
    --ascribe-review-thread: #13171f;
    --ascribe-review-input: #0c0f16;
    --ascribe-review-accent-text: #0c0f16;
  }
  ```

  </details>

  See [the elements' custom properties](https://ascribed-dev.com/docs/astro/elements/#custom-properties).

### <a id="v0.4.2-agents"></a>Agents

- **The agents' hook, `ascribe_check`, and the `fix` prompt hold back what `ascribe.baseline` lists.** An agent working on a page isn't sent after the project's backlog: the hook leaves the advice the baseline lists out and says how many it held back, and `ascribe_check` counts it as `ascribe check` does (`, 3 in the baseline`, and `summary.baselined`). See [The baseline](https://ascribed-dev.com/docs/checks/baseline/#for-agents).
- **The plugin names Bryo Studio, Ascribe's maker, as its author and its marketplace's owner.** Its manifests named Ascribe, with the GitHub organization's address; they now name Bryo Studio, with https://bryostudio.com. The npm packages already named Bryo Studio as their author, and the platform packages now do too.

### <a id="v0.4.2-the-docs-and-the-site"></a>The docs and the site

- **Seven more of the docs' guides have a permalink.** [Content that varies](https://ascribed-dev.com/docs/writing/variants/) is at `/go/variants/`, [Publish a site for each version](https://ascribed-dev.com/docs/versions/) at `/go/versions/`, [Set up a glossary](https://ascribed-dev.com/docs/content-model/glossary/) at `/go/glossary/`, [The preview and builds](https://ascribed-dev.com/docs/editor/preview/) at `/go/preview/`, [Permalinks](https://ascribed-dev.com/docs/permalinks/) at `/go/permalinks/`, [Leave a memo for whoever edits next](https://ascribed-dev.com/docs/writing/memos/) at `/go/memos/`, and [Publish for AI agents](https://ascribed-dev.com/docs/astro/agents/) at `/go/publish-for-agents/`, so a link to one keeps working when the page moves. The landing page links to each by it.
- **The landing page is rebuilt around Ascribe's pillars.** ascribed-dev.com opens with the install command and getting started above the fold, then shows a page's source beside what readers see, the tools of a docs team, the checks, and what Ascribe gives AI agents, each with a real example and a permalink into the docs, then where engineers, writers, and agents each start. Why Ascribe says "docs as code" in its opening.
- **The site takes the new colors and serif headings, and the landing page a new headline.** ascribed-dev.com follows the system's scheme in Shallows or Deep River, as the elements and review now do, and sets its headings in the system's serif. The landing page leads with "Docs as code, with the tools docs need.", and its sentence says what Ascribe is: a toolchain for documentation written as code. The screenshots in the docs are retaken in the new colors.
- **The site's notes carry the gutter's split bar, and the landing page's mark moves.** On ascribed-dev.com, every note, tip, important note, warning, and caution draws its bar as the mark's two change marks, in its own color; the element library's own notes keep one bar. The mark beside the landing page's headline plays once through writing, changed, and reviewed, and holds still in the changed state for a reader who prefers reduced motion.
- **Why Ascribe no longer says the content model has no navigation.** Its "What it doesn't do" list now says what `ascribe.nav` leaves out, that a section is a folder, and says the element library works on any site. The [Navigation](https://ascribed-dev.com/docs/navigation/) page has the permalink `navigation`.

### <a id="v0.4.2-installing"></a>Installing

- **The npm packages, the extension, and the plugin say what Ascribe is in the same words.** Each package's description on npm, the extension's on the Marketplace, and the agents' plugin's start with the line `ascribe --help` opens with, then say what that package does. Each package lists the same keywords, and its homepage is its guide on ascribed-dev.com rather than its folder on GitHub.

### <a id="v0.4.2-fixes"></a>Fixes

- **Fixed:** an empty widget is no longer wrapped in a paragraph by a CommonMark renderer. The site output writes its opening tag on one line and its closing tag on the next, an HTML block of its own, as the element contract promised, so `<p><story-pillar></story-pillar></p>` becomes `<story-pillar>` alone, in the editor's preview and in Astro alike. A site whose CSS selected the paragraph around an empty widget, such as `p:has(> my-widget)`, selects the element instead.
- **Fixed:** This page and the Project view sit on the sidebar's color in a dark theme, not on gray. Their stylesheet set a color scheme on the view's root, which VS Code's own page doesn't, so Chromium painted its dark backdrop behind the view, and the sticky section headers showed as a strip of the sidebar's color across it. The build switch's dropdown still draws in the theme's scheme.

## <a id="v0.4.1"></a>0.4.1 (2026-10-21)

### <a id="v0.4.1-the-language"></a>The language

- **Old availability marks are reported in a build that shows badges.** In a build that badges availability and isn't versioned, such as one site, a mark whose versions are all before the oldest live line, with its last state available, is now `available-same-in-every-line` advice too, with its own message: the badge is shown, and says nothing a reader of any live line needs. Its fix, the mark without the versions, is `unsafe` there, since it removes the badge; where no output shows the versions it stays `safe`. A project that keeps such badges sets `available-same-in-every-line = "off"` in `[checks]` ([docs/content/content-model/content.md](https://ascribed-dev.com/docs/content-model/content/)).

### <a id="v0.4.1-the-ascribe-command"></a>The `ascribe` command

- **`ascribe report links` asks again about a link whose server answered with an error.** An address that answers with a `5xx` status is asked about a second time after 30 seconds, on every run, and only that answer counts, so a server's passing failure isn't reported. One that fails again is still reported, as `link-external-broken`, with a message that says the server failed, which may be temporary, instead of that the link is broken for readers. When asking again can't run, the first answer stands, and the message doesn't say it was asked twice. A `404` or a `410` is asked about once and reported as before. `ASCRIBE_LINKS_RETRY_WAIT` sets the wait in seconds. See [Check external links](https://ascribed-dev.com/docs/checks/external-links/).
- **`ascribe model` shows every section of `ascribe.toml`, each key with the value in effect.** `--section` takes `project`, `consumer`, `editor`, `checks`, `crawlers`, `listing`, and `permalinks`, and the MCP tool's `section` with it; each key is shown with its value, the default where the file leaves it out. The JSON has each section, and the text without `--section` names them at its end, still within 4,000 characters. The keys and defaults come from the registry the loader reads. See `docs/content/command/questions.md`.
- **`ascribe observe` asks before it runs the commands, when its input is a terminal.** It lists the `test` and `[environment]` commands of `ascribe.examples.toml`, asks **Run them? [y/N]**, and runs nothing, and writes nothing, unless the answer is yes; then it exits 2. `--yes` skips the question. In CI or a hook, where standard input isn't a terminal, it runs them as before, without asking. See [`ascribe observe`](https://ascribed-dev.com/docs/examples/sources/#cli-observe).
- **`ascribe refs` takes an image, and lists the files that show it, link to it, or name it.** `ascribe refs images/keys.png` lists each place as `image`, `link`, `html` (raw HTML such as `<img src>`), or `frontmatter` (a field such as `cover`), the uses `image-unused` reads, and an image nothing uses answers with no places and exit code `0`. A file that's neither a page, a fragment, nor an image says what `refs` takes, instead of that it doesn't exist. The MCP server's `ascribe_refs` takes an image too. See `docs/content/command/questions.md`.
- **Behavior change: `ascribe render` is now `ascribe resolve`, and its MCP tool is `ascribe_resolve`.** It takes the same arguments and options, and writes the same output and exit codes: a page resolved for one build, written as the build's plain Markdown output. Its JSON's schema is `schemas/resolve.schema.json`, still at `schema_version` 1 with every field as it was. There's no alias: `ascribe render` is an unknown command, and `ascribe_render` an unknown tool. Agent files and skills written by `ascribe agents sync` before this release name the old command until it runs again. See `docs/content/command/questions.md`.
- **`ascribe search` finds a fragment's text, a snippet's code, and a page's title and description.** A line of a fragment is one place however many pages include it, with the pages that show it: the first three, each with the heading it's under, then the `ascribe refs` command for the rest. A match in a snippet's code is at the code file's line, `through code:<file>#<region>`. A page's title and its description are at their keys in the frontmatter, and titles come first. See `docs/content/command/questions.md`.
- **`ascribe search --format json` writes the places as a document of its own.** Each place has its `file`, `line`, and `column`, what it is (`in`), the block's whole `text` and where the `match` is in it, in characters, what it reads `through` (a phrase, a link's borrowed title, a snippet's code), its `arm`, and the first three `pages` that show it, each with its title, heading, id, and builds. The document has the totals, `truncated` and `next_command` when the list is cut at 500, the pages with `--summary`, and, when nothing matched, `source_places` and `not_searched_builds`. Its schema is `schemas/search.schema.json`. See `docs/content/command/questions.md`.
- **`ascribe search` narrows to paths, a type, case, or whole words, and summarizes by page.** Paths after the query keep it to the pages there, a fragment's line counted only for them; `--type` keeps to a content type; `--case` matches case as written; `--word` matches only where a word or a phrase isn't inside a longer one. `--summary` lists the pages and how many places each shows, most first. When nothing matches, the last line says whether the words are in no page, written where no build searched publishes them (at the first place, and how many more), or, with `--build`, published by builds not searched, which it names. See `docs/content/command/questions.md`.
- **`ascribe search` finds words as readers read them, and names the line to edit.** It reads each page as every build publishes it, phrases filled in, so `ascribe search '"sign in to quill cloud"'` finds the line that says `Sign in to {cloud}`, says it reads through the phrase `cloud`, and names the heading and the arm it's under. A match wholly inside a phrase's value is at the phrase's line in `ascribe.toml`, and the rest of its block is still searched for the line's own matches. See `docs/content/command/questions.md`.

### <a id="v0.4.1-the-editor"></a>The editor

- **Observe Examples runs `ascribe observe` as a task, from the Project view or Run Task.** It runs in the folder of each source with tested examples, which the binary's `ascribe model` names, and `ascribe observe` lists the commands and asks in the terminal before running them. `tasks.json` names a source with `source`. It isn't offered in Restricted Mode. See [Tasks](https://ascribed-dev.com/docs/editor/settings/#tasks).
- **Pages starts with every section closed.** Opening a page still selects it in Pages and opens the sections above it, so the open page's path is what you see first. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#pages).
- **Project Settings shows and changes the settings in `ascribe.toml`.** **Ascribe: Open Project Settings** opens a tab of the project's `ascribe.toml` settings, each with its value, its default, its description, and the line that sets it: every simple setting, under its table, and every configurable check, with its level and, for `page-size`, `image-large`, and `memo-long`, its limit. A value is changed in place, unsaved, with the file's comments kept; one the file leaves out is added to its table; and **Reset to default** takes a setting's line out, with the comment that touches it. The tab follows the file as it's typed and undone. A search finds a setting by a word of its name, its key, or any sentence of its description, and **Set in ascribe.toml** keeps to what the file sets; content types, dimensions, releases, builds, and sources are cards that open the file at their table. The tab opens from `ascribe.toml`'s title bar too, on a project the extension found and not on a fixture's that `ascribe.projects.exclude` leaves out. See [Project Settings](https://ascribed-dev.com/docs/editor/settings/#project-settings).
- **The Project view counts the rest of Health:** tested examples changed or never tested, overdue reviews, and the permalinks list from the last **Check Every Build**; pages nothing links to and content model entries nothing uses, as you type; and the agent files out of date, checked again after each save. Each count opens its list: the Problems panel at the first problem, Pages or Content model with their filter on, or **Sync Agent Files** with the files it would change. Each goes out of date by its own rule, overdue reviews when the day changes, and a dash never looks like a dimmed `0`. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#the-project-view).
- **Apply Every Safe Fix and Format Every Page edit the whole project, from the Project view.** Each is one edit the language server makes, over the text the editor has, unsaved edits included, and the extension says how many fixes in how many files before it applies it. The files are left unsaved on purpose, to read before you save them, and Undo takes the edit back. The safe fixes are the editor build's; every build's stay `ascribe check --fix`'s. Both are in the Command Palette too. See [Edits to the whole project](https://ascribed-dev.com/docs/editor/sidebar/#edits-to-the-whole-project).
- **The Project view switches the build, restarts its server, and opens Project Settings.** Its head has **Show Output** and **Restart** beside the server's state, a **Build** switch that moves the status bar, the preview, and the build lens with it, and, beside the project's name, a gear for Project Settings and a button for Ascribe's settings in VS Code. Its Actions are grouped as Check, Write, and Review and agents, with **Start Review** and **Prompt Agent to Fix This Project** among them, and **About**, closed at first, names the content root, the builds, the release lines, the sources, and the content types. In Restricted Mode it says Ascribe isn't running, with every action off. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#the-project-view).
- **Behavior change: Switch Project is in the Project view's title bar, not Pages' or Content model's.** It's still in the Command Palette and the status bar's menu, in a workspace with several projects.
- **Build and Sync Agent Files run as tasks, from the Project view or Run Task.** Each runs the binary the project's server runs, in the terminal, in the project's folder, with `--config` when the project isn't the workspace folder. The task type, `ascribe`, can be named in `tasks.json`. Neither is offered in Restricted Mode. See [Tasks](https://ascribed-dev.com/docs/editor/settings/#tasks).
- **A Project view heads the Ascribe sidebar, counting every build's problems while they're current.** It shows the project's language server and binary, and the problems the last **Check Every Build** found, by severity, with when it ran. A change to any file of the project marks the count out of date instead of showing it as current, and **Check Every Build** beside it checks again. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#the-project-view).
- **Fixed:** Check Every Build goes out of date when `ascribe.nav`, a memo, or any project file changes. Before, only a change under the content root counted, so an edit to `ascribe.nav` or `ascribe.permalinks` left the status bar saying every build was checked.
- **Search Pages as Readers See Them finds words as readers read them, as you type, and opens the line.** The command in the Command Palette is `ascribe search` in the editor: each place is the line to edit, through phrases and fragments, in the build the status bar names or, with its button, every build, and it searches unsaved text. See [Search](https://ascribed-dev.com/docs/editor/writing/#search).
- **This page heads the page with its title, type, and path, and badges what needs doing.** The title draws its code spans as code where its type's `title` reads them. A row that can warn says its state first, such as an example's "changed since it was tested" before its address, with what it was tested with and the heading it's under after it. An open section's header stays at the top while you scroll its rows. The view's badge counts Health's things to do, so you see them with Health closed. When the page's language server failed to start, the view says so with **Show Output** and **Restart**, and in Restricted Mode it says Ascribe isn't checking the folder, with **Manage Workspace Trust**. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#this-page).
- **This page opens includes beside the page, and keeps your place as the cursor moves.** An include, a snippet, or a tested example opens in the editor group beside the page's, with the focus left in the view, and This page stays on the page. As the cursor moves, the view changes only what it says, keeping its scroll and the row you're on, and **Up** and **Down** move between its rows. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#this-page).
- **This page wraps its rows, and starts with only About open.** The view is drawn by a webview now, so a row shows all it says at any width instead of cutting it off, and a closed section's header says whether to open it. Which sections you open stays the same on every page. It stays on its page while you click in it or the preview, and **Escape** puts the cursor back in the page. See [the sidebar](https://ascribed-dev.com/docs/editor/sidebar/#this-page).

### <a id="v0.4.1-the-language-server"></a>The language server

- **`ascribe/projectEdit` answers with one edit for the whole project.** Its `kind` is `fixAll`, every safe fix of the problems the server shows, in every source file, or `format`, every source file as `ascribe fmt` formats it. The edit is to the text the server has, each open document's naming its version, with how many files it changes and how many problems it fixes. See [the language server's README](https://github.com/ascribed-dev/ascribe/blob/main/crates/ascribe-lsp/README.md).

### <a id="v0.4.1-agents"></a>Agents

- **Agents search before they write a page, and `ascribe mcp` offers `ascribe_search`.** The tool takes the command's arguments (`query`, `paths`, `build`, `type`, `case`, `word`, `summary`, `limit`) and `project`, and returns the command's text, or with `response_format: detailed` its JSON; the server keeps each build's pages as search reads them, so a second search only scans. The rules `ascribe agents sync` writes, the skill, and the `new-page` prompt each say to search for a subject before writing a page about it, and to edit the page that covers it instead. See `docs/content/agents/commands.md`.

### <a id="v0.4.1-the-docs-and-the-site"></a>The docs and the site

- **The agents guide is an overview and a page for each task.** [Agents](https://ascribed-dev.com/docs/agents/) is now a short overview that leads to [Work with an agent](https://ascribed-dev.com/docs/agents/loop/), [Write the agent files](https://ascribed-dev.com/docs/agents/instructions/), [Prompt your agent](https://ascribed-dev.com/docs/agents/prompts/), [Set up the MCP server](https://ascribed-dev.com/docs/agents/mcp/), [hooks](https://ascribed-dev.com/docs/agents/hooks/), [Claude Code](https://ascribed-dev.com/docs/agents/claude-code/), [GitHub Copilot](https://ascribed-dev.com/docs/agents/copilot/), and [Index your docs for your product's agents](https://ascribed-dev.com/docs/agents/product-index/), with [What's sent where](https://ascribed-dev.com/docs/agents/whats-sent/) as a page of its own. What the guide said about VS Code is in [Agents in the editor](https://ascribed-dev.com/docs/editor/agents/), and what the command reference says is linked rather than repeated.
- **The Astro guide is four how-tos, each one task.** [Astro](https://ascribed-dev.com/docs/astro/) sets a site up; [Put the docs in a site with more than the docs](https://ascribed-dev.com/docs/astro/more-than-docs/), [Publish for AI agents](https://ascribed-dev.com/docs/astro/agents/), and [Serve the Markdown versions](https://ascribed-dev.com/docs/astro/serve-markdown/), with nginx's and Cloudflare's configuration, are pages of their own. The components, several projects, versions, the permalinks' forwarding page, and `MissingPermalink` are in [the `@ascribed/astro` reference](https://ascribed-dev.com/docs/astro/reference/). [Permalinks](https://ascribed-dev.com/docs/permalinks/) keeps a product's links working, a nested section's place is written once, in [Navigation](https://ascribed-dev.com/docs/navigation/), and moving a site's own list of pages over is [a how-to of its own](https://ascribed-dev.com/docs/navigation/site-list/).
- **Each of the six commands filed under examples is documented in its own feature's section.** [`ascribe permalinks`](https://ascribed-dev.com/docs/permalinks/command/) and [`ascribe templates`](https://ascribed-dev.com/docs/templates/command/) have a page each, beside their guides; `ascribe vale eject` is on [Prose, through Vale](https://ascribed-dev.com/docs/checks/vale/); and `ascribe init` is on [Install](https://ascribed-dev.com/docs/install/), as "Start a project". [`ascribe sources` and `observe`](https://ascribed-dev.com/docs/examples/sources/) keep the page, and each command shows its real output and its JSON, in place of a paragraph listing the fields. Their permalinks lead to the new places.
- **A command's reference no longer repeats its help or its JSON schema.** [`ascribe check`, `build`, and `fmt`](https://ascribed-dev.com/docs/checks/check/), [`ascribe agents` and `mcp`](https://ascribed-dev.com/docs/agents/commands/), and `ascribe explain`, `model`, and `resolve` in [the commands that answer questions](https://ascribed-dev.com/docs/command/questions/) keep each option's table and say only what the help can't; their JSON is shown as real output, with a link to its schema.
- **The diagnostics reference says when each content check is reported.** The nine content checks that didn't say, from `page-size` to `prose-not-checked`, now do, as the others do, in [the content checks' reference](https://ascribed-dev.com/docs/checks/diagnostics/content-checks/).
- **The `ascribe.toml` reference explains, and its contract states the rules.** [The format contract](https://ascribed-dev.com/docs/contracts/content-model/) keeps the rules, with `MUST`, the file formats, and the rule tables, and shares the reference's generated key tables in place of its own; [the reference](https://ascribed-dev.com/docs/content-model/) keeps the explanation and the examples, and says no `MUST`. Each links to the other at the top. The decisions the contract listed in its §26 are now files in `project-docs/decisions/`.
- **The `ascribe.toml` reference's tables of keys are generated from the keys the loader reads.** Each table says what the editor will. `[[intended]]` lists `phrase`, `feature`, and `image` as a row each, and `[checks]` lists `[checks.<check>]`'s `level`, in [the reference's checks page](https://ascribed-dev.com/docs/content-model/checks/).
- **The directive reference is four pages.** [Directive syntax](https://ascribed-dev.com/docs/writing/syntax/) has how a directive is written, its attributes, forms, groups, titles, and binding; the [directive reference](https://ascribed-dev.com/docs/writing/directives/) has each built-in directive and project widgets; [Frontmatter](https://ascribed-dev.com/docs/writing/frontmatter/) has pages and fragments, with the reserved keys as a table; and [Phrases, links, and images](https://ascribed-dev.com/docs/writing/inline/) has the rest of what a page writes. The directive reference links to [Permalinks](https://ascribed-dev.com/docs/permalinks/) and [release lines](https://ascribed-dev.com/docs/versions/lines/) instead of repeating them, and a region's tags are in [Show an example from the code](https://ascribed-dev.com/docs/examples/snippets/).
- **The docs say what the code does in two dozen places where they'd drifted.** Among them: every page that lists the reserved frontmatter keys has `type`, `intended`, and `nav-label`; the [content model contract's validation rules](https://ascribed-dev.com/docs/contracts/content-model/validation/) list the seven loader rules they were missing, `[releases]`'s four among them; the [diagnostics pages](https://ascribed-dev.com/docs/checks/diagnostics/) name every group they hold; the home page links all nine contracts; and [What's sent where](https://ascribed-dev.com/docs/agents/whats-sent/) names `ascribe report links` and `ascribe report agents` among the commands that reach the network.
- **The editor's pages say each thing once, where it belongs.** [The language server](https://ascribed-dev.com/docs/editor/language-server/) is a page of its own, for an editor other than VS Code: `ascribe lsp`, the project a server serves, the commands it asks the editor to run, and each request of its own with its schema. [The sidebar](https://ascribed-dev.com/docs/editor/sidebar/) has a heading for each view, Project, This page, Pages, and Content model, and the status bar item, which leaves the preview's page; the preview keeps [the build you're looking at](https://ascribed-dev.com/docs/editor/preview/#the-build-youre-looking-at). The Problems panel and Check Every Build are on [Problems and fixes](https://ascribed-dev.com/docs/editor/problems/#the-problems-panel), other formatters on [Formatting](https://ascribed-dev.com/docs/editor/writing/#other-formatters), and how a drag in Pages is placed on [Put your pages in order](https://ascribed-dev.com/docs/navigation/order/#or-drag-them-in-the-editor).
- **The editor's pages show the editor.** Pictures, in light and dark, of the build lens with its hover, the actions bar, the Review view, the status bar in its states, and a code file's "Shown in 3 pages" with its references peek replace the paragraphs that described them, on [The preview and builds](https://ascribed-dev.com/docs/editor/preview/), [Actions, new pages, and memos](https://ascribed-dev.com/docs/editor/actions/), [Review in the preview](https://ascribed-dev.com/docs/editor/review/), and [Writing in the editor](https://ascribed-dev.com/docs/editor/writing/), and the build lens ends [Content that varies](https://ascribed-dev.com/docs/writing/variants/). A script takes each, and a test fails when one is more than a minor release old.
- **The examples section is an overview and a how-to per task.** [Examples and drift](https://ascribed-dev.com/docs/examples/) leads to three pages that were its sections: [Show an example from the code](https://ascribed-dev.com/docs/examples/snippets/), [Keep the docs in another repository](https://ascribed-dev.com/docs/examples/docs-repository/), and [Report the pages whose examples changed](https://ascribed-dev.com/docs/examples/drift/). [Page templates](https://ascribed-dev.com/docs/templates/) keeps its task, and links to the diagnostics reference for what `template-placeholder-left` and `template-section-empty` report.
- **Checking external links has a page of its own.** [Check external links](https://ascribed-dev.com/docs/checks/external-links/) takes acting on `ascribe report links` a step at a time, and says once what the link cache and history keep. The project report's page is now about reading the report, and checking the published site with `ascribe report agents` is in [What to measure](https://ascribed-dev.com/docs/agents/measuring/).
- **The docs have an Install page, and getting started has one path.** [Install](https://ascribed-dev.com/docs/install/) has every way to install `ascribe`: npm, Homebrew, the install script, PowerShell, and a release's archive, with which `ascribe` runs when a machine has several. [Getting started](https://ascribed-dev.com/docs/getting-started/) installs with npm and starts with `ascribe init`, without the branch for writing `ascribe.toml` by hand, and its CI recipe is [Checks in CI](https://ascribed-dev.com/docs/checks/ci/).
- **The docs have a Publishing section, for what search engines and agents are told.** [Publishing](https://ascribed-dev.com/docs/publishing/) says what a page's `listing`, `[listing]`, `[crawlers]`, and the Markdown versions' headers tell search engines, AI crawlers, and agents, with what each crawler choice costs, and [the head, sitemap, `robots.txt`, and headers](https://ascribed-dev.com/docs/publishing/site/) has what `@ascribed/astro` writes from the page records, which moved there from the `@ascribed/astro` reference. The `ascribe.toml` reference keeps its tables and links to it.
- **`ascribe report` and `ascribe lsp` are documented in their own sections.** `ascribe report`'s reference is now in Checks and reports, beside the guide to reading the report, at [its own page](https://ascribed-dev.com/docs/checks/report-command/), and `ascribe diff` and `drift` keep their page in Review. `ascribe lsp` is now on the editor's page, [Editing](https://ascribed-dev.com/docs/editor/), and the agents' command page covers `ascribe agents` and `mcp`. Their permalinks lead to the new places.
- **The daily report job keeps what `links` remembers as an artifact.** The job in [The project report](https://ascribed-dev.com/docs/checks/report/) downloads the newest `ascribe-links` artifact before the report and uploads its own after it, in place of `actions/cache`, which GitHub evicts first in a busy repository, so a run often started with nothing remembered. When the artifact can't be listed or downloaded, the job warns and the report runs with nothing remembered. It needs `actions: read`.
- **Review's rules are each written once.** [Review](https://ascribed-dev.com/docs/review/) is the steps of reviewing a pull request; [How review works](https://ascribed-dev.com/docs/review/how-it-works/) has what review compares, the marks, how comments map to the pull request, signing in, and what reaches GitHub; [Attach the review report to every pull request](https://ascribed-dev.com/docs/review/ci-report/) is the CI job; and [Review in the site preview](https://ascribed-dev.com/docs/review/site-preview/), which was a section of the `@ascribed/astro` reference, is a page of the Review section. The editor's page, `ascribe diff`'s, and troubleshooting link to them instead of saying them again. The permalink `review-in-the-site-preview` leads to the new page.
- **Each rule about memos, and `ASCRIBE_TODAY`, is written on one page.** What reads a memo, and what an HTML comment is, are in the [`@memo` reference](https://ascribed-dev.com/docs/writing/directives/) and the `html-comment` diagnostic, and [Leave a memo for whoever edits next](https://ascribed-dev.com/docs/writing/memos/) links to them; the day a check runs as is under [`ascribe check`](https://ascribed-dev.com/docs/checks/check/). [Set up a glossary](https://ascribed-dev.com/docs/content-model/glossary/) links to the checks and the site output's reference instead of repeating them, and the contracts' reasoning moved to the decisions.
- **The sidebar's views in pictures.** Pictures, in light and dark, of the Project view, This page with every section open, and Pages with its sections, a page that's not placed, pages the build leaves out, and one with an error replace the paragraphs that described them on [The sidebar and the status bar](https://ascribed-dev.com/docs/editor/sidebar/). A script takes each, and a test fails when one is more than a minor release old.
- **The site's and the report's pages show them.** Pictures, in light and dark, of the review panel, the HTML report with each kind of mark, the elements with their default colors, the starter's sidebar with a nested section, and an older line's page with its banner and list of lines, on [Review in the site preview](https://ascribed-dev.com/docs/review/site-preview/), [`ascribe diff` and `drift`](https://ascribed-dev.com/docs/review/diff/), the [Element library reference](https://ascribed-dev.com/docs/astro/elements/), [Astro](https://ascribed-dev.com/docs/astro/), the [navigation contract](https://ascribed-dev.com/docs/contracts/navigation/), and [Versions](https://ascribed-dev.com/docs/versions/), replace the sentences that described them. A script takes each, and a test fails when one is more than a minor release old.
- **The versions guide is two how-tos and a reference.** [Publish a site for each version](https://ascribed-dev.com/docs/versions/) reaches its first step on its first screen and ends with what to do when a line ships; [Archive and retire a line](https://ascribed-dev.com/docs/versions/archive/) is the task of its own it was inside; and [Release lines](https://ascribed-dev.com/docs/versions/lines/) holds what each line's output has, the links between lines, and what an older line says about itself. The `ascribe versions` reference shows each command's JSON as real output, and links to its schema.

### <a id="v0.4.1-fixes"></a>Fixes

- **Fixed: `ascribe check` is faster again on a project with many broken links.** Looking for the pages a broken link meant indexed the project a second time, and worked out every page's distance in full. The checks now share one index, and stop measuring a page once it's too far to offer. On the converted Elastic sample, with 1,193 errors, the check went from 5.9 s to 4.5 s on GitHub's runner, and reports the same.
- **Fixed:** `ascribe diff` compares inline HTML as a reader reads it. A block's text in the report, and the words marked in it, leave out inline HTML's tags and comments, and read a `<br>` as a space, as `ascribe search` and the plain output do; a change only inside a comment still marks the block as changed, with no words marked. See `docs/content/review/diff.md`.
- **Fixed:** typing in a fragment is as fast in the editor as it was before 0.4.0. Since `replaces`, each page the language server resolved on its own read every page's frontmatter to look for one, so a keystroke in a fragment many pages include read the whole project once for each of them. A page now looks only when it has a `replaces` of its own. In a project of 3,000 pages, a keystroke in a fragment 30 pages include takes about a third of the time, and opening the project is faster too.
- **Fixed: Pages dims the title of a page the status bar's build leaves out, not only its icon.** A problem's red or yellow still shows over it, and the Explorer and the editor's tabs dim the file too. See [The sidebar](https://ascribed-dev.com/docs/editor/sidebar/#pages).
- **Fixed:** A build in a source's folder no longer keeps the language server busy. Since VS Code started telling each server about its sources' folders wherever they are, a source such as `path = ".."` sent it every file a build wrote, such as `target/`, and each one asked for every lens again. VS Code now tells the server only of the files a source's `include` and `ignore` patterns read, and its `ascribe.examples.toml` and observations, and the server asks for the lenses again only for those ([docs/content/editor/writing.md](https://ascribed-dev.com/docs/editor/writing/#code-files-a-page-shows)).
- **Fixed:** This page opens the row you chose while it's drawn again, and gives the row the focus back. A choice made just as new problems or a new answer reached the view was dropped, since only a row of the newest answer was opened; it now opens the row of the answer you saw. And focusing the view while a row had the focus left nothing focused in it; the row gets the focus back, without scrolling the view, so a click into it lands on the row it was aimed at.

## <a id="v0.4.0"></a>0.4.0 (2026-10-18)

### <a id="v0.4.0-the-language"></a>The language

- **A description field's `default` is the description of a page that leaves the field out or empty.** `llms.txt`, `pages.json`, and the compact index show it, where they showed no description before, and [`description-too-long`](https://ascribed-dev.com/docs/content-model/output/) reports a long default once, at the `default` in `ascribe.toml`. Pages that share a default aren't reported by `description-duplicate`.
- **A folder's name in the navigation has its `-` and `_` as spaces.** A section with no `label` and no page of its own is named by its folder with `-` and `_` as spaces and its first letter capitalized, so `getting-started/` is "Getting started" in a sidebar, an archive, `nav.json`, and `ascribe model`, as it already was in `llms.txt`. See [the navigation](https://ascribed-dev.com/docs/navigation/).
- **A glossary page's mistakes are reported where they're written.** A term whose section doesn't open with a paragraph is `glossary-definition-missing`, at its heading, and isn't linked. A definition over 300 characters in a build, the limit a page's description has, is the advice `glossary-definition-long`, which names the builds. `@term` on a page `[glossary] pages` doesn't list is `term-off-glossary-page`, with a fix that lists the page, which `ascribe check --fix` leaves to you since it links the page's terms everywhere. A term follows its section and its paragraph in each build: a build that removes either doesn't link the term. See `docs/content/content-model/content.md`.
- **Behavior change: an unused term is acknowledged at its heading, and `[[intended]] term` is gone.** Write `@intended {check=glossary-term-unused}: …` among the directives under the term's heading: at the top of a section, `@intended` now acknowledges a problem reported at the heading, and, as before, one in the block directly under it when no blank line comes between, and nothing else in the section. An `[[intended]]` table with `term` is an error that says so. See `docs/content/writing/directives.md`.
- **Behavior change: a glossary is a page, and `[glossary.terms]` is gone.** List the glossary's pages in `[glossary] pages`, and mark each term's heading on them with `@term`; the paragraph under the heading is the term's definition, which a built page shows as the title of each link to the term. `@term` takes `aliases`, separated by `|`, and its own `match` and `case-sensitive`. A `[glossary.terms]` table is now an error that says how to move it. A link you write to a term's heading is a use of the term, with its marker and its definition, and, for a `first` term, the page's one link to it. That's how a `marked` term is linked: one that no link names is reported by `glossary-term-unused`. Two terms with the same text are `glossary-term-duplicate`, and a path in `pages` that isn't a page is `model-glossary-page`; `model-glossary-duplicate-term`, `model-glossary-link`, and `glossary-link-target-missing` are gone. `ascribe model` lists each term with its page and line, at `schema_version` 3, and the JSON output lists each use's definition. See `docs/content/content-model/content.md` and `docs/content/writing/directives.md`.
- **An HTML comment in a page is advice, `html-comment`, with a fix that makes it a memo.** A comment is in the source of every published page, and a `@memo` keeps the same text out of every output. `ascribe check` reports each comment, a block or inside a paragraph, and the editor's **Rewrite as a memo** rewrites one that's a block of its own. Vale's own comments aren't reported, and a comment for another tool is acknowledged with `@intended {check=html-comment}`. See [`@memo`](https://ascribed-dev.com/docs/writing/directives/#memo).
- **An empty container memo is advice, `memo-empty`, with a fix that removes it.** A `@memo:` with nothing before its `@end`, or only blank lines, says nothing and is almost always a slip, as a line `@memo` with no text is already an error. `ascribe check --fix` and the editor's quick fix remove it, which changes no output. See [`@memo`](https://ascribed-dev.com/docs/writing/directives/#memo).
- **A longer memo goes in a file of its own in `memos/`, placed on the page, section, or block it's about.** A memo file is Markdown with two frontmatter keys: `about`, which names a page, a heading, or an entry of `ascribe.toml` as `ascribe refs` does, and `quote`, which narrows it to the block that holds those words. Where it is is worked out each time it's read, and it's never published. `ascribe check` reports a memo file that isn't one as an error, `memo-invalid`; one whose `about` names nothing as a warning, `memo-detached`; and as advice, one whose quote isn't found, `memo-outdated`, and one over 2,000 characters, `memo-long`, whose `[checks.memo-long] limit` sets another. See [Memo files](https://ascribed-dev.com/docs/writing/memo-files/).
- **`@memo` keeps a note for whoever maintains a page, and is never published.** Write `@memo: <text>` at the top of a section or above a block, or `@memo:` … `@end` around several lines, to say why the page is written the way it is, beside what it's about. Nothing reads a memo: its links aren't checked, a `{key}` in it isn't a phrase, Vale isn't given it, and `ascribe fmt` leaves its lines as written. No output shows it, and a change to a memo alone isn't a change to `ascribe diff`. See `@memo` in the [directive reference](https://ascribed-dev.com/docs/writing/directives/#memo).
- **`nav-label` gives a page a shorter name in a sidebar, and the JSON output has the navigation.** A page's `nav-label`, one line, read as its `title` is, is its label in the sidebar, previous and next, an archive, `nav.json`, and `ascribe model`; it keeps its title everywhere else, `llms.txt` included. It's reserved, so a content type can't declare it, and one longer than the title is advice, `nav-label-long`. The JSON output now has `_ascribe/nav.json` as the site output does, its formatted labels as pieces. See [A page's label](https://ascribed-dev.com/docs/navigation/).
- **`ascribe.nav` is checked against the project, and an unplaced page or section gets a safe fix.** The checks come in the same release as `ascribe.nav` itself, so no release reads the file without them. A `pages` entry that isn't a page in its folder is `nav-page-unknown`, a `folder` that isn't a section is `nav-folder-unknown`, each with a fix to the closest name, and a folder or a page listed twice is `nav-duplicate`, with a fix that removes the second; all three are errors at the value in `ascribe.nav`. A folder with both an `index.md` and a page beside it with its name is `section-page-both`, an error on the `index.md`. A page a section's `pages` leaves out is `nav-page-unplaced`, advice on the page, and a section the file leaves out beside ones it lists is `nav-section-unplaced`, advice on its first page; `ascribe check --fix` places them all in the order they already had, keeping the file's comments and layout. See [Navigation](https://ascribed-dev.com/docs/navigation/#problems).
- **`ascribe.nav` orders a project's sections and pages, and every site output gains `_ascribe/nav.json`.** A section is a folder of the content root, and its own page is its `index.md` or the page beside the folder with its name. With no file, a project has the default order: sections in path order, the root first, and in each its own page, then its pages by type in `ascribe.toml`'s order, then by title. `ascribe.nav` lists sections, each with `folder`, and optionally `pages` and `label`, and what it leaves out keeps the default. A page that's `listing: unlisted` is left out of the navigation, and so out of the sidebar. A file that can't be read is `nav-invalid`. Every project's site output changes: each build's has `_ascribe/nav.json`, its share of the navigation, `generated` in its manifest, whether or not the project has the file. A `folder` or a `pages` entry that names nothing is reported by the navigation's checks, which come in the same release. See [Navigation](https://ascribed-dev.com/docs/navigation/) and [the navigation contract](https://ascribed-dev.com/docs/contracts/navigation/).
- **The default order sorts pages by their titles as a reader sees them.** A title read with code spans sorts without its backticks, and one that takes phrases with their values, so "`zeta.yaml` options" comes after "Apply" and `{product} overview` sorts by the product's name; two titles with the same text sort as written. Below the top level, a section whose own page sits beside its folder, placed where that page is, is no longer `nav-section-unplaced` when `ascribe.nav` lists a sibling and not it: its table wouldn't move it. See [Navigation](https://ascribed-dev.com/docs/navigation/#the-default-order).
- **`page-orphan` says what reaches the page: only the sidebar, or nothing but its address.** A page no other page links to or includes is still reported, and its message no longer hedges that a site's own menu may list it: the navigation is known, so a page in it is reached only through the sidebar, and one that's `listing: unlisted`, which is in no sidebar, only by someone given its address. The editor's Pages view says the same of its orphans. See [`page-orphan`](https://ascribed-dev.com/docs/checks/diagnostics/content-checks/).
- **A page template's instructions to the writer are memos, never published.** In the six templates `ascribe templates eject` writes, a paragraph that tells the writer what to do, such as `{This section is optional}`, is now `@memo: This section is optional`, at the top of its section or above the block it's about, so one left in a page is never seen by a reader. A paragraph that marks where the page's own text goes stays a placeholder. `template-placeholder-left` doesn't report a memo the page keeps, and no longer treats a paragraph in one of the template's block quotes as an instruction: a placeholder in a block quote is reported as any other is. The `new-page` prompt tells an agent to follow the memos. A project's own copies are its own: `ascribe templates status` says what changed in the set. See the [templates guide](https://ascribed-dev.com/docs/templates/#where-the-templates-come-from).
- **`template-section-empty` reports a template's section a page leaves empty.** A section a template gives only memos, such as the concept's "Use cases" or the troubleshooting template's "Cause 1 of symptom 1", has no placeholder, so one a page doesn't fill in builds as a heading with nothing under it, and `template-placeholder-left` can't find it. On a page of a type with a template, `ascribe check` now reports it as advice at its heading when the page's section, its subsections included, has nothing but memos, directives such as `@id`, and empty subsections' headings. It's configurable in `[checks]`. See the [templates guide](https://ascribed-dev.com/docs/templates/#whats-left-to-fill-in).
- **A top section is ordered by `ascribe.nav`'s `[[sections]]`, even when its own page is beside its folder.** `guides/` with `guides.md` comes after Start's pages among the other top sections, in the file's order and then path order, where it used to take its place from where `guides.md` sorted among Start's pages, its table ignored. Below the top level, a section whose own page is beside its folder still sits where that page sits. See [Navigation](https://ascribed-dev.com/docs/navigation/#the-default-order).

### <a id="v0.4.0-the-ascribe-command"></a>The `ascribe` command

- **An archive's sidebar and previous and next follow its line's navigation.** `ascribe versions archive` lists the pages as the line's site sidebar does, `ascribe.nav`'s order or the default one, each top folder under its label and each nested folder in its place, and links each page to its previous and next in reading order; the contents page, `all.html`, and the search follow it too. A page that's `unlisted` is still written, with no place in the sidebar. See [Archive a line](https://ascribed-dev.com/docs/versions/).
- **`ascribe outline` and `ascribe refs` list the memo files about a page, a heading, or an entry.** `ascribe outline` lists each memo under the page or heading it's about, with its first line, and its JSON has `memos` on the page and each heading, as the MCP tool `ascribe_outline` does. `ascribe refs` lists a memo file as a place with the use `memo`, and lists one whose target is gone under the target it names. See [`ascribe outline`](https://ascribed-dev.com/docs/command/questions/#cli-outline).
- **`ascribe model --format json` reports the navigation.** Without `--section`, its `navigation` is the project's sections and pages in order, each section with its label, its own page, its pages, its nested sections, and `listed`, whether `ascribe.nav` lists it, and each page with its `source`, `label`, and `type`; a page that's `unlisted` isn't in it. `schema_version` stays `3`. See [`ascribe model`](https://ascribed-dev.com/docs/command/questions/).
- **`ascribe model` labels a page by its title's plain text, as a sidebar does.** A page with no `nav-label`, whose type's `title` sets `inline`, is labeled `loom.yaml options` rather than `` `loom.yaml` options ``, and so is a section labeled by its own page's title. The editor's **Pages** view shows the same labels. See [`ascribe model`](https://ascribed-dev.com/docs/command/questions/).
- **Personal memos, kept on one machine in `.ascribe/memos/`.** They're memo files in the same format as `memos/`'s. `ascribe outline` and `ascribe refs` list them with `personal`, and `ascribe check` doesn't read them, so what it reports is the same on every machine ([memo files](https://ascribed-dev.com/docs/writing/memo-files/#personal-memos)).
- **`ascribe rename` keeps memo files attached.** Renaming a heading's id or moving a page rewrites the `about` of each memo in `memos/` and `.ascribe/memos/` that names it, keeping its `#id`, and so do `F2` and moving a file in the editor ([`ascribe rename`](https://ascribed-dev.com/docs/command/questions/#cli-rename)).

### <a id="v0.4.0-the-editor"></a>The editor

- **Memo files in the editor: a lens counts them, and Add a memo writes one.** A CodeLens above a heading or a block with memos opens them, **This page** lists the memos about the heading at the cursor, and **Ascribe: Add a memo** and **Ascribe: Add a personal memo** make a memo about the section at the cursor, quoting the selection or the block. In a memo file, `about` completes and links to what it names, and the file shows its problems as you type ([memo files in the editor](https://ascribed-dev.com/docs/editor/actions/#memo-files)).
- **The editor knows `@memo`: completion, hover, highlighting, and folding.** Typing `@me` offers `@memo`, hovering it says it's never published, a memo's text is colored as a comment in either form, and a container memo folds to its first line. The language server answers folding ranges for it, which VS Code adds to its own. See [Highlighting](https://ascribed-dev.com/docs/editor/writing/#highlighting).
- **`ascribe.nav` is checked and completed as you type.** Its problems appear where each is, with their fixes, and a page its section leaves out offers a quick fix that adds it to the file. Completion offers the keys, the sections the file doesn't list yet, and in `pages` the section's pages it doesn't name yet. The extension gives `ascribe.nav`, `ascribe.permalinks`, and `ascribe.lock` to the TOML language, which colors them with a TOML extension installed. See [the editor](https://ascribed-dev.com/docs/editor/writing/#completion).
- **Rename Label… in the Pages view sets a page's `nav-label`.** It's in a page's menu as it is in a section's, offering the page's label as it's written, and writes `nav-label` in the page's frontmatter, unsaved and keeping the rest of it; leave it empty to take it out, and the page's title is its label again. Pages draws each page by its label, as a sidebar does: its `nav-label` when it has one, else its title. See [the editor](https://ascribed-dev.com/docs/navigation/order/#or-drag-them-in-the-editor) and [a page's label](https://ascribed-dev.com/docs/navigation/#a-page-s-label).
- **Find All References lists the pages of a page type.** In **Content model**, a page type's menu has **Find All References**, and so does its table in `ascribe.toml` and a page's frontmatter `type`: each lists the pages whose `type` names it ([the Content model view](https://ascribed-dev.com/docs/editor/sidebar/#content-model)).
- **The Pages view shows the navigation, and dragging in it orders your sections and pages.** **Pages** lists Start, then each section by its label with its pages in the order a sidebar shows them, then the fragments by folder, and selects the page you open. Drag a section or a page to put it before or after another, and the editor writes [`ascribe.nav`](https://ascribed-dev.com/docs/navigation/), unsaved, keeping its comments; **Undo** takes it back. Drag a page onto another section and, once you say so, its file moves there with its links. A page the file leaves out is marked, with **Place Here** in its menu, and **Rename Label…** sets a section's label. The view no longer groups pages by type or lists orphans, which `ascribe check` reports. See [the editor](https://ascribed-dev.com/docs/editor/sidebar/#pages).
- **Moving a page or a folder keeps `ascribe.nav` right.** `ascribe rename` and a move in the editor rename a page's entry in its section's `pages`, or take it out when the page leaves its folder, and rename a moved folder's sections. Moving a folder in the editor also updates the links to the pages in it. See [`ascribe rename`](https://ascribed-dev.com/docs/command/questions/#cli-rename).
- **Behavior change: Switch Project replaces the Projects view.** In a workspace with several projects, **Ascribe: Switch Project**, in the Command Palette, Pages' and Content model's title bars, and the status bar item's menu, lists every project with its folder, its server's state, and its binary, with buttons to show its output and restart it. Choosing one shows it in the sidebar and starts its server; the sidebar follows the next file of another project you open. With one project, it isn't offered. When a project's server fails to start, Pages and Content model say so, with **Show Output** and **Restart**, and in a workspace with no project, Pages says how to start one. See [the editor](https://ascribed-dev.com/docs/editor/sidebar/#switch-project).
- **This page says whether the page is published, listed, due, over size, or unfinished.** About adds the page's description against its limit, when it's due for review, its availability, the builds that publish it and leave it out, its listing and the rule that sets it, its permalinks, and what its template still leaves to fill in; each row opens where it's set. A new **Health** section counts the page's problems, gives its size in the build where it's largest, and says whether each tested example is tested, and **Connections** adds what the page includes. See [This page, Pages, and Content model](https://ascribed-dev.com/docs/editor/sidebar/#this-page).
- **The sidebar's This page view replaces Used by.** Its **About** section gives the active page's title, content type, address, and Markdown version's address, and its **Connections** section lists what links to and includes the page, or the heading at the cursor, with unsaved edits counted. It asks the language server once for each cursor move, review on or off, where Used by asked twice. See [This page, Pages, and Content model](https://ascribed-dev.com/docs/editor/sidebar/#this-page).
- **Pages and Content model have menus and filters.** In **Pages**, a page with a problem is colored as in the Explorer, and one the status bar's build leaves out is dimmed. A page's menu opens its preview, copies its link, renames or moves it, finds its references, and reveals it in the Explorer; a section's starts a page in it, and **Orphans Only** lists what nothing links to or includes. **Content model** lists page types and each dimension's values. An entry's menu finds its references, renames a phrase or a value, and inserts a phrase or a widget at the cursor of the page you last had open; **Unused Only** lists what nothing uses. See [the editor](https://ascribed-dev.com/docs/editor/sidebar/#pages).

### <a id="v0.4.0-the-language-server"></a>The language server

- **Apply a page's safe fixes in one action, or on save.** **Source Action… → Apply the safe fixes in this page** (`source.fixAll.ascribe`) applies every safe fix of the problems the editor shows in the page, settled as `ascribe check --fix` settles them; with `editor.codeActionsOnSave` it runs on save. See [Apply the safe fixes](https://ascribed-dev.com/docs/editor/problems/#apply-the-safe-fixes).
- **Go to definition takes a glossary term to its heading, and `@term` completes on a glossary page.** Find All References on a term's heading, or its `@term`, lists the term's uses with the links to the heading. Hover on the heading shows the definition for the editor's build and how many pages use the term. `@term` is offered at the top of a section on a glossary page, with its attributes and `match`'s values. `glossary-definition-missing` has a fix that writes a paragraph under the heading, selected for you to type over. Renaming a term's `@id` with `F2`, or `ascribe rename`, renames the term, and rewording a term's heading that has no `@id` says that its id changes too. See `docs/content/editor/writing.md`.
- **The language server reads memo files.** It checks the memo files in `memos/` and `.ascribe/memos/` as they change, completes and links their `about`, shows a CodeLens on what they're about, lists them in `ascribe/page`'s `memos`, and writes one with the `ascribe/edit` actions `addMemo` and `addPersonalMemo` ([memo files in the editor](https://ascribed-dev.com/docs/editor/actions/#memo-files)).
- **Find All References works on a content model entry in `ascribe.toml`.** On a phrase's key, or the table header of a feature, dimension, note type, or widget, it lists the places that use it, as on a use in a page. `ascribe/inventory` also lists page content types, with how many pages each has, and each dimension's values. See [the language server](https://github.com/ascribed-dev/ascribe/blob/main/crates/ascribe-lsp/README.md#the-projects-inventory-ascribeinventory).
- **`ascribe/page` answers whether a page is published, listed, due, over size, or unfinished.** Its `about` adds the page's description (or its field's default) and whether `description-too-long` reports it, its review date and whether `review-overdue` reports it, availability, builds, listing, permalinks, and template, and the answer adds `health` (its size, as `page-size` measures it, and its tested examples) and `includes`; each page in `ascribe/inventory` adds the `builds` that publish it.
- **`ascribe/review/baseFile` answers a source file's text at the review base.** An editor's diff of a review's source changes reads its base side from it where it can't ask git itself. See [the language server's README](https://github.com/ascribed-dev/ascribe/blob/main/crates/ascribe-lsp/README.md).
- **A code file a page shows says which pages show it.** Over each region a `@snippet` takes, and on the first line of a file one takes whole, a lens reads "Shown in 3 pages", counting each page that includes a fragment that shows it, and lists the places in the references peek. It's placed from the saved file. A file in two projects' sources gets a lens from each, naming its project. See [Code files a page shows](https://ascribed-dev.com/docs/editor/writing/#code-files-a-page-shows).

### <a id="v0.4.0-astro-and-the-elements"></a>Astro and the elements

- **`neighbors(entry, entries)` gives a page's previous and next, in the navigation's reading order.** From `@ascribed/astro/content`, it returns `{ previous?, next? }`, each a sidebar page with its entry, address, and label, from the build's `_ascribe/nav.json`, in the entry's own line for a versioned build, or from a site's own list with `{ nav }`. The home page has no previous. `examples/astro-site`'s layout links them at each page's foot. See [Previous and next](https://ascribed-dev.com/docs/astro/reference/).
- **Behavior change: `sidebar()` draws the project's navigation, and takes a site's own list as `nav`.** `sidebar(await getCollection("docs"), { entry })` gives a layout the project's sections and pages, in `ascribe.nav`'s order or the default one, from the build's `_ascribe/nav.json`, with no list for the site to keep. A site that passes its list moves it into the options, one line:

  ```diff
  - const groups = sidebar(nav, await getCollection("docs"), { entry });
  + const groups = sidebar(await getCollection("docs"), { nav, entry });
  ```

  A group gains `sections`, its nested sections, and `folder`, `page`, and `formatted` from the navigation; each page gains `label`, its title, and `formatted.label`, its HTML when the title has code. See [`sidebar()`](https://ascribed-dev.com/docs/astro/reference/#sidebar).
- **`npx @ascribed/astro init` writes no `src/nav.ts`, and its sidebar is the project's navigation.** The starter's layout calls `sidebar(entries, { entry })`, and `neighbors(entry, entries)` for links to the previous and next pages, so a page you add is in the sidebar with no list to update, a nested section's pages right after their section's own page, each `<li>` with its `data-depth`. `init` writes three files, and no longer builds the project first. A site started with an older `init` keeps its `src/nav.ts`, passed as `nav`; [Put your pages in order](https://ascribed-dev.com/docs/navigation/order/) says how to move it over to `ascribe.nav`.

### <a id="v0.4.0-agents"></a>Agents

- **The edit hook names the advice with a safe fix, and the fix for the agent to make.** After an edit, `ascribe agents hook` reports, after the errors, each advice in the files the agent wrote that has a safe fix, with what to change and where, such as "Add `new.md` to `guides/`'s pages in ascribe.nav" for a page `ascribe.nav` doesn't place. It still writes no file. See [Agents](https://ascribed-dev.com/docs/agents/).
- **Behavior change: `llms.txt` and the agents' index follow the navigation, in every project.** Their pages come in the project's [navigation](https://ascribed-dev.com/docs/navigation/) order, `ascribe.nav`'s or the default one: the content root's first, under **Start** in place of **Pages**, then a section for each top folder, headed by its label, with its nested folders' pages in reading order. A page keeps its title there. Every project's outputs change once, in `llms.txt`, its split files, and `ascribe agents index`, and `ascribe agents index --into` rewrites the index in a product's `AGENTS.md` with the new order the next time it runs. The block `ascribe agents sync` writes lists no pages, so it doesn't change. See [`llms.txt`](https://ascribed-dev.com/docs/content-model/output/).
- **Agents' prompts name the memo files about the page they're about.** `ascribe agents prompt fix`, `ascribe check --format prompt`, and **Prompt agent** list the memos about each page they name, after the problems; `review` and `ascribe diff --format prompt` list each memo on a changed block or on the section a change is in, and ask the agent to check the change agrees with it. Each line is the memo's path, what it's about, and its state when it isn't placed, never its text; a personal memo is marked as one, and a list stops at ten. In a project with a memo file, the rules `ascribe agents sync` writes say that `ascribe outline <page>` lists a page's memos. See [Agents](https://ascribed-dev.com/docs/agents/).
- **The rules `ascribe agents sync` writes tell an agent to read and keep a block's `@memo`.** In a project whose pages have a memo, `AGENTS.md` and the rules beside it say to read a block's memo before changing the block, and to keep the two together. When the rules run over their 4,000 characters, that sentence is cut first. See [Agents](https://ascribed-dev.com/docs/agents/).
- **VS Code's chat has Ascribe's skill in a project that never ran `ascribe agents sync`.** The extension ships the skill, and offers it to chat while no workspace folder has a synced copy in `.agents/skills/`, `.claude/skills/`, or `.github/skills/`; once one does, chat lists only the project's. See [Agents](https://ascribed-dev.com/docs/editor/agents/#agents-in-vs-code).

### <a id="v0.4.0-review"></a>Review

- **A removed page opens in the preview as it was.** Choosing a page the branch removes, in the Review view or in Changed Pages, shows it in the preview as it was at the base, with no Now side, where before the Review view opened its source diff and Changed Pages said there was no page to open. The language server's `ascribe/preview` answers for such a page too: with `review: true`, its `review` has the page as it was and its title. See [The Review view](https://ascribed-dev.com/docs/editor/review/#the-review-view).
- **A Review view lists the changed pages while review is on.** It's first in the Ascribe sidebar, with the pages under Added, Changed, and Removed, each with its counts, what it changed through, and its open threads; then the problems on those pages. Its badge counts the comments you haven't submitted, and its title bar has **Submit Review…**, **Open Source Changes**, which opens every changed source file at the base in one diff, and **Stop Review**. While review is on, This page has a Review section too, with whether the branch changes the page and its open threads, each opening the thread in the preview. See [The Review view](https://ascribed-dev.com/docs/editor/review/#the-review-view).

### <a id="v0.4.0-the-docs-and-the-site"></a>The docs and the site

- **The editor guide is split into a page for each part of the editor.** [The editor](https://ascribed-dev.com/docs/editor/) keeps installing the extension and other editors, and lists its pages: problems and fixes, writing, actions and new pages, the preview and builds, the sidebar, review, and agents. Together they were over 66,000 characters, past `page-size`'s limit. Switch Project is its own section at the end of [the sidebar's page](https://ascribed-dev.com/docs/editor/sidebar/#switch-project), so This page, Pages, and Content model no longer read as unreleased.
- **A guide says how to set up a glossary.** [Set up a glossary](https://ascribed-dev.com/docs/content-model/glossary/) covers writing a glossary page with `@term`, listing it in `[glossary] pages`, choosing which occurrences are linked, a term that's also an ordinary word, terms that differ by version or product, a glossary in several pages, and what the checks and a site get. The Ascribe skill tells agents a term is defined once, on a glossary page.
- **A guide says when to write a memo, a memo file, or a review comment.** [Leave a memo for whoever edits next](https://ascribed-dev.com/docs/writing/memos/) covers `@memo` in both forms, memo files with `about` and `quote`, what outdated and detached mean, personal memos, and what agents are told, and links each reference. Ascribe's own docs keep some of their reasons in memos.
- **A guide says how to put a project's pages in order.** [Put your pages in order](https://ascribed-dev.com/docs/navigation/order/) covers the default order, writing `ascribe.nav` or dragging in the editor, short labels, leaving a page out, and moving a site's own list of pages over. Ascribe's own site draws its sidebar from the docs' `ascribe.nav`, with no list of its own.

### <a id="v0.4.0-fixes"></a>Fixes

- **Fixed:** two glossary terms with the same id are reported, and the second isn't a term. Before, `ascribe check` was clean, and a build linked the second term's words to the first's heading and definition. Two terms on two pages whose headings have the same text, such as case-sensitive `Go` and `GO`, have the same id too. `glossary-term-duplicate` reports it at the second; give one of them another id with `@id` ([docs/content/content-model/glossary.md](https://ascribed-dev.com/docs/content-model/glossary/)).
- **Fixed:** VS Code shows the "Shown in N pages" lens in a Markdown file a `@snippet` shows. The editor treated every Markdown file as a page and asked only its own project, so a shown Markdown file outside the project's pages had no lens. And a file two projects' sources hold but only one shows now has one lens, unnamed, as it should: the lens named its project whenever two projects' sources covered the file ([docs/content/editor/writing.md](https://ascribed-dev.com/docs/editor/writing/#code-files-a-page-shows)).
- **Fixed:** VS Code moves the "Shown in N pages" lens when a file outside the project's folder is saved. A source whose folder is beside the project, such as `path = ".."` or one another project shares, kept its lens where the region was, and This page's tested-example rows kept a source's old observations, because the editor didn't tell the language server those files changed. It now tells each server about its sources' folders, wherever they are ([docs/content/editor/writing.md](https://ascribed-dev.com/docs/editor/writing/#code-files-a-page-shows)).
- **Fixed:** a container memo stays colored as a comment past an `@end` at another indent. The editor's highlighting ended the memo at an `@end` of any indent, though the memo's lines run to an `@end` at its opener's indent (§3.5 of [SPEC.md](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md)).
- **Fixed:** **Ascribe: Add a memo** names its file within the file system's limit. The name was the quote's first five words, which in prose without spaces, such as Chinese or Japanese, could be over the 255 bytes a file name can have, and the editor then said the page had changed. The name is cut to 100 bytes now, and when a memo file can't be written, the message says what the file system said ([docs/content/editor/actions.md](https://ascribed-dev.com/docs/editor/actions/#memo-files)).
- **Fixed:** the Pages view draws a top section whose own page is beside its folder after Start. `guides/` with `guides.md` was drawn among Start's pages, unlike every sidebar; it's now a top section with `guides.md` first in it, and dragging it moves it among the top sections. See [the editor](https://ascribed-dev.com/docs/navigation/order/#or-drag-them-in-the-editor).
- **Fixed:** a rename keeps every memo about what it renames attached. Renaming a glossary term's heading id rewrites the memos whose `about` is `term:<id>`, renaming a phrase rewrites those about `phrase:<key>`, and renaming a heading in a fragment rewrites those that name it through the page that includes it (`page.md#id`), as it does links. An `about` written as a block scalar or on the line under its key is rewritten too, where it was skipped; one written with an escape is written again on its key's line. `ascribe rename` and the editor's rename alike ([docs/content/writing/memo-files.md](https://ascribed-dev.com/docs/writing/memo-files/)).
- **Fixed:** `available-same-in-every-line` no longer reports a patch version inside the oldest line. With `live = ["3.4", "3.5"]`, `self-managed 3.4.2` was reported as shown by no output, with a safe fix to write `self-managed`, though the 3.4 and 3.5 lines' outputs show "3.4.2". The check now decides what's hidden the way a line's output does, so only a version at or before the oldest line, such as `3.4` or `3.3`, is reported ([docs/content/versions/index.md](https://ascribed-dev.com/docs/versions/)).
- **Fixed:** a memo about a glossary term is placed on the term's heading. A memo file whose `about` is `term:<id>` was listed nowhere, its `quote` was refused as an entry of `ascribe.toml`, and a missing term was "not declared in ascribe.toml". It's now on the term's heading on its glossary page, as a memo about `glossary.md#<id>` is, in `ascribe outline`, the editor, and the `fix` prompt; its `quote` is found in the term's section; and a missing term is reported as one no glossary page has ([docs/content/writing/memo-files.md](https://ascribed-dev.com/docs/writing/memo-files/)).
- **Fixed:** an `unlisted` page is no longer reported as unplaced in the navigation. A page that's `listing: unlisted` is in no sidebar, so a section's `pages` in `ascribe.nav` leaving it out isn't `nav-page-unplaced` advice any more, and `ascribe check --fix` and the agents' hook no longer add it there. The editor's Pages view marks it **unlisted** instead of **not placed**, and Place Here refuses it ([the navigation](https://ascribed-dev.com/docs/navigation/)).

## <a id="v0.3.2"></a>0.3.2 (2026-10-16)

### <a id="v0.3.2-the-language"></a>The language

- **A line's output leaves out a history's states from before the window.** With 3.5 and 3.6 live, `self-managed (preview 3.3, ga 3.5, deprecated 3.6)` shows as `self-managed (ga 3.5, deprecated 3.6)` in each line's output, as `ascribe versions retire` writes the mark, where it showed the whole history before. See [Build a site for each line](https://ascribed-dev.com/docs/versions/lines/#what-each-line-has).
- **Behavior change: a link to a page a line doesn't publish leads to another line.** In a line of a versioned build, the link lands on the page that replaces it, or that it replaces, else on the page in the nearest line that has it, the closest newer line first. A link out is marked with its line: `data-ascribe-release-line` in the site output, which the elements' stylesheet shows as " (3.5)", and " (3.5)" after the link in the plain output. Only a page no live line has is still `link-page-dropped`. A link from the current line into an older one is advice, `link-into-older-line`, and a `#id` the page it lands on doesn't have is dropped, with advice, `link-fragment-dropped`. See [Versions](https://ascribed-dev.com/docs/versions/lines/#links-between-lines).
- **Behavior change: `replaces` is a reserved frontmatter key.** A page rewritten under a new path says which page it takes the place of, `replaces: configure-legacy.md`, and a versioned build follows it. It must name a page; a chain that comes back to the page is an error, and so is a line that publishes a page and the page it replaces. A content model can no longer declare a field named `replaces`. See [A page that replaces another](https://ascribed-dev.com/docs/writing/frontmatter/#a-page-that-replaces-another).
- **`[listing] older-lines` keeps every line but the current one out of an index.** `older-lines = "unlisted"` makes each page of an older line `unlisted`, so it's `noindex` and left out of the sitemap and `llms.txt`; a page there that sets a looser `listing` is `listing-loosened-in-line`. See [Keep the older lines out of an index](https://ascribed-dev.com/docs/versions/lines/#keep-the-older-lines-out-of-an-index).
- **A phrase can be a target's current line.** In `[phrases]`, `release = { line = "self-managed" }` makes `{release}` the newest live line of `[releases.self-managed]`, as the list writes it, and in an older line's output of a versioned build, that line, so a version a page names follows the list with no value kept by hand. A target with no live line is an error, `model-phrase-line`. See [`[phrases]`](https://ascribed-dev.com/docs/content-model/content/#phrases).
- **A live line whose support has ended is reported.** `[releases.<target>.ends]` gives each live line's last day of support, and `ascribe check` reports a line whose date has passed as advice, `release-line-ended`, as of today in UTC, or the day `ASCRIBE_TODAY` gives, as `review-overdue` does. See [`ascribe check`](https://ascribed-dev.com/docs/checks/check/).
- **A mark that no output shows is found.** When every build that publishes a mark's content is versioned by its target, or filtered for it in the window, a mark whose versions are all at or before the oldest live line is advice, `available-same-in-every-line`, with the mark without them as a safe fix, keeping its state: `self-managed 3.1` becomes `self-managed`. Content in no live or planned line is a warning, `available-in-no-line`. A project with a badge build that isn't versioned, such as one site, gets neither, since that build shows every version. See [`[releases.<target>]`](https://ascribed-dev.com/docs/content-model/variation/#121-releasestarget).
- **A version `[releases]` can't place is reported.** For a target with a list, a version in a mark, a feature, or a build's filter that's newer than every declared line is an error, `available-version-ahead`, with the newest line offered as a fix; one in a line between two declared ones that isn't declared itself is advice, `available-line-undeclared`, with the lines either side offered, and declaring the line, in `archived` with no address when nothing publishes it, clears it. A version older than every declared line is before the window, and reports nothing. See [`[releases.<target>]`](https://ascribed-dev.com/docs/content-model/variation/#121-releasestarget).
- **`[releases.<target>]` says which lines are planned, ending, and archived.** Beside `live`, a target's list takes `planned`, lines that haven't shipped, each newer than every live line; `ends`, the last day each live line is supported; and `archived`, lines that have left the window, each with an optional `url` and `ended` date. `[releases] planned-label` sets the word a reader sees for a planned version. A line is in one list, in the order the lists mean (`model-releases-order`), and `ends` names live lines only (`model-releases-ends`). See [`[releases.<target>]`](https://ascribed-dev.com/docs/content-model/variation/#121-releasestarget).
- **A version that hasn't shipped reads "planned".** A version in a line `[releases.<target>]` lists as `planned` is planned, and says so in every output: its availability reads `Self-managed (planned, 3.6)` in the site output and the plain output, the availability element's target has a `planned` attribute, the page-level `available` frontmatter and the JSON output's target have `planned: true`, and a project with `[releases]` gets each availability's `targets` in its JSON output. It stops being planned when its line moves to `live`, with no other edit. `[releases] planned-label` sets the word. See [`[releases.<target>]`](https://ascribed-dev.com/docs/content-model/variation/#121-releasestarget).
- **A build can publish an output for each live version of a target.** `[releases.<target>] live` lists the lines a reader can be on, such as `["3.4", "3.5"]`, and `versions = "<target>"` on a build gives one output for each: the newest, the current line, named as the build is and at its addresses, and each other as `<build>@<line>`, under its number after the base path. A line keeps what's available for the target at some version in it, and doesn't show a version at or before the oldest live line. `<output-dir>/_ascribe/versions.json` says which lines have each page, at which route. See [Versions](https://ascribed-dev.com/docs/versions/).
- **An older line's canonical address is the same page in the current line.** In a versioned build, a page of `site@3.4` names the current line's page as canonical, or the page that replaced it, following `replaces`; a page the current line has neither of keeps its own. The sitemap lists a page only when its canonical address is its own, so each current page once. See [What an older line says about itself](https://ascribed-dev.com/docs/versions/lines/#what-an-older-line-says-about-itself).
- **Behavior change: `[versions] current` is removed; `[releases]` says which release is current.** Each target's current release is the newest line in `[releases.<target>] live`, and a project that sets `current` gets an error that says so (`model-versions-current`). The key shipped in 0.3.0, so a project that set it moves it: `[versions] current = "3.4"` becomes `[releases.self-managed] live = ["3.4"]`, for the target it was a release of. `availability-left-behind` compares each feature's versions with its targets' current lines, and reports nothing for a target with no list. See [`[versions]`](https://ascribed-dev.com/docs/content-model/variation/#12-versions).
- **A versioned page's Markdown says which version it documents.** It opens with "Documentation for Self-managed 3.4.", the day the line's support ends, and on an older line, the current line and the address of the same page there. `versions.json` gains the target's `label`, each line's `ends`, and the archived lines with their `url`. See [What an older line says about itself](https://ascribed-dev.com/docs/versions/lines/#what-an-older-line-says-about-itself).

### <a id="v0.3.2-the-ascribe-command"></a>The `ascribe` command

- **`ascribe link` says where a link goes in each line of a versioned build.** Its text says so for each line where the link lands on another page or in another line, and its JSON has `lines`, with the page it lands on and the line a link out leads to. See [Versions](https://ascribed-dev.com/docs/versions/lines/#links-between-lines).
- **An archive can be searched, from a folder opened from a disk.** `ascribe versions archive` writes a search box at the top of every page's sidebar, which finds pages and headings by their words, best first: a page's title, then a heading, then the text under one, code blocks included. Its index is `search-index.js`, loaded the first time the box is used, and the command says its size. Nothing is fetched from anywhere else, and with scripts off the box is absent. See [Archive a line](https://ascribed-dev.com/docs/versions/archive/#archive-a-line) and the [archive contract](https://ascribed-dev.com/docs/contracts/archive/).
- **`ascribe versions archive` renders a line into a folder of HTML that needs nothing else.** `ascribe versions archive "self-managed 3.4" --to archive/3.4` writes every page of the line with a statement of what it is, a sidebar, a contents page, `all.html` to print the whole line, and `noindex` on every page, with no script, working from a disk with no server. It writes nothing into the project, and says what to add to `[releases]` once the folder is hosted. See [Archive a line](https://ascribed-dev.com/docs/versions/archive/#archive-a-line) and the [archive contract](https://ascribed-dev.com/docs/contracts/archive/).
- **`ascribe model` shows release lines, and `ascribe render` puts an older line under its folder.** The model's `releases` section lists each line with its status and the build that is it, and a versioned build says its target and line. `ascribe render --build site@3.4` and `ascribe_render` give the line's route, such as `/3.4/install/`, with its links under the same folder. See [Versions](https://ascribed-dev.com/docs/versions/lines/#in-the-editor-in-review-and-in-the-tools).
- **`ascribe versions release` makes a planned line live in one command.** It rewrites each mark that names the line through a feature, such as `@available: next`, to the version itself, points the feature at the next planned line or `--next`, moves the line to `live`, and, where no build is versioned by the target, moves the old current line to `archived`. It builds the project before and after, refuses when the rewrite would change what a reader sees, writes only files git can put back unless `--force`, and lists the pages the release changes. `--dry-run` writes nothing. See [`ascribe versions`](https://ascribed-dev.com/docs/versions/release/).
- **`ascribe versions list` shows each target's release lines.** Newest first, with each line's status, the output, base path, and pages of each live line, when its support ends, and where an archived line can be read.
- **`ascribe versions changes` says what differs between two lines.** From the marks alone, with nothing built: the pages, sections, blocks, and rows added, removed, and changed in state, by page. A page that takes another's place by `replaces` is one change, which names both. `--format markdown` writes a first draft of release notes.
- **`ascribe versions retire` takes the oldest live line out of the source once its archive is hosted.** `ascribe versions retire "self-managed 3.4" --url https://archive.example.com/3.4/` moves the line to `archived` with its address and the day it ended, rewrites each mark for its target to the simplest spec that means the same in every line still live, and proves no live line's output changed. It lists what no line has any more, and deletes it only with `--remove-unused`, and it lists where each of the line's addresses should go, for a host's redirects. `ascribe build` no longer writes an archived line's output, and removes the folder it wrote before. See [Retire a line](https://ascribed-dev.com/docs/versions/archive/#retire-a-line).

### <a id="v0.3.2-the-editor"></a>The editor

- **`ascribe.projects.exclude` leaves folders out of the search for projects.** A list of glob patterns, from each workspace folder, for folders whose `ascribe.toml` isn't a project, such as test fixtures: `"ascribe.projects.exclude": ["tests/**"]`. A project there isn't listed and gets no language server. Folders that `files.exclude` hides are left out too, as `node_modules` always is, and changing either setting looks for projects again. The message about the 50-project cap names the setting. See [Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/#workspaces-with-several-projects).
- **The editor says what a snippet's example was tested with.** A hover on a `@snippet`'s address shows its file, as a link that opens it, its region and lines, and, for a tested example, what it was tested with, "Changed after its last test run", or "Not tested", with the observations behind it and the folder to run `ascribe observe` in. `example-stale` and `example-unobserved` follow the source's observations and `ascribe.examples.toml` as they change on disk, without the page being edited, and offer **Copy the command**, which copies the line that runs `ascribe observe` there; the editor never runs it. A source's `ascribe.examples.toml` reports its problems as it's edited, and completes its keys. See `docs/content/editor/writing.md`.
- **Switch build lists a versioned build's lines, and the lens and the preview follow the line chosen.** The current line comes first. In an older line, the preview is at the line's address and the lens dims a page new in a later line whole. See [Versions](https://ascribed-dev.com/docs/versions/lines/#in-the-editor-in-review-and-in-the-tools).

### <a id="v0.3.2-the-language-server"></a>The language server

- **The editor checks every line of a versioned editor build, and says which lines a problem is in.** A problem only some lines have ends "(only in build `site@3.4`)". A hover on a mark's version says which line it falls in, a hover on `replaces` says which page each line publishes, and Go to Definition on `replaces` opens the replaced page. `ascribe/targets` and `ascribe/preview` list each build's lines. See [The editor](https://ascribed-dev.com/docs/editor/writing/#hover).

### <a id="v0.3.2-astro-and-the-elements"></a>Astro and the elements

- **A planned version has a badge of its own.** The element library styles an `<ascribe-availability-target planned>` as the badge of no state with a dashed outline, and `Availability.astro` and the generated Zod schema carry a page's `planned` targets. See [the element contract](https://github.com/ascribed-dev/ascribe/blob/main/packages/elements/CONTRACT.md).
- **`VersionBanner.astro` tells a reader on an older line which version it is.** "Documentation for Self-managed 3.4. Support ends on 31 January 2027." and a link to the same page in the current line, with its words as properties. `Versions.astro` now shows the lines without the page, unlinked, and the archived lines that have an address. See [Publish with Astro](https://ascribed-dev.com/docs/versions/#publish-with-astro).
- **`Versions.astro` lists the lines of the page a page replaces, and of the page that replaces it.** A page rewritten under a new path and its old page link to each other, from `replaces` and `replaced-by` in `versions.json`. See [Publish with Astro](https://ascribed-dev.com/docs/versions/#publish-with-astro).
- **`@ascribed/astro` serves every line of a versioned build from one site.** The collection has every live line's pages, an older line's with its number in front of their ids, and each entry's data has `line` and `current`. `@ascribed/astro/Versions.astro` lists the lines that have a page, `sidebar(nav, entries, { entry })` shows the entry's line, and `astro dev` rebuilds every line. A permalink forwards to the current line, and only the current line's Markdown and `llms.txt` are published. See [Versions](https://ascribed-dev.com/docs/versions/#publish-with-astro).

### <a id="v0.3.2-agents"></a>Agents

- **Each line of a versioned build has its own `llms.txt` and Markdown.** An older line's are at its base path, `/docs/3.4/llms.txt`, its index's heading names the line and points at the current line's, and `@ascribed/astro` publishes them. `ascribe agents index --build site@3.4` indexes a line, under a heading that names it; without `--build`, the current one. See [For agents](https://ascribed-dev.com/docs/versions/lines/#for-agents).

### <a id="v0.3.2-review"></a>Review

- **`ascribe diff` lists a page the lines share once, with the lines it changed in.** The text says "in every line" or names the lines, and the HTML report lists the page once with a **Line** switch. In JSON each line stays a build of its own, its routes under the line's folder, and the site preview's review marks the changes of the line a page is in. See [Diff](https://ascribed-dev.com/docs/review/diff/).

### <a id="v0.3.2-fixes"></a>Fixes

- **Fixed:** review no longer shows a tested example's snippet as changed when it isn't. With a source outside the project folder whose `include` doesn't reach its root, `ascribe diff` read the base from git without the source's `ascribe.examples.toml` and `examples.observations/`, so at the base no snippet was a tested example, and every one read as changed. On Ascribe's own docs, a clean branch reported 15 changed pages. The base now has both files, and the editor's review and the HTML report, which read it the same way, show only real changes. See `docs/content/review/diff.md`.
- **Fixed:** The editor's page no longer shows `{available=…}` as text in its lists. The items new since 0.3.1 are marked with `@available: next` instead, so the page badges them ([docs/content/editor/index.md](https://ascribed-dev.com/docs/editor/)).

## <a id="v0.3.1"></a>0.3.1 (2026-10-14)

This release is about **page types**, **page templates**, and **tested examples**. A page now names its type in its frontmatter, with `type`, so a folder can hold every kind of page about one feature; a project that declares types gives each page `type:`, and `files` and `default` are gone from `[types.<name>]`. `ascribe init` starts a project, `ascribe templates eject` gives it The Good Docs Project's six page templates with their guides, and the editor's New Page and the agents' `new-page` prompt start a page from its type's template. A code example can say what it was tested with, and `ascribe check` says when it changed since. `ascribe` installs without Node, with Homebrew or a script. See [Page templates](https://ascribed-dev.com/docs/templates/), [the content model](https://ascribed-dev.com/docs/content-model/), and [Tested examples](https://ascribed-dev.com/docs/examples/tested/).

### <a id="v0.3.1-the-language"></a>The language

- **A glossary term's `link` to a heading its page doesn't have is an error.** The new diagnostic `glossary-link-target-missing` (ASC203) is reported at the `link` in `ascribe.toml` when its `#id` isn't a heading's id on the page, written in it or in a fragment it includes, as `permalink-retired-target-missing` is for a retired permalink's `to`. Before, such a term was never linked, and `ascribe check` said nothing. See `docs/content/content-model/content.md` and [decision #468](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/468-glossary-link-heading.md).
- **`ascribe check` reports a Markdown link in a page's description.** A description is plain text, so a link written in one is never a link: a search result and a link preview show its brackets and destination, and nothing checks it or updates it when its target moves. `description-link` (ASC198), an advice a project can set the level of, reports each one at the link, with a fix that keeps the link's text. See `docs/content/content-model/index.md` and [decision #455](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/455-description-link.md).
- **Behavior change: a page names its content type in its frontmatter, with `type`.** In a project whose `ascribe.toml` declares types, every page says which one it is, such as `type: guide`, wherever its file is, and a folder can hold pages of every type. A page that names no type is an error (`content-type-missing`, ASC196), with a fix for each declared type, and one that names a type that isn't declared is an error at the value (`content-type-unknown`, ASC197), with a fix to the closest name. `[types.<name>]` no longer takes `files` or `default`: each is an unknown key (`model-unknown-key`), whose message says to name the type with `type:` on its pages. Their checks, ASC051, ASC071, and ASC072, are removed. A project that declares no types has the implicit type `page` and needs no change; `type` is now a reserved frontmatter key everywhere. The Zod output gives each type `type: z.literal("<name>")`, and `schema` is a `z.discriminatedUnion("type", …)` of the types when there are several; `contentTypes` is gone. The JSON of `ascribe model`, `ascribe templates eject`, and `ascribe init` is at `schema_version` 2: a type has no `files`, and `eject` lists the pages that name no type as `untyped`. The starter templates begin with `type:`. See `docs/content/content-model/index.md` and [decision #450](https://github.com/ascribed-dev/ascribe/blob/main/project-docs/decisions/450-page-type-key.md).
- **`ascribe check` reports a template's placeholder left in a page.** On a page whose content type names a template, `template-placeholder-left`, an advice a project can set the level of, reports each of the template's placeholders the page still has word for word, a frontmatter field with the template's value, such as `title: "Title"`, and a paragraph the template quotes as an instruction. A placeholder changed by a word isn't found: the check finds what was never touched. See `docs/content/templates/index.md`.
- **An example from another repository says what it was tested with.** `ascribe sources fetch` and `update` copy the code's repository's `ascribe.examples.toml` and its `examples.observations/` beside the files snippets use, whatever the source's `include` says, and `ascribe.lock` lists them. A page then says what each example was tested with, as it does for a folder in the same repository, and `example-stale` and `example-unobserved` say the fix is in the other repository and arrives with the next update. `ascribe sources update` names the examples that went stale, lost their observation, or were tested again, in its text, Markdown, and JSON (`examples`, a new key on each source). Nothing in a copied config is run, and `ascribe observe` refuses to run in `sources/<name>/`. No snippet can address `ascribe.examples.toml` or an observation. See `docs/content/examples/tested.md`.
- **A code example can say what it was tested with, and `ascribe check` says when it changed since.** A source's `ascribe.examples.toml` says which of its files are examples and the line of shell that tests them, and `ascribe observe`, run in the code's folder, runs it and, when it passes, records where it ran and a hash of each example in `examples.observations/default.toml`. A `@snippet` of an example the record lists as it is now gets a caption on the site, "Tested with Node 24.9.0 on Linux", from the info string's new `tested` attribute, and a line under it in the plain output. An example edited since reports `example-stale`, one no record lists reports `example-unobserved`, both advice, and a config that can't be read reports `examples-config-invalid`. A project without the file sees no change. See `docs/content/examples/tested.md`.
- **An example tested under several environments names them all.** `ascribe observe --name node-22` writes `examples.observations/node-22.toml` beside `default.toml`, and a page names every environment whose observation lists the example as it is, merged into one line: "Tested with Node 22.4.0 and 24.9.0 on Linux". A source in another repository reads every observation its copies hold, and `ascribe sources update` says an example is stale only when none of them has it as it is. An observation that lists a file that isn't in the folder reports `observation-file-missing`, advice, at its line, and one in a newer format is one `examples-config-invalid` naming the `ascribe` that wrote it, while the others still count. A code block's `snippet` in the JSON output gains `tested`: the example's status and the observations current for it. See `docs/content/examples/tested.md`.
- **A content type can name its template and its guide.** `[types.<name>]` takes `template`, the page a page of the type starts from, and `guide`, a list of what to read before writing one, both files from the folder `ascribe.toml` is in, outside the content root. A file that isn't there, or is in the content root, is an error in `ascribe.toml` (`model-type-file`), and so is a template that doesn't read as a page (`model-type-template`). `ascribe model` shows both keys. See `docs/content/content-model/index.md`.

### <a id="v0.3.1-the-ascribe-command"></a>The `ascribe` command

- **`ascribe templates eject --keep-type` takes a template for a type the project declares.** It writes the template, its guide, Ascribe's notes, and `templates.lock` as `eject` does, and adds only `template` and `guide` to the type, leaving its frontmatter, other keys, and comments as they are. It refuses a type whose frontmatter doesn't declare the template's fields as strings. Without the flag, `eject`'s refusal of a declared type names it. See [the command reference](https://ascribed-dev.com/docs/examples/sources/).
- **`ascribe templates eject` lists the pages that don't name a type yet.** They're the ones to give `type:`. A type it adds also declares `title` and `description` as the project's first type to declare them does, `inline` and `role` with them, when they're strings there. The JSON gains `copied` on each type added, and `untyped`. See [the command reference](https://ascribed-dev.com/docs/examples/sources/).
- **Behavior change: a diagnostic that's removed is gone.** `ascribe explain` no longer answers for `ASC039`, `ASC051`, `ASC071`, or `ASC072`, which were kept as retired, and the diagnostics reference has no "Retired" section. A code that's removed leaves a gap; no other code changes.
- **`ascribe init` starts a project, writing its first `ascribe.toml`.** In a folder with no `ascribe.toml` at or above it, it writes a small, commented one, with the one type `page`, and makes `docs/`; `--templates` takes the six page templates too, as `ascribe templates eject` does. Where there's a project, it names it and writes nothing. `ascribe templates eject` without a project now says to run it. See `docs/content/examples/sources.md`.
- **`ascribe observe --check` says whether an observation still matches, and runs nothing.** It exits 1 when the observation is stale against the folder, for a pre-commit hook, and starts no program. A run of `ascribe observe` says what changed since the observation it replaces, `ascribe report`'s inventory counts each source's examples as current, stale, and unobserved, a source in another repository's from its copies, and `ascribe model` names each source's observations and their environments. See `docs/content/examples/tested.md`.
- **`ascribe templates eject` writes a page template and its guide into a project.** It writes The Good Docs Project's how-to template, converted into an Ascribe page, and its guide into `templates/`, or `--to`'s folder, adds `[types.how-to]` to the end of `ascribe.toml` with its `template` and `guide`, and records what it copied in `templates/templates.lock`. It checks everything first, and writes nothing when a type is declared already or a file is in the way. `ascribe templates list` shows the set. See `docs/content/templates/index.md`.
- **`ascribe templates eject` writes Ascribe's notes on each type beside its guide.** Each type gets `templates/<type>.ascribe.md`, a minute's reading on what's particular to writing that kind of page in Ascribe, listed second in the type's `guide`, after their guide. `templates.lock` records the notes under `ascribe-files`, apart from their files. See `docs/content/templates/index.md`.
- **Five more page templates: concept, tutorial, reference, troubleshooting, and release notes.** They're The Good Docs Project's, converted from their v1.6.0 release as the how-to is, each with its guide. `ascribe templates eject` with no name writes all six and declares their types, and `ascribe templates list` shows them. See `docs/content/templates/index.md`.
- **`ascribe templates status` says what's changed in the templates since the project's copy.** For each template a project took, it says whether the set this `ascribe` carries has changed it since the copy, with the sections that differ, and whether the project has changed its copy, telling the two apart by the hashes in `templates.lock`; then each template the project doesn't have. Without a lock or an entry it says so, and compares the files with the set alone. It reads no network and changes no file. `--diff <NAME>` prints the change to one template, `--exit-code` exits with 1 when the set has changed or dropped one, and `--format json` has a schema. See `docs/content/examples/sources.md`.

### <a id="v0.3.1-the-editor"></a>The editor

- **New Page starts a page from its type's template, with the first placeholder selected.** **Ascribe: New Page**, in the Command Palette, the **Pages** view, and the Explorer's menu on a folder, asks for the type, each with what it's for, and the title, makes the page in the content root or in the folder, with `type:` in its frontmatter, and opens it with the first thing to fill in selected. **Ascribe: Go to Next Placeholder** steps through the rest. See [Start a page](https://ascribed-dev.com/docs/editor/actions/#start-a-page).

### <a id="v0.3.1-agents"></a>Agents

- **The `new-page` prompt starts the page from its type's template, and sends the agent to the guide.** For a type with a template, `ascribe agents prompt new-page`, the MCP server's prompt, and the plugin's `/ascribe:new-page` give the template with the title set, and the paths of the type's `guide` to read before writing. The page goes where `path` says, a file or a folder in the content root, or in the content root, named for the title; never over a file that's there. A type with no template gets its frontmatter, with each field to fill in left empty ([page templates](https://ascribed-dev.com/docs/templates/), [agents guide](https://ascribed-dev.com/docs/agents/)).
- **The rules for agents say what each page type is for.** A type's new `purpose` key, a string, says it in a few words, and `ascribe model` shows it. A type `ascribe templates eject` added says The Good Docs Project's summary of it without one, from `templates.lock`. When the rules don't fit in 4,000 characters, the page types' lines are cut first, and the cut names `ascribe model --section types` ([content model reference](https://ascribed-dev.com/docs/content-model/), [agents guide](https://ascribed-dev.com/docs/agents/)).

### <a id="v0.3.1-the-docs-and-the-site"></a>The docs and the site

- **The docs say what their command output was tested with.** A page that shows what a command prints, or one of the example projects the same tests check, reads "Tested with Rust" and the toolchain's version under the example, from an observation of the repository's own tests, and the docs don't build with a tested line that's no longer true. See [Show what an example was tested with](https://ascribed-dev.com/docs/examples/tested/).
- **Two repositories to copy for tested examples, one for Node with TypeScript and one for Python.** Each has examples with their outputs, a test for each, two observations, docs that show them, and CI that fails when an example changes without `ascribe observe`. [Show what an example was tested with](https://ascribed-dev.com/docs/examples/tested/#start-from-a-repository) points at both, and so does Getting started.
- **`@ascribed/astro` has a reference page of its own, apart from the Astro guide.** The [Astro guide](https://ascribed-dev.com/docs/astro/) sets a site up; [the `@ascribed/astro` reference](https://ascribed-dev.com/docs/astro/reference/) has the options, what the integration does in `astro build` and `astro dev`, the head, sitemap, `robots.txt`, and response headers, review in the site preview, and styling. What `ascribe agents sync` writes for each target and each hook, and what a prompt says, moved from the [Agents guide](https://ascribed-dev.com/docs/agents/) to [`ascribe agents`](https://ascribed-dev.com/docs/agents/commands/). Every permalink still leads to its section.
- **The docs site's pages put their content before the sidebar in the HTML.** The sidebar is still drawn on the left, and a second skip link reaches it from the top; the menu at phone width moves focus into it. An agent reading a page's HTML meets the content first, which the delivery spec's `content-start-position` check measures.
- **The docs are grouped by feature, and every page's address moved.** A folder is now a feature or a subject, with every type of page about it: [review](https://ascribed-dev.com/docs/review/)'s guide, its [troubleshooting](https://ascribed-dev.com/docs/review/troubleshooting/), and the [commands it runs](https://ascribed-dev.com/docs/review/diff/) are together, where they were under guides and reference. The sidebar has a group for each folder: writing pages, the content model, checks and reports, review, the editor, agents, examples and drift, publishing with Astro, permalinks, page templates, and the command. Every permalink still leads to its page, and no page's content changed.
- **Every page of the docs has one of the page types.** The docs take the set's how-to, tutorial, concept, and troubleshooting types beside its reference. What to do when review shows no pull request, no comments, or no marks moved from the [Review guide](https://ascribed-dev.com/docs/review/) to [Troubleshooting review](https://ascribed-dev.com/docs/review/troubleshooting/), a symptom per heading. [Page templates](https://ascribed-dev.com/docs/templates/#in-a-project-that-has-pages) says what to expect when a project with pages takes the set, from the docs doing it. Every permalink still leads to its page.
- **Getting started starts the project with `ascribe init`.** Its "Create `ascribe.toml`" runs the command and shows the file it writes, then adds what the guide's page uses. Writing `ascribe.toml` by hand stays, as a short section, for a project that wants nothing written for it and for 0.3.0. See [Getting started](https://ascribed-dev.com/docs/getting-started/).
- **The page templates guide covers taking the whole set, changing a template, and what the check can't find.** See `docs/content/templates/index.md`.
- **The schemas of `ascribe templates eject` and `ascribe templates status` have a page of their own.** They're in [JSON reports: `ascribe templates`](https://ascribed-dev.com/docs/contracts/json-reports/templates/), beside the page of `ascribe init`, `ascribe sources`, and `ascribe permalinks`, which they had made longer than the Web Documentation Delivery Spec's page size.

### <a id="v0.3.1-installing"></a>Installing

- **`ascribe` installs without Node, with Homebrew or a script.** `brew install ascribed-dev/tap/ascribe`, or `curl -fsSL https://ascribed-dev.com/install.sh | sh`, which puts the release's binary in `~/.local/bin` after checking it against the release's `SHA256SUMS`, and never asks for `sudo`. On Windows, [Install](https://ascribed-dev.com/docs/install/#without-node) gives the PowerShell that downloads, checks, and installs the release's archive.

### <a id="v0.3.1-fixes"></a>Fixes

- **Fixed:** a heading's id `ascribe rename` changes is changed in `ascribe.toml` too. A glossary term's `link` and a retired permalink's `to` that name the heading follow it, as the links in pages do, and so does `F2` in the editor. Before, they kept the old id, and nothing reported it. `ascribe.toml` keeps its comments and order. See `docs/content/command/questions.md`.
- **Fixed:** `ascribe report agents` no longer says `<main>` is enough for `content-start-position`. The checker measures the whole page, so the advice is to put the content before the navigation in the HTML and place the navigation with CSS. See `docs/content/checks/report.md`.
- **Fixed:** the diagnostics reference shows the image examples as they're written. The fixes for `image-alt-missing` and `image-attribute-missing` showed `../settings.png` and `../image.png` where a page writes `settings.png` and `image.png`, because the reference's generator rebased links inside inline code. It now leaves code alone, so the reference agrees with `ascribe explain` ([the pages' diagnostics](https://ascribed-dev.com/docs/checks/diagnostics/pages/)).
- **Fixed:** `ascribe rename --config <dir>` works from outside the project. Run from the repository's root with `--config docs`, it said "The project has no pages to rename in." and renamed nothing, for every kind of target. It renames now, reading a path from the current folder or from that project's content root, as `check` and `build` do. A page it can't find is named with both places it was looked for. See `docs/content/command/questions.md`.
- **Fixed:** a page `ascribe rename` moves takes its glossary links in `ascribe.toml` along. So do the retired permalinks whose `to` names it, and the editor's move does the same. Before, a glossary term's `link` kept the old path, so the model had an error and every later rename refused. Each keeps its `#id`, and `ascribe.toml` keeps its comments and order. See `docs/content/command/questions.md`.
- **Fixed:** a table row's availability reads the same in the site's HTML as in its Markdown. The badge's targets are between a visually hidden "(Available: " and ")", so the delivery spec's `markdown-content-parity` check passes, and a screen reader says what the badges are. The page looks as before. See `packages/elements/CONTRACT.md`.
- **Fixed:** a phrase or code with a placeholder's name isn't reported as the template's placeholder. On a page of a type with a template, `template-placeholder-left` reported a phrase such as `{product}`, which the tutorial's notes tell a writer to use, and a placeholder quoted in inline code. Only a placeholder whose brace is still escaped, as the template writes it, is reported now ([the templates guide](https://ascribed-dev.com/docs/templates/)).

## <a id="v0.3.0"></a>0.3.0 (2026-10-11)

This release is about **agents**, **advice**, and **permalinks**. Agents that write docs get an MCP server, a skill, hooks, a plugin, and instructions written from your content model; agents that read docs get `llms.txt`, a Markdown version of every page, and your say over what each kind of crawler may do. `ascribe check` gains advice beside its errors: checks of a page's content and across the project, and prose checked with Vale, each with what to do next, and `ascribe report` says what state a project is in. Permalinks give a product addresses into its docs that don't move. VS Code gains the actions bar, the Ascribe sidebar, and a walkthrough, and `@ascribed/astro` writes a page's head, the sitemap, and `robots.txt`. See [Agents](https://ascribed-dev.com/docs/agents/), [Reports](https://ascribed-dev.com/docs/checks/report/), and [Permalinks](https://ascribed-dev.com/docs/permalinks/).

### <a id="v0.3.0-the-language"></a>The language

- **`ascribe check` reports two pages of a build with the same description (`description-duplicate`).** Each is reported at its description, naming the others, since a search result, a link preview, and `llms.txt` would say the same of both. It's advice, beside `title-duplicate`, and a project can raise it or turn it off in `[checks]`. See [the diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/).
- **A project can set how a folder's, a type's, or the whole site's pages are listed.** `[listing]` in `ascribe.toml` has `default` for every page, `[listing.folders]` for the pages under a folder, and `[listing.types]` for the pages of a content type, each `listed`, `unquoted`, or `unlisted`, so `"internal/" = "unlisted"` keeps a folder out of the sitemap and `llms.txt` with no change to its pages. A page gets the strictest that applies. A page can set a stricter value than its rule, and one that sets a looser value is an error that quotes the rule and its line (`listing-loosened`), with a quick fix that removes the page's own setting; a rule looser than one it's under is an error too. Each page's record in `_ascribe/pages.json` says where its listing came from, in `listing_from`. `@ascribed/astro`'s `Head.astro` now asks search engines not to quote an `unquoted` page (`nosnippet`). In the editor, `listing:` completes its values, and hovering it names the rule the page is under. See the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/) and the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **A page that isn't listed has a Markdown version that isn't either, on Netlify.** Each build writes `_ascribe/headers.json` in the site output: the response headers each page's Markdown version needs from the host, `X-Robots-Tag: noindex` for a page that's `unlisted` and `nosnippet` for one that's `unquoted`, and a canonical `Link` to the page on every one, so a search engine lists the page and not its copy. With `headers = "netlify"` in `[consumer]`, `@ascribed/astro` writes them into the built site's `_headers`; a site with its own `public/_headers` keeps it, and the build fails with the lines it lacks. `astro dev` sends the headers with each `.md` file. On another host, a deploy step reads the list. See the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/) and the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **A page can say it isn't listed, and each build writes its page records.** A page's frontmatter `listing` is `listed`, `unquoted`, or `unlisted`; an `unlisted` page is still built, and is left out of `llms.txt`. Each build's site output holds `_ascribe/pages.json`: a record of every page, with its route, source, title, description, canonical address, `listing`, and Markdown version, and the project's own `robots.txt` lines from the new `[crawlers] lines`, with the base path put before each `Allow` and `Disallow` path. `ascribe check` advises when a page that isn't listed is at an address those lines disallow for every crawler (`listing-disallowed`), since the crawler can't read that it isn't to be listed. See the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/) and the [output-layout contract](https://ascribed-dev.com/docs/contracts/output-layout/).
- **A page in the site output says where its source is.** Every page's frontmatter has `source`, its path from the content root, such as `guides/install.md`, and the generated schema types it, so a layout's "Edit this page" link is `entry.data.source` after the repository's address and the content root's folder, whatever the build folder's path. **Behavior change:** `source` is reserved on page types, as `formatted` is, so a content type that declares a field named `source` fails with `model-field-reserved`; rename the field. See the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **`ascribe check` reports a page whose own top heading says something its title doesn't.** The check is `title-heading-differs`. A page that writes its own level-1 heading, as a landing page does in its hero, is reported at its title when neither contains the other, ignoring case, since a search engine may show either as the page's name. Most pages write none, since the layout draws the title, and aren't looked at. It's advice that may be fine as it is: acknowledge it in the page's `intended` frontmatter. See [the diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/).
- **What to do next, advice, and `[checks]`.** Each diagnostic says what kind of next step it has: `fix` (Ascribe can make the edit), `choose` (you pick among things Ascribe lists), `write` (it needs writing or judgment), `outside`, or `review`. It's `next` in `ascribe check --format json` and `ascribe explain --format json`, in each diagnostic's `data` in the editor, and in the [diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/#what-to-do-next). A new severity, **advice**, sits below a warning: `ascribe check` lists it last and counts it apart (`0 errors, 2 warnings, 5 advice`, and `advice` in the JSON's `summary`), the editor shows it as information, and it never fails the check, even with `--deny-warnings`, or reaches an agent from `ascribe agents hook`. `severity` may gain values without a new `schema_version`; treat one you don't know as advice. `ascribe.toml` gains [`[checks]`](https://ascribed-dev.com/docs/content-model/checks/#22-checks), which sets how loudly each check about your content's quality speaks; a diagnostic about whether the project is valid can't be named there (`model-check-not-configurable`).
- **Say a problem is intended.** Some checks report what may be fine as it is: their next step is `review`. Such a problem can be acknowledged, with a reason, where its check reports it: `@intended {check=<check>}: <reason>` above a block, the reserved `intended` frontmatter key for a page or a fragment, or an [`[[intended]]` table](https://ascribed-dev.com/docs/content-model/checks/#intended-acknowledgements) in `ascribe.toml` for a content model entry or an image. An acknowledged problem isn't reported: `ascribe check` and `ascribe build` count it (`1 acknowledged`), and their JSON lists it with its reason under a new `acknowledged` key, with `summary.acknowledged`, both left out when there are none; `schema_version` stays `1`. An acknowledgement that covers nothing in any build is reported as advice (`intended-unused`) with a fix that removes it, and the editor offers **Mark as intended, and write why** on a review check's problem. Acknowledgements change no output. The first review checks are `review-overdue`, acknowledged in a page's `intended` frontmatter, and the checks across the project, below. **Behavior change:** `intended` is reserved frontmatter, so a content type or `[fragments.frontmatter]` that declares it is an error (`model-field-reserved`). See [`@intended`](https://ascribed-dev.com/docs/writing/directives/#intended).
- **Checks of a page's content.** Five new checks give advice, which a project can raise to a warning or an error, or turn off, in `[checks]`. `page-size` reports a page whose plain Markdown, in a build, is 50,000 characters or more, the Web Documentation Delivery Spec's limit, listing its largest sections; `[checks.page-size] limit` sets another. `heading-level-skipped` reports a heading more than one level below the one before it, or a level-1 heading, since the page's title is its level 1, with a fix. `code-language-missing` reports a fenced code block with no language, with a fix that marks it `text`. `page-description-missing` and `review-overdue` read the fields a content type marks with the new `role` option: `role = "description"` on a string field, and `role = "review-date"` on a date field, whatever the fields are called (`model-field-role`). `review-overdue` checks as of today in UTC, or of `ASCRIBE_TODAY`. **Behavior change:** a project that has level-1 headings in its pages, or code blocks without a language, gets advice it didn't, which fails nothing. See [Setting a check's level](https://ascribed-dev.com/docs/checks/diagnostics/#setting-a-checks-level) and [Field roles](https://ascribed-dev.com/docs/content-model/#54-field-roles).
- **Checks across the project.** Eight checks about your content's quality, each advice, which you can raise, lower, or turn off in [`[checks]`](https://ascribed-dev.com/docs/content-model/checks/#22-checks): a page no other page links to or includes (`page-orphan`), a fragment nothing includes (`fragment-unused`), a phrase, feature, or glossary term nothing uses (`phrase-unused`, `feature-unused`, `glossary-term-unused`), an image under the content root that no page shows, in Markdown, raw HTML, or frontmatter (`image-unused`) or that's over a size (`image-large`, 500 KB unless `[checks.image-large] limit` says otherwise), and two pages of a build with one title (`title-duplicate`). What's unused is what the editor's **Content model** view and **Find All References** count no use of. An orphan and a duplicate title are found per build, and reported once with the builds they're in. The editor reports all but the image checks when you save, and the levels in `[checks]` now apply to the problems in `ascribe.toml` too. Each but `title-duplicate` is a review check: an orphan or an unused fragment is acknowledged in its `intended` frontmatter, and an unused phrase, feature, term, or image in `[[intended]]`. An agent prompt about one says what helps decide it: the pages that mention an orphan's title or share its folder, an image's size and dimensions, or the descriptions of the pages that share a title. See [Content checks](https://ascribed-dev.com/docs/checks/diagnostics/content-checks/).
- **Features left behind by a release.** `[versions] current` in `ascribe.toml` names the latest release. With it, `availability-left-behind` (advice, configurable) reports a feature whose availability names a version for every target, none later than the current release: a feature such as `next` whose release has shipped, so what it marks no longer reads as new. It's a review check, acknowledged in `[[intended]]` for a feature kept as a named history. Set `current` to each release as it ships. See [`[versions]`](https://ascribed-dev.com/docs/content-model/variation/#12-versions).
- **Permalinks.** `@id {permalink=true}: rotate-keys` makes a heading's id a permalink: a name for the section that something outside the docs links to, at an address that forwards to it wherever the page moves. Each build lists its permalinks in the site output's `_ascribe/permalinks.json` (written for every build, with an empty table when there are none), and `@ascribed/astro` gives each an address, `go/<id>` under the base path, a page that forwards to the heading without a script and isn't indexed, in `astro build` and `astro dev`. `[consumer] permalink-path` publishes them elsewhere, as a path from the site's root (`model-consumer-permalink-path` when it doesn't start with `/`). A page has one with `permalink: cli-reference` in its frontmatter, which leads to the top of the page; pages and headings share one set of names, and a fragment can't have one. A build that publishes one permalink twice is an error (`permalink-duplicate`). A permalink that's removed is reported once the project commits its list, below. See [Permalinks](https://ascribed-dev.com/docs/writing/directives/#permalinks) and [the Astro guide](https://ascribed-dev.com/docs/astro/reference/#permalinks).
- **What a permalink's link carries.** A permalink's address takes the content model's dimensions as parameters: `/go/weave-config/?pm=pnpm` opens the section with the `pnpm` tab showing, and the choice holds on the next pages, as a click's does. A parameter the docs don't know is ignored, and with scripts off the address still forwards. The site output's `_ascribe/permalinks.json` lists the dimensions and their values, `@ascribed/elements` exports `choose(dimension, value)` for a script to make a reader's choice, and `@ascribed/astro`'s `MissingPermalink` component explains, on a site's 404 page, an address under the permalinks' path that names nothing. See [Linking from a product](https://ascribed-dev.com/docs/permalinks/#linking-from-a-product).
- **Retiring and aliasing permalinks.** `ascribe.toml`'s new `[permalinks]` ends a permalink on purpose and gives one a second name. `[permalinks.retired]` names each retired permalink with a `reason`, and optionally `to`, a page or a heading on one: its address leads there, or, without it, is a page that says what was there was removed, with the reason and a link to the docs' home. `[permalinks.aliases]` gives a permalink another name whose address leads to the same place. `ascribe permalinks retire <name>... --reason "..."` (or `--page`, `--under`) writes the entries in place, keeping the file's comments, and records them in `ascribe.permalinks`, which gains `[aliases]` and `[retired]` tables; a retired permalink is no longer reported as removed. In the editor, the quick fix on `permalink-removed` retires it with a placeholder reason, an error until it's replaced. `verify` accepts an alias, and fails on a retired name, giving the reason. New diagnostics: `model-permalinks`, `permalink-alias-target-missing`, `permalink-name-conflict`, and `permalink-retired-target-missing`. The site output's `_ascribe/permalinks.json` lists aliases and retired permalinks, and `removed` for those with nowhere to lead. See [Moving, aliasing, and retiring](https://ascribed-dev.com/docs/permalinks/#moving-aliasing-and-retiring).
- **Prose, checked with Vale.** `[checks.vale]` in `ascribe.toml` runs [Vale](https://vale.sh/) 3.16 or later, which you install, on your pages' prose: Ascribe gives it the prose alone, with phrases replaced by their text, and never frontmatter, directive lines, attributes, or code, and reports each alert as a `prose` diagnostic at its place in the source, one in a phrase's text at the `{key}`. Start from `preset = "quiet"`, Ascribe's own selection of rules that are almost never wrong (`Ascribe.Repeated` and `Ascribe.Typos`, both advice), with nothing to download or write, or point `config` at your own `.vale.ini`. Your phrase values, glossary terms, and labels are a Vale vocabulary, so they aren't misspellings. The editor checks a page when you open and save it, never as you type; `ascribe check` checks with `--vale`, or always with `in-check = true`, and its JSON gives the rule as `rule`. A Vale that isn't installed is one `prose-not-checked` advice. `ascribe vale eject` writes the preset out as your own configuration. Add `.ascribe/` to your `.gitignore`. See [Prose, through Vale](https://ascribed-dev.com/docs/checks/vale/).
- **A snippet whose file isn't at the pin says so.** When a file a snippet names isn't in the code's repository at the commit `ascribe.lock` pins, because it was moved or deleted there, `ascribe check` says that, and how to fix it, where it said to run `ascribe sources fetch`, which can't help. The update pull request quotes the new message. `ascribe sources fetch` and `update` record those files in `ascribe.lock`, under a new key, `missing`, and `ascribe sources status` shows them as `not_at_pin`. The lock's `version` stays `1`; a lock with `missing` can't be read by an earlier release. See [`ascribe.lock` and the copies](https://ascribed-dev.com/docs/contracts/content-model/sources/#201-ascribelock-and-the-copies).

### <a id="v0.3.0-the-ascribe-command"></a>The `ascribe` command

- **Check one rule at a time.** `ascribe check --code ASC037` (or `--code link-id-missing`, repeatable) shows only the problems with that code, across the whole project, so a rule can be fixed everywhere instead of file by file. The counts, the exit code, `--format prompt`, and `--fix` are about what's shown, and the concise list's "and N more" command keeps the filter. `--summary` ends with the command for the code it counts most. The MCP tool `ascribe_check` takes `code`. See [Checking one rule at a time](https://ascribed-dev.com/docs/checks/check/#checking-one-rule-at-a-time).
- **Behavior change: the Markdown version of a page says a switcher is one, and heads each of its arms.** A variant group a build keeps more than one arm of, which the site shows as tabs, now opens with a line saying a reader sees one of its arms at a time, and by what: `A reader sees one of the next 3 sections at a time, chosen by package manager.` Each arm is under a heading one level below its section that names the dimension and the value, `### Package manager: pnpm`, or its title, instead of a bold label, so an agent reading a page knows the arms are alternatives and which is which. `ascribe render` prints the same text, and its JSON's `text` has it too; the HTML comment it wrote above a switcher is gone. A build that leaves one arm is unchanged. See the [variants guide](https://ascribed-dev.com/docs/writing/variants/) and the [`ascribe render` reference](https://ascribed-dev.com/docs/command/).
- **`ascribe report links` holds a link that answered before until it keeps failing.** A link whose address answered on an earlier run and fails now is listed as unconfirmed, with what it answered, the last day it answered, and the day it's reported from, until it has kept failing for `[checks.links] confirm-days`, 3 days unless set; `0` reports every failure at once. A 404 or a 410 is reported at once, a moved link isn't held, and an acknowledged link stays acknowledged. An unconfirmed link isn't counted, doesn't fail `--exit-code`, and is left out of `--format prompt`. What each address last did is kept in `.ascribe/history/links.json`, so the report also says which problems are new since the last run and how many it reported are gone. A first run, or one with `--no-history`, reports every failure, and says so. The JSON's `links` gains `previous_run`, `new`, `resolved`, and `unconfirmed`, and `schema_version` stays `1`. The scheduled job in [The project report](https://ascribed-dev.com/docs/checks/report/#on-a-schedule) keeps the history with `actions/cache` too. Deleting `.ascribe/history/` costs one noisier report, and never a missed problem.
- **`ascribe report links` doesn't ask again about an address that answered.** An address that answered with nothing to report is kept in `.ascribe/cache/links/`, with the day, and isn't asked about again for up to `[checks.links] reuse-days`, 14 days unless set; `0` asks about every address on every run, and `--recheck` asks about every address once. A link that moved, failed, or timed out is asked about on every run, so every problem in a report was found in that run. The report says what it did, `Checked 1786 addresses: asked about 64, and reused 1722 answers, the oldest from 28 September.`, and its JSON's `links` gains `asked`, `reused`, and `oldest_answer`, with `checked` their sum and `schema_version` still `1`. The scheduled job in [The project report](https://ascribed-dev.com/docs/checks/report/#on-a-schedule) keeps the answers with `actions/cache` and runs daily. Deleting `.ascribe/` costs time and nothing else.
- **`ascribe report pages` lists every page's record.** For each build, every page it publishes, sorted by address, with its title, description, and listing, and where the listing comes from: as a table cut at `--limit`, or as JSON whose records have the fields of the site output's `_ascribe/pages.json`. `--not-listed` lists only the pages kept out of search, and why. It reads nothing a build hasn't worked out, so it needs no network. See [the project report](https://ascribed-dev.com/docs/checks/report/).
- **One word for what Ascribe reports, and one count.** A *problem* is what Ascribe found in your project, and a *diagnostic* is the kind of problem, with a code, that the [diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/) and `ascribe explain` describe. `ascribe report` says problems where it said findings, and its JSON's counts, at the top and in `problems`, are `summary`, as in `ascribe check`'s JSON, where they were `findings`. The prompt `ascribe check --format prompt` and the editor's **Prompt agent** write about a file or a project counts as the summary does, `Fix the 1 error`, and lists advice after the errors and warnings as something to look at, since it never fails the check.
- **`ascribe check --fix` applies the safe fixes.** Every fix `ascribe check` marks `safe` (one that can't change what the page says and leaves nothing to decide, such as linking to a page's file instead of its route, or removing an acknowledgement that covers nothing) is applied, then the check reports what's left; an `unsafe` fix never is. With paths, only the fixes of what counts for them. The JSON lists what was applied as `fixed`. See [Applying the safe fixes](https://ascribed-dev.com/docs/checks/check/#applying-the-safe-fixes).
- **`ascribe model` shows sources, and `agents sync` says who reads what.** `ascribe model` lists the project's sources, with their folders, the files they make readable, and the repository of one in another, and `--section sources` shows them alone; the JSON has a `sources` field. `ascribe agents sync` says beside each file it writes which agents read it, and when Claude Code reads none of the skill, that `--target claude` writes it where Claude Code does.
- **The JSON Schemas give integers no `format`.** The schemas in `schemas/`, and the MCP tools' output schemas built from them, no longer say `"format": "uint"` (or `uint32`, `uint8`, `int32`), which JSON Schema doesn't define and which made an MCP client print a warning for each. An integer is still `"type": "integer"`, with `"minimum": 0` where it can't be negative.
- **Behavior change: `--format summary` is `--format markdown`.** On `ascribe drift`, `ascribe sources update`, and `ascribe report`, the format that writes Markdown for a CI job's summary, a pull request's description, or an issue's body is named `markdown`, and `summary` is no longer accepted. A workflow that runs one of them with `--format summary` changes it to `--format markdown`. `ascribe check --summary` is unchanged. See [`ascribe drift`](https://ascribed-dev.com/docs/review/diff/#cli-drift).
- **A command run above your projects names them.** Where there's no `ascribe.toml` at or above the current directory but there are projects in the folders below, as at the root of a repository with several, the error lists their folders and says to run the command in one or pass `--config`. See [the command reference](https://ascribed-dev.com/docs/command/#options-every-command-accepts).
- **Commands that answer questions.** Six new commands tell an author, or an agent, what it would otherwise guess, and change nothing. `ascribe explain ASC036` says what a diagnostic means and how to fix it, with a checked example for the diagnostics people meet most; `ascribe model` shows what the content model allows, in under 4,000 characters; `ascribe outline <page>` lists a page's headings with the ids links use, including those from its fragments; `ascribe link <target> --from <page>` says whether a link works and what to write; `ascribe refs <target>` lists where a page, heading, fragment, phrase, feature, or other content model entry is used, as **Find All References** does in the editor; and `ascribe render <page> --build <name>` shows a page as that build's readers see it. Each takes `--format json`, described by a JSON Schema. `ascribe --help` starts with examples. See the [command reference](https://ascribed-dev.com/docs/command/questions/#cli-explain).
- **Rename from the command line.** `ascribe rename` does what the editor's rename does, for an agent or a script: `ascribe rename phrase:cloud hosted` renames a phrase and every use of it, including the ones a search and replace misses, in frontmatter fields, code blocks, and the code a `@snippet {phrases=true}` shows. It renames a dimension's value (`value:self-managed`), a heading's id (`keys.md#rotate-keys`), giving a heading whose id comes from its text an `@id`, and a page, which moves, with the links to it and on it. It exits with `1` when what it names doesn't exist. See [the command reference](https://ascribed-dev.com/docs/command/questions/#cli-rename).
- **Check one file, or text that isn't saved.** `ascribe check docs/guides/install.md` reports only what counts for that file: its own problems, and those its fragments cause on it; checking a fragment shows each of its problems once. `--stdin --path <file>` checks standard input as that file's text without writing it. `--editor-build` runs the editor's build's checks alone, and with a file named checks only it and the pages that include it, quick enough to run after every edit. `--format concise` writes one line per diagnostic, for an agent, and `--summary` counts the diagnostics by code and by file, listing at most 20 files in text. In the JSON, each diagnostic has its fix advice (`help`) and a link to its entry in the diagnostics reference (`docs`), and each fix says whether it's safe to apply as it is (`applicability`); `schema_version` stays `1`. In the editor, a diagnostic's code links to its entry. See [Checking some files](https://ascribed-dev.com/docs/checks/check/#checking-some-files).
- **The binary links to the docs by permalink.** Every address of the docs that Ascribe writes is a permalink of its own docs, so the docs can move: each command's `--help` (`https://ascribed-dev.com/go/cli-check/`), each diagnostic's `docs` in `check`'s JSON, `explain`, and the editor's link (`https://ascribed-dev.com/go/ASC036/`), the agents' rules, the Claude Code plugin's homepage, the VS Code extension's links, and the READMEs published to npm and the Marketplace. Diagnostics' headings in the [diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/) have their codes as anchors, `#ASC036`, and the commands' headings in the [command reference](https://ascribed-dev.com/docs/command/) have `cli-` and the command's name, `#cli-sources-update`.
- **A committed list of permalinks.** `ascribe permalinks sync` writes `ascribe.permalinks` beside `ascribe.toml`: each permalink the content declares, in any source file, with the file and heading it's declared in. Commit it with the docs. With it, `ascribe check` and the editor warn when a permalink is removed (`permalink-removed`), since something outside the docs may still link to it, and when the list is behind the content (`permalinks-list-behind`), with a quick fix in the editor that writes it. Moving a permalink to another page isn't removing it, and `sync` never drops a name, so running it can't hide a removal. `ascribe permalinks sync --check` writes nothing and exits with 1 when the list is behind, for CI. `ascribe permalinks list` shows each permalink and whether the list has it, and `ascribe permalinks verify <name>...` checks names against the list alone (or against `--against <path>`), so a product's CI can check its links against a copy of the file, failing on a name the docs don't have. A file this version can't read is an error (`permalinks-list-invalid`). A project without the file is told nothing. See [The permalinks list](https://ascribed-dev.com/docs/contracts/permalinks-list/) and [Checking a product's links](https://ascribed-dev.com/docs/permalinks/#checking-a-products-links).
- **Behavior change: headings inside a variant name it in the plain-markdown output.** When a `@variant` group stays labeled sections, each heading inside an arm ends with the arm's label, as `## Install (pnpm)`, so every section's headings are distinct. A build that selects one arm is unchanged.
- **`ascribe report`: what state the project is in.** A new command for what's too slow, needs the network, or is too broad for `ascribe check`, section by section. `problems` counts what the check finds by check, by file, and by kind of next step, and lists every acknowledgement with its reason; `inventory` counts the pages by content type and by owner, and lists the overdue reviews, the pages nothing links to, and the entries nothing uses; `builds` lists what each build leaves out that another keeps, and why; `links` runs [lychee](https://lychee.cli.rs/), which you install, on every external link, and reports each broken one (`link-external-broken`, to review, acknowledged with `@intended` above its block) and each that moved permanently (`link-external-moved`, with a fix that writes the new address) at the link; and `agents` runs the delivery spec's checker, `afdocs`, on a built site (`--site`), and reports each failing check by who changes it: the hosting (`delivery-hosting`) or Ascribe (`delivery-output`). With no section named, it runs the first three; a section that can't run says why and how to get it, and the others still run. `ascribe report` makes no request itself: the programs it runs do. `--format markdown` writes Markdown for a CI job or an issue, `--format json` capped lists described by a JSON Schema, and `--format prompt` one prompt about a section's problems for an agent. `--exit-code` fails on problems, and on a section that couldn't run. lychee's cache is turned off whatever the project's `lychee.toml` says, since a cached answer forgets that a link moved. `[checks.links]` in `ascribe.toml` names lychee's command and the hosts to leave out, and a content type's new `role = "owner"`, on a string or enum field, is what `inventory` counts pages by. See [The project report](https://ascribed-dev.com/docs/checks/report/), with a weekly GitHub Actions job.
- **A faster `ascribe diff` and `ascribe drift`.** `diff` compares only the pages a change can reach, and reads the two versions side by side: on 3,000 pages it takes a third less time than it did, and a third less memory. `drift` takes a third less time when an example changed. The reports are the same.
- **JSON Schemas for the commands' JSON.** What `ascribe check`, `build`, `diff`, `drift`, `sources status`, `sources update`, and the commands that answer questions write with `--format json` is described by a JSON Schema, generated from the code that writes it. See the [JSON report contract](https://ascribed-dev.com/docs/contracts/json-reports/).
- **The `json` output's page `format` is `"ascribe-page"`,** where it was `"tessera-page"`, the project's working name. Its `schemaVersion` stays `1`. A tool that checks the value needs the new one. See [the JSON output](https://github.com/ascribed-dev/ascribe/blob/main/crates/ascribe-emit/README.md#json).
- **A broken link is offered the page it meant.** `ascribe check` gives a link to a page that doesn't exist, to a heading its page lacks, or to a fragment the targets `ascribe link` lists as closest, as fixes marked `unsafe`, so the editor's lightbulb, `check --format json`, and the MCP tools offer them. For a page that doesn't exist, the closest targets now start with the pages that have the heading the link names, so a renamed page is found however different its new name is.
- **The commands' text says more.** `ascribe check` and `ascribe build` end with one form of summary that names the builds checked and gives all three counts, as `checked 12 files for every build, reported on 1: 1 error, 0 warnings, 0 advice`, or `for build` and the build's name with `--build`, and a diagnostic with no fix to name shows its fix advice as `Help:`, as the JSON's `help` does. `ascribe link` gives the empty form too, `[](keys.md#rotate-keys)`, which shows the same title. `ascribe render` says above a switcher's arms that a reader sees one of them at a time, and which dimension chooses. `ascribe diff --base <commit>` names the commit once, and `ascribe explain` with no code says `--list` lists them all. The command reference gives the exit codes of `agents rules`, `agents skill`, `agents prompt`, `agents hook`, and `sources status` as a table, as for the other commands. The text output isn't a contract; a script that reads the summary line should read `check`'s JSON instead.

### <a id="v0.3.0-the-editor"></a>The editor

- **Problems for files changed on disk, in VS Code.** A project's language server now also starts when one of its files changes on disk while the window is open, not only when one is opened. An agent or a script that writes pages without opening them gets their problems in the Problems panel. A `git checkout` starts each touched project's server once, and opening the window still starts nothing. The Problems panel checks pages as the editor's build only; `ascribe check` checks every build. See [When servers start](https://ascribed-dev.com/docs/editor/settings/#when-servers-start).
- **Actions in VS Code, and the actions bar.** Actions write Ascribe for you, such as **Wrap in a note**, **Make the list steps**, **Link the selected text**, **Insert content that varies**, and **Mark where it's available**. Each asks what it needs in short steps, choosing from the project's note types, pages, phrases, dimensions, and features, and writes canonical Ascribe that one Undo takes back. `Ctrl+K A` (`Cmd+K A` on macOS), or **Ascribe: Actions for the Cursor**, opens the actions bar: the fixes for problems at the cursor first, then the actions that apply to the cursor or the selection, grouped as **Write**, **Structure**, **Link**, **Media**, and **Content model**. Every action is also in the Command Palette, in an **Ascribe** submenu of the editor's context menu, and, for those that rewrite what's at the cursor, in the lightbulb. The **Content model** actions make the selected text a phrase (and, if you choose, its other occurrences), add it to the glossary, change a feature's availability, and rename a phrase or a dimension value everywhere, editing `ascribe.toml` in place with its comments and layout; a rename that changes other files shows them in the refactor preview first. See [Actions](https://ascribed-dev.com/docs/editor/actions/).
- **The Ascribe sidebar, the status bar, and the build lens in VS Code.** The Ascribe sidebar's **Projects** view lists every project in the workspace, started or not, with buttons to show a server's output or restart it; **Used by** lists what links to or includes the active page, or the heading at the cursor; **Pages** lists the project's pages by type, its fragments with what includes them, and pages nothing links to; **Content model** lists every phrase, feature, glossary term, dimension, note type, widget, and build with how many places use it, marks the unused ones, and opens each one's declaration. Filling a view never starts a server. While a page or an `ascribe.toml` is open, the status bar names its project, the build you're looking at, and whether its language server is running; click it to switch the build, show the server's output, restart the server, or open the preview. Each project has one build you're looking at: the preview renders it, and **Ascribe: Dim What the Build Leaves Out** turns on the build lens, which dims the variant arms it doesn't select and the sections, blocks, and table rows its availability filter removes, with a hover that says why. Switching the build in the status bar, with **Ascribe: Switch Build**, or in the preview's **Build** picker changes all three. The language server answers a new request, `ascribe/buildView`, from the same decisions `ascribe build` makes. See [The Ascribe sidebar](https://ascribed-dev.com/docs/editor/sidebar/), [The status bar](https://ascribed-dev.com/docs/editor/sidebar/#the-status-bar), and [The build lens](https://ascribed-dev.com/docs/editor/preview/#the-build-lens).
- **VS Code says when it isn't checking a project.** In Restricted Mode, where the extension runs nothing, the status bar shows **Ascribe: not checking**, and the Projects view says why; trusting the folder starts it. In a trusted folder, while no page of a project is open, the status bar says the project isn't checked yet, such as **docs: not checked yet**, instead of nothing beside a Problems panel that reads as clean. The language server's line naming its project, such as `ascribe-lsp 0.2.0: using the project at …/ascribe.toml`, is now a `window/logMessage` at Info with the server's version, so the output channel no longer shows it as an error.
- **Check every build from VS Code.** **Ascribe: Check Every Build**, also in the status bar's menu, runs `ascribe check` over every build of the active file's project and adds to the Problems panel the problems the language server doesn't show, those of builds other than the editor's, each naming the builds it's in. A file's added problems go when it changes, since they may have moved, until the next check. While a project has several builds and the Problems panel holds the editor build's problems alone, the status bar says so, such as **docs · site · problems: this build only**, or **docs · cloud · problems: site only** while you look at another build. See [The Problems panel](https://ascribed-dev.com/docs/editor/problems/#the-problems-panel).
- **Find All References, and more renames.** **Find All References** lists the links to a page or heading, including links through a page that includes it, the includes of a fragment, and the uses of a phrase, feature, glossary term, note type, widget, or dimension, in VS Code and, through the language server, in other editors. `F2` renames a dimension value too, a phrase rename reaches frontmatter fields and `@snippet {phrases=true}` files, and `F2` on something that can't be renamed says why. See [Navigation](https://ascribed-dev.com/docs/editor/writing/#navigation) and [Refactoring](https://ascribed-dev.com/docs/editor/writing/#refactoring).
- **A getting-started walkthrough in VS Code.** After you install the extension, **Get Started with Ascribe** goes through opening a project, previewing a page, the actions bar, the Ascribe sidebar, the build lens, and checking the project in CI. It's under **Help → Welcome**. See [The walkthrough](https://ascribed-dev.com/docs/editor/#the-walkthrough).
- **VS Code offers to be the formatter for your pages.** When you open a page and another extension, such as Prettier, is the formatter for Markdown, the extension offers to be the Markdown formatter for that workspace folder, since a general Markdown formatter can change what a page builds to. **Use Ascribe** writes `editor.defaultFormatter` under `[markdown]` in the folder's `.vscode/settings.json`. See [Formatting](https://ascribed-dev.com/docs/editor/writing/#formatting).
- **The VS Code extension has an icon:** the Ascribe mark, in the Extensions view and on its Marketplace page, whose header takes the icon's dark tile color.

### <a id="v0.3.0-the-language-server"></a>The language server

- The language server's log lines, in the editor's output panel, start with `ascribe-lsp:`, where they started with `tessera-lsp:`.

### <a id="v0.3.0-astro-and-the-elements"></a>Astro and the elements

- **`@ascribed/astro` writes a page's head, the sitemap, and `robots.txt` from the page records.** A layout includes `<Head entry={entry} />`, from `@ascribed/astro/Head.astro`, for the page's `<title>`, description, canonical link, and `<meta name="robots" content="noindex">` when the page is `listing: unlisted`; its `title` property is a function from the page's title and its record to the whole title. The site gets one `sitemap.xml`, at Astro's `base`, with every listed page of every project and every other page Astro built, less permalinks' forwarding pages, the not-found page, and the routes in the new `sitemap: { exclude }` option, and one `robots.txt`, at the host's root, with each project's `[crawlers] lines` and the sitemap's address, in `astro build` and `astro dev`. A site with its own `public/robots.txt` or `public/sitemap.xml` keeps it, a site whose `base` isn't `/` gets no `robots.txt` and the lines its host's needs in the build's output, and a site with no `site` gets no sitemap and a warning. The layout `npx @ascribed/astro init` writes includes `Head.astro`. See the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **`@ascribed/astro` gives Astro the site's address.** When `astro.config.mjs` leaves out `site`, `base`, or `trailingSlash`, the integration takes it from `ascribe.toml`'s `[consumer]` (`site`, `base-path`, `trailing-slash`), so the address is written once, and a preview built for its own address changes only `ascribe.toml`. A setting the Astro config gives must still agree, as before. With `projects`, `site` and `trailingSlash` come from the first entry whose project sets them, and `base` stays the site's. **Behavior change:** Astro can't tell an integration an unset setting from its default, so with `build`, `base: "/"` now becomes `base-path`, and `trailingSlash: "ignore"` becomes `trailing-slash`. A site with a home page at `/` in front of one project's docs lists the project in `projects` instead (`projects: [{ name: "docs", build: "site" }]`, and `name: "docs"` in its collection) to keep `base` at `/`. See the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **`Head.astro` writes everything a page's head says about the page, from its record.** Besides the title, description, canonical link, and robots rule, it writes `og:title`, `og:description`, `og:url`, and `og:type` from the same record, a `rel="alternate" type="text/markdown"` link to the page's Markdown version when the build publishes one and the page isn't `unlisted`, and the page's breadcrumbs as `BreadcrumbList` structured data. Two new properties carry what the site knows: `name`, the site's name, for `og:site_name` and, on the page at the host's root, the site-name structured data; and `card`, the page's social card, for the `og:image` tags. Each page's record in `_ascribe/pages.json` has its `breadcrumbs`: the page at each folder of its route, by title, down to the page itself. `astro dev` warns once about a tag `Head.astro` writes that the layout writes too. See the [Astro guide](https://ascribed-dev.com/docs/astro/).
- **A starter for an Astro site.** `npx @ascribed/astro init` writes the four files a site starts from into an Astro project, once, as yours from then on: the content collection, the route, a layout that loads the elements, and the sidebar's list, `src/nav.ts`, written from your project's pages. It never writes over a file. The layout's sidebar comes from `sidebar()`, new in `@ascribed/astro/content`, which gives each listed page its address and fails the build when the list leaves out a page or names one that isn't there, so the sidebar stays complete. The Astro guide's steps 4 to 6 are now this command. See [Write the site's files](https://ascribed-dev.com/docs/astro/#4-write-the-sites-files).
- **A home page in front of the docs, and several projects in one Astro site.** `@ascribed/astro` accepts a `[consumer] base-path` under Astro's `base`, such as `/docs/` under `/`, and publishes the files pages link to, `llms.txt` and each page's Markdown, and `.ascribe/dev.json`'s address at the base path; a base path outside `base` still fails the build. `ascribe({ projects: [{ name, build, project }] })` builds several projects into one site, each under its own base path, with `ascribeCollection({ schema, name })` for each and the schema's copy in `.astro/integrations/_ascribed_astro/<name>/schema.ts`; the form with one project is unchanged. One route serves every project's permalinks, `astro dev` rebuilds the project a change is in, and `review` takes an entry's name as well as `true` or `false`. The build fails, naming the entries, on two projects at one base path, a page of one under another's base path, a permalink two projects declare, and an entry's name used twice. A removed permalink's page and `MissingPermalink` link to their project's base path, which is the site's root when the two are the same. See [A home page in front of the docs](https://ascribed-dev.com/docs/astro/more-than-docs/#a-home-page-in-front-of-the-docs) and [Several projects in one site](https://ascribed-dev.com/docs/astro/more-than-docs/#several-projects-in-one-site).
- **New default colors for the elements and review.** The element library, review's marks and comment threads, the review panel in Astro's dev toolbar, and the HTML report take Ascribe's palette: an indigo accent over cool grays, with notes, availability badges, and review's marks in matching hues. Added and removed differ in more than red and green, for readers with red-green color blindness. The report's frame uses Ascribe's type scale. **Behavior change:** a site that doesn't set the `--ascribe-*` and `--ascribe-review-*` custom properties looks different. Their names are unchanged, and their defaults may change again in a release, always noted here. To keep the previous look, add this to the site's stylesheet, after the element library's and review's:

  <details>
  <summary>The previous default colors</summary>

  ```css
  /* The element library */
  @supports (color: light-dark(#000, #fff)) {
    :root {
      --ascribe-border-color: light-dark(#d0d7de, #3d444d);
      --ascribe-muted-color: light-dark(#57606a, #9198a1);
      --ascribe-note-color: light-dark(#0969da, #4493f8);
      --ascribe-note-background: light-dark(#ddf4ff, #121d2f);
      --ascribe-tip-color: light-dark(#1a7f37, #3fb950);
      --ascribe-tip-background: light-dark(#dafbe1, #12261e);
      --ascribe-important-color: light-dark(#8250df, #ab7df8);
      --ascribe-important-background: light-dark(#fbefff, #1f1a33);
      --ascribe-warning-color: light-dark(#9a6700, #d29922);
      --ascribe-warning-background: light-dark(#fff8c5, #272115);
      --ascribe-caution-color: light-dark(#cf222e, #f85149);
      --ascribe-caution-background: light-dark(#ffebe9, #2d1517);
      --ascribe-steps-color: light-dark(#0969da, #4493f8);
      --ascribe-steps-marker-text-color: light-dark(#ffffff, #0d1117);
      --ascribe-tab-color: light-dark(#57606a, #9198a1);
      --ascribe-tab-active-color: light-dark(#0969da, #4493f8);
      --ascribe-tab-focus-color: light-dark(#0969da, #4493f8);
      --ascribe-tab-hover-background: light-dark(#f6f8fa, #151b23);
      --ascribe-state-color: light-dark(#57606a, #9198a1);
      --ascribe-state-background: light-dark(#eaeef2, #212830);
      --ascribe-state-ga-color: light-dark(#1a7f37, #3fb950);
      --ascribe-state-ga-background: light-dark(#dafbe1, #12261e);
      --ascribe-state-preview-color: light-dark(#8250df, #ab7df8);
      --ascribe-state-preview-background: light-dark(#fbefff, #1f1a33);
      --ascribe-state-beta-color: light-dark(#0969da, #4493f8);
      --ascribe-state-beta-background: light-dark(#ddf4ff, #121d2f);
      --ascribe-state-deprecated-color: light-dark(#9a6700, #d29922);
      --ascribe-state-deprecated-background: light-dark(#fff8c5, #272115);
      --ascribe-state-removed-color: light-dark(#cf222e, #f85149);
      --ascribe-state-removed-background: light-dark(#ffebe9, #2d1517);
    }
  }

  /* Review's marks and overlay */
  :root {
    --ascribe-review-added: #1a7f37;
    --ascribe-review-added-background: #dcf7e3;
    --ascribe-review-changed: #8a5a00;
    --ascribe-review-changed-background: #fff1c2;
    --ascribe-review-removed: #c4222d;
    --ascribe-review-removed-background: #ffe4e2;
    --ascribe-review-moved: #7a3fd0;
    --ascribe-review-moved-background: #f0e6ff;
    --ascribe-review-muted: #5b6475;
    --ascribe-review-surface: #ffffff;
    --ascribe-review-flash: #fff6bf;
    --ascribe-review-focus: #1f6feb;
    --ascribe-review-text: #1d2330;
    --ascribe-review-border: #d3d8e0;
    --ascribe-review-thread: #f5f7fb;
    --ascribe-review-input: #ffffff;
    --ascribe-review-accent-text: #ffffff;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-ascribe-scheme="light"]) {
      --ascribe-review-added: #4fc572;
      --ascribe-review-added-background: #133520;
      --ascribe-review-changed: #dcb13a;
      --ascribe-review-changed-background: #3a2f0d;
      --ascribe-review-removed: #ff8279;
      --ascribe-review-removed-background: #401918;
      --ascribe-review-moved: #c59bff;
      --ascribe-review-moved-background: #2d2440;
      --ascribe-review-muted: #929bab;
      --ascribe-review-surface: #1e2026;
      --ascribe-review-flash: #3b3514;
      --ascribe-review-focus: #5aa2ff;
      --ascribe-review-text: #d9dde5;
      --ascribe-review-border: #343a45;
      --ascribe-review-thread: #272b33;
      --ascribe-review-input: #1e2026;
      --ascribe-review-accent-text: #0b1220;
    }
  }

  :root[data-ascribe-scheme="dark"] {
    --ascribe-review-added: #4fc572;
    --ascribe-review-added-background: #133520;
    --ascribe-review-changed: #dcb13a;
    --ascribe-review-changed-background: #3a2f0d;
    --ascribe-review-removed: #ff8279;
    --ascribe-review-removed-background: #401918;
    --ascribe-review-moved: #c59bff;
    --ascribe-review-moved-background: #2d2440;
    --ascribe-review-muted: #929bab;
    --ascribe-review-surface: #1e2026;
    --ascribe-review-flash: #3b3514;
    --ascribe-review-focus: #5aa2ff;
    --ascribe-review-text: #d9dde5;
    --ascribe-review-border: #343a45;
    --ascribe-review-thread: #272b33;
    --ascribe-review-input: #1e2026;
    --ascribe-review-accent-text: #0b1220;
  }
  ```

  </details>

  See [theming](https://github.com/ascribed-dev/ascribe/blob/main/packages/elements/README.md#theming).
- `@ascribed/astro` declares `satteri` and `@types/hast` as optional peer dependencies. Its `@ascribed/astro/satteri` and `@ascribed/astro/rehype` declarations import types from them, so a project that type-checks those declarations (`skipLibCheck: false`) needs the one it imports, which a Sätteri or `unified()` processor already brings.

### <a id="v0.3.0-agents"></a>Agents

- **`ascribe agents index` writes a compact index of your docs for your product's `AGENTS.md`.** One line a page, grouped as `llms.txt` is, held to 8 KB by leaving out descriptions before pages and saying what it left out. It prints the index, or writes it between markers in a file with `--into`, and with `--pages` copies each page's Markdown version into a folder a package can ship, with the index pointing into it. See [Index your docs for your product's agents](https://ascribed-dev.com/docs/agents/).
- **A project says what each kind of AI crawler may do, in `[crawlers]`.** `training`, `search`, and `on-request` are each `"allow"` or `"disallow"`. A disallowed purpose writes one group in `robots.txt` naming every crawler of that kind Ascribe knows, from their operators' own pages, with `Disallow: /`; `allow`, and leaving a purpose out, write nothing, so those crawlers stay under the lines for every crawler. The page records' `robots` gains `purposes` and `groups`, and `@ascribed/astro` writes the groups first; projects of one site that disagree on a purpose fail the build, naming both. A crawler named in two groups is an error (`model-crawlers-named-twice`). `listing-disallowed` now reads, for each search crawler, the one group that applies to it, so it reports a page that isn't listed when `search = "disallow"` or a group naming a search crawler keeps it from being fetched, and `crawlers-disallowed`, advice, says what disallowing `search` or `on-request` costs. The reference lists each crawler with its operator's statement, dated and linked ([the `ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/), [the Astro guide](https://ascribed-dev.com/docs/astro/)).
- **The skill covers more, and the plugin's `/ascribe:new-page` takes a request.** The Ascribe skill says that `ascribe agents rules` prints a project's rules when there's no `AGENTS.md`; lists `ascribe diff` and `ascribe drift`; names the two commands a CI job runs; and its directive reference says how to declare a source and mark a region for `@snippet`, and how to declare a dimension. The plugin's `/ascribe:new-page` takes what the page is for, and has the agent choose the type from `ascribe model` before it runs the prompt. The plugin's description says what each part is for.
- **Instructions for AI coding agents.** `ascribe agents sync` writes your project's rules into the files agents read on their own, from `ascribe.toml`: `AGENTS.md` beside it, with the check to run after each edit, your page types and their frontmatter, your phrases, and the directives your pages use, in at most 4,000 characters, and a short block in the repository root's `AGENTS.md` when the project is in a subfolder. A project with another nested in its content root says that the nested project's pages follow their own rules. It writes only between its markers, so your own text in those files stays as it is. It also writes the Ascribe skill, which teaches an agent the check-and-fix loop and the commands that answer its questions, to `.agents/skills/ascribe/`. `--target claude` adds an import of `AGENTS.md` to `CLAUDE.md` and the skill for Claude Code, `--target claude-rules` and `--target copilot` write rules loaded only for your pages, and a target whose files exist is kept up to date. `--check` fails when a file is out of date, for CI. Run at the root of a repository with several projects and no `ascribe.toml` of its own, it syncs each of them. `ascribe agents skill` prints the skill, and `@ascribed/cli` ships it as `skills/ascribe/`. See [Agents](https://ascribed-dev.com/docs/agents/).
- **Docs AI agents can read.** With `agents = true` in `[consumer]`, each build publishes what agents read, as the [Web Documentation Delivery Spec](https://agentdocsspec.com/spec/web/) lays it out: `llms.txt`, an index of every page by folder with its description, split into one per folder past 50,000 characters; each page's Markdown at its URL with `.md`; and, at the top of each page, a pointer to both that the element library hides from sight. The plain-markdown output is then laid out by URL, with its files under `_ascribe/files/`, so it's copied as it is beside the site, and `@ascribed/astro` publishes it with the site and serves it in `astro dev`. It needs `site` (`model-consumer-agents-site`). `ascribe check` adds two pieces of advice you can set in `[checks]`: a description too long for its line in `llms.txt` (`description-too-long`), and an `llms.txt` still too long when split (`llms-section-large`). The element contract gains `<ascribe-for-agents>`. See [Publish for AI agents](https://ascribed-dev.com/docs/astro/agents/#publish-for-ai-agents).
- **Prompt agent.** In VS Code, the lightbulb on a problem offers **Prompt agent: fix this problem**, after its quick fixes, and **Ascribe: Prompt Agent to Fix This File** and **Ascribe: Prompt Agent to Fix This Project** cover every problem in the file or the project. Each builds a prompt for your AI agent: what to fix and where, the problem's message and fix advice, the values the content model allows, whether Ascribe has a fix, and the `ascribe check` command to finish with. The new `ascribe.agents.promptTarget` setting copies it (the default), or opens VS Code's chat, Claude Code, or Cursor's chat with it filled in; nothing is ever sent for you. `ascribe check --format prompt` writes the same prompt for an agent in a terminal. The language server answers a new request, `ascribe/agentPrompt`. See [Prompt your agent](https://ascribed-dev.com/docs/agents/prompts/).
- **An MCP server.** `ascribe mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io/) server, for an agent with no shell, or a host that approves each command. Its tools, `ascribe_check`, `ascribe_explain`, `ascribe_model`, `ascribe_outline`, `ascribe_link`, `ascribe_refs`, `ascribe_render`, `ascribe_format`, and `ascribe_changes`, return what their commands write, and none writes a file. One server serves every project in a repository, keeps each loaded, and loads it again when its files change, so a second check of a large project is quick. Its resources are each directive's syntax and each project's content model and rules, and its prompts are `new-page`, `fix`, and `review`. `ascribe agents prompt` prints the same prompts, `ascribe agents rules` prints a project's rules, and `ascribe agents skill references/directives.md` the skill's directive reference. For the tools, `ascribe fmt --format json` lists the edits that format each file (with `--check`, without writing them), and `ascribe diff --pages-only` leaves the block-level changes out of the JSON. See [The MCP server](https://ascribed-dev.com/docs/agents/mcp/).
- **Agents in VS Code get Ascribe's tools.** In a workspace with an Ascribe project, the extension offers `ascribe mcp` to VS Code's agents as an MCP server, with nothing to set up. It adds three tools of its own: `#ascribe_editor_problems` gives the language server's problems of a file or project as `ascribe check --format json` writes them, including unsaved edits and an edit made a moment before, for the editor's build; and, while review is on, `#ascribe_review_threads` lists the pull request's open threads with the pages that show each, their comments quoted as data, and `#ascribe_review_changes` the changed pages with their counts and causes. None changes a file, starts a server, or turns review on. The language server now publishes diagnostics for a closed file changed on disk even when they didn't change, so a tool waiting on the write answers at once. Its diagnostics carry their fix advice and each fix's safety in their `data`, and `@ascribed/review/github` has `buildThreadsList`, from `openThreadsList` in `@ascribed/review/prompt`. See [Agents in VS Code](https://ascribed-dev.com/docs/editor/agents/#agents-in-vs-code).
- **Hooks and a plugin for agents.** `ascribe agents hook claude-code`, `codex`, or `copilot` is the command an agent's hook runs: after each file it writes, it tells the agent the errors in that file, at most 10, as `ascribe check <file> --editor-build` finds them; and with `--event stop`, before the agent finishes, it checks every build of the projects whose pages changed and keeps the agent working while there are errors, asking once. The first hook in a project starts a check server in the background, which keeps the project loaded so a check after an edit takes milliseconds, and stops after 10 idle minutes (`ASCRIBE_HOOK_SERVER=off` turns it off). `ascribe agents sync --with-hook` writes the entries that run them into `.claude/settings.json` (with the MCP server in `.mcp.json`), `.codex/hooks.json` (a new target, `codex`), and `.github/hooks/ascribe.json`, merging into the files you have. The Ascribe plugin, `plugins/ascribe/`, gives Claude Code the language server's diagnostics after each edit, the stop hook, the MCP server, the skill, and `/ascribe:check`, `/ascribe:new-page`, and `/ascribe:review`. See [Hooks](https://ascribed-dev.com/docs/agents/hooks/) and [The plugin](https://ascribed-dev.com/docs/agents/claude-code/#the-plugin).
- **Ascribe for Copilot's cloud agent.** `ascribe agents sync --cloud` writes what Copilot's cloud agent needs on GitHub: steps in `.github/workflows/copilot-setup-steps.yml` that install Node.js and `@ascribed/cli`, at the version your project pins or the one that wrote them, added between markers to a workflow you have; and the MCP server's JSON, printed for the repository's settings, or with `--agent` written into a custom agent, `.github/agents/ascribe-docs.md`. When the repository's projects install Ascribe in different ways, the JSON is one document to paste and the custom agent one file, each carrying a server for every way, under the same names (`ascribe`, `ascribe-docs`). With the skill and `--with-hook`'s hooks, which the cloud agent reads from the repository, it checks its edits and is held at the end until the project passes. `--check` covers the new files. See [GitHub Copilot](https://ascribed-dev.com/docs/agents/copilot/).
- **An agent in VS Code runs the binary the editor runs.** When `ascribe` on the window's path isn't the binary the extension runs, such as the one bundled with it, the extension puts that binary's folder first on the path of the window's terminals, so an agent that works in a terminal runs `ascribe` and gets the same binary. **Prompt agent**'s prompts name the binary by its path in their commands, such as `…/bin/linux-x64/ascribe check docs/install.md`, so an agent another extension starts, such as Claude Code's, can run them too. `ascribe/agentPrompt` takes an optional `program`, the command its prompt's commands start with, and so does `@ascribed/review`'s `PromptProject`, for the prompts about review comments. See [VS Code](https://ascribed-dev.com/docs/editor/agents/#agents-in-vs-code).

### <a id="v0.3.0-review"></a>Review

- **Prompt agent in review.** While you review a pull request, each comment thread has **Prompt agent**, which asks your agent to address the comment, with the block's text and the comments fenced as data it shouldn't take orders from; the list of comments has **Prompt agent: all open**; and **More review actions** (**⋯**) prompts your agent to review the page, or to check every page a changed fragment reaches. They're in the page preview and the source editor in VS Code, which deliver them as `ascribe.agents.promptTarget` says, and in the site preview, which copies them. The HTML report has **Copy prompt** for each page. `ascribe diff --format prompt` writes the prompt about the change, and `ascribe diff --format prompt <page>` the one about a page or a fragment. The report's JSON data gives each page its `prompt`, and `ascribe/agentPrompt` takes `pageChanges` and `fragmentReach`. `@ascribed/review/prompt` builds the prompts about threads, and an overlay host may offer `promptAgent`. See [In review](https://ascribed-dev.com/docs/agents/prompts/#in-review).
- **One page's changes in a terminal.** `ascribe diff docs/guides/rollouts.md` shows what changed on that page, in each build: every changed block as it was and as it is, with the words that differ marked as `git diff --word-diff` marks them, added and removed blocks with their lines, and where each is written. Name a fragment and it shows the pages that changed through it. The JSON and the HTML report are narrowed the same way, with the same shape, so `schema_version` stays `1`; with `--format prompt`, a page means what it did. Before, a page could be named only with `--format prompt`. See [`ascribe diff`](https://ascribed-dev.com/docs/review/diff/#cli-diff).
- **A title with code shows it in review.** The page preview's heading in VS Code, the HTML report's list of pages and its page headings, and the list of comments in the editor and the site preview show a title's code spans as code, where they showed plain text. **Ascribe: Changed Pages** writes them between backticks. The HTML report's data, and the language server's `ascribe/preview` and `ascribe/review/changes` answers, have the formatted title beside the plain one. See [code in a field](https://ascribed-dev.com/docs/content-model/#53-code-in-a-field).

### <a id="v0.3.0-the-docs-and-the-site"></a>The docs and the site

- **The changelog page groups each release by area, and shows each entry as its first sentence.** An entry opens to the rest with a click, or every entry with **Expand all**, and an entry marked as a behavior change has a badge, with **Behavior changes only** to show those alone. Each area's heading counts its entries, and the contents beside the page lead to each release and the newest ones' areas. On a phone the contents are above the page, behind a button. A link to the changelog's headings works as it did.
- **The site has a credits page.** [Credits](https://ascribed-dev.com/credits/) says who builds Ascribe, the projects it's built on, from the comrak fork its parser is to the programs its reports run, and where its ideas came from, each linked to where Ascribe says so. The site's footer and the landing page link to it.
- **The docs' six longest pages are split, so each is under the delivery spec's page size.** The command reference, the diagnostics reference, the `ascribe.toml` reference and its contract, the JSON report contract, and the editor guide each keep their address and list the pages their sections moved to, in a folder of the same name: the commands are on [five pages by what they're for](https://ascribed-dev.com/docs/command/), and the diagnostics on four. Permalinks such as `/go/cli-check/` and `/go/ASC037/` lead to the new pages.
- **A reference page for the element library.** The [element library reference](https://ascribed-dev.com/docs/astro/elements/) lists the custom elements the site output writes and every CSS custom property that themes them, with what each one sets, taken from `@ascribed/elements`' stylesheet, and shows how to style a project's own note types and lifecycle states. It's in the sidebar, `llms.txt`, and search, at the permalink `elements`; the element library's README and the Astro guide link to it.
- **A guide says what a team can measure about search and agents, and what nobody has.** Ascribe sees no traffic, so [What to measure](https://ascribed-dev.com/docs/agents/measuring/) says where a team looks: the search engines' own reports, requests for the Markdown versions in a host's logs, and its own questions asked again after a change; and which of the advice about search and AI has evidence behind it.
- **A page lists every file Ascribe writes into a project.** [Files Ascribe writes](https://ascribed-dev.com/docs/command/project-files/) says, in one table, which command writes each file Ascribe puts in a project, from `.ascribe/` to `ascribe.lock`, `ascribe.permalinks`, and the agents' files, and whether to commit it or leave it to `.gitignore`.
- **The site has a footer.** Every page of <https://ascribed-dev.com>, the docs, the site's own pages, and the changelog, ends with links to the changelog, the license, and the repository. The changelog's link moves there from the header, which keeps Docs, Why Ascribe, and GitHub.
- **The `ascribe.toml` contract is numbered as its reference.** The [contract](https://ascribed-dev.com/docs/contracts/content-model/)'s map of SPEC §7.2 moved to the end, as §23, so each section has the number it has in the [reference](https://ascribed-dev.com/docs/content-model/) (`[builds.<name>]` is §16 in both), and what the two pages say alike is written once and included in each.
- **A guide to permalinks.** [Permalinks](https://ascribed-dev.com/docs/permalinks/) is a guide of its own, no longer a section of the Astro guide: publishing the addresses, with Astro or another generator, linking to them from a product, checking those links in the product's CI, and moving, aliasing, and retiring one. The Astro guide keeps what the integration does, and links to it.
- **A guide to content that varies.** [Content that varies](https://ascribed-dev.com/docs/writing/variants/) walks through a page that differs by platform: a dimension in `ascribe.toml`, a `@variant` group, an `@available` mark, and two builds, one with every platform and one for Windows alone, with what each keeps. Its project is `examples/platforms`.
- **A guide to working with agents.** [Agents](https://ascribed-dev.com/docs/agents/) covers the whole surface in one page: the loop, agent instructions, **Prompt agent**, the MCP server, VS Code, hooks and the plugin, GitHub Copilot, what works with any other agent, such as Cursor, and what Ascribe sends where (nothing, to no one). Getting started links to it.
- **The home page lists every guide, and getting started says where it ends.** The docs' home page links to [Prose, through Vale](https://ascribed-dev.com/docs/checks/vale/) and [The project report](https://ascribed-dev.com/docs/checks/report/), which it left out, and neither it nor getting started keeps a list of commands by hand. Getting started says that [Astro](https://ascribed-dev.com/docs/astro/) is its second half, tells a new site from an existing one, and says that `[consumer] site` must match Astro's `site` and that the integration runs `ascribe build` for you; the Astro guide ends with the site open in a browser. See [Publish it with Astro](https://ascribed-dev.com/docs/getting-started/#publish-it-with-astro).
- **One recipe for checking a project in CI.** Getting started's GitHub Actions workflow is the one `examples/docs-repository` runs: it installs the latest release of `@ascribed/cli`, runs `ascribe check --deny-warnings` and `ascribe fmt --check`, and pins its actions by commit. Before, the two disagreed on how to install `ascribe` and whether to check the formatting. See [Checks in CI](https://ascribed-dev.com/docs/checks/ci/).
- **Each diagnostic's entry links to the page written about it.** In the [diagnostics reference](https://ascribed-dev.com/docs/checks/diagnostics/), every entry ends with **Read more:** and the section of the [directive reference](https://ascribed-dev.com/docs/writing/directives/) or the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model/) it's about, such as [Links](https://ascribed-dev.com/docs/writing/inline/#links) for `link-target-missing`, so the address a problem leads to goes on to the page with a working example, not only to the specification.
- **The docs moved under `/docs/`, and the site's root is a landing page written in Ascribe.** The docs are at <https://ascribed-dev.com/docs/>. A page's old address, such as `/guides/astro/`, isn't redirected; the permalinks the binary, the editor, and the READMEs link to, under `/go/`, lead to the pages at their new addresses.
- **The landing page makes Ascribe's case.** It shows a page's source beside the page as the site draws it, both built from one file, and four claims (checked, one source with several outputs, examples from real code, reviewed as pages), each with its example and with what no release has yet marked as the docs mark it. The README's "Get started" installs the VS Code extension too, as getting started does. [Review](https://ascribed-dev.com/docs/review/) has a permalink, `review-guide`.
- **Why Ascribe.** <https://ascribed-dev.com/why/>, linked from the site's header and the landing page, says which problems teams that keep docs as code report, what Ascribe does about each, with examples read from files in the repository and what no release has marked, and what it doesn't do. It links to [`examples/comparison`](https://github.com/ascribed-dev/ascribe/tree/main/examples/comparison), the same page written for other documentation sites.
- **The site shows the editor and review.** The landing page and Why Ascribe have pictures of VS Code with a problem and its quick fix, and of a docs page in the site's preview during review, with a pull request's comments beside the paragraph they're about, each in the reader's light or dark scheme. The landing page, Why Ascribe, and the changelog each have a social card of their own, with the page's title.

### <a id="v0.3.0-installing"></a>Installing

- **Behavior change: `@ascribed/cli` no longer exports `nativeBinaryPath`.** Use `resolveBinary()`, or `resolveBinary({ platform, arch })` where you passed a platform and architecture; it returns the same path. There is no alias.
- **Smaller downloads.** The `ascribe` binary is built with link-time optimization: about a third smaller on macOS and Linux (7.0 MB on macOS arm64, from 10.5 MB) and 8 percent smaller on Windows, and a little faster.

### <a id="v0.3.0-fixes"></a>Fixes

- **Fixed:** permalinks that differ only in case, such as `Setup` on one heading and `setup` on another, are reported as one permalink published twice (`permalink-duplicate`), naming both spellings. A site may serve every spelling of an address at one, and both were published. See [Permalinks](https://ascribed-dev.com/docs/writing/directives/#permalinks).
- **Fixed:** `ascribe fmt` no longer follows a symbolic link out of the content root. A file that's a link, or is in a linked folder, that leads to a file that isn't a source file of the content root is left alone and reported as `ascribe check` reports it (`source-unreadable`), and `fmt` exits with 2. It was formatted and written where the link led. See [`ascribe fmt`](https://ascribed-dev.com/docs/checks/check/#cli-fmt).
- **Fixed:** in a high-contrast dark theme, the page preview's **Page | Site** switch shows which side is on, with the theme's active border, and the preview's buttons are in the theme's text color. They took a button background the theme doesn't set.
- **Fixed:** in VS Code, review uses a GitHub sign-in only for its own host: VS Code's GitHub sign-in for `github.com`, and its GitHub Enterprise sign-in for the server `github-enterprise.uri` names. A remote on any other host gets no sign-in, and the page preview says why. `@ascribed/review/github`'s `tokenTransport` accepts a token with the host it's for (`HostToken`), and refuses to send it to another host or follow a redirect. See [What you need for each part](https://ascribed-dev.com/docs/review/how-it-works/#signing-in-to-github).
- **Fixed:** in the editor, a link outside the docs is clickable only when it's a web or email address (`http`, `https`, or `mailto`). A link with any other scheme stays in the page for the site's readers, and is no longer a link the editor follows. The VS Code extension drops such links from an older `ascribe` too. See [the editor guide](https://ascribed-dev.com/docs/editor/writing/#navigation).
- **Fixed:** the page preview acts only on what the extension and its own page send. It ignores messages from the site preview's page, the extension checks each of the page's requests before acting on it (a file must be in the project, a git command one of the preview's buttons), and a form in a page's raw HTML can't be submitted. See [Preview](https://ascribed-dev.com/docs/editor/preview/).
- **Fixed:** `ascribe build` writes and removes only through real folders. It fails, naming the link, when a symbolic link or junction is on the way to the output or in it, and reads a manifest's paths as input, refusing one that's absolute. `ascribe.lock`, `ascribe.permalinks`, `ascribe agents sync`'s files, Vale's files under `.ascribe/vale/`, and `@ascribed/astro`'s `.ascribe/dev.json` aren't written through a link either. See the [output-layout contract](https://ascribed-dev.com/docs/contracts/output-layout/#2-what-ascribe-owns).
- **Fixed:** with no git repository around a project, the editor reads a source only inside a folder of the workspace it opened, and `ascribe mcp` only inside the folder it was started in; a source outside is `model-source-outside-repository`, which now says to open the folder that holds it. `ascribe build`, `check`, and the other commands read a source wherever `path` leads, as before. See [sources](https://ascribed-dev.com/docs/content-model/).
- **Fixed:** a page's includes can expand it to at most 100,000 blocks and 16 MiB of text, far more than any real page. The include in the page that takes it past is an error, `include-limit`, and isn't expanded, nor is any include after it. See [`@include`](https://ascribed-dev.com/docs/writing/directives/#include).
- **Fixed:** the language server, when told a folder was created, and `ascribe fmt` go into each folder once, by where it really is, as `ascribe check` does.
- **Fixed:** reading a repository runs nothing the repository supplies. Every `git` Ascribe starts runs with no file system monitor, and takes a bare repository only when one is named, so a folder in the project shaped like one is refused instead of read. The MCP server's tools and prompts never start Vale, even with `in-check = true`, and say the prose wasn't checked; the agents' hooks run Vale only when `[checks.vale] command` is a name on the `PATH`; and the editor asks once a session before it runs a `command` that's a program in the project. See [Prose, through Vale](https://ascribed-dev.com/docs/checks/vale/#where-vale-runs).
- **Fixed:** a page has one name across the commands: its path from the folder you ran the command in, as `ascribe check` prints it. `ascribe link`'s `from`, in its text and JSON, names the page that way, however `--from` gave it. `ascribe permalinks retire --page`, `--under`, and `--to` take that path, or one from the content root, and still write `ascribe.toml` from the content root; `--page docs/keys.md` was refused with a message saying the page declared no permalinks. An agent prompt says once which folder its paths and commands are from, and writes every path from there: the editor's from the repository's root, and `ascribe check --format prompt`, `ascribe diff --format prompt`, and `ascribe agents prompt` from the folder they were run in. A prompt named a page from the project's folder and its commands from the repository's root, so a command copied out of it could fail where the agent stood. See [What a prompt says](https://ascribed-dev.com/docs/agents/commands/#what-a-prompt-says).
- **Fixed:** a binary built from anything but a release or the nightly canary, such as from `main`, says so in `ascribe --version`, with its commit, as the canary does: `ascribe 0.2.0+dev (42f00096ceb0)`, not `ascribe 0.2.0`, which is the release's. Commands' JSON still gives the version alone as `ascribe_version`.
- **Fixed:** in review, a comment on a one-line note, such as `@note {type=tip}: …`, shows once beside the note. It showed twice, because the note and its paragraph are on the same line.

## <a id="v0.2.0"></a>0.2.0 (2026-10-06)

This release adds **review**: reading a pull request as readers will see it, page by page, with the changed blocks marked and the pull request's review comments beside them. It works in VS Code's page preview, in your site's own pages under `astro dev`, and as an HTML report CI can attach to every pull request. Comments are ordinary GitHub review comments, so there's nothing to set up or host. See [Review](https://ascribed-dev.com/docs/review/).

### <a id="v0.2.0-the-language"></a>The language

- **`@snippet`** puts a code example from a file in the page as a fenced code block: `@snippet: code:examples/quill/ascribe.toml#dimensions`. The file is read on every check, build, and diff, so the example changes when the code does. A region is marked in the code with Bluehawk's tags (`:snippet-start:`, `:snippet-end:`, `:remove:`). `ascribe check` reports a missing address, file, or region, and tags that don't balance (`snippet-address` and five more). See [`@snippet`](https://ascribed-dev.com/docs/writing/directives/#snippet) and [Drift](https://ascribed-dev.com/docs/examples/).
- **`[sources.<name>]`** in `ascribe.toml` names a folder of code outside the content that snippets may read, with `include` and `ignore` patterns. It's the only way a page reads outside its project's folder, and the folder must be in the project's git repository. See [`[sources.<name>]`](https://ascribed-dev.com/docs/content-model/sources/#20-sourcesname).
- **A source in another repository:** `git` (and `branch`) in place of `path`. The files snippets use are copied into `sources/<name>/` and pinned to a commit in `ascribe.lock`. Both are committed with the docs, so checking, building, diffing, and drift never reach the other repository. `ascribe check` reports copies that don't match the lock or the snippets (`lock-invalid`, `source-copy-changed`, and three more). See [a source in another repository](https://ascribed-dev.com/docs/content-model/sources/#a-source-in-another-repository) and [Docs kept apart from the code](https://ascribed-dev.com/docs/examples/docs-repository/).
- **A table row can have an availability:** an attribute block at the end of its first cell, `` | `stream` {available="self-managed preview 3.4"} | … | ``. A badge build marks the row, and a filter build removes it where it isn't available. The spec is checked as an `@available` line's is, within the table's own availability. **Behavior change:** a `{key=value}` that ends a body row's first cell was text and is now an attribute block, so a key other than `available` is an error (`attribute-unknown-key`). See [table rows](https://ascribed-dev.com/docs/writing/directives/#table-rows).
- **A page title can show code.** `inline = "code"` on a content type's `string` field reads code spans in it: `title = { type = "string", inline = "code" }` lets a page be titled "`` `ascribe.toml` reference ``". The site output writes the field as plain text, for `<title>` and search, and as HTML under the new key `formatted` (`entry.data.formatted.title` in an Astro layout). The JSON output has `formatted` too. **Behavior changes:** a type can no longer declare a field named `formatted` (`model-field-reserved`), and in a field that turns `inline` on, a backslash before punctuation is an escape and is removed, as in Markdown. See [code in a field](https://ascribed-dev.com/docs/content-model/#53-code-in-a-field).
- **A link can name a heading from a fragment the page includes:** `setup.md#prerequisites` works when `prerequisites` comes from a fragment `setup.md` includes. It was an error (`link-id-in-fragment`, now retired). See [`@include`](https://ascribed-dev.com/docs/writing/directives/#include).

### <a id="v0.2.0-the-ascribe-command"></a>The `ascribe` command

- `ascribe diff` shows what changed between a git revision and the working tree, as readers will see it: each build's changed pages, and the blocks added, removed, changed, or moved on them, with the words that changed. It compares resolved pages, so a page that changed only through a fragment, a phrase, or a build's settings is listed with the cause, and reformatting is no change. The default base is the merge base with the default branch, as in a pull request. `--format json` adds each block's source lines, for tools. It needs only `git`. It also says how many errors the working tree has, as do the HTML report and both previews, so a broken render isn't read as the change. See [`ascribe diff`](https://ascribed-dev.com/docs/review/diff/#cli-diff).
- `ascribe diff --format html` writes the changes as one self-contained HTML file: every changed page rendered with its changes marked, and the page as it will be and as it was a click away. It makes no network requests and runs nothing from the pages, so CI can attach it to a pull request. See [the HTML report](https://ascribed-dev.com/docs/review/diff/#the-html-report) and [the report in CI](https://ascribed-dev.com/docs/review/ci-report/).
- `ascribe drift` lists the pages whose code examples changed between a git revision and the working tree: first those whose own text didn't change, so the words around the code may be out of date, then those that changed with their examples, then those whose examples no longer resolve. A region is compared as text, so an edit elsewhere in its file isn't a change. `--format summary` writes Markdown for a CI job's summary, `--format json` is for tools, and `--exit-code` exits 1 when an example broke, or changed while its page didn't. See [`ascribe drift`](https://ascribed-dev.com/docs/review/diff/#cli-drift) and [Report the pages whose examples changed](https://ascribed-dev.com/docs/examples/drift/).
- `ascribe build --emit site --anchors` marks each block of the site output with its source file and lines (`data-ascribe-source`, and `data-ascribe-via` for a block from a fragment). Without the flag, the output is unchanged. See the [site-render contract](https://ascribed-dev.com/docs/contracts/site-render/#7-source-anchors).
- `ascribe sources fetch` makes the copies of sources in other repositories match `ascribe.lock`. `ascribe sources update` moves a source's pin to the head of its branch (or `--to`), copies again, and reports the commits, the copies, and the pages whose examples changed, as text, JSON, or Markdown for a pull request. `ascribe sources status` shows each pin and copy, offline. `fetch` and `update` run `git` with its own credentials, and are the only commands that reach the network. See [`ascribe sources`](https://ascribed-dev.com/docs/examples/sources/#cli-sources).
- **Each command's `--help` describes its options in the command reference's words.** The [command reference](https://ascribed-dev.com/docs/command/)'s option lists are generated from it, and `--help` ends with a link to its section on the docs site, <https://ascribed-dev.com>. `-h` shows the first paragraph of each.
- **Fixed:** the plain-markdown output (`--emit plain`) writes an inline `<br>` as a line break, or as `; ` in a table cell or heading. It was dropped, so code spans on either side ran together.

### <a id="v0.2.0-the-editor"></a>The editor

- **Review in the preview.** **Ascribe: Start Review** compares the active page's project with a base: the base of the branch's pull request, the default branch, or a revision you type. The preview marks what changed on the page and follows your edits. Its header shows the base and the page's changes, switches between **Changes / As it will be / As it was**, and steps through the changes and the changed pages. **Ascribe: Changed Pages** lists the pages the change touches, and a status bar item shows whether review is on and starts it. See [Review in the preview](https://ascribed-dev.com/docs/editor/review/).
- **Comments in the preview.** When the branch has an open pull request, the preview shows its review threads beside their blocks. You can reply, resolve, comment on any block, and submit or discard your review. New comments are visible only to you until you submit. **Ascribe: Refresh Comments** reads the threads again. Threads also show on their source lines, unless the GitHub Pull Requests extension shows them already (the new setting `ascribe.review.sourceComments`). Comments use VS Code's GitHub sign-in or the GitHub CLI's. Without either, review shows the changes only. See [Comments in the preview](https://ascribed-dev.com/docs/editor/review/#comments-in-the-preview).
- **The site preview.** **Ascribe: Open Site Preview** opens the active page on its project's dev server, in the browser, at the heading the editor shows. The preview panel's **Page | Site** switch shows it in the panel, following the active file. See [Site preview](https://ascribed-dev.com/docs/editor/preview/#site-preview).
- **Ascribe: Open Preview to the Side** is now **Ascribe: Open Page Preview to the Side** (the command id is unchanged). The new **Ascribe: Open Page Preview** opens the preview in place of the editor.
- The preview and the editor scroll together by block instead of by heading, in both directions, and double-clicking a block puts the cursor on its source line. `ascribe.preview.scrollPreviewWithEditor` and `ascribe.preview.scrollEditorWithPreview` turn each direction off.
- The preview takes scripts, event handlers, and `javascript:` URLs out of a page's HTML before showing it, as the review report does.
- **The Settings editor describes each setting in the editor guide's words.** The [editor guide](https://ascribed-dev.com/docs/editor/settings/#editor-settings)'s settings table is generated from it.

### <a id="v0.2.0-astro"></a>Astro

- **Review in the site preview.** In `astro dev`, an **Ascribe review** app in Astro's dev toolbar marks a change's blocks on the real page, in your site's layout, and shows the pull request's review threads beside them. Commenting goes through the GitHub CLI, which the dev server runs. It stays off when the dev server listens on the network, or when the Vite config lets other pages reach it. The `review` option (on by default) turns the app off. `astro build` output has no anchors, overlay, or review code. See [Review in the site preview](https://ascribed-dev.com/docs/review/site-preview/).
- `astro dev` writes its address to `.ascribe/dev.json` in the project, for the editor's site preview, and removes it when it stops.
- The `anchors` option turns source anchors on: `"dev"` in `astro dev` only, `true` always.
- A code block's title, such as a `@snippet`'s, shows above it, in a `<figure class="code-title">` with a `<figcaption>`. The `codeTitles` option turns it off. See [code block titles](https://ascribed-dev.com/docs/astro/reference/#code-block-titles).
- `@ascribed/astro/Availability.astro` renders a page's `available` frontmatter as the element library's badge: `<Availability available={entry.data.available} />`.
- The integration copies the generated schema into `.astro/integrations/_ascribed_astro/schema.ts` after each build. A site whose Ascribe project is outside the Astro root imports that copy, so type-checking finds `astro/zod`. See [define the collection](https://ascribed-dev.com/docs/astro/#the-collection).
- A clean build's check summary (`checked 14 files: 0 errors, 0 warnings`) is logged as info, not as a warning.
- The npm packages' source maps include their sources, so Vite no longer warns that they point to missing files.

### <a id="v0.2.0-the-language-server"></a>The language server

- It answers `ascribe/review/setBase` and `ascribe/review/changes`, and `ascribe/preview` with `review: true` adds the page's changes, for editors other than VS Code. See [`crates/ascribe-lsp/README.md`](https://github.com/ascribed-dev/ascribe/blob/main/crates/ascribe-lsp/README.md).

### <a id="v0.2.0-installing"></a>Installing

- The Linux binaries run on glibc 2.28 or later, instead of 2.39, so `@ascribed/cli` installs and runs in the build images of Vercel, AWS Amplify, and Cloudflare Pages as well as Netlify's.
- A canary of the npm packages, built from `main`, is published every night under the `next` tag: `npm install @ascribed/cli@next @ascribed/astro@next`. It comes with no promise of stability, and its `ascribe --version` names the commit it was built from. Releases still publish under `latest`.

### <a id="v0.2.0-ascribedreview"></a>`@ascribed/review`

- A new package, for hosts that show review. Its Node part finds the open pull request for a checkout's branch, reads its review threads, places each on its rendered block, and posts comments and replies into the reviewer's pending review. It talks to GitHub through the GitHub CLI or a token its host supplies, and stores no token. Its browser part marks a rendered page's changed blocks and draws the threads beside them. Comment bodies render from a safe subset of Markdown, with no raw HTML, and no image loads without a click. See [`packages/review`](https://github.com/ascribed-dev/ascribe/blob/main/packages/review/README.md).

## <a id="v0.1.1"></a>0.1.1 (2026-10-02)

### <a id="v0.1.1-the-language"></a>The language

- **Behavior change:** a directory under the content root that holds an `ascribe.toml`, other than the project's own folder, is another project's folder, and is skipped like a directory whose name starts with `.`. `ascribe check`, `ascribe build`, `ascribe fmt`, and the language server leave its files to that project, so an editor shows each file's diagnostics from one project only. A link or an include from the outer project to a Markdown file in it is reported as a missing file.

### <a id="v0.1.1-the-editor"></a>The editor

- A workspace can hold several Ascribe projects. Every `ascribe.toml` is a project with its own language server, and a file belongs to the nearest `ascribe.toml` above it, so projects can be nested. Diagnostics, completion, the preview, format on save, and the other features use the project that owns the file. See [Workspaces with several projects](https://ascribed-dev.com/docs/editor/settings/#workspaces-with-several-projects).
- A project's server starts the first time one of its files is opened or previewed. The new setting `ascribe.startServers` (`onDemand` or `all`) can start every project's server when the workspace opens instead. The Problems panel lists only the projects whose server is running; `ascribe check` covers any project.
- The extension serves at most 50 projects in a workspace, the first 50 by path, and says so in the first project's output when there are more.
- Each project uses its own `node_modules/.bin/ascribe`, and logs to its own output channel, `Ascribe (<project>)`.
- The extension warns about a project's `ascribe` older than 0.1.1: an older server checks a nested project's files as its own.
- **Ascribe: Restart Language Server** restarts every server that has started. **Ascribe: Show Server Output** shows the active file's project, or asks which project. The preview's build choice is kept for each project.

### <a id="v0.1.1-the-language-server"></a>The language server

- **Behavior change for editors other than VS Code:** `ascribe lsp` looks for `ascribe.toml` only in its workspace folder and the folders above it. A workspace folder whose projects are all in subfolders gets no project; the server doesn't pick one of them. Start one `ascribe lsp` per project, rooted at the folder that holds its `ascribe.toml` ([Other editors](https://ascribed-dev.com/docs/editor/language-server/#the-project-a-server-serves)).
- It logs the project it uses, or why it has none.
- `ascribe lsp --config` is an error (exit code `2`). The server takes its project from the editor's workspace folders, and ignored the option.

## <a id="v0.1.0"></a>0.1.0 (2026-10-01)

The first release. It implements version 0.1 of the [Ascribe specification](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md).

### <a id="v0.1.0-the-language"></a>The language

- Directives: `@id`, `@include`, `@variant`, `@available`, `@note`, `@steps`, and `@details`, with attributes, titles, line and container forms, groups, and binding to headings and blocks.
- Project widgets: directives a project declares in `ascribe.toml`.
- Phrases (`{key}`), links and includes by file path, image attributes, heading ids, and a glossary.
- The content model, `ascribe.toml`: content types and frontmatter schemas, fragments, dimensions, versions, lifecycle states, features, note types, phrases, glossary, image attributes, widgets, the consumer profile, builds, and the editor's build.

### <a id="v0.1.0-the-ascribe-command"></a>The `ascribe` command

- `ascribe check`: every diagnostic, for every build, as text or JSON.
- `ascribe build`: the site output (markdown with web components, and a generated Zod schema), the plain-markdown output, and the JSON output, for each build.
- `ascribe fmt`: rewrites Ascribe constructs into canonical form; `--check` for CI.
- `ascribe lsp`: the language server.
- 126 diagnostics, each with a code (`ASC001` to `ASC126`) and a stable name. See [docs/content/checks/diagnostics.md](https://ascribed-dev.com/docs/checks/diagnostics/).
- Binaries for macOS on Apple silicon, Linux (arm64 and x64, glibc 2.39 or later), and Windows (x64). Intel Macs aren't supported. Each binary's archive and package carries a `THIRD-PARTY-NOTICES` file with the license text of the crates in it.

### <a id="v0.1.0-the-editor"></a>The editor

- The VS Code extension: diagnostics as you type, completion, hover, go to definition, document links, CodeLens, inline hints for empty links, rename and move refactoring, quick fixes, formatting, semantic highlighting, and a live preview that renders pages as the site does.
- It uses the project's own `ascribe` when the project installs one, and the binary bundled with it otherwise.

### <a id="v0.1.0-astro"></a>Astro

- `@ascribed/astro`: builds the site output before Astro loads content, provides the content collection and its generated schema, applies heading ids and image attributes in Astro's markdown pipeline, loads the element library, serves linked files, and rebuilds in `astro dev`.
- `@ascribed/elements`: the web components the site output uses (`<ascribe-note>`, `<ascribe-tabs>`, `<ascribe-availability>`, and the rest), styled with CSS custom properties.
