Doc Metadata
Ship package documentation as metadata — flat src/docs/*.md files compiled into the manifest and rendered in the console
Doc Metadata
A Doc is a single page of package documentation. You write plain
Markdown files in a flat src/docs/ directory; os build compiles each
one into a doc metadata item that ships inside the package artifact and
renders in the console at /docs/<name>. Docs are also the grounding the
AI assistant reads when answering questions about your package.
Unlike content/docs/ (which builds a standalone website), package docs
are package source — they travel with the package, version with it,
and are addressable as metadata like any Object or View.
Authoring: flat src/docs/*.md
The normal way to author a doc is to drop a Markdown file in src/docs/:
src/docs/
crm_index.md → doc name "crm_index"
crm_user_guide.md → doc name "crm_user_guide"The filename stem becomes the doc name. There is no directory taxonomy
and no ordering file — the flat layout is what keeps cross-references
stable (a link resolves by basename, never by path).
Frontmatter
A leading YAML frontmatter block is optional. title and description
are read:
---
title: User Guide
description: How to create, organize, and complete records in CRM.
---
# Getting started with CRM
...The display title resolves in order: frontmatter title: → the first
# heading → the doc name. The optional description is a one-line
summary the docs portal renders under the title (it travels in the doc
list, so the portal shows summaries without fetching each body).
Doc Properties
When a *.md file is collected, it becomes a doc item with this shape:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | ✅ | Machine name (snake_case), from the filename stem. Must match ^[a-z][a-z0-9_]*$. The build lint still requires a namespace prefix for authoring hygiene (ADR-0048), though doc resolution is package-scoped |
label | string | — | Display title (from frontmatter title) |
description | string | — | One-line summary for listings (from frontmatter description) |
content | string | ✅ | The Markdown body |
You can also declare a doc programmatically with DocSchema in a stack
definition's docs array, but the src/docs/*.md path is the convention
— it gives you native editor and GitHub preview for free.
Naming and routing
A doc renders under /docs/<name> (in dev os serve mounts each
collected doc there). The build lint still requires every doc name to be
namespace-prefixed, as a same-package authoring-hygiene rule:
src/docs/crm_user_guide.md ✅ package namespace is "crm"
src/docs/user_guide.md ❌ bare name — lint error (docs/namespace-prefix)This is the same namespace-prefix lint the platform applies to other
named metadata for readable, globally-unique filenames. It is no longer
load-bearing for uniqueness, however: per ADR-0048, doc
resolution is package-scoped. The metadata registry key carries no
package coordinate, but each item retains its _packageId, so two
installed packages may each ship a doc with the same bare name and
coexist — neither silently overwrites the other.
Resolution model. Doc resolution is package-scoped (ADR-0048). The single-doc detail route resolves a name within a package id —
getItem('doc', name, packageId), surfaced as?package=<packageId>onGET /api/v1/meta/doc/<name>. A bare/docs/<name>link is best-effort (first match), while a caller that carries its package id resolves to its own package's doc. The earlier "one global URL per doc" framing from ADR-0046 was superseded by ADR-0048's package-scoped resolution.
Cross-references
Link to a sibling doc with a plain relative Markdown link:
See the [overview](./crm_index.md).The console rewrites *.md links to /docs/<target> (anchors are
preserved); in an editor or on GitHub the same link resolves natively.
Broken same-package links fail the build, so references can't rot
silently.
Markdown support
Docs render CommonMark + GFM through a sanitizing pipeline. Supported out of the box:
- Standard Markdown — headings, lists, tables, blockquotes, inline/fenced code, links.
- Heading anchors — every heading gets a slug id and a hover anchor,
so
#sectiondeep-links work. - Syntax highlighting — fenced code blocks are highlighted by language.
- GitHub alerts —
> [!NOTE],> [!TIP],> [!WARNING], etc. render as callouts.
Two things are rejected at build time, by design:
- MDX / embedded components. Docs are publisher-supplied content rendered inside the platform; executing authored code would cross a trust boundary. Markdown is data, not code.
- Image references. Images need a content-addressed asset service
(a later, additive concern). Until then,
fails the build rather than producing broken<img>tags in a tenant.
For dynamic content (a live flow diagram, a record table), don't try to embed a component — link to the metadata by URL instead. The platform renders the live view; the doc just points at it.
Example
---
title: CRM Overview
---
# CRM
The CRM package manages accounts, contacts, and opportunities.
> [!TIP]
> New here? Start with the [user guide](./crm_user_guide.md).
## Objects
| Object | Purpose |
| :--- | :--- |
| `crm_account` | Companies and organizations |
| `crm_contact` | People at an account |Saved as src/docs/crm_index.md, this compiles to a crm_index doc and
renders at /docs/crm_index.
Next Steps
- See the in-repo authoring reference in the showcase package:
examples/app-showcase/src/docs/showcase_docs_guide.md. - Read ADR-0046 for the full design rationale and rollout phases.