Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/adr-0046-doc-guide-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
---

docs(metadata): ADR-0046 — add the `doc`-type authoring guide
(`content/docs/guides/metadata/doc.mdx` + index/sidebar) and cover package
docs in the objectstack-ui skill. Documentation and AI-skill content only;
no package release.
159 changes: 159 additions & 0 deletions content/docs/guides/metadata/doc.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
title: Doc Metadata
description: 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. Only `title` is read:

```md
---
title: User Guide
---

# Getting started with CRM
...
```

The display title resolves in order: frontmatter `title:` → the first
`#` heading → the doc `name`.

## 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_]*$` and be **namespace-prefixed** |
| `label` | `string` | — | Display title (from frontmatter `title`) |
| `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

Doc names live in the same flat, instance-global namespace as the URL.
The console route is **single-coordinate** — `/docs/<name>`, with no
package or app prefix — so a doc has exactly one URL no matter who links
it.

That only works if names don't collide, so the build requires every doc
name to be **namespace-prefixed**:

```
src/docs/crm_user_guide.md ✅ package namespace is "crm"
src/docs/user_guide.md ❌ bare name — collides across packages
```

This is the same uniqueness rule the platform already applies to other
named metadata (ADR-0048). Two packages that both ship `user_guide` would
overwrite each other on the registry key and fail at install; the prefix
makes the collision impossible.

> **Routing model.** The viewer is platform-level: one global
> `/docs/<name>` route resolves any doc regardless of which app you came
> from. Anything that wants to *link into* docs — an app's navigation, a
> registry package page, an agent's answer — does so with that same URL.
> Per-app or per-package URL nesting is explicitly **not** the model
> (it would give one doc many URLs and break cross-references).

## Cross-references

Link to a sibling doc with a plain relative Markdown link:

```md
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

```md
---
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](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0046-package-docs-as-metadata.md)
for the full design rationale and rollout phases.
8 changes: 8 additions & 0 deletions content/docs/guides/metadata/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ Define access control and data visibility.
| :--- | :--- | :--- |
| [**Permission**](./permission) | Permission sets — object CRUD, field security, tab visibility | `*.permission.ts` / `*.permission.yml` |

### Documentation Layer

Ship package documentation that travels and versions with the package.

| Metadata Type | Description | File Convention |
| :--- | :--- | :--- |
| [**Doc**](./doc) | Package documentation pages — flat Markdown, rendered at `/docs/<name>` | `src/docs/*.md` |

## How Metadata Works Together

```
Expand Down
3 changes: 2 additions & 1 deletion content/docs/guides/metadata/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"flow",
"workflow",
"validation",
"permission"
"permission",
"doc"
]
}
94 changes: 91 additions & 3 deletions skills/objectstack-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
name: objectstack-ui
description: >
Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt),
Apps (navigation), Pages, Dashboards, Reports, Charts, Actions. Use when
Apps (navigation), Pages, Dashboards, Reports, Charts, Actions, and
package Docs (`src/docs/*.md`). Use when
the user is adding `*.view.ts` / `*.app.ts` / `*.dashboard.ts` /
`*.action.ts` files or designing a Studio-rendered UI surface, including
`*.action.ts` / `src/docs/*.md` files or designing a Studio-rendered UI
surface, including
dataset-bound dashboard/report widgets. Do not use for: data schema (see
objectstack-data), interactive screen flows /
wizards (those are `*.flow.ts` with `type: 'screen'` — see
Expand All @@ -18,7 +20,7 @@ metadata:
author: objectstack-ai
version: "1.1"
domain: ui
tags: view, app, page, dashboard, report, chart, action, widget
tags: view, app, page, dashboard, report, chart, action, widget, doc
---

# UI Design — ObjectStack UI Protocol
Expand All @@ -37,6 +39,8 @@ App navigation, Dashboards, Reports, and Actions.
- You need a **dashboard** with widget grids.
- You are adding **reports** (tabular, summary, matrix, chart).
- You are configuring **actions** (buttons, URL jumps, screen flows).
- You are writing **package documentation** (`src/docs/*.md`) that ships
with the package and renders at `/docs/<name>`.

---

Expand Down Expand Up @@ -616,6 +620,90 @@ export const LeadDetailPage: Page = {

---

## Docs — Package Documentation (ADR-0046)

A **Doc** is a page of package documentation shipped *as metadata*. You
author plain Markdown in a flat `src/docs/` directory; `os build`
compiles each `*.md` into a `doc` item that travels inside the package
artifact and renders in the console at `/docs/<name>`. Docs are also the
grounding the AI assistant reads about a package.

```
src/docs/
crm_index.md → doc "crm_index" → /docs/crm_index
crm_user_guide.md → doc "crm_user_guide" → /docs/crm_user_guide
```

### Authoring rules (each enforced by `os build`)

1. **Flat directory.** Every `.md` lives directly in `src/docs/`;
subdirectories are a build error. Flatness is what keeps links stable
— a reference resolves by basename, never by path.
2. **Namespace-prefixed filename.** The filename stem becomes the doc
`name` (`^[a-z][a-z0-9_]*$`) and must start with the package namespace
(`crm_…`). Names share one flat, instance-global space with the URL, so
a bare `user_guide` would collide across packages and fail at install
(ADR-0048).
3. **Title** resolves: frontmatter `title:` → first `#` heading → `name`.
4. **Pure Markdown.** CommonMark + GFM only, plus heading anchors, fenced
code highlighting, and GitHub alerts (`> [!NOTE]`, `> [!WARNING]`, …).
**MDX and image references are rejected at build time** — docs are
publisher content rendered inside the platform (no authored code across
the trust boundary; images await a content-addressed asset service).
5. **Cross-references** use plain relative links — `[overview](./crm_index.md)`.
The console rewrites `*.md` → `/docs/<target>` (anchors preserved);
broken same-package links fail the build.

### Routing model — platform-level viewer, opt-in entry

The viewer is **platform-level**: one global `/docs/<name>` route
resolves any doc regardless of which app you came from. The URL is
**single-coordinate** — no package or app prefix — so a doc has exactly
one URL. Do **not** design per-app or per-package doc URLs; that gives one
doc many addresses and breaks cross-references.

To surface a doc inside an app, add a navigation item that **links into**
that global URL. There is no dedicated `doc` nav-item type yet, so use a
`url` item pointing at `/docs/<name>`:

```typescript
navigation: [
{ id: 'nav_help', type: 'url', url: '/docs/crm_user_guide',
label: 'User Guide', icon: 'book-open' },
]
```

A platform-level "Documentation" portal (browse/search all docs by
package) is a later, additive concern — author-side, nothing to model now.

> **Dynamic content.** Don't try to embed a live component (a flow
> diagram, a record table) in a doc — there is no inline SDUI. Link to the
> metadata by URL; the platform renders the live view, the doc points at it.

### Example

```md
---
title: CRM Overview
---

# CRM

Manages accounts, contacts, and opportunities.

> [!TIP]
> New here? Start with the [user guide](./crm_user_guide.md).

| Object | Purpose |
| :--- | :--- |
| `crm_account` | Companies and organizations |
| `crm_contact` | People at an account |
```

In-repo reference: `examples/app-showcase/src/docs/showcase_docs_guide.md`.

---

## CRM UI Blueprint (Metadata-First)

Use this CRM-style structure as the canonical UI assembly reference:
Expand Down