Release lines
What a release line is, what each line’s output holds and says about itself, how links go between lines, and what a site, the editor, and agents get from them.
For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.
A product a team supports in more than one version needs docs for each. Ascribe makes them from one source, the marks the pages already have, in four levels a project opts into one at a time:
- No versions. The default. Nothing to configure, and nothing changes.
- One site that knows its versions.
[releases]lists a target’s lines: the live ones, the planned ones, when a line’s support ends, and the ones that have left. One site still, with checks that the marks name versions the list knows, and “planned” worked out instead of kept by hand. - A window of live versions.
versions = "<target>"on a build gives one output for each live line, with links from one line to another, and an older line that says what it is and where the current one is. Publish a site for each version sets it up. - An archive. A line rendered once more into a folder of HTML that needs nothing else, kept after it leaves the window. Export and retire a line makes one.
Lines
A line is a version as a project writes it, and holds every version that starts with it: 3.4 holds 3.4.0, 3.4.2, and 3.4.2.1. A line is live when a reader can be on it, planned before it ships, and archived once it has left; the newest live line is the current line. The [releases] reference has the keys and their rules, and what ascribe check reports about the versions the marks name once the list is there.
A phrase can follow the list: release = { line = "self-managed" } makes {release} the current line, and in an older line’s output of a versioned build, that line.
What each line has
A line keeps content whose available doesn’t name the target, and content that’s available for the target at some version in the line. Content that begins at 3.4.2 is in 3.4; so is content removed at 3.4.2, since it was there at 3.4.0. What isn’t in a line is removed from its output, as a filtered build removes it: a section, a table row, or a whole page. The current line also keeps content marked for a version after it, shown as it is in any badge build, such as “3.6+”.
What’s left is marked as a badge build marks it, but a version at or before the oldest live line isn’t shown, since every reader has it: with 3.4 and 3.5 live, self-managed 3.3 shows the target alone, self-managed deprecated 3.2 shows “deprecated”, and self-managed 3.4.2 shows its version. A history keeps the state in effect when the window opens and what follows: with 3.5 and 3.6 live, self-managed (preview 3.3, ga 3.5, deprecated 3.6) shows self-managed (ga 3.5, deprecated 3.6). A build that isn’t versioned shows them all.
A versioned build’s availability is badge: a build that filters is already one version of one target. With one live line, the build writes what it would without versions, apart from versions.json, and older marks no longer show their versions. Every address in an older line’s output starts with its folder: links between pages, the files they link to, and the canonical addresses in its page records.
Links between lines
A link to a page that isn’t in a line still goes somewhere, in three steps:
-
The page that takes its place. A page that’s rewritten under a new name says which page it replaces, with
replacesin its frontmatter:--- title: Configure the agent available: self-managed 3.5 replaces: configure-legacy.md ---A link to either page lands on whichever one the line has. Neither page links to the other by hand, and a link keeps its
#idwhen the page it lands on has a heading with that id, and drops it otherwise, withlink-fragment-droppedadvice. -
The page in another line. Otherwise the link leads to the page in the nearest line that has it, the closest newer line first. In 3.4, a link to a page new in 3.5 goes to
/docs/webhooks/, and the line follows the link: “Webhooks (3.5)”. A site styles it by itsdata-ascribe-release-lineattribute; the plain output writes the line after the link. -
Nowhere. When no live line has the page, the link is
link-page-dropped, as in any build.
A link from the current line to a page only an older line has is advice, link-into-older-line: it’s a reader on the newest docs sent back to an older line. replaces has its rules, and ascribe link says where a link goes in each line.
What an older line says about itself
A reader who lands on an older line from a search should know it’s older, and find the current one:
-
The canonical address of a page in an older line is the same page in the current line, or the page that replaced it there, following
replacesas a link does. A page the current line has neither of keeps its own. So a search engine indexes the current line’s page, and an older line’s page only when it’s the one place the page is. -
The sitemap lists a page when it’s listed and its canonical address is its own: each current page once, and an older line’s page only when the current line has no such page.
-
The page’s Markdown opens with a line that says which version it is:
Documentation for Self-managed 3.4. Support ends on 31 January 2027. The current version is 3.5: https://docs.example.com/docs/install/.
The current line’s says its own version alone. The date comes from
ends, and the sentence is left out without one; the target is named by its label, or by[releases.<target>] labelfor a target of the implicitreleasedimension. -
A site’s banner says the same to a person:
VersionBanner.astrowith Astro, or the site’s own fromversions.json.
Nothing is added to robots.txt. An older line is kept out of an index by its pages, not by a rule over its folder, which would also stop a crawler reading the canonical link.
Keep the older lines out of an index
A project that doesn’t want its older lines found at all says so under [listing]:
[listing]
older-lines = "unlisted"
Every page of every line but the current one is then unlisted: it asks not to be indexed, and it’s left out of the sitemap and llms.txt. unquoted asks that they not be quoted. The rule only restricts, as every listing rule does: a page in an older line that sets a looser listing, such as listing: listed, is listing-loosened-in-line. A folder’s or a type’s rule still applies on top of it.
What the lines know about each other
Each ascribe build of a project with [releases] writes .ascribe/build/_ascribe/versions.json: for each versioned build, the target’s label, its lines, and for each page the lines that publish it, with its route in each, and the page it replaces or that replaces it. The output layout contract has its format.
A site that isn’t built with Astro has all a banner and a list of versions need in it and in each line’s page records (_ascribe/pages.json):
| To write | Read |
|---|---|
| “Documentation for Self-managed 3.4” | builds.<name>.label, and the line from lines[], the one whose base the page is under |
| “Support ends on 31 January 2027” | that line’s ends |
| The link to the current line | the page’s canonical in its record when it isn’t the page’s own address; else the base of the line whose status is current |
| A page’s other lines | pages.<path>.lines, following replaces and replaced-by |
| The lines that have left | the lines[] whose status is archived, and their url |
The Astro components
@ascribed/astro’s collection has every line’s pages: the current line’s with their usual ids, and an older line’s with the line in front, 3.4/install, so the route [...slug].astro gives each is where Ascribe’s links point. Each entry’s data has line and current.
<Versions entry={entry} />, from @ascribed/astro/Versions.astro, lists the lines, each a link to the page there, with the page’s own line marked aria-current="page". A page that replaces another lists the lines of the page it replaces too, and the other way around, so configure/ in 3.5 and 3.4/configure-legacy/ link to each other. A line that has neither is shown unlinked, “(not in this version)”, and the archived lines that have a url follow, each linking to it, “(archived)”. It renders nothing for a build that isn’t versioned, or one with a single line to list.
<VersionBanner entry={entry} />, from @ascribed/astro/VersionBanner.astro, writes the banner on an older line’s pages, and nothing on the current line’s:
Documentation for Self-managed 3.4. Support ends on 31 January 2027. See this page for 3.5.
The last sentence links to the page in the current line, or the page that replaced it, and to the current line’s home otherwise, as “See the documentation for 3.5.” Each sentence’s words are a property, documentation, supportEnds, samePage, and home, with locale for the date, so a site can translate them; Versions.astro’s are label, missing, and archived.
The sidebar shows the pages of the page’s own line when the layout passes the entry: sidebar(entries, { entry }), or, with a site’s own list, sidebar(entries, { nav, entry }).
astro build and astro dev build every line, and astro dev rebuilds them all on a change. When the build publishes the outputs for agents (on by default with a [consumer] site), each line’s llms.txt and Markdown are published at its base path, /docs/3.4/llms.txt, with the response headers each line’s pages need. A permalink forwards to the current line, whichever lines have the page: it’s the address a product prints, and it should land on the newest docs.
In the editor, in review, and in the tools
The lines show wherever a build does, and a page that’s the same in every line shows once:
- The editor. Switch build lists a versioned build’s lines under it, the current line first, and the build lens and the preview follow the line you choose: in 3.4, a page new in 3.5 is dimmed whole, and the preview is at the line’s address,
/3.4/install/. The editor checks every line of its build, and a problem only some lines have says which, such as “(only in buildsite@3.4)”. A hover on a mark’s version says which line it falls in, and a hover onreplacessays which page each line publishes. The editor has the rest. - Review.
ascribe difflists a changed page once with the lines it changed in: “in every line” for a page the lines share, “only in 3.4” for a change inside a section only 3.4 has. The HTML preview lists the page once too, and switches between the lines it changed in. Diff has the details. - The tools.
ascribe resolve install.md --build site@3.4resolves the page as that line publishes it, at its address under/3.4/, and so doesascribe_resolve.ascribe modelhas areleasessection: each target’s lines, with their status and the build that is each.
For agents
When the build publishes the outputs for agents, on by default with a [consumer] site, each line has its own outputs for agents, in its plain output:
llms.txtlists the line’s pages, and its heading names the line:# Loom (Self-managed 3.4). An older line’s opens with a line pointing at the current line’s: “This is an older version. The current version is 3.5: llms.txt.”- Each page’s Markdown opens with the line that says which version it is, above.
ascribe agents index --build site@3.4writes the compact index of a line for a product’sAGENTS.md, its heading naming the line asllms.txt’s does. Without--build, it’s the current line’s.
A line kept out of an index with [listing] older-lines = "unlisted" lists nothing in its llms.txt.