ObjectStackObjectStack

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:

PropertyTypeRequiredDescription
namestringMachine 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
labelstringDisplay title (from frontmatter title)
descriptionstringOne-line summary for listings (from frontmatter description)
contentstringThe 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> on GET /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 #section deep-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.

On this page