Docs Authoring Conventions
This guide covers the conventions for adding, moving, and formatting TomoriBot docs pages.
Source of Truth
Section titled “Source of Truth”Write docs content in repo-root docs/.
apps/docs/src/content/docs is a junction/symlink to docs/ for Astro. Do not treat it as
a second copy. apps/docs/src/pages is for custom Astro routes and redirects, not ordinary
Markdown docs.
Routes
Section titled “Routes”docs/foo/bar.mdbecomes/foo/bar/.docs/foo/README.mdbecomes/foo/.- A top-level folder appears in the main sidebar when it has a
README.mdorREADME.mdxwith nosidebar.hidden: true. - Nested folders are discovered recursively under visible top-level folders.
Frontmatter
Section titled “Frontmatter”Use simple YAML frontmatter. The sidebar builder understands strings, numbers, booleans, and one nested level.
Page example:
---title: "Entry Point and Initialization Flow"sidebar: order: 4---Folder README example:
---title: "User Guides"sidebar: groupLabel: "User Guides" order: 90---Common fields:
| Field | Use |
|---|---|
title |
Page title and default sidebar label |
description |
Meta description for search engines and link previews (optional; see SEO below) |
sidebar.label |
Sidebar-only page label override |
sidebar.groupLabel |
Folder/group label when set on that folder’s README |
sidebar.order |
Manual ordering among siblings |
sidebar.hidden |
Hide a page or top-level folder from the sidebar |
aiGenerated |
Set false to opt out of the docs app disclaimer, if enabled |
Filenames and folder names are URL slugs. Keep them short, lowercase, and stable. Use
title and groupLabel for human-facing names.
The aiGenerated disclaimer and translations
Section titled “The aiGenerated disclaimer and translations”The disclaimer note is injected at render time by the MarkdownContent.astro override in
apps/docs (never written into markdown files), and it is locale-aware. Which notice a
page shows:
| Page | EN source aiGenerated |
Notice shown |
|---|---|---|
English page, flag unset or true |
n/a | English “Disclaimer” (AI drafts) |
English page, flag false |
n/a | None |
Japanese page under docs/ja/, flag unset |
unset or true |
Japanese 免責事項 (AI drafts + translation) |
Japanese page under docs/ja/, flag unset |
false (human-written) |
Japanese 翻訳について (AI translation of a human page, links to the English version). Hidden by default; see below |
Japanese page with its own aiGenerated: false |
any | None (translation has been human-reviewed) |
The 翻訳について notice is reader-facing only when DOCS_SHOW_TRANSLATION_NOTICE=true is
set in the docs build environment; it defaults to hidden. Review state is tracked in
frontmatter either way: a docs/ja/ page without aiGenerated: false is an unreviewed
machine translation (grep -rL "aiGenerated: false" docs/ja/ lists them).
Practical rules: machine-translated pages must NOT carry aiGenerated: false (delete the
line when translating a page that has it). After a human reviews and corrects a
translation, set aiGenerated: false in the translated file to clear its notice.
The docs site handles most SEO automatically:
- Meta descriptions: when a page has no
descriptionfrontmatter, the route middleware (apps/docs/src/routeData.ts) derives one from the page’s first prose paragraph at build time. A hand-writtendescription:always wins, so add one when the opening paragraph does not summarize the page well. Keep it under ~160 characters. - First paragraphs matter: because they become search snippets, open each page with one or two plain sentences that describe the page, before any heading, list, aside, or component.
- Internal pages: everything under
docs/wiki/is markednoindexand stays out of search engines. Put maintainer-only records there. - robots.txt / sitemap:
apps/docs/public/robots.txtadvertises the auto-generatedsitemap-index.xml. No per-page action needed.
Internal Links
Section titled “Internal Links”Always link between docs pages with root-absolute URLs (leading /, trailing slash),
e.g. [Manual Setup](/self-hosting/manual-setup/) or with an anchor
[…](/self-hosting/manual-setup/#optional-extras-the-manual-full-install).
Do not use relative ./sibling links from a normal (non-index) page. Pages deploy in
directory format (/self-hosting/manual-setup/), and the build does not rewrite relative
links, so ./setup-wizard from that page resolves to
/self-hosting/manual-setup/setup-wizard — a 404. Only README.md index pages, which deploy
at their directory root, may use ./child links.
Moving Pages
Section titled “Moving Pages”When moving docs:
- Use
git mvfor tracked files when possible. - Update links in
docs/,README.md,.github/, and release notes when relevant. - Update
docs/README.mdwhen section structure changes. - When old URLs should keep working, add both an entry in the
redirectsmap inapps/docs/astro.config.mts(meta-refresh fallback page) and matching 301 rules inapps/docs/public/_redirects(real redirects on Cloudflare, listed with and without the trailing slash). - Run the docs build:
cd apps/docsbun run buildFor code/config changes outside Markdown, also run root bun run check and bun run lint.
Cards and Images
Section titled “Cards and Images”Use Starlight built-ins for most docs:
CardandCardGridfor compact reference content.LinkCardwhen the whole card is a navigation target.LinkButtonfor call-to-action rows.
Use custom image cards only for landing or task-router pages where screenshots are worth maintaining.
Docs-site static images must be present under apps/docs/public unless the Astro build
explicitly bundles or copies them. Root assets/img is useful for repo and README assets,
but it is not automatically deployed to docs.tomoribot.app.