Shoalrun documentation framework
For agents
Start with https://www.shoalrun.com/llms.txt for the documentation index, then read the Markdown pages it links. Skip to article. Documentation index. Read this page as Markdown.
Reference

Markdown metadata

Public field projection, source paths, profiles, precedence, and required values.

Updated
Read as Markdown

The build publishes the page metadata guide as Markdown alongside its HTML page. That .md file starts with a YAML header containing selected values from the source page header. Source headers can grow long and contain review notes or other details that do not help an agent read the page. Selecting a small set of useful fields keeps the agent's context focused and avoids exposing internal metadata in that header. Compare the two custom values in that guide's source:

docs/content/frontmatter.mdx
agent:
  intent: Help authors understand and configure page metadata.
editorial:
  reviewNote: Check the frontmatter examples before release.

The generated .md header includes agent.intent but leaves out editorial.reviewNote:

docs/dist/author/frontmatter.md
---
id: guide.frontmatter
title: Page metadata
summary: Set page identity, publication, and presentation fields before publishing.
description: Learn how page metadata, project defaults, and publication choices
  shape the site and its Markdown output.
type: reference
audiences:
  - authors
  - maintainers
canonical: /author/frontmatter
agent:
  intent: Help authors understand and configure page metadata.
---

The agent.intent value appears because this site's docs/docs.config.yaml selects it for reference pages. The relevant settings are:

docs/docs.config.yaml
outputs:
  markdown:
    metadata:
      format: yaml-frontmatter
      fields:
        id: frontmatter.id
        title: frontmatter.title
        summary: frontmatter.summary
        description: frontmatter.description
        type: frontmatter.type
        audiences: frontmatter.audiences
        subjectLifecycle: frontmatter.subjectLifecycle
        canonical: publication.canonicalUrl
      omitNull: true
      byType:
        reference:
          addFields:
            agent.intent: frontmatter.agent.intent

Each entry under fields names an output key on the left and its source on the right. Here summary and description stay separate, while audiences comes from the page's intended-reader labels. The byType.reference rule adds agent.intent only for pages whose type is reference. subjectLifecycle is absent from the example output because that guide has no subjectLifecycle value and omitNull is true.

The review note is absent because its declaration sets public: false and the output config does not select it. This controls only the generated YAML header: text written in the page body still appears in the published Markdown and HTML.

Keep this header short. Include the fields an agent needs for the page, and leave out long descriptions or notes that take up its context.

Projection settings

format must be yaml-frontmatter to write a header. It defaults to none, so a projection with fields but no format writes no header and reports no error. format: none removes the header while leaving the full page body in Markdown.

A field source is either frontmatter.<path> or one of these publication values:

Source Value
publication.channel The page's publication channel.
publication.version The page's publication version.
publication.language The page's language.
publication.canonicalUrl The page route, such as /author/frontmatter. It is site-relative, not an absolute URL.

Built-in public fields can be selected directly. A custom field needs a declaration with public: true. The protected roots access, seo, markdownMetadata, variables, tocDepth, private, internal, card, and displayText cannot be selected under any name. With omitNull: false, a missing value fails the build instead of being skipped.

Layers apply in order. The base settings come first, then byType.<type>, then a named profile, then the page's own override. Each layer accepts format, fields, addFields, removeFields, and omitNull. fields replaces the inherited fields; addFields merges into them; removeFields drops output names and fails if a name is missing.

Define named profiles under profiles, then select one from a page:

docs.config.yaml
outputs:
  markdown:
    metadata:
      format: yaml-frontmatter
      fields: { id: frontmatter.id, title: frontmatter.title }
      profiles:
        minimal:
          removeFields: [title]
A page's frontmatter
markdownMetadata: minimal

A page can also write the override as a mapping, such as markdownMetadata: { profile: minimal, addFields: { language: publication.language } }. The example API project under framework/example/ uses the same declaration and projection pattern.

See the generated frontmatter reference for the local project field inventory. See cards and collections for card inheritance and collection filters, and related reading for authored reading paths.

Put a field on every page

List the field under fields at the top level of outputs.markdown.metadata. Top-level fields apply to every page. A byType rule adds fields only for pages of that type, so use it for extras and not for a field that every page needs.

To require the value as well, declare the field with required: true. A page that lacks it then fails the build, and the error names the file:

docs.config.yaml
frontmatter:
  fields:
    agent.intent:
      type: string
      description: The reader task this page helps an agent complete.
      public: true
      required: true

outputs:
  markdown:
    metadata:
      format: yaml-frontmatter
      fields:
        id: frontmatter.id
        title: frontmatter.title
        summary: frontmatter.summary
        type: frontmatter.type
        canonical: publication.canonicalUrl
        agent.intent: frontmatter.agent.intent

Each page then writes its own value:

A page's frontmatter
agent:
  intent: Help a reader install Acme.

Every Markdown copy carries the field, one copy for each publication the page is in. Without required: true, a page that lacks the value is skipped when omitNull is true and fails the build when it is false. A shared default from frontmatter.defaults also satisfies required: true.

Published Markdown size

Each page record in documents.json and search-index.min.json includes markdownBytes, markdownTokens, and markdownTokenEncoding. Bytes count the complete published .md file as UTF-8, including its metadata, navigation links, reader labels, and final newline. Tokens count that same text using cl100k_base; special-token spellings are treated as ordinary text. This count is specific to that encoding, not a universal model budget.

Use the record's markdownUrl and publication coordinates to select the file. The manifest's digest remains the SHA-256 of those exact Markdown bytes. Counts live outside the Markdown itself, avoiding a self-referential size field. The compact index still omits article text and reader segments.

Declare sections that can be read alone

Search recommends a section only when its heading uniquely matches the query and the author has listed that exact heading text in the page's standaloneSections frontmatter:

standaloneSections:
  - Try the page controls

Review the section together with the introduction that every section file repeats. All required setup, definitions, warnings, and steps for the section's stated purpose must be present there. A procedure that depends on configuration elsewhere in the page should use a full-page read. For example, the reader-views section about marking content needs facet configuration from another section, so this site leaves it undeclared.

This field is page-specific; site defaults and enforced values cannot set it. Each value must match exactly one top-level level-two heading's text, including case. Missing or duplicate headings fail the build, even on short pages. The compiler checks identity and size eligibility; it cannot verify the author's claim about completeness. Re-review declarations when the content or its prerequisites change. Do not mark every section merely to obtain smaller reads.

Omitting the list, using an empty list, or reading an older search index makes search use full pages for those headings. Size eligibility still applies to declared headings. Undeclared resources remain available through outlines and explicit section reads for callers that already have the context. Fetch-only consumers should require standalone: true before automatically choosing a section. A declaration does not guarantee relevance to every question; use the full-page fallback for broader tasks.

Fetch one section

Each page record in documents.json now includes a sections outline. Entries describe top-level level-two headings in the normalized content tree. A section includes its nested headings and components through the next top-level heading of the same or higher level. Headings inside tabs, reader sections, lists, or disclosures do not become separate resources. Section files are generated only when the full page has at least 2,000 cl100k_base tokens and the complete section, including repeated context, uses at most half the page’s bytes and tokens. Other pages and sections use the full Markdown page; their outlines can be empty.

Fetch the entry's markdownUrl for a complete section file. For example, the agent-friendliness page has a section at /publish/agent-friendliness.sections/try-the-page-controls.md. These are real static resources: adding #heading to the full page URL does not make an HTTP request smaller. The compact search index includes eligible section titles, URLs, sizes, and standalone declarations, so fetch-only agents can select them without downloading the full manifest. The short llms.min.txt index lists full pages and nests only author-declared standalone section links, with exact sizes and a Full page fallback. A fetch-only agent can use those text links without reading JSON or calling a search tool.

Each file repeats the parent's projected metadata, title, publication dates, navigation, introductory content, and fictional-content notice when present. It then includes the selected section with all nested reader labels and code fences. Top-level current-page RelatedReading prerequisites and next steps omitted by the section boundary are included as a labeled Page reading path, without duplicating links already in the selected content. Nested or other-page relationship views remain in their original scope; see reading paths. Shared reference definitions are retained. Local fragment links point back to the full parent Markdown page. Section files are complete representations, not byte slices of the parent.

Outline field Meaning
standalone Present as true only when the author declares the section self-contained. Absence means search recommends the full page.
id, title, depth Generated section ID, heading text, and level two.
markdownUrl Fetch URL and citation for the section resource.
parentMarkdownUrl, parentDigest Full page URL and SHA-256 digest of its published Markdown.
digest SHA-256 of the complete section file.
markdownBytes, markdownTokens, markdownTokenEncoding Exact section size, using the same UTF-8 and cl100k_base rules as page metadata.

Section IDs use normalized lowercase title text, ASCII letters and digits, and hyphens, limited to an 80-character stem. A title with no usable characters uses section. Collisions receive suffixes beginning with -2 in document order. These IDs are not permanent: renaming, inserting, or reordering headings can change them. Cite the stable page ID, publication coordinates, section ID, section URL, and both digests. The surrounding manifest supplies the page identity, publication, and build ID.

From the repository root, inspect and read the agent-friendliness example's sections with the local CLI:

node framework/dist/cli.js docs outline guide.agent-friendliness --site docs/dist --max-tokens 10000
node framework/dist/cli.js docs section guide.agent-friendliness try-the-page-controls --site docs/dist --max-tokens 10000

For ordinary retrieval, start with docs search and follow the result’s read instruction; the outline command is optional. Search returns a recommended markdownUrl and a pageMarkdownUrl fallback. Both commands above return JSON. Select --channel, --version, and --language when a page has multiple publications. The outline and section must fit the requested token budget in full; increase --max-tokens when they do not. Section reads verify the file digest and its link to the parent record. MCP clients can use get_outline and get_section for the same operations. Rebuild older sites to add section resources.

Reference while working