Unreleased
In main, not yet released.
The language 29
An API’s
referencethat isn’t Redoc’s is called an address pattern.link-operation-form-unsupportedandmodel-api-referencesay so in their messages, so they no longer read like a content type’s template. See Link to your API reference.Behavior change: the OpenAPI example checks are
api-example-*.They were
example-schema-mismatch,example-not-json,example-body-missing, andexample-schema-unsupported, and keep their codes, ASC277 to ASC280, whichascribe explainstill finds them by (Check a JSON example against its schema).Attributes written without braces are one error, with a fix that adds the braces.
@note type=warning: textno longer also reports the heading below it, andascribe check --fixbraces it. See Directive lines.A build filtered for a version older than every declared line is advice.
build-filter-line-undeclaredreports a filter whose version is in no live, planned, or archived line: declare the line inarchived, or useversions =. See[builds.<name>].A missing include or snippet file is offered the path that names it.
And
@variantin a project with no dimensions gives the[dimensions.<name>]table to add. See@include.draft: truekeeps a page out of every build until it’s ready.ascribe checkstill checks a draft, and a link to one from a published page islink-page-dropped. See Frontmatter.Behavior change:
example-staleis a warning, which--deny-warningsfails 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).ascribe checkreports anascribe.examples.tomlthat no source reads.One below a source’s root was ignored without a word; now
check, the editor, andascribe observesay so, and how to fix it. See tested 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.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 getinfo-pair-malformed, with a fix where the pair is clear. See 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
titleandtestedare read as §3.3.1 says, and the site and plain outputs leave outrequestandresponse. See SPEC.md.@Note:drawsdirective-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.
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.Behavior change: a memo file’s
abouthas 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 andascribe renamewrite it the same way. See Memo files.A draft isn’t reported as left out of
ascribe.nav.nav-page-unplacedpasses over a page withdraft: true, which no sidebar shows yet. See A draft.Behavior change: retired and aliased permalinks are kept in
ascribe.addresses, notascribe.toml.[permalinks]inascribe.tomlis an error, whichascribe addresses initmoves, andpermalink-removedis nowpermalink-lost. See Permalinks.ascribe checkreports 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.
A template’s placeholder in a page that publishes is an error.
template-placeholder-publishedfailscheckandbuild; in a draft,template-placeholder-leftstays advice. See What’s left to fill in.Behavior change: a page’s review date is its recheck date.
review-overdueisrecheck-overdue, keeping ASC153, and the rolereview-dateisrecheck-date; for a release, both old names still work inascribe.toml, acknowledgements, frontmatter, andascribe explain(Field roles).A link to a renamed
operationIdis offered the operation now at its method and path.ascribe checkreads the base’s spec for it, and--fixapplies it. See 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.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.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.
The specification describes the implicit
releasedimension.Ascribe already worked this way; the spec now says so, in §4.4 and §7.2. See SPEC.md.
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.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 and the diagnostics reference.
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.
Behavior change:
summary-missingandsummary-longare 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.
A page with no
titlecan take its opening heading as one.frontmatter-missing-fieldoffers the fix, safe while nothing links to the heading, soascribe check --fixtitles a folder of pages at once. See Bring existing docs to Ascribe.
The ascribe command 36
Behavior change:
ascribe buildshows 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 jsonis unchanged. See ascribe build.ascribe buildandascribe reporthold back whatascribe.baselinelists, asascribe checkdoes.Both count what they held back, and
--no-baselinereports everything. See The baseline.ascribe check --exit-code=warningfails on warnings.--deny-warningsstays, as its alias, so the command takes the option the conventions name. Seeascribe check.ascribe check --format githubshows each problem on the pull request’s lines in GitHub Actions.ascribe fmt --checktakes it too, and the CI recipe uses both. See Output for GitHub Actions.ascribe checkprints 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.
ascribe initsends you to a first page, andascribe checksays when there are none yet.In a project with no pages,
ascribe checksaysNo pages in docs/ yet.and exits with 0. Seeascribe init.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. Seeascribe check.Behavior change:
--checkwrites a unified diff inpermalinks sync,agents sync, andobserve.Each still exits with
1when something would change. See Theascribecommand.ascribe check --format concisekeeps 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.ascribe diff mainandascribe drift HEADname the--baseoption.A revision given where a page goes, as
git difftakes one, gets an error that says to write--base main. Seeascribe diff.Behavior change:
ascribe fmt --checklists each change, and--diffwrites 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. Seeascribe fmt.Behavior change:
ascribe fmt --checkwrites a unified diff.--dry-runlists each change, as--checkdid, and--diffis--check’s old spelling. Seeascribe fmt.Every command that takes
--format texttakesconcisetoo.A command with no one-line form writes its text, so an agent can pass
--format conciseto any of them. See Theascribecommand.sources fetch,templates list,vale eject,agents sync, andagents prompttake--format json.Each writes one document, and takes
conciseas text. See JSON report contract.Behavior change:
agents sync,agents index, andobserverefuse files git couldn’t put back.Each takes
--dry-run, and--forcewrites a file with changes not committed. See Theascribecommand.Behavior change:
init,sources,templates eject, andvale ejectrefuse uncommitted files.sources fetch,sources update, and the others each take--dry-run, and--forcewrites a file git couldn’t put back. See Theascribecommand.Behavior change:
fmt,check --fix, andrenamerefuse pages git couldn’t put back.Each takes
--dry-run, and--forcewrites a page with changes not committed. See Theascribecommand.Behavior change:
baseline,permalinks, andaddressesrefuse files git couldn’t put back.Each that writes takes
--dry-run, and--forcewrites a file with changes not committed. See Theascribecommand.ascribe --helplists the commands in groups, with short descriptions.A command’s own
--helpsays the rest, and the note for agents moved toascribe agents --help. See the command reference.A diagnostic’s
helplinks to the docs’ permalinks.The JSON of
ascribe checkandascribe explain, and the editor’s, has addresses an agent can follow, such ashttps://ascribed-dev.com/go/22-checks/, in place of links relative to the docs. Seeascribe check.ascribe initwritesascribe.addresses, so a new project keeps the addresses it publishes.A page moved with
git mvis thenaddress-loston the next check against the base, and its fix records the move. Seeascribe init.ascribe initadopts 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-rootnames their folder. See Bring existing docs to Ascribe.ascribe modelandascribe_modelshow every table ofascribe.toml,[apis]among them.New sections:
frontmatter,fragments,lifecycle,notes,images,apis,from,intended, andversions, so an agent without a shell can read each API’s spec and where its reference is. Seeascribe model.ascribe newstarts a page from its type’s template, as a draft.It sets the page’s
typeandtitle, addsdraft: true, as the editor’s New Page now does too, and never writes over a file. See Start a page.Behavior change:
ascribe observewithout 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. Seeascribe observe.ascribe previewshows 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.ascribe report deliverynames the linksllms-txt-links-resolvedidn’t reach.delivery-output(ASC171) lists each address with its status or why it couldn’t be fetched. See What to measure.Behavior change:
ascribe report buildslists 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 atschema_version3 (the project report).Behavior change:
ascribe report agentsisascribe report delivery.The section is named for the Web Documentation Delivery Spec it checks: its JSON key is
delivery, atschema_version2, andagentsstill runs it for now (the project report).ascribe versions retireneeds no--forcefor its target’s part of other builds’ availability.It lists those pages apart, as
expectedin the JSON. See Archive and retire a line.ascribe versions retirerecords the line’s addresses inascribe.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.ascribe searchcuts a long block to its match, in JSON and over MCP too.A place’s
textis at most about 240 characters around the match, so a large block no longer comes back whole. Seeascribe search.ascribe searchlists partial matches when no block has every word, and skips common words.The closest places come with the words each lacks, as
partial_placesin JSON;how do I publish llms.txtfinds whatpublish llms.txtdoes. See the search reference.sources fetchandsources updatereach 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. Seeascribe sourcesandobserve.Help printed in a terminal has no Markdown links.
ascribe checkandascribe explainwrite a link in a diagnostic’s help as its text; JSON keeps the Markdown. Seeascribe explain.ascribe versions archiveis nowascribe versions export.It writes a copy of a line and doesn’t archive it, which
retiredoes. The old name still works, with a warning, for one more release. Seeascribe versions export.
The editor 2
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.
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.
The language server 1
@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.
Astro and the elements 4
Code blocks have a copy button.
@ascribed/elementsadds one to each code block, which a site can restyle or turn off in CSS. See the element library reference.initwrites a stylesheet for the starter’s layout.src/styles/docs.cssgives 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).MissingPermalinkno 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).
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.
Agents 13
The edit hook names the placeholders a page’s template left.
After an edit, the agent hears each
template-placeholder-leftandtemplate-section-emptyin the files it wrote, so it fills them in while the page is its own. See 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).Agents’ Markdown says what a page is for and which release it describes.
After the title, each
.mdgives the page’s type and description, and with[releases], its first line andllms.txtname the release (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).
The MCP server’s
ascribe_renamegives whatascribe renamechanges, 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 jsonwrites the same (ascribe rename).The MCP tools
ascribe_formatandascribe_changesare nowascribe_fmtandascribe_diff.They’re named for their commands; the old names still work for one more release (ascribe mcp).
The MCP tools give no output schema, and
tools/listis 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.
A permalink’s address gives an agent its section in one fetch.
Its section is at
go/<name>.md, and withhost = "netlify"the address answers301. See Permalinks.ascribe agents syncnames the command the project runs Ascribe with.AGENTS.md, the skill, and the hooks saynpx ascribe,pnpm exec ascribe, oryarn ascribewhen apackage.jsoninstalls it, and--commandnames another. See the command they name.ascribe_resolvesays to leave outprojectwhenpageis a path.The page’s project is found from it, and the error gives the call to make instead. See the MCP tools.
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.
The rules give a dimension’s values, and an
@availableexample from the project’s own targets.The check line loses its “Programmatic check:” label, and
new-pageasks to fill in empty fields only when there are some. See Write the agent files.The skill’s commands work from the root when the only project is in a folder.
ascribe agents syncgives the commands that check or read the whole project--configwith the project’s folder. See the skill.
Review 6
ascribe difflists the pages an API spec’s change reaches.A page whose
@operationor 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.ascribe diffsays 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.The review report is now the HTML preview.
ascribe diff --format htmlis named beside the page preview and the site preview, so “report” meansascribe reportalone (Attach the HTML preview to every pull request).ascribe diffand review show a moved page once, as moved.A page git sees renamed, or whose move
ascribe.addressesrecords, is listed where it is now with what it moved from, and the JSON carriesmoved_from. Seeascribe 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.
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.
The docs and the site 14
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.
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.txtlists it under Optional. See the site’s README.The CI recipe runs the
ascribeyour project pins.Its workflow runs
npm ciandnpx ascribe, so CI checks with the editor’s version; without Node, it installs a named release. See Checks in CI.The
ascribe.tomlreference 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.
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.
Getting started’s dimension has
labels.Its example gives each package manager the name readers see on the site’s tabs, and says what
labelandlabelsare for. See 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.
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.
The docs no longer badge features with the release that added them.
Every reader is on the live release, so only what’s in
mainbut not yet released, or a lifecycle state such as deprecated, is marked. See@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.
The glossary tells the four previews apart, and defines the recheck date.
The page preview, the site preview, the HTML preview, and
ascribe previeweach have one name (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.
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.
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.
Fixes 14
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/astrowrites fromascribe.addresses. See Keep the addresses you’ve published.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.
Fixed:
ascribe agents syncno longer importsAGENTS.mdtwice in aCLAUDE.mdthat imports it already.It leaves such a file as it is, and removes a block it added there before. See 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 moveandretireread a page’s path from here.Before,
retirerecorded that path as an address no page had;rename,diff, andpermalinksread 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 fmtkeeps 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
@intendedexample on the directives page names a check that exists.It said
check=link-dead, whichascribe checkrejects; it’scheck=link-external-broken, the external link check, as on the external links page.Fixed: a lone carriage return in frontmatter no longer shifts what
ascribe fmtedits.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\nline ending no longer loses its position.Its span was empty, so what pointed at it pointed nowhere.
Fixed:
ascribe report deliverynames 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 reportnames 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
@snippetshows 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.
0.4.4 (2026-10-26)
The language 9
ascribe checksays which OpenAPI operations no page links to.It’s advice,
api-operation-unreferenced, andascribe report inventorycounts each API’s operations, linked and checked. See Find the operations no guide links to.@operationputs 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.
Check a JSON example in a guide against its operation’s schema in your OpenAPI spec.
Mark a
jsonblock withrequest=orresponse=, andascribe checkreports each disagreement at its place. See Check a JSON example against its schema.Link a guide to an operation in your OpenAPI spec, checked against the spec.
Declare the spec in
[apis], and a link likeopenapi.yaml#getUseris checked and written as the operation’s address in the reference. See Link to your API reference.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.Every lost address has its fixes, and
ascribe.addresses’ entries are checked.address-lostoffers recording a rename, forwarding, or retiring, and nine checks report an entry that no longer works. See Keep the addresses you’ve published.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.Keep the addresses you’ve published: a moved page records itself, and the build redirects it.
With
ascribe.addresses,ascribe checkwarns about an address nothing accounts for (address-lost). See Keep the addresses you’ve published.Behavior change:
[consumer] host = "netlify"replacesheaders, and writes_redirectstoo.headersis now an unknown key. See the host’s files.
The ascribe command 1
ascribe resolve --emit sitewrites a page as the site output does.--frontmatteradds the site output’s frontmatter, without building the project. Seeascribe resolve.
The editor 2
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.The editor shows lost addresses between checks, and the old addresses that lead to a page.
A deleted page’s
address-lostis in the Problems panel within seconds of a save. See Keep the addresses you’ve published.
The language server 1
ascribe/addressessays what comparing the addresses with the base’s found.It’s sent after a load, a save, or a change on disk, and
ascribe/pagelists a page’s old addresses. See Keep the addresses you’ve published.
Agents 3
ascribe linkand the MCP toolascribe_linkanswer a link to an OpenAPI operation with the operation.Its
kindisoperation, with the operation’s method, path, summary, and address. Seeascribe link.Agents are told how to move pages without losing their addresses.
AGENTS.mdand the skill say to useascribe rename, and the MCP server can record a move or a retirement. See Agent instructions.ascribe_resolvetakesemit, and the agents’ rules say when to ask for the site form.emit: "site"returns a page as a site gets it. Seeascribe mcp.
Fixes 2
Fixed: a page or fragment can be renamed by its case alone on macOS and Windows.
ascribe renameand the editor rename it and change the links to it (ascribe rename).Fixed: with
trailing-slash = "never", an Astro site serves each page at its address, not a redirect.@ascribed/astrosets Astro’sbuild.formatto"file"(the Astro reference).
0.4.3 (2026-10-24)
The language 9
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, 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 retirechange a taken key in the otherascribe.toml, at the same path, and say which file they changed; renaming a keytakenames renames it intaketoo, 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 releaseandascribe versions retirerun in a project that takes the releases are made in the project that writes them, and say so.ascribe templates ejectandascribe vale ejectrefuse a taken table, naming its file. See theascribe.tomlreference.A problem in a generated file points at what generates it.
A top-level
@memothat beginsGenerated fromorGenerated bysays the file is generated: each problem in the file names the memo as a related place, with its text;ascribe check --format prompttells 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 withascribe check --fix. See Memos.help.jsonhas a section’s text with each arm of its switcher chosen, inchoices.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 showschoices.pm.pnpm(Show the docs in your product, output-layout contract §1.7).page-sizenames 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
@includelines. A page that’s large because of one included file says which. See the checks.available-same-in-every-lineremoves 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.0in a project with one product, the fix removes the@availableline, the page’savailablekey, or the row’s{available=…}instead of writingascribe, 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.@summarygives a section a summary, which embedded help shows.@summary: textunder a heading gives the section its own summary, which the page doesn’t show;@summaryon 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@variantarm is an error._ascribe/help.json’ssummaryis the section’s@summary, and a section without one has none; the JSON output gives a heading its section’ssummary. 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@summaryand Show the docs in your product.ascribe checkreports a permalink with no summary, and a summary too long to show.summary-missingis advice at the@idline of a heading whose@idmarks 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-longis advice at a@summarywhose text is 300 characters or more, as plain text on one line;[checks.summary-long] limitsets another length. A permalink or a summary in a fragment is reported once, in the fragment. A page’s own permalink has its description, whichpage-description-missingalready reports. See Write the summaries.An unknown key in
ascribe.tomlnames 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 keyfromin[ascribe.toml]; this is ascribe 0.4.0, and the project may need a newer one”, inascribe check, the language server, and the MCP tools alike, and a command or tool that stops becauseascribe.tomlhas 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’sfilesanddefault,[glossary] terms, and[[intended]] term. Seedocs/content/contracts/content-model/validation.mdand decision #849.
The ascribe command 4
ascribe check --format concisetakes--limit, and itsand 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).ascribe refs release:<target>@<version>lists the marks that name a version in one release line.ascribe refs release:ascribe@0.4.0finds each availability spec that names a version in that line of[releases.ascribe], a history’s steps andplannedincluded, where a text search ordimension:releasecouldn’t say which release a mark was for. Everyavailabilityplacerefslists now shows its spec as written, after the use in text and asspecin JSON, which is null for every other use. Theascribe_refstool takes the same target. Seeascribe refs.ascribe versions releaseremoves 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 oneavailable-same-in-every-linewould report, as its fix does, along with a feature’s spec, and lists them;--dry-runlists them without writing. The JSON lists them asclearedandcleared_features(ascribe 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.jsonhas them, and--format jsonprints the entry as the file writes it, withversion,build, andline. A name the build doesn’t publish exits with1and names the closest; a retired one gives its reason. Theascribe_resolvetool takes the samego:<name>, and the rulesascribe agents syncwrites say an agent can read a permalink’s help with it. Seeascribe resolveand Show the docs in your product.
The editor 3
A problem’s source names its project when the workspace has several.
The Problems panel shows
ascribe · handbookwhere it showedascribe, so you can tell which project’s server found it (Workspaces with several projects).The editor shows a section’s summary on its heading, and on its Permalink row.
The hover on a heading, or on its
@idline, shows the section’s@summaryin the editor’s build, as_ascribe/help.jsonwrites it, and This page’s Permalink row has it in its tooltip, or says the section has none. The language server’sascribe/pagegives each permalink itssummary. See Writing in the editor and the sidebar.Behavior change: a change on disk no longer starts a project’s language server.
With
ascribe.startServersat 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.
The language server 1
The language server and the MCP server give the version
ascribe --versionprints.A build that isn’t a release says
0.4.1+dev (7ae1340440fc), with its commit, in the language server’sserverInfoand its first log line,ascribe-lsp 0.4.1+dev (7ae1340440fc): using the project at …, and in the MCP server’sserverInfo, where each said0.4.1before. Which binary is answering an editor or an agent is there to read, withoutpsorlsof.
Astro and the elements 1
@ascribed/astroserves 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.jsonfor an older line, and falls back to the copy of_ascribe/help.jsonit bundled. The build’s_ascribe/headers.jsongives each addressAccess-Control-Allow-Origin: *, so a product on another origin can read it (Show the docs in your product).
Agents 2
The plugin runs the project’s
ascribebinary, as the VS Code extension does.Its language server, stop hook, and MCP server used to run
ascribefrom the path, so a session got whatever was installed globally, not the version the project pins. They now run, in order, the binaryASCRIBE_PATHnames, the project’snode_modules/.bin/ascribe, and thenascribeon the path, through a launcher the plugin ships. See 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 syncwrites say to give each section a@summary, the sentence a product shows with its help, and how to choose between@summaryabove a paragraph and@summary: <text>; the skill says so too.ascribe check --format promptquotes 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.
The docs and the site 3
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 to Diagnostics: content checks of outputs and the site, since with them the page was over
page-size’s 50,000 characters. Their permalinks lead to the new page. See decision #876.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 to Diagnostics: the content model’s builds and site, since with them the page was overpage-size’s 50,000 characters. Their permalinks lead to the new page. See decision #896.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 to
docs/content/contracts/json-reports/model.md, since with it the questions page was overpage-size’s 50,000 characters. The schema itself is unchanged. See decision #845.
Fixes 8
Fixed:
ascribe buildtakes 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 --helpdescribesbaselineandbuildeach as itself.baselineread “Build the documentation set’s outputs”, andbuildread “Arguments ofascribe build”. A test now holds every command in--helpto 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.baselineeven 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 onceascribe.tomlloads (Problems and fixes).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.tomlinside 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).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).
0.4.2 (2026-10-22)
The language 9
Behavior change: a project with a
sitepublishesllms.txtand each page’s Markdown.[consumer] agentsistruewhensiteis set andfalsewhen it isn’t, so a site with an address publishes what agents read unless it saysagents = false. Only an explicitagents = truewithoutsiteis stillmodel-consumer-agents-site.@ascribed/astroreads it the same way, and publishes them with the site. See The outputs for agents.Every source leaves out
node_modulesand.gitfolders.Whatever its
ignoresays, no file in anode_modulesor.gitfolder, at any depth, is readable from a source, so a source over a JavaScript project needs noignore = ["**/node_modules/**"]. See[sources.<name>].prose-not-checkedis reported only where the project asked for prose checks everywhere.With
[checks.vale] in-check = true, orascribe 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.[from]takes tables and keys from another project’sascribe.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); andspec,project, andfromcan’t be taken. The editor watches the other file, hovers a taken key with where it’s written, and goes to it; edit commands andascribe versions releasestop with an error naming the file that holds a taken key; and@ascribed/astroand the extension ask the binary for what’s taken. See theascribe.tomlreference.The editor and
ascribe modelsay 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 implicittitle; on a shared field it names the types that replace it.ascribe modelsays the same for each field, in its text and in a newdeclaredfield 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.plannedis a version word in an availability spec.@available: ascribe planned, orcloud, 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.plannedisn’t a feature key, a feature’s spec can’t use it, and no lifecycle state, dimension, value, or feature key can be namedplanned. The editor offersplannedafter a target and after a state, and its hover names the line. Seedocs/content/writing/directives.mdanddocs/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] labellabels the dimension,Releaseunless set, and[releases.<target>] labelthe target. A project that declares its ownreleasedimension while a target is left to the implicit one getsmodel-releases-target. See the[releases]reference.Behavior change:
[frontmatter]declares fields every page type has, andtitleis 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 stringtitlewhether it declares it or not, somodel-type-titleis gone; a declaredtitlethat’s optional or not a string ismodel-field-reserved. A field nameddescription,review-date, orownernow has that role withoutrole, unless the type gives the role to another field, sopage-description-missingandreview-overduecan report pages they didn’t before. Project Settings shows[frontmatter]as a card under Content types, andascribe templates ejectleaves out of a type it adds the fields every type has already. See the content model reference.A type with no
templateorguidetakes them fromtemplates/templates.lock.When the lock
ascribe templates ejectwrites records a template under the type’s name, the type has that template and its guide, with nothing about them inascribe.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 ejectintotemplates/no longer writes the two keys, and with--keep-typeleavesascribe.tomluntouched; with--toanother folder it still names them. In the editor, the hover on a type’s name inascribe.tomlsays where its template comes from. The docs’ own five set types drop theirtemplateandguidelines, with the same outputs (page types, page templates).
The ascribe command 5
ascribe.baselineputs a project’s existing advice off, soascribe checkreports only what’s new.ascribe baseline writelists every advice the project has inascribe.baseline, besideascribe.toml, to commit; from then onascribe checkholds those back and counts them (2 advice, 3412 in the baseline, andsummary.baselinedin its JSON), and reports an entry that matches nothing asbaseline-entry-unmatched, whichascribe baseline pruneremoves. Only advice is ever held back, and--no-baselinereports everything. The baseline has the commands and the file’s syntax.ascribe --helpopens 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 releaserewritesplannedand moves no features.Releasing a target’s planned line rewrites each mark’s
plannedfor that target to the line’s version, asascribe plannedbecomesascribe 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,--nextonly 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:featuresis gone, and each mark’sfromis its spec as written. Seedocs/content/versions/release.md.ascribe report agentssays 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
agentsgainsprevious_run,new,resolved, andnew_checks,nullor 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 says what’s new means.ascribe reportsays which problems are new since the last report, and how many are gone.From its second run,
problemssays “14 problems, 2 of them new since 9 October; 5 the last report listed are gone” and lists the new ones; its JSON gainsprevious_run,new,resolved, andnew_items,nullor 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 says how a problem is told from another.
The editor 8
The editor leaves folders git ignores, and build and dependency folders, out of its projects.
An
ascribe.tomlthat git ignores, one in a folder such astarget,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 whencargo testwrites projects intotarget/. To work on one, open its folder as a workspace folder of its own. Workspaces with several projects 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.
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.
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.
The preview and a page’s problems follow an edit to the code a
@snippetshows.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.A code file’s tag problems are shown in the code file, at the tag.
A
snippet-tagsproblem, a region’s tag never closed, an-endwith 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@snippets show the file, naming each. The page keeps its own; saving the fixed file clears both.ascribe checkand its JSON don’t change. See writing in the editor.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 asite, for a fragment, and where the build the status bar shows leaves the page out, they stay text (the sidebar).
The language server 3
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 checkdoes, 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@intendedwhere the check’s place says. An entry that matches nothing is shown onascribe.baselineasbaseline-entry-unmatched, when the editor’s build is the project’s every build. See The baseline.Rename a release target from its
[releases.<target>]header.F2on the target in the header of a target the implicitreleasedimension has renames it everywhere a rename from a page does. See 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
removetags applied, in a fenced block in the snippet’s language, cut at 20 lines with how many more there are (writing in the editor).
Astro and the elements 1
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:The previous default colors
/* 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; }
Agents 2
The agents’ hook,
ascribe_check, and thefixprompt hold back whatascribe.baselinelists.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_checkcounts it asascribe checkdoes (, 3 in the baseline, andsummary.baselined). See The baseline.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.
The docs and the site 5
Seven more of the docs’ guides have a permalink.
Content that varies is at
/go/variants/, Publish a site for each version at/go/versions/, Set up a glossary at/go/glossary/, The preview and builds at/go/preview/, Permalinks at/go/permalinks/, Leave a memo for whoever edits next at/go/memos/, and Publish for AI 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.navleaves out, that a section is a folder, and says the element library works on any site. The Navigation page has the permalinknavigation.
Installing 1
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 --helpopens 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.
Fixes 2
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 asp: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.
0.4.1 (2026-10-21)
The language 1
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-lineadvice 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, isunsafethere, since it removes the badge; where no output shows the versions it stayssafe. A project that keeps such badges setsavailable-same-in-every-line = "off"in[checks](docs/content/content-model/content.md).
The ascribe command 9
ascribe report linksasks again about a link whose server answered with an error.An address that answers with a
5xxstatus 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, aslink-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. A404or a410is asked about once and reported as before.ASCRIBE_LINKS_RETRY_WAITsets the wait in seconds. See Check external links.ascribe modelshows every section ofascribe.toml, each key with the value in effect.--sectiontakesproject,consumer,editor,checks,crawlers,listing, andpermalinks, and the MCP tool’ssectionwith 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--sectionnames them at its end, still within 4,000 characters. The keys and defaults come from the registry the loader reads. Seedocs/content/command/questions.md.ascribe observeasks before it runs the commands, when its input is a terminal.It lists the
testand[environment]commands ofascribe.examples.toml, asks Run them? [y/N], and runs nothing, and writes nothing, unless the answer is yes; then it exits 2.--yesskips the question. In CI or a hook, where standard input isn’t a terminal, it runs them as before, without asking. Seeascribe observe.ascribe refstakes an image, and lists the files that show it, link to it, or name it.ascribe refs images/keys.pnglists each place asimage,link,html(raw HTML such as<img src>), orfrontmatter(a field such ascover), the usesimage-unusedreads, and an image nothing uses answers with no places and exit code0. A file that’s neither a page, a fragment, nor an image says whatrefstakes, instead of that it doesn’t exist. The MCP server’sascribe_refstakes an image too. Seedocs/content/command/questions.md.Behavior change:
ascribe renderis nowascribe resolve, and its MCP tool isascribe_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 atschema_version1 with every field as it was. There’s no alias:ascribe renderis an unknown command, andascribe_renderan unknown tool. Agent files and skills written byascribe agents syncbefore this release name the old command until it runs again. Seedocs/content/command/questions.md.ascribe searchfinds 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 refscommand 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. Seedocs/content/command/questions.md.ascribe search --format jsonwrites the places as a document of its own.Each place has its
file,line, andcolumn, what it is (in), the block’s wholetextand where thematchis in it, in characters, what it readsthrough(a phrase, a link’s borrowed title, a snippet’s code), itsarm, and the first threepagesthat show it, each with its title, heading, id, and builds. The document has the totals,truncatedandnext_commandwhen the list is cut at 500, the pages with--summary, and, when nothing matched,source_placesandnot_searched_builds. Its schema isschemas/search.schema.json. Seedocs/content/command/questions.md.ascribe searchnarrows 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;
--typekeeps to a content type;--casematches case as written;--wordmatches only where a word or a phrase isn’t inside a longer one.--summarylists 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. Seedocs/content/command/questions.md.ascribe searchfinds 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 saysSign in to {cloud}, says it reads through the phrasecloud, and names the heading and the arm it’s under. A match wholly inside a phrase’s value is at the phrase’s line inascribe.toml, and the rest of its block is still searched for the line’s own matches. Seedocs/content/command/questions.md.
The editor 14
Observe Examples runs
ascribe observeas 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 modelnames, andascribe observelists the commands and asks in the terminal before running them.tasks.jsonnames a source withsource. It isn’t offered in Restricted Mode. See 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.
Project Settings shows and changes the settings in
ascribe.toml.Ascribe: Open Project Settings opens a tab of the project’s
ascribe.tomlsettings, 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, forpage-size,image-large, andmemo-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 fromascribe.toml’s title bar too, on a project the extension found and not on a fixture’s thatascribe.projects.excludeleaves out. See 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.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.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.
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
--configwhen the project isn’t the workspace folder. The task type,ascribe, can be named intasks.json. Neither is offered in Restricted Mode. See 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.
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.navorascribe.permalinksleft 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 searchin 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.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
titlereads 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.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.
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.
The language server 1
ascribe/projectEditanswers with one edit for the whole project.Its
kindisfixAll, every safe fix of the problems the server shows, in every source file, orformat, every source file asascribe fmtformats 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.
Agents 1
Agents search before they write a page, and
ascribe mcpoffersascribe_search.The tool takes the command’s arguments (
query,paths,build,type,case,word,summary,limit) andproject, and returns the command’s text, or withresponse_format: detailedits JSON; the server keeps each build’s pages as search reads them, so a second search only scans. The rulesascribe agents syncwrites, the skill, and thenew-pageprompt each say to search for a subject before writing a page about it, and to edit the page that covers it instead. Seedocs/content/agents/commands.md.
The docs and the site 22
The agents guide is an overview and a page for each task.
Agents is now a short overview that leads to Work with an agent, Write the agent files, Prompt your agent, Set up the MCP server, hooks, Claude Code, GitHub Copilot, and Index your docs for your product’s agents, with What’s sent where as a page of its own. What the guide said about VS Code is in Agents in the editor, and what the command reference says is linked rather than repeated.
The Astro guide is four how-tos, each one task.
Astro sets a site up; Put the docs in a site with more than the docs, Publish for AI agents, and Serve the Markdown versions, with nginx’s and Cloudflare’s configuration, are pages of their own. The components, several projects, versions, the permalinks’ forwarding page, and
MissingPermalinkare in the@ascribed/astroreference. Permalinks keeps a product’s links working, a nested section’s place is written once, in Navigation, and moving a site’s own list of pages over is a how-to of its own.Each of the six commands filed under examples is documented in its own feature’s section.
ascribe permalinksandascribe templateshave a page each, beside their guides;ascribe vale ejectis on Prose, through Vale; andascribe initis on Install, as “Start a project”.ascribe sourcesandobservekeep 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, andfmt,ascribe agentsandmcp, andascribe explain,model, andresolvein the commands that answer 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-sizetoprose-not-checked, now do, as the others do, in the content checks’ reference.The
ascribe.tomlreference explains, and its contract states the rules.The format contract 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 keeps the explanation and the examples, and says noMUST. Each links to the other at the top. The decisions the contract listed in its §26 are now files inproject-docs/decisions/.The
ascribe.tomlreference’s tables of keys are generated from the keys the loader reads.Each table says what the editor will.
[[intended]]listsphrase,feature, andimageas a row each, and[checks]lists[checks.<check>]’slevel, in the reference’s checks page.The directive reference is four pages.
Directive syntax has how a directive is written, its attributes, forms, groups, titles, and binding; the directive reference has each built-in directive and project widgets; Frontmatter has pages and fragments, with the reserved keys as a table; and Phrases, links, and images has the rest of what a page writes. The directive reference links to Permalinks and release lines instead of repeating them, and a region’s tags are in Show an example from the code.
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, andnav-label; the content model contract’s validation rules list the seven loader rules they were missing,[releases]’s four among them; the diagnostics pages name every group they hold; the home page links all nine contracts; and What’s sent where namesascribe report linksandascribe report agentsamong the commands that reach the network.The editor’s pages say each thing once, where it belongs.
The 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 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. The Problems panel and Check Every Build are on Problems and fixes, other formatters on Formatting, and how a drag in Pages is placed on Put your pages in order.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, Actions, new pages, and memos, Review in the preview, and Writing in the editor, and the build lens ends Content that varies. 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 leads to three pages that were its sections: Show an example from the code, Keep the docs in another repository, and Report the pages whose examples changed. Page templates keeps its task, and links to the diagnostics reference for what
template-placeholder-leftandtemplate-section-emptyreport.Checking external links has a page of its own.
Check external links takes acting on
ascribe report linksa 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 withascribe report agentsis in What to measure.The docs have an Install page, and getting started has one path.
Install has every way to install
ascribe: npm, Homebrew, the install script, PowerShell, and a release’s archive, with whichascriberuns when a machine has several. Getting started installs with npm and starts withascribe init, without the branch for writingascribe.tomlby hand, and its CI recipe is Checks in CI.The docs have a Publishing section, for what search engines and agents are told.
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 has what@ascribed/astrowrites from the page records, which moved there from the@ascribed/astroreference. Theascribe.tomlreference keeps its tables and links to it.ascribe reportandascribe lspare 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, andascribe diffanddriftkeep their page in Review.ascribe lspis now on the editor’s page, Editing, and the agents’ command page coversascribe agentsandmcp. Their permalinks lead to the new places.The daily report job keeps what
linksremembers as an artifact.The job in The project report downloads the newest
ascribe-linksartifact before the report and uploads its own after it, in place ofactions/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 needsactions: read.Review’s rules are each written once.
Review is the steps of reviewing a pull request; How review 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 is the CI job; and Review in the site preview, which was a section of the
@ascribed/astroreference, 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 permalinkreview-in-the-site-previewleads 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
@memoreference and thehtml-commentdiagnostic, and Leave a memo for whoever edits next links to them; the day a check runs as is underascribe check. Set up a 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. 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,
ascribe diffanddrift, the Element library reference, Astro, the navigation contract, and 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 reaches its first step on its first screen and ends with what to do when a line ships; Archive and retire a line is the task of its own it was inside; and Release lines holds what each line’s output has, the links between lines, and what an older line says about itself. The
ascribe versionsreference shows each command’s JSON as real output, and links to its schema.
Fixes 6
Fixed:
ascribe checkis 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 diffcompares 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, asascribe searchand the plain output do; a change only inside a comment still marks the block as changed, with no words marked. Seedocs/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 areplacesof 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.
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 astarget/, and each one asked for every lens again. VS Code now tells the server only of the files a source’sincludeandignorepatterns read, and itsascribe.examples.tomland observations, and the server asks for the lenses again only for those (docs/content/editor/writing.md).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.
0.4.0 (2026-10-18)
The language 17
A description field’s
defaultis 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, anddescription-too-longreports a long default once, at thedefaultinascribe.toml. Pages that share a default aren’t reported bydescription-duplicate.A folder’s name in the navigation has its
-and_as spaces.A section with no
labeland no page of its own is named by its folder with-and_as spaces and its first letter capitalized, sogetting-started/is “Getting started” in a sidebar, an archive,nav.json, andascribe model, as it already was inllms.txt. See the 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 adviceglossary-definition-long, which names the builds.@termon a page[glossary] pagesdoesn’t list isterm-off-glossary-page, with a fix that lists the page, whichascribe check --fixleaves 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. Seedocs/content/content-model/content.md.Behavior change: an unused term is acknowledged at its heading, and
[[intended]] termis gone.Write
@intended {check=glossary-term-unused}: …among the directives under the term’s heading: at the top of a section,@intendednow 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 withtermis an error that says so. Seedocs/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.@termtakesaliases, separated by|, and its ownmatchandcase-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 afirstterm, the page’s one link to it. That’s how amarkedterm is linked: one that no link names is reported byglossary-term-unused. Two terms with the same text areglossary-term-duplicate, and a path inpagesthat isn’t a page ismodel-glossary-page;model-glossary-duplicate-term,model-glossary-link, andglossary-link-target-missingare gone.ascribe modellists each term with its page and line, atschema_version3, and the JSON output lists each use’s definition. Seedocs/content/content-model/content.mdanddocs/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
@memokeeps the same text out of every output.ascribe checkreports 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.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@memowith no text is already an error.ascribe check --fixand the editor’s quick fix remove it, which changes no output. See@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 ofascribe.tomlasascribe refsdoes, andquote, 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 checkreports a memo file that isn’t one as an error,memo-invalid; one whoseaboutnames 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] limitsets another. See Memo files.@memokeeps 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:…@endaround 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, andascribe fmtleaves its lines as written. No output shows it, and a change to a memo alone isn’t a change toascribe diff. See@memoin the directive reference.nav-labelgives a page a shorter name in a sidebar, and the JSON output has the navigation.A page’s
nav-label, one line, read as itstitleis, is its label in the sidebar, previous and next, an archive,nav.json, andascribe model; it keeps its title everywhere else,llms.txtincluded. 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.jsonas the site output does, its formatted labels as pieces. See A page’s label.ascribe.navis checked against the project, and an unplaced page or section gets a safe fix.The checks come in the same release as
ascribe.navitself, so no release reads the file without them. Apagesentry that isn’t a page in its folder isnav-page-unknown, afolderthat isn’t a section isnav-folder-unknown, each with a fix to the closest name, and a folder or a page listed twice isnav-duplicate, with a fix that removes the second; all three are errors at the value inascribe.nav. A folder with both anindex.mdand a page beside it with its name issection-page-both, an error on theindex.md. A page a section’spagesleaves out isnav-page-unplaced, advice on the page, and a section the file leaves out beside ones it lists isnav-section-unplaced, advice on its first page;ascribe check --fixplaces them all in the order they already had, keeping the file’s comments and layout. See Navigation.ascribe.navorders 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.mdor 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 inascribe.toml’s order, then by title.ascribe.navlists sections, each withfolder, and optionallypagesandlabel, and what it leaves out keeps the default. A page that’slisting: unlistedis left out of the navigation, and so out of the sidebar. A file that can’t be read isnav-invalid. Every project’s site output changes: each build’s has_ascribe/nav.json, its share of the navigation,generatedin its manifest, whether or not the project has the file. Afolderor apagesentry that names nothing is reported by the navigation’s checks, which come in the same release. See Navigation and the navigation contract.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.yamloptions” comes after “Apply” and{product} overviewsorts 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 longernav-section-unplacedwhenascribe.navlists a sibling and not it: its table wouldn’t move it. See Navigation.page-orphansays 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. Seepage-orphan.A page template’s instructions to the writer are memos, never published.
In the six templates
ascribe templates ejectwrites, 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-leftdoesn’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. Thenew-pageprompt tells an agent to follow the memos. A project’s own copies are its own:ascribe templates statussays what changed in the set. See the templates guide.template-section-emptyreports 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-leftcan’t find it. On a page of a type with a template,ascribe checknow 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.A top section is ordered by
ascribe.nav’s[[sections]], even when its own page is beside its folder.guides/withguides.mdcomes 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 whereguides.mdsorted 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.
The ascribe command 6
An archive’s sidebar and previous and next follow its line’s navigation.
ascribe versions archivelists 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’sunlistedis still written, with no place in the sidebar. See Archive a line.ascribe outlineandascribe refslist the memo files about a page, a heading, or an entry.ascribe outlinelists each memo under the page or heading it’s about, with its first line, and its JSON hasmemoson the page and each heading, as the MCP toolascribe_outlinedoes.ascribe refslists a memo file as a place with the usememo, and lists one whose target is gone under the target it names. Seeascribe outline.ascribe model --format jsonreports the navigation.Without
--section, itsnavigationis the project’s sections and pages in order, each section with its label, its own page, its pages, its nested sections, andlisted, whetherascribe.navlists it, and each page with itssource,label, andtype; a page that’sunlistedisn’t in it.schema_versionstays3. Seeascribe model.ascribe modellabels a page by its title’s plain text, as a sidebar does.A page with no
nav-label, whose type’stitlesetsinline, is labeledloom.yaml optionsrather 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. Seeascribe model.Personal memos, kept on one machine in
.ascribe/memos/.They’re memo files in the same format as
memos/’s.ascribe outlineandascribe refslist them withpersonal, andascribe checkdoesn’t read them, so what it reports is the same on every machine (memo files).ascribe renamekeeps memo files attached.Renaming a heading’s id or moving a page rewrites the
aboutof each memo inmemos/and.ascribe/memos/that names it, keeping its#id, and so doF2and moving a file in the editor (ascribe rename).
The editor 11
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,
aboutcompletes and links to what it names, and the file shows its problems as you type (memo files in the editor).The editor knows
@memo: completion, hover, highlighting, and folding.Typing
@meoffers@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.ascribe.navis 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
pagesthe section’s pages it doesn’t name yet. The extension givesascribe.nav,ascribe.permalinks, andascribe.lockto the TOML language, which colors them with a TOML extension installed. See the editor.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-labelin 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: itsnav-labelwhen it has one, else its title. See the editor and 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.tomland a page’s frontmattertype: each lists the pages whosetypenames it (the Content model view).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, 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, whichascribe checkreports. See the editor.Moving a page or a folder keeps
ascribe.navright.ascribe renameand a move in the editor rename a page’s entry in its section’spages, 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. Seeascribe 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.
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.
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.
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.
The language server 7
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 asascribe check --fixsettles them; witheditor.codeActionsOnSaveit runs on save. See Apply the safe fixes.Go to definition takes a glossary term to its heading, and
@termcompletes 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.@termis offered at the top of a section on a glossary page, with its attributes andmatch’s values.glossary-definition-missinghas a fix that writes a paragraph under the heading, selected for you to type over. Renaming a term’s@idwithF2, orascribe rename, renames the term, and rewording a term’s heading that has no@idsays that its id changes too. Seedocs/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 theirabout, shows a CodeLens on what they’re about, lists them inascribe/page’smemos, and writes one with theascribe/editactionsaddMemoandaddPersonalMemo(memo files in the editor).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/inventoryalso lists page content types, with how many pages each has, and each dimension’s values. See the language server.ascribe/pageanswers whether a page is published, listed, due, over size, or unfinished.Its
aboutadds the page’s description (or its field’s default) and whetherdescription-too-longreports it, its review date and whetherreview-overduereports it, availability, builds, listing, permalinks, and template, and the answer addshealth(its size, aspage-sizemeasures it, and its tested examples) andincludes; each page inascribe/inventoryadds thebuildsthat publish it.ascribe/review/baseFileanswers 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.
A code file a page shows says which pages show it.
Over each region a
@snippettakes, 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.
Astro and the elements 3
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.Behavior change:
sidebar()draws the project’s navigation, and takes a site’s own list asnav.sidebar(await getCollection("docs"), { entry })gives a layout the project’s sections and pages, inascribe.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:- const groups = sidebar(nav, await getCollection("docs"), { entry }); + const groups = sidebar(await getCollection("docs"), { nav, entry });A group gains
sections, its nested sections, andfolder,page, andformattedfrom the navigation; each page gainslabel, its title, andformatted.label, its HTML when the title has code. Seesidebar().npx @ascribed/astro initwrites nosrc/nav.ts, and its sidebar is the project’s navigation.The starter’s layout calls
sidebar(entries, { entry }), andneighbors(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 itsdata-depth.initwrites three files, and no longer builds the project first. A site started with an olderinitkeeps itssrc/nav.ts, passed asnav; Put your pages in order says how to move it over toascribe.nav.
Agents 5
The edit hook names the advice with a safe fix, and the fix for the agent to make.
After an edit,
ascribe agents hookreports, after the errors, each advice in the files the agent wrote that has a safe fix, with what to change and where, such as “Addnew.mdtoguides/’s pages in ascribe.nav” for a pageascribe.navdoesn’t place. It still writes no file. See Agents.Behavior change:
llms.txtand the agents’ index follow the navigation, in every project.Their pages come in the project’s 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, inllms.txt, its split files, andascribe agents index, andascribe agents index --intorewrites the index in a product’sAGENTS.mdwith the new order the next time it runs. The blockascribe agents syncwrites lists no pages, so it doesn’t change. Seellms.txt.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;reviewandascribe diff --format promptlist 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 rulesascribe agents syncwrites say thatascribe outline <page>lists a page’s memos. See Agents.The rules
ascribe agents syncwrites tell an agent to read and keep a block’s@memo.In a project whose pages have a memo,
AGENTS.mdand 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.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.
Review 2
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/previewanswers for such a page too: withreview: true, itsreviewhas the page as it was and its title. See 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.
The docs and the site 4
The editor guide is split into a page for each part of the editor.
The 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, 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 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 covers
@memoin both forms, memo files withaboutandquote, 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 covers the default order, writing
ascribe.navor 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.
Fixes 10
Fixed: two glossary terms with the same id are reported, and the second isn’t a term.
Before,
ascribe checkwas 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-sensitiveGoandGO, have the same id too.glossary-term-duplicatereports it at the second; give one of them another id with@id(docs/content/content-model/glossary.md).Fixed: VS Code shows the “Shown in N pages” lens in a Markdown file a
@snippetshows.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).
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).Fixed: a container memo stays colored as a comment past an
@endat another indent.The editor’s highlighting ended the memo at an
@endof any indent, though the memo’s lines run to an@endat its opener’s indent (§3.5 of 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).
Fixed: the Pages view draws a top section whose own page is beside its folder after Start.
guides/withguides.mdwas drawn among Start’s pages, unlike every sidebar; it’s now a top section withguides.mdfirst in it, and dragging it moves it among the top sections. See the editor.Fixed: a rename keeps every memo about what it renames attached.
Renaming a glossary term’s heading id rewrites the memos whose
aboutisterm:<id>, renaming a phrase rewrites those aboutphrase:<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. Anaboutwritten 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 renameand the editor’s rename alike (docs/content/writing/memo-files.md).Fixed:
available-same-in-every-lineno longer reports a patch version inside the oldest line.With
live = ["3.4", "3.5"],self-managed 3.4.2was reported as shown by no output, with a safe fix to writeself-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 as3.4or3.3, is reported (docs/content/versions/index.md).Fixed: a memo about a glossary term is placed on the term’s heading.
A memo file whose
aboutisterm:<id>was listed nowhere, itsquotewas refused as an entry ofascribe.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 aboutglossary.md#<id>is, inascribe outline, the editor, and thefixprompt; itsquoteis found in the term’s section; and a missing term is reported as one no glossary page has (docs/content/writing/memo-files.md).Fixed: an
unlistedpage is no longer reported as unplaced in the navigation.A page that’s
listing: unlistedis in no sidebar, so a section’spagesinascribe.navleaving it out isn’tnav-page-unplacedadvice any more, andascribe check --fixand 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).
0.3.2 (2026-10-16)
The language 14
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 asself-managed (ga 3.5, deprecated 3.6)in each line’s output, asascribe versions retirewrites the mark, where it showed the whole history before. See Build a site for each line.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-linein 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 stilllink-page-dropped. A link from the current line into an older one is advice,link-into-older-line, and a#idthe page it lands on doesn’t have is dropped, with advice,link-fragment-dropped. See Versions.Behavior change:
replacesis 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 namedreplaces. See A page that replaces another.[listing] older-lineskeeps every line but the current one out of an index.older-lines = "unlisted"makes each page of an older lineunlisted, so it’snoindexand left out of the sitemap andllms.txt; a page there that sets a looserlistingislisting-loosened-in-line. See 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].A live line whose support has ended is reported.
[releases.<target>.ends]gives each live line’s last day of support, andascribe checkreports a line whose date has passed as advice,release-line-ended, as of today in UTC, or the dayASCRIBE_TODAYgives, asreview-overduedoes. Seeascribe 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.1becomesself-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>].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, inarchivedwith 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>].[releases.<target>]says which lines are planned, ending, and archived.Beside
live, a target’s list takesplanned, lines that haven’t shipped, each newer than every live line;ends, the last day each live line is supported; andarchived, lines that have left the window, each with an optionalurlandendeddate.[releases] planned-labelsets the word a reader sees for a planned version. A line is in one list, in the order the lists mean (model-releases-order), andendsnames live lines only (model-releases-ends). See[releases.<target>].A version that hasn’t shipped reads “planned”.
A version in a line
[releases.<target>]lists asplannedis planned, and says so in every output: its availability readsSelf-managed (planned, 3.6)in the site output and the plain output, the availability element’s target has aplannedattribute, the page-levelavailablefrontmatter and the JSON output’s target haveplanned: true, and a project with[releases]gets each availability’stargetsin its JSON output. It stops being planned when its line moves tolive, with no other edit.[releases] planned-labelsets the word. See[releases.<target>].A build can publish an output for each live version of a target.
[releases.<target>] livelists the lines a reader can be on, such as["3.4", "3.5"], andversions = "<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.jsonsays which lines have each page, at which route. See Versions.An older line’s canonical address is the same page in the current line.
In a versioned build, a page of
site@3.4names the current line’s page as canonical, or the page that replaced it, followingreplaces; 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.Behavior change:
[versions] currentis removed;[releases]says which release is current.Each target’s current release is the newest line in
[releases.<target>] live, and a project that setscurrentgets 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-behindcompares each feature’s versions with its targets’ current lines, and reports nothing for a target with no list. See[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.jsongains the target’slabel, each line’sends, and the archived lines with theirurl. See What an older line says about itself.
The ascribe command 8
ascribe linksays 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.An archive can be searched, from a folder opened from a disk.
ascribe versions archivewrites 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 issearch-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 and the archive contract.ascribe versions archiverenders a line into a folder of HTML that needs nothing else.ascribe versions archive "self-managed 3.4" --to archive/3.4writes every page of the line with a statement of what it is, a sidebar, a contents page,all.htmlto print the whole line, andnoindexon 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 and the archive contract.ascribe modelshows release lines, andascribe renderputs an older line under its folder.The model’s
releasessection 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.4andascribe_rendergive the line’s route, such as/3.4/install/, with its links under the same folder. See Versions.ascribe versions releasemakes 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 tolive, and, where no build is versioned by the target, moves the old current line toarchived. 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-runwrites nothing. Seeascribe versions.ascribe versions listshows 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 changessays 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
replacesis one change, which names both.--format markdownwrites a first draft of release notes.ascribe versions retiretakes 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 toarchivedwith 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 buildno longer writes an archived line’s output, and removes the folder it wrote before. See Retire a line.
The editor 3
ascribe.projects.excludeleaves folders out of the search for projects.A list of glob patterns, from each workspace folder, for folders whose
ascribe.tomlisn’t a project, such as test fixtures:"ascribe.projects.exclude": ["tests/**"]. A project there isn’t listed and gets no language server. Folders thatfiles.excludehides are left out too, asnode_modulesalways is, and changing either setting looks for projects again. The message about the 50-project cap names the setting. See 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 runascribe observein.example-staleandexample-unobservedfollow the source’s observations andascribe.examples.tomlas they change on disk, without the page being edited, and offer Copy the command, which copies the line that runsascribe observethere; the editor never runs it. A source’sascribe.examples.tomlreports its problems as it’s edited, and completes its keys. Seedocs/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.
The language server 1
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 onreplacessays which page each line publishes, and Go to Definition onreplacesopens the replaced page.ascribe/targetsandascribe/previewlist each build’s lines. See The editor.
Astro and the elements 4
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, andAvailability.astroand the generated Zod schema carry a page’splannedtargets. See the element contract.VersionBanner.astrotells 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.astronow shows the lines without the page, unlinked, and the archived lines that have an address. See Publish with Astro.Versions.astrolists 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
replacesandreplaced-byinversions.json. See Publish with Astro.@ascribed/astroserves 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
lineandcurrent.@ascribed/astro/Versions.astrolists the lines that have a page,sidebar(nav, entries, { entry })shows the entry’s line, andastro devrebuilds every line. A permalink forwards to the current line, and only the current line’s Markdown andllms.txtare published. See Versions.
Agents 1
Each line of a versioned build has its own
llms.txtand 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/astropublishes them.ascribe agents index --build site@3.4indexes a line, under a heading that names it; without--build, the current one. See For agents.
Review 1
ascribe difflists 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.
Fixes 2
Fixed: review no longer shows a tested example’s snippet as changed when it isn’t.
With a source outside the project folder whose
includedoesn’t reach its root,ascribe diffread the base from git without the source’sascribe.examples.tomlandexamples.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. Seedocs/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: nextinstead, so the page badges them (docs/content/editor/index.md).
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, the content model, and Tested examples.
The language 8
A glossary term’s
linkto a heading its page doesn’t have is an error.The new diagnostic
glossary-link-target-missing(ASC203) is reported at thelinkinascribe.tomlwhen its#idisn’t a heading’s id on the page, written in it or in a fragment it includes, aspermalink-retired-target-missingis for a retired permalink’sto. Before, such a term was never linked, andascribe checksaid nothing. Seedocs/content/content-model/content.mdand decision #468.ascribe checkreports 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. Seedocs/content/content-model/index.mdand decision #455.Behavior change: a page names its content type in its frontmatter, with
type.In a project whose
ascribe.tomldeclares types, every page says which one it is, such astype: 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 takesfilesordefault: each is an unknown key (model-unknown-key), whose message says to name the type withtype:on its pages. Their checks, ASC051, ASC071, and ASC072, are removed. A project that declares no types has the implicit typepageand needs no change;typeis now a reserved frontmatter key everywhere. The Zod output gives each typetype: z.literal("<name>"), andschemais az.discriminatedUnion("type", …)of the types when there are several;contentTypesis gone. The JSON ofascribe model,ascribe templates eject, andascribe initis atschema_version2: a type has nofiles, andejectlists the pages that name no type asuntyped. The starter templates begin withtype:. Seedocs/content/content-model/index.mdand decision #450.ascribe checkreports 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 astitle: "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. Seedocs/content/templates/index.md.An example from another repository says what it was tested with.
ascribe sources fetchandupdatecopy the code’s repository’sascribe.examples.tomland itsexamples.observations/beside the files snippets use, whatever the source’sincludesays, andascribe.locklists them. A page then says what each example was tested with, as it does for a folder in the same repository, andexample-staleandexample-unobservedsay the fix is in the other repository and arrives with the next update.ascribe sources updatenames 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, andascribe observerefuses to run insources/<name>/. No snippet can addressascribe.examples.tomlor an observation. Seedocs/content/examples/tested.md.A code example can say what it was tested with, and
ascribe checksays when it changed since.A source’s
ascribe.examples.tomlsays which of its files are examples and the line of shell that tests them, andascribe observe, run in the code’s folder, runs it and, when it passes, records where it ran and a hash of each example inexamples.observations/default.toml. A@snippetof 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 newtestedattribute, and a line under it in the plain output. An example edited since reportsexample-stale, one no record lists reportsexample-unobserved, both advice, and a config that can’t be read reportsexamples-config-invalid. A project without the file sees no change. Seedocs/content/examples/tested.md.An example tested under several environments names them all.
ascribe observe --name node-22writesexamples.observations/node-22.tomlbesidedefault.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, andascribe sources updatesays 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 reportsobservation-file-missing, advice, at its line, and one in a newer format is oneexamples-config-invalidnaming theascribethat wrote it, while the others still count. A code block’ssnippetin the JSON output gainstested: the example’s status and the observations current for it. Seedocs/content/examples/tested.md.A content type can name its template and its guide.
[types.<name>]takestemplate, the page a page of the type starts from, andguide, a list of what to read before writing one, both files from the folderascribe.tomlis in, outside the content root. A file that isn’t there, or is in the content root, is an error inascribe.toml(model-type-file), and so is a template that doesn’t read as a page (model-type-template).ascribe modelshows both keys. Seedocs/content/content-model/index.md.
The ascribe command 9
ascribe templates eject --keep-typetakes a template for a type the project declares.It writes the template, its guide, Ascribe’s notes, and
templates.lockasejectdoes, and adds onlytemplateandguideto 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.ascribe templates ejectlists the pages that don’t name a type yet.They’re the ones to give
type:. A type it adds also declarestitleanddescriptionas the project’s first type to declare them does,inlineandrolewith them, when they’re strings there. The JSON gainscopiedon each type added, anduntyped. See the command reference.Behavior change: a diagnostic that’s removed is gone.
ascribe explainno longer answers forASC039,ASC051,ASC071, orASC072, 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 initstarts a project, writing its firstascribe.toml.In a folder with no
ascribe.tomlat or above it, it writes a small, commented one, with the one typepage, and makesdocs/;--templatestakes the six page templates too, asascribe templates ejectdoes. Where there’s a project, it names it and writes nothing.ascribe templates ejectwithout a project now says to run it. Seedocs/content/examples/sources.md.ascribe observe --checksays 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 observesays 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, andascribe modelnames each source’s observations and their environments. Seedocs/content/examples/tested.md.ascribe templates ejectwrites 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 ofascribe.tomlwith itstemplateandguide, and records what it copied intemplates/templates.lock. It checks everything first, and writes nothing when a type is declared already or a file is in the way.ascribe templates listshows the set. Seedocs/content/templates/index.md.ascribe templates ejectwrites 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’sguide, after their guide.templates.lockrecords the notes underascribe-files, apart from their files. Seedocs/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 ejectwith no name writes all six and declares their types, andascribe templates listshows them. Seedocs/content/templates/index.md.ascribe templates statussays what’s changed in the templates since the project’s copy.For each template a project took, it says whether the set this
ascribecarries 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 intemplates.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-codeexits with 1 when the set has changed or dropped one, and--format jsonhas a schema. Seedocs/content/examples/sources.md.
The editor 1
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.
Agents 2
The
new-pageprompt 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-pagegive the template with the title set, and the paths of the type’sguideto read before writing. The page goes wherepathsays, 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, agents guide).The rules for agents say what each page type is for.
A type’s new
purposekey, a string, says it in a few words, andascribe modelshows it. A typeascribe templates ejectadded says The Good Docs Project’s summary of it without one, fromtemplates.lock. When the rules don’t fit in 4,000 characters, the page types’ lines are cut first, and the cut namesascribe model --section types(content model reference, agents guide).
The docs and the site 9
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.
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 points at both, and so does Getting started.@ascribed/astrohas a reference page of its own, apart from the Astro guide.The Astro guide sets a site up; the
@ascribed/astroreference has the options, what the integration does inastro buildandastro dev, the head, sitemap,robots.txt, and response headers, review in the site preview, and styling. Whatascribe agents syncwrites for each target and each hook, and what a prompt says, moved from the Agents guide toascribe agents. 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-positioncheck 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’s guide, its troubleshooting, and the commands it runs 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 to Troubleshooting review, a symptom per heading. Page templates 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. Writingascribe.tomlby hand stays, as a short section, for a project that wants nothing written for it and for 0.3.0. See 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 ejectandascribe templates statushave a page of their own.They’re in JSON reports:
ascribe templates, beside the page ofascribe init,ascribe sources, andascribe permalinks, which they had made longer than the Web Documentation Delivery Spec’s page size.
Installing 1
ascribeinstalls without Node, with Homebrew or a script.brew install ascribed-dev/tap/ascribe, orcurl -fsSL https://ascribed-dev.com/install.sh | sh, which puts the release’s binary in~/.local/binafter checking it against the release’sSHA256SUMS, and never asks forsudo. On Windows, Install gives the PowerShell that downloads, checks, and installs the release’s archive.
Fixes 7
Fixed: a heading’s id
ascribe renamechanges is changed inascribe.tomltoo.A glossary term’s
linkand a retired permalink’stothat name the heading follow it, as the links in pages do, and so doesF2in the editor. Before, they kept the old id, and nothing reported it.ascribe.tomlkeeps its comments and order. Seedocs/content/command/questions.md.Fixed:
ascribe report agentsno longer says<main>is enough forcontent-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-missingandimage-attribute-missingshowed../settings.pngand../image.pngwhere a page writessettings.pngandimage.png, because the reference’s generator rebased links inside inline code. It now leaves code alone, so the reference agrees withascribe explain(the pages’ diagnostics).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, ascheckandbuilddo. A page it can’t find is named with both places it was looked for. Seedocs/content/command/questions.md.Fixed: a page
ascribe renamemoves takes its glossary links inascribe.tomlalong.So do the retired permalinks whose
tonames it, and the editor’s move does the same. Before, a glossary term’slinkkept the old path, so the model had an error and every later rename refused. Each keeps its#id, andascribe.tomlkeeps its comments and order. Seedocs/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-paritycheck passes, and a screen reader says what the badges are. The page looks as before. Seepackages/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-leftreported 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).
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, Reports, and Permalinks.
The language 16
ascribe checkreports 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.txtwould say the same of both. It’s advice, besidetitle-duplicate, and a project can raise it or turn it off in[checks]. See the diagnostics reference.A project can set how a folder’s, a type’s, or the whole site’s pages are listed.
[listing]inascribe.tomlhasdefaultfor every page,[listing.folders]for the pages under a folder, and[listing.types]for the pages of a content type, eachlisted,unquoted, orunlisted, so"internal/" = "unlisted"keeps a folder out of the sitemap andllms.txtwith 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.jsonsays where its listing came from, inlisting_from.@ascribed/astro’sHead.astronow asks search engines not to quote anunquotedpage (nosnippet). In the editor,listing:completes its values, and hovering it names the rule the page is under. See theascribe.tomlreference and the Astro guide.A page that isn’t listed has a Markdown version that isn’t either, on Netlify.
Each build writes
_ascribe/headers.jsonin the site output: the response headers each page’s Markdown version needs from the host,X-Robots-Tag: noindexfor a page that’sunlistedandnosnippetfor one that’sunquoted, and a canonicalLinkto the page on every one, so a search engine lists the page and not its copy. Withheaders = "netlify"in[consumer],@ascribed/astrowrites them into the built site’s_headers; a site with its ownpublic/_headerskeeps it, and the build fails with the lines it lacks.astro devsends the headers with each.mdfile. On another host, a deploy step reads the list. See theascribe.tomlreference and the Astro guide.A page can say it isn’t listed, and each build writes its page records.
A page’s frontmatter
listingislisted,unquoted, orunlisted; anunlistedpage is still built, and is left out ofllms.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 ownrobots.txtlines from the new[crawlers] lines, with the base path put before eachAllowandDisallowpath.ascribe checkadvises 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 theascribe.tomlreference and the output-layout contract.Behavior change A page in the site output says where its source is.
Every page’s frontmatter has
source, its path from the content root, such asguides/install.md, and the generated schema types it, so a layout’s “Edit this page” link isentry.data.sourceafter the repository’s address and the content root’s folder, whatever the build folder’s path. Behavior change:sourceis reserved on page types, asformattedis, so a content type that declares a field namedsourcefails withmodel-field-reserved; rename the field. See the Astro guide.ascribe checkreports 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’sintendedfrontmatter. See the diagnostics reference.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, orreview. It’snextinascribe check --format jsonandascribe explain --format json, in each diagnostic’sdatain the editor, and in the diagnostics reference. A new severity, advice, sits below a warning:ascribe checklists it last and counts it apart (0 errors, 2 warnings, 5 advice, andadvicein the JSON’ssummary), the editor shows it as information, and it never fails the check, even with--deny-warnings, or reaches an agent fromascribe agents hook.severitymay gain values without a newschema_version; treat one you don’t know as advice.ascribe.tomlgains[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).Behavior change 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 reservedintendedfrontmatter key for a page or a fragment, or an[[intended]]table inascribe.tomlfor a content model entry or an image. An acknowledged problem isn’t reported:ascribe checkandascribe buildcount it (1 acknowledged), and their JSON lists it with its reason under a newacknowledgedkey, withsummary.acknowledged, both left out when there are none;schema_versionstays1. 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 arereview-overdue, acknowledged in a page’sintendedfrontmatter, and the checks across the project, below. Behavior change:intendedis reserved frontmatter, so a content type or[fragments.frontmatter]that declares it is an error (model-field-reserved). See@intended.Behavior change 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-sizereports 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] limitsets another.heading-level-skippedreports 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-missingreports a fenced code block with no language, with a fix that marks ittext.page-description-missingandreview-overdueread the fields a content type marks with the newroleoption:role = "description"on a string field, androle = "review-date"on a date field, whatever the fields are called (model-field-role).review-overduechecks as of today in UTC, or ofASCRIBE_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 and 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]: 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] limitsays 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 inascribe.tomltoo. Each buttitle-duplicateis a review check: an orphan or an unused fragment is acknowledged in itsintendedfrontmatter, 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.Features left behind by a release.
[versions] currentinascribe.tomlnames 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 asnextwhose 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. Setcurrentto each release as it ships. See[versions].Permalinks.
@id {permalink=true}: rotate-keysmakes 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/astrogives each an address,go/<id>under the base path, a page that forwards to the heading without a script and isn’t indexed, inastro buildandastro dev.[consumer] permalink-pathpublishes them elsewhere, as a path from the site’s root (model-consumer-permalink-pathwhen it doesn’t start with/). A page has one withpermalink: cli-referencein 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 and the Astro guide.What a permalink’s link carries.
A permalink’s address takes the content model’s dimensions as parameters:
/go/weave-config/?pm=pnpmopens the section with thepnpmtab 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.jsonlists the dimensions and their values,@ascribed/elementsexportschoose(dimension, value)for a script to make a reader’s choice, and@ascribed/astro‘sMissingPermalinkcomponent explains, on a site’s 404 page, an address under the permalinks’ path that names nothing. See 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 areason, and optionallyto, 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 inascribe.permalinks, which gains[aliases]and[retired]tables; a retired permalink is no longer reported as removed. In the editor, the quick fix onpermalink-removedretires it with a placeholder reason, an error until it’s replaced.verifyaccepts an alias, and fails on a retired name, giving the reason. New diagnostics:model-permalinks,permalink-alias-target-missing,permalink-name-conflict, andpermalink-retired-target-missing. The site output’s_ascribe/permalinks.jsonlists aliases and retired permalinks, andremovedfor those with nowhere to lead. See Moving, aliasing, and retiring.Prose, checked with Vale.
[checks.vale]inascribe.tomlruns Vale 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 aprosediagnostic at its place in the source, one in a phrase’s text at the{key}. Start frompreset = "quiet", Ascribe’s own selection of rules that are almost never wrong (Ascribe.RepeatedandAscribe.Typos, both advice), with nothing to download or write, or pointconfigat 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 checkchecks with--vale, or always within-check = true, and its JSON gives the rule asrule. A Vale that isn’t installed is oneprose-not-checkedadvice.ascribe vale ejectwrites the preset out as your own configuration. Add.ascribe/to your.gitignore. See Prose, through 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.lockpins, because it was moved or deleted there,ascribe checksays that, and how to fix it, where it said to runascribe sources fetch, which can’t help. The update pull request quotes the new message.ascribe sources fetchandupdaterecord those files inascribe.lock, under a new key,missing, andascribe sources statusshows them asnot_at_pin. The lock’sversionstays1; a lock withmissingcan’t be read by an earlier release. Seeascribe.lockand the copies.
The ascribe command 23
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--fixare about what’s shown, and the concise list’s “and N more” command keeps the filter.--summaryends with the command for the code it counts most. The MCP toolascribe_checktakescode. See 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 renderprints the same text, and its JSON’stexthas it too; the HTML comment it wrote above a switcher is gone. A build that leaves one arm is unchanged. See the variants guide and theascribe renderreference.ascribe report linksholds 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;0reports 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’slinksgainsprevious_run,new,resolved, andunconfirmed, andschema_versionstays1. The scheduled job in The project report keeps the history withactions/cachetoo. Deleting.ascribe/history/costs one noisier report, and never a missed problem.ascribe report linksdoesn’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;0asks about every address on every run, and--recheckasks 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’slinksgainsasked,reused, andoldest_answer, withcheckedtheir sum andschema_versionstill1. The scheduled job in The project report keeps the answers withactions/cacheand runs daily. Deleting.ascribe/costs time and nothing else.ascribe report pageslists 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-listedlists 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.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 and
ascribe explaindescribe.ascribe reportsays problems where it said findings, and its JSON’s counts, at the top and inproblems, aresummary, as inascribe check’s JSON, where they werefindings. The promptascribe check --format promptand 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 --fixapplies the safe fixes.Every fix
ascribe checkmarkssafe(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; anunsafefix never is. With paths, only the fixes of what counts for them. The JSON lists what was applied asfixed. See Applying the safe fixes.ascribe modelshows sources, andagents syncsays who reads what.ascribe modellists the project’s sources, with their folders, the files they make readable, and the repository of one in another, and--section sourcesshows them alone; the JSON has asourcesfield.ascribe agents syncsays beside each file it writes which agents read it, and when Claude Code reads none of the skill, that--target claudewrites 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"(oruint32,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": 0where it can’t be negative.Behavior change:
--format summaryis--format markdown.On
ascribe drift,ascribe sources update, andascribe report, the format that writes Markdown for a CI job’s summary, a pull request’s description, or an issue’s body is namedmarkdown, andsummaryis no longer accepted. A workflow that runs one of them with--format summarychanges it to--format markdown.ascribe check --summaryis unchanged. Seeascribe drift.A command run above your projects names them.
Where there’s no
ascribe.tomlat 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.Commands that answer questions.
Six new commands tell an author, or an agent, what it would otherwise guess, and change nothing.
ascribe explain ASC036says what a diagnostic means and how to fix it, with a checked example for the diagnostics people meet most;ascribe modelshows 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; andascribe render <page> --build <name>shows a page as that build’s readers see it. Each takes--format json, described by a JSON Schema.ascribe --helpstarts with examples. See the command reference.Rename from the command line.
ascribe renamedoes what the editor’s rename does, for an agent or a script:ascribe rename phrase:cloud hostedrenames 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 with1when what it names doesn’t exist. See the command reference.Check one file, or text that isn’t saved.
ascribe check docs/guides/install.mdreports 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-buildruns 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 concisewrites one line per diagnostic, for an agent, and--summarycounts 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_versionstays1. In the editor, a diagnostic’s code links to its entry. See 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’sdocsincheck‘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 have their codes as anchors,#ASC036, and the commands’ headings in the command reference havecli-and the command’s name,#cli-sources-update.A committed list of permalinks.
ascribe permalinks syncwritesascribe.permalinksbesideascribe.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 checkand 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, andsyncnever drops a name, so running it can’t hide a removal.ascribe permalinks sync --checkwrites nothing and exits with 1 when the list is behind, for CI.ascribe permalinks listshows each permalink and whether the list has it, andascribe 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 and Checking a product’s links.Behavior change: headings inside a variant name it in the plain-markdown output.
When a
@variantgroup 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.problemscounts what the check finds by check, by file, and by kind of next step, and lists every acknowledgement with its reason;inventorycounts the pages by content type and by owner, and lists the overdue reviews, the pages nothing links to, and the entries nothing uses;buildslists what each build leaves out that another keeps, and why;linksruns lychee, which you install, on every external link, and reports each broken one (link-external-broken, to review, acknowledged with@intendedabove its block) and each that moved permanently (link-external-moved, with a fix that writes the new address) at the link; andagentsruns 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 reportmakes no request itself: the programs it runs do.--format markdownwrites Markdown for a CI job or an issue,--format jsoncapped lists described by a JSON Schema, and--format promptone prompt about a section’s problems for an agent.--exit-codefails on problems, and on a section that couldn’t run. lychee’s cache is turned off whatever the project’slychee.tomlsays, since a cached answer forgets that a link moved.[checks.links]inascribe.tomlnames lychee’s command and the hosts to leave out, and a content type’s newrole = "owner", on a string or enum field, is whatinventorycounts pages by. See The project report, with a weekly GitHub Actions job.A faster
ascribe diffandascribe drift.diffcompares 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.drifttakes 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 jsonis described by a JSON Schema, generated from the code that writes it. See the JSON report contract.The
jsonoutput’s pageformatis"ascribe-page", where it was"tessera-page", the project’s working name.Its
schemaVersionstays1. A tool that checks the value needs the new one. See the JSON output.A broken link is offered the page it meant.
ascribe checkgives a link to a page that doesn’t exist, to a heading its page lacks, or to a fragment the targetsascribe linklists as closest, as fixes markedunsafe, 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 checkandascribe buildend with one form of summary that names the builds checked and gives all three counts, aschecked 12 files for every build, reported on 1: 1 error, 0 warnings, 0 advice, orfor buildand the build’s name with--build, and a diagnostic with no fix to name shows its fix advice asHelp:, as the JSON’shelpdoes.ascribe linkgives the empty form too,[](keys.md#rotate-keys), which shows the same title.ascribe rendersays 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, andascribe explainwith no code says--listlists them all. The command reference gives the exit codes ofagents rules,agents skill,agents prompt,agents hook, andsources statusas a table, as for the other commands. The text output isn’t a contract; a script that reads the summary line should readcheck’s JSON instead.
The editor 9
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 checkoutstarts 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 checkchecks every build. See 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 Aon 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, editingascribe.tomlin place with its comments and layout; a rename that changes other files shows them in the refactor preview first. See 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.tomlis 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 decisionsascribe buildmakes. See The Ascribe sidebar, The status bar, and 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 awindow/logMessageat 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 checkover 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.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.
F2renames a dimension value too, a phrase rename reaches frontmatter fields and@snippet {phrases=true}files, andF2on something that can’t be renamed says why. See Navigation and 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.
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.defaultFormatterunder[markdown]in the folder’s.vscode/settings.json. See 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.
The language server 1
The language server’s log lines, in the editor’s output panel, start with
ascribe-lsp:, where they started withtessera-lsp:.
Astro and the elements 7
@ascribed/astrowrites a page’s head, the sitemap, androbots.txtfrom 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 islisting: unlisted; itstitleproperty is a function from the page’s title and its record to the whole title. The site gets onesitemap.xml, at Astro’sbase, 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 newsitemap: { exclude }option, and onerobots.txt, at the host’s root, with each project’s[crawlers] linesand the sitemap’s address, inastro buildandastro dev. A site with its ownpublic/robots.txtorpublic/sitemap.xmlkeeps it, a site whosebaseisn’t/gets norobots.txtand the lines its host’s needs in the build’s output, and a site with nositegets no sitemap and a warning. The layoutnpx @ascribed/astro initwrites includesHead.astro. See the Astro guide.Behavior change
@ascribed/astrogives Astro the site’s address.When
astro.config.mjsleaves outsite,base, ortrailingSlash, the integration takes it fromascribe.toml’s[consumer](site,base-path,trailing-slash), so the address is written once, and a preview built for its own address changes onlyascribe.toml. A setting the Astro config gives must still agree, as before. Withprojects,siteandtrailingSlashcome from the first entry whose project sets them, andbasestays the site’s. Behavior change: Astro can’t tell an integration an unset setting from its default, so withbuild,base: "/"now becomesbase-path, andtrailingSlash: "ignore"becomestrailing-slash. A site with a home page at/in front of one project’s docs lists the project inprojectsinstead (projects: [{ name: "docs", build: "site" }], andname: "docs"in its collection) to keepbaseat/. See the Astro guide.Head.astrowrites 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, andog:typefrom the same record, arel="alternate" type="text/markdown"link to the page’s Markdown version when the build publishes one and the page isn’tunlisted, and the page’s breadcrumbs asBreadcrumbListstructured data. Two new properties carry what the site knows:name, the site’s name, forog:site_nameand, on the page at the host’s root, the site-name structured data; andcard, the page’s social card, for theog:imagetags. Each page’s record in_ascribe/pages.jsonhas itsbreadcrumbs: the page at each folder of its route, by title, down to the page itself.astro devwarns once about a tagHead.astrowrites that the layout writes too. See the Astro guide.A starter for an Astro site.
npx @ascribed/astro initwrites 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 fromsidebar(), 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.A home page in front of the docs, and several projects in one Astro site.
@ascribed/astroaccepts a[consumer] base-pathunder Astro’sbase, such as/docs/under/, and publishes the files pages link to,llms.txtand each page’s Markdown, and.ascribe/dev.json’s address at the base path; a base path outsidebasestill fails the build.ascribe({ projects: [{ name, build, project }] })builds several projects into one site, each under its own base path, withascribeCollection({ 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 devrebuilds the project a change is in, andreviewtakes an entry’s name as well astrueorfalse. 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 andMissingPermalinklink 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 and Several projects in one site.Behavior change 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:The previous default colors
/* 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; }See theming.
@ascribed/astrodeclaressatteriand@types/hastas optional peer dependencies.Its
@ascribed/astro/satteriand@ascribed/astro/rehypedeclarations import types from them, so a project that type-checks those declarations (skipLibCheck: false) needs the one it imports, which a Sätteri orunified()processor already brings.
Agents 11
ascribe agents indexwrites a compact index of your docs for your product’sAGENTS.md.One line a page, grouped as
llms.txtis, 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--pagescopies 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.A project says what each kind of AI crawler may do, in
[crawlers].training,search, andon-requestare each"allow"or"disallow". A disallowed purpose writes one group inrobots.txtnaming every crawler of that kind Ascribe knows, from their operators’ own pages, withDisallow: /;allow, and leaving a purpose out, write nothing, so those crawlers stay under the lines for every crawler. The page records’robotsgainspurposesandgroups, and@ascribed/astrowrites 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-disallowednow reads, for each search crawler, the one group that applies to it, so it reports a page that isn’t listed whensearch = "disallow"or a group naming a search crawler keeps it from being fetched, andcrawlers-disallowed, advice, says what disallowingsearchoron-requestcosts. The reference lists each crawler with its operator’s statement, dated and linked (theascribe.tomlreference, the Astro guide).The skill covers more, and the plugin’s
/ascribe:new-pagetakes a request.The Ascribe skill says that
ascribe agents rulesprints a project’s rules when there’s noAGENTS.md; listsascribe diffandascribe 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-pagetakes what the page is for, and has the agent choose the type fromascribe modelbefore it runs the prompt. The plugin’s description says what each part is for.Instructions for AI coding agents.
ascribe agents syncwrites your project’s rules into the files agents read on their own, fromascribe.toml:AGENTS.mdbeside 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’sAGENTS.mdwhen 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 claudeadds an import ofAGENTS.mdtoCLAUDE.mdand the skill for Claude Code,--target claude-rulesand--target copilotwrite rules loaded only for your pages, and a target whose files exist is kept up to date.--checkfails when a file is out of date, for CI. Run at the root of a repository with several projects and noascribe.tomlof its own, it syncs each of them.ascribe agents skillprints the skill, and@ascribed/cliships it asskills/ascribe/. See Agents.Docs AI agents can read.
With
agents = truein[consumer], each build publishes what agents read, as the Web Documentation Delivery Spec 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/astropublishes it with the site and serves it inastro dev. It needssite(model-consumer-agents-site).ascribe checkadds two pieces of advice you can set in[checks]: a description too long for its line inllms.txt(description-too-long), and anllms.txtstill too long when split (llms-section-large). The element contract gains<ascribe-for-agents>. See 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 checkcommand to finish with. The newascribe.agents.promptTargetsetting 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 promptwrites the same prompt for an agent in a terminal. The language server answers a new request,ascribe/agentPrompt. See Prompt your agent.An MCP server.
ascribe mcpruns a Model Context Protocol 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, andascribe_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 arenew-page,fix, andreview.ascribe agents promptprints the same prompts,ascribe agents rulesprints a project’s rules, andascribe agents skill references/directives.mdthe skill’s directive reference. For the tools,ascribe fmt --format jsonlists the edits that format each file (with--check, without writing them), andascribe diff --pages-onlyleaves the block-level changes out of the JSON. See The MCP server.Agents in VS Code get Ascribe’s tools.
In a workspace with an Ascribe project, the extension offers
ascribe mcpto VS Code’s agents as an MCP server, with nothing to set up. It adds three tools of its own:#ascribe_editor_problemsgives the language server’s problems of a file or project asascribe check --format jsonwrites them, including unsaved edits and an edit made a moment before, for the editor’s build; and, while review is on,#ascribe_review_threadslists the pull request’s open threads with the pages that show each, their comments quoted as data, and#ascribe_review_changesthe 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 theirdata, and@ascribed/review/githubhasbuildThreadsList, fromopenThreadsListin@ascribed/review/prompt. See Agents in VS Code.Hooks and a plugin for agents.
ascribe agents hook claude-code,codex, orcopilotis the command an agent’s hook runs: after each file it writes, it tells the agent the errors in that file, at most 10, asascribe check <file> --editor-buildfinds 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=offturns it off).ascribe agents sync --with-hookwrites 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 and The plugin.Ascribe for Copilot’s cloud agent.
ascribe agents sync --cloudwrites what Copilot’s cloud agent needs on GitHub: steps in.github/workflows/copilot-setup-steps.ymlthat 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--agentwritten 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.--checkcovers the new files. See GitHub Copilot.An agent in VS Code runs the binary the editor runs.
When
ascribeon 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 runsascribeand 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/agentPrompttakes an optionalprogram, the command its prompt’s commands start with, and so does@ascribed/review’sPromptProject, for the prompts about review comments. See VS Code.
Review 3
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.promptTargetsays, and in the site preview, which copies them. The HTML report has Copy prompt for each page.ascribe diff --format promptwrites the prompt about the change, andascribe diff --format prompt <page>the one about a page or a fragment. The report’s JSON data gives each page itsprompt, andascribe/agentPrompttakespageChangesandfragmentReach.@ascribed/review/promptbuilds the prompts about threads, and an overlay host may offerpromptAgent. See In review.One page’s changes in a terminal.
ascribe diff docs/guides/rollouts.mdshows what changed on that page, in each build: every changed block as it was and as it is, with the words that differ marked asgit diff --word-diffmarks 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, soschema_versionstays1; with--format prompt, a page means what it did. Before, a page could be named only with--format prompt. Seeascribe 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/previewandascribe/review/changesanswers, have the formatted title beside the plain one. See code in a field.
The docs and the site 18
Behavior change 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 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.tomlreference 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, 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 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 permalinkelements; 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 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 says, in one table, which command writes each file Ascribe puts in a project, from
.ascribe/toascribe.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.
A guide to permalinks.
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 walks through a page that differs by platform: a dimension in
ascribe.toml, a@variantgroup, an@availablemark, and two builds, one with every platform and one for Windows alone, with what each keeps. Its project isexamples/platforms.A guide to working with agents.
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 and The project report, which it left out, and neither it nor getting started keeps a list of commands by hand. Getting started says that Astro is its second half, tells a new site from an existing one, and says that
[consumer] sitemust match Astro’ssiteand that the integration runsascribe buildfor you; the Astro guide ends with the site open in a browser. See Publish it with Astro.One recipe for checking a project in CI.
Getting started’s GitHub Actions workflow is the one
examples/docs-repositoryruns: it installs the latest release of@ascribed/cli, runsascribe check --deny-warningsandascribe fmt --check, and pins its actions by commit. Before, the two disagreed on how to installascribeand whether to check the formatting. See Checks in CI.Each diagnostic’s entry links to the page written about it.
In the diagnostics reference, every entry ends with Read more: and the section of the directive reference or the
ascribe.tomlreference it’s about, such as Links forlink-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 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, 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.
Installing 2
Behavior change:
@ascribed/clino longer exportsnativeBinaryPath.Use
resolveBinary(), orresolveBinary({ platform, arch })where you passed a platform and architecture; it returns the same path. There is no alias.Smaller downloads.
The
ascribebinary 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.
Fixes 14
Fixed: permalinks that differ only in case, such as
Setupon one heading andsetupon 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.
Fixed:
ascribe fmtno 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 checkreports it (source-unreadable), andfmtexits with 2. It was formatted and written where the link led. Seeascribe 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 servergithub-enterprise.urinames.A remote on any other host gets no sign-in, and the page preview says why.
@ascribed/review/github’stokenTransportaccepts 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.Fixed: in the editor, a link outside the docs is clickable only when it’s a web or email address (
http,https, ormailto).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
ascribetoo. See the editor guide.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.
Fixed:
ascribe buildwrites 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.jsonaren’t written through a link either. See the output-layout contract.Fixed: with no git repository around a project, the editor reads a source only inside a folder of the workspace it opened, and
ascribe mcponly inside the folder it was started in; a source outside ismodel-source-outside-repository, which now says to open the folder that holds it.ascribe build,check, and the other commands read a source whereverpathleads, as before. See sources.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.Fixed: the language server, when told a folder was created, and
ascribe fmtgo into each folder once, by where it really is, asascribe checkdoes.Fixed: reading a repository runs nothing the repository supplies.
Every
gitAscribe 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 within-check = true, and say the prose wasn’t checked; the agents’ hooks run Vale only when[checks.vale] commandis a name on thePATH; and the editor asks once a session before it runs acommandthat’s a program in the project. See Prose, through Vale.Fixed: a page has one name across the commands: its path from the folder you ran the command in, as
ascribe checkprints it.ascribe link’sfrom, in its text and JSON, names the page that way, however--fromgave it.ascribe permalinks retire --page,--under, and--totake that path, or one from the content root, and still writeascribe.tomlfrom the content root;--page docs/keys.mdwas 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, andascribe check --format prompt,ascribe diff --format prompt, andascribe agents promptfrom 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.Fixed: a binary built from anything but a release or the nightly canary, such as from
main, says so inascribe --version, with its commit, as the canary does:ascribe 0.2.0+dev (42f00096ceb0), notascribe 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.
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.
The language 6
@snippetputs 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 checkreports a missing address, file, or region, and tags that don’t balance (snippet-addressand five more). See@snippetand Drift.[sources.<name>]inascribe.tomlnames a folder of code outside the content that snippets may read, withincludeandignorepatterns.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>].A source in another repository:
git(andbranch) in place ofpath. The files snippets use are copied intosources/<name>/and pinned to a commit inascribe.lock. Both are committed with the docs, so checking, building, diffing, and drift never reach the other repository.ascribe checkreports copies that don’t match the lock or the snippets (lock-invalid,source-copy-changed, and three more). See a source in another repository and Docs kept apart from the code.Behavior change 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@availableline’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 thanavailableis an error (attribute-unknown-key). See table rows.Behavior change A page title can show code.
inline = "code"on a content type’sstringfield 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 keyformatted(entry.data.formatted.titlein an Astro layout). The JSON output hasformattedtoo. Behavior changes: a type can no longer declare a field namedformatted(model-field-reserved), and in a field that turnsinlineon, a backslash before punctuation is an escape and is removed, as in Markdown. See code in a field.A link can name a heading from a fragment the page includes:
setup.md#prerequisitesworks whenprerequisitescomes from a fragmentsetup.mdincludes. It was an error (link-id-in-fragment, now retired). See@include.
The ascribe command 7
ascribe diffshows 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 jsonadds each block’s source lines, for tools. It needs onlygit. 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. Seeascribe diff.ascribe diff --format htmlwrites 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 and the report in CI.
ascribe driftlists 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 summarywrites Markdown for a CI job’s summary,--format jsonis for tools, and--exit-codeexits 1 when an example broke, or changed while its page didn’t. Seeascribe driftand Report the pages whose examples changed.ascribe build --emit site --anchorsmarks each block of the site output with its source file and lines (data-ascribe-source, anddata-ascribe-viafor a block from a fragment).Without the flag, the output is unchanged. See the site-render contract.
ascribe sources fetchmakes the copies of sources in other repositories matchascribe.lock.ascribe sources updatemoves 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 statusshows each pin and copy, offline.fetchandupdaterungitwith its own credentials, and are the only commands that reach the network. Seeascribe sources.Each command’s
--helpdescribes its options in the command reference’s words.The command reference’s option lists are generated from it, and
--helpends with a link to its section on the docs site, https://ascribed-dev.com.-hshows 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.
The editor 7
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.
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.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.
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.scrollPreviewWithEditorandascribe.preview.scrollEditorWithPreviewturn 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’s settings table is generated from it.
Astro 8
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. Thereviewoption (on by default) turns the app off.astro buildoutput has no anchors, overlay, or review code. See Review in the site preview.astro devwrites its address to.ascribe/dev.jsonin the project, for the editor’s site preview, and removes it when it stops.The
anchorsoption turns source anchors on:"dev"inastro devonly,truealways.A code block’s title, such as a
@snippet’s, shows above it, in a<figure class="code-title">with a<figcaption>.The
codeTitlesoption turns it off. See code block titles.@ascribed/astro/Availability.astrorenders a page’savailablefrontmatter as the element library’s badge:<Availability available={entry.data.available} />.The integration copies the generated schema into
.astro/integrations/_ascribed_astro/schema.tsafter 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.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.
The language server 1
It answers
ascribe/review/setBaseandascribe/review/changes, andascribe/previewwithreview: trueadds the page’s changes, for editors other than VS Code.
Installing 2
The Linux binaries run on glibc 2.28 or later, instead of 2.39, so
@ascribed/cliinstalls 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 thenexttag:npm install @ascribed/cli@next @ascribed/astro@next.It comes with no promise of stability, and its
ascribe --versionnames the commit it was built from. Releases still publish underlatest.
@ascribed/review 1
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.
0.1.1 (2026-10-02)
The language 1
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.
The editor 6
A workspace can hold several Ascribe projects.
Every
ascribe.tomlis a project with its own language server, and a file belongs to the nearestascribe.tomlabove 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.A project’s server starts the first time one of its files is opened or previewed.
The new setting
ascribe.startServers(onDemandorall) can start every project’s server when the workspace opens instead. The Problems panel lists only the projects whose server is running;ascribe checkcovers 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
ascribeolder 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.
The language server 3
Behavior change Behavior change for editors other than VS Code:
ascribe lsplooks forascribe.tomlonly 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 oneascribe lspper project, rooted at the folder that holds itsascribe.toml(Other editors).It logs the project it uses, or why it has none.
ascribe lsp --configis an error (exit code2).The server takes its project from the editor’s workspace folders, and ignored the option.
0.1.0 (2026-10-01)
The first release. It implements version 0.1 of the Ascribe specification.
No behavior changes.
The language 4
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.
The ascribe command 6
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;--checkfor CI.ascribe lsp: the language server.126 diagnostics, each with a code (
ASC001toASC126) and a stable name.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-NOTICESfile with the license text of the crates in it.
The editor 2
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
ascribewhen the project installs one, and the binary bundled with it otherwise.
Astro 2
@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 inastro dev.@ascribed/elements: the web components the site output uses (<ascribe-note>,<ascribe-tabs>,<ascribe-availability>, and the rest), styled with CSS custom properties.