Skip to content

Frontmatter and Build Rules

The documentation site has two kinds of expectations:

  • Enforced rules — checked by make pr-checks (and the equivalent CI gates). A violation fails the build and CI rejects the pull request. These are listed under each section below with an 🔒 Enforced badge.
  • Conventional rules — agreed by the authors and reinforced by review, but not mechanically checked. These carry a 🤝 Convention badge. Violating them is not a CI failure but is a review comment.

Both serve the same goal: keep the site readable by humans and automated agents (LLM assistants, MCP clients, search bots). The enforced rules cover the most consequential gaps; the conventional ones cover the rest.

Most rules are inexpensive to follow once you start from a template under about/templates/. When you copy a template you inherit the required frontmatter and the canonical section structure for free.

Every Markdown or MDX page under src/content/docs/ carries a YAML frontmatter block. The schema lives in src/content.config.ts and is enforced by Astro at content-collection load time.

FieldTypeConstraint
titlestringStarlight base; required
descriptionstringRequired, 40–300 characters, single sentence
authorstringRequired, non-empty

The schema in src/content.config.ts is checked by Astro at content-collection load time, which make pr-checks runs via the preview build. A missing description (or one shorter than 40 / longer than 300 characters), or an empty author, fails the build with an InvalidContentEntryDataError. CI catches the same violation through the same path.

FieldTypeNotes
tagsarray of stringsDefaults to []; populate it
domainenum (see below)Optional; aligns with the directory
maturitystub / draft / review / published / proposedDefaults to published
lastVerifiedISO dateOptional; the date a human last reviewed the page
supersedespathOptional; points to an earlier page this replaces

domainproduct, domain, system, vision, roadmap, process, technology, legal, decisions, about, notes.

The convention is to use maturity: published once the page is written and substantively complete. Pages do not need long stays at draft — the build does not care, but consumers of llms.txt and agents reading the site will treat published as the authoritative signal.

The description field is the most consequential. It appears in:

  • <meta name="description"> for search engines.
  • The curated /llms.txt index (when the page is promoted).
  • /llms-small.txt and /llms-full.txt (where it routes retrieval).
  • Site search results.

Compose it as a single sentence (no newlines, no double quotes, no backticks):

  • 80–200 characters is the sweet spot. The schema allows 40–300, but 80–200 captures the page without bloating the index.
  • Lead with the noun phrase: “Reference for…”, “Specification of…”, “Playbook covering…”, “Index of…”. Avoid “This page describes…” and “A guide to…”.
  • Capture what the page documents and (when relevant) for whom — a developer, an operator, an agent.
  • Use US English spelling.

A few examples drawn from the corpus:

Cedar policy language reference covering core concepts, decision logic, and entity model used as the basis for the Arda Multi-Enterprise Authorization Model.

Operator runbook for sending email through the Free Kanban Tool Postmark server, covering token resolution, From-address constraints, SDK and curl recipes, and troubleshooting.

Bitemporal persistence model tracking valid time and transaction time to support historical queries, auditing, and corrections without data loss.

Starlight already renders the frontmatter title: as the page H1. A second # Heading in the body produces a duplicate visual H1 and confuses Markdown parsers. Start the body with ## Section, not # Title.

This is not currently enforced by make pr-checks — the build will succeed with an in-body H1 — but every existing page follows the rule and reviewers flag deviations.

Code fences carry a language tag 🤝 Convention

Section titled “Code fences carry a language tag 🤝 Convention”

Every fenced code block should declare a language. Common tags in the corpus:

Language familyUse tag
Shell sessionsbash
TypeScript / JavaScripttypescript, tsx, javascript
Kotlinkotlin
JSON / YAML / TOMLjson, yaml, toml
HTTP request / responsehttp
SQLsql
Markdown samples (nested)markdown
Prose / file paths / pseudocodetext

Untagged fences (``` alone) degrade syntax highlighting and lose information for agents that route fenced content through language-specific tools. The convention is to use text for prose-like blocks rather than leaving the tag off. This is not currently enforced by make pr-checks; it is a review-time expectation.

PlantUML diagrams are paired with a textual summary 🤝 Convention

Section titled “PlantUML diagrams are paired with a textual summary 🤝 Convention”

Agents rarely consume images, even SVG-rendered diagrams. Every PlantUML block should be preceded by a 1–3 sentence prose summary that captures the same semantic content the diagram conveys:

The sequence below shows the upload flow from the browser through the BFF to S3. The browser obtains a presigned URL, uploads directly to S3, then notifies the BFF on success.
```plantuml
@startuml
title Upload flow
actor Browser
participant BFF
participant S3
Browser -> BFF: GET /upload-url
BFF --> Browser: presigned URL
Browser -> S3: PUT (presigned)
S3 --> Browser: 200
Browser -> BFF: POST /upload-complete
@enduml
```

Not enforced by make pr-checks. See the PlantUML guide for additional styling conventions.

Links between documentation pages must include the .md extension. The remark-resolve-md-links plugin rewrites them to the rendered URL at build time. Linking without the extension passes the dev server but fails the preview-base link checker that runs as part of make pr-checks (Lychee).

Anchors are preserved through the rewrite: [Section 3.2](../foo.md#section-32) resolves correctly.

.mdx files are parsed as both Markdown and JSX, so a few Markdown idioms collide with JSX expectations. The conventions:

  • Use <table> HTML for non-trivial tables in .mdx files. Simple Markdown tables can work, but cells that contain |, JSX expressions, or multi-line content cause the parser to misalign rows. The homepage index.mdx uses Markdown tables in the showcase section because the cells are simple — when in doubt or when a cell needs structure, switch to <table>.
  • No named HTML entities (&ge;, &times;) — use numeric entities (&#8805;, &#215;) or direct Unicode.
  • No bare curly braces in prose — wrap {foo} in backticks to disambiguate from JSX expressions.
  • Use <Aside> instead of :::tip shorthand in .mdx files. The triple-colon admonition shorthand is the .md convention; in .mdx import and use the Starlight <Aside> component for consistency.

The full list of MDX gotchas is in the repo’s knowledge-base/mdx-gotchas.md.

Start from a template before writing from scratch. Templates already model the required frontmatter, the canonical sections, and any per-artifact rules.

The catalogue is at about/templates/ — design documents, decision logs, runbooks, content pages, ADRs, user stories, and more. Each template’s own description tells you when to use it.

There are two layers of automated checks. make pr-checks is the local gate you run before every push; CI runs the same gate plus a few GitHub-Actions-only checks that cannot live locally.

Runs in order:

  1. Astro preview build — content-collection schema validation runs here. Missing or out-of-range description, missing author, an unknown maturity enum value, etc., all fail at this step with an InvalidContentEntryDataError.
  2. Link checker (Lychee) — runs against the preview-base build (/documentation/...). Catches internal links that omit the .md extension or point at non-existent files.
  3. Smoke tests (Playwright) — runs against the built site.

The cycle is fast (~30 seconds for an incremental build) and matches what CI runs for the same checks.

These run in GitHub Actions and cannot be triggered locally:

  • changelog-check — verifies the pull request description carries a ## CHANGELOG section with at least one valid category, and that the diff does not include CHANGELOG.md unless the manual-changelog label is set. Workflow: .github/workflows/changelog-check.yaml.
  • review-required-gate — enforces the CODEOWNERS / REVIEW-REQUIRED policy.
  • Build production / deploy — runs only on the merge-queue group event, not the PR head.

If a CI-only check rejects your PR, the fix is to update the pull request description (for changelog-check) or to ensure the right approvals are in place (for review-required-gate).

The most common failures and how to fix them:

ErrorFix
description: Required or string-length errorAdd or extend the description field in the page’s frontmatter
author: RequiredAdd author: "<your name>" to the frontmatter
maturity: Invalid enum valueUse one of stub / draft / review / published / proposed
Link checker: Cannot find file …Add .md to the internal link, or fix the relative path
CHANGELOG.md change rejected by CIMove the entry to a ## CHANGELOG block in the PR description and revert the file edit (or add the manual-changelog label if a hand-edit is genuinely required)
  • Authoring guide overview — the starting point for new contributors.
  • PlantUML guide — diagram syntax, summaries, and validation.
  • Cross-site linking — when to link across sibling repos.
  • Templates index — start from a template.
  • Agent Access — how the same content is consumed by AI assistants and MCP clients.
  • knowledge-base/frontmatter-schema.md — repo-side notes (visible from a clone, not on the site) on the schema and backfill scripts.