Skip to content

Commit 57ed5e3

Browse files
os-zhuangclaude
andcommitted
ADR-0046: package documentation as metadata
Docs become a manifest element compiled from src/docs/ (pure Markdown, no MDX, no images in v1; meta.json subset for ordering). Derived content (field dictionaries, permission matrices, state machines) is rendered from metadata, never authored or generated. Repo layout separates four doc lifecycles (src / delivery / internal / CHANGELOG). Defines minimal docs set per package type, registry/console/AI-assistant rendering surfaces, i18n via locale-suffix files with AI-derived translations, and machine quality gates for AI authorship. Piloted on os-tianshun-mtc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b10aa78 commit 57ed5e3

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# ADR-0046: Package documentation as metadata — `src/docs/` compiled into the manifest, derived content rendered not written
2+
3+
**Status**: Proposed (2026-06-12)
4+
**Deciders**: ObjectStack Protocol Architects
5+
**Builds on**: [ADR-0003](./0003-package-as-first-class-citizen.md) (package + versioned releases), [ADR-0016](./0016-studio-package-authoring-and-publish.md) (publish pipeline, `manifest_json` snapshot), [ADR-0019](./0019-app-as-consumer-unit.md) (App is the only consumer-facing unit), [ADR-0025](./0025-plugin-package-distribution.md) (artifact distribution, trust boundary), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI as primary author)
6+
**Consumers**: `@objectstack/spec` (manifest `docs` element), `@objectstack/cli` (collection, publish lint, `os docs lint|verify`), `@objectstack/console` (help center, contextual help, metadata views), cloud control plane (registry rendering, grants — implementation design lives in the cloud repo), `@objectstack/core` (kernel skips `docs` at load)
7+
**Pilot**: `os-tianshun-mtc` (delivery project; repo layout + authoring conventions validated there first)
8+
9+
---
10+
11+
## TL;DR
12+
13+
A package today carries every behavioral fact about itself — objects,
14+
fields, validations, flows, permissions — but not one sentence of *intent*:
15+
what it is, how its users operate it, how its admins run it. That prose
16+
lives in ad-hoc repo trees, wikis, and static sites that rot the moment
17+
metadata changes, and reaches customers through channels (file shares,
18+
hosted sites) that duplicate the registry's versioning and access control.
19+
20+
This ADR makes documentation a **metadata element**. Markdown under
21+
`src/docs/` compiles into the manifest exactly like objects and views do;
22+
the registry, console, and in-app AI assistant render it from there. Two
23+
disciplines keep it honest:
24+
25+
1. **Derived content is rendered, never written.** Field dictionaries,
26+
permission matrices, validation lists, state-machine tables are *views
27+
of metadata* — the platform renders them live; they must not exist as
28+
authored or generated files. The only legitimate generation is a
29+
**frozen export** (e.g. a confirmation PDF) bound to a released version.
30+
2. **Docs bind to what they change with.** Repo layout separates four
31+
lifecycles: `src/` (ships with the package), `delivery/` (bound to one
32+
customer's contract, never ships), `internal/` (engineering notes,
33+
never ships), plus root `CHANGELOG.md` (collected at publish).
34+
35+
```
36+
src/docs/ compiled into manifest.docs
37+
├── meta.json {"pages": ["index", "user", "admin"]}
38+
├── index.md what this package is (floor: required)
39+
├── user/
40+
│ ├── meta.json {"title": "...", "pages": ["lead", ...]}
41+
│ └── lead.md … how each role operates it
42+
└── admin/ … how to run and configure it
43+
CHANGELOG.md what changed per version (floor: required)
44+
```
45+
46+
---
47+
48+
## 1. Context
49+
50+
- The manifest schema (`packages/spec/src/kernel/manifest.zod.ts`) has a
51+
`description` string and nothing else documentation-shaped. ADR-0025
52+
defines how code and dependencies travel; nothing defines how *intent*
53+
travels.
54+
- Pure-element packages publish as a single self-contained JSON
55+
(`dist/objectstack.json``sys_package_version.manifest_json`,
56+
ADR-0016). Anything added to the manifest therefore reaches the registry
57+
**with zero registry schema changes** — the cheapest possible v1.
58+
- Delivery teams hand customers docs via static sites or files, rebuilding
59+
versioning (directories), identity (logins), and authorization (per-site
60+
auth) that the registry/grant system already provides. The cloud repo's
61+
publisher-catalog design (grants, share tokens) makes the duplication
62+
explicit: granting a package *should* grant its docs.
63+
- Field-reference documents generated into git rot by construction: they
64+
are caches of a render. The pilot project shipped a 26-page generated
65+
dictionary and deleted it within a day once this principle was named.
66+
- Going forward AI writes most documentation (ADR-0033 extends to prose).
67+
The scarce resource is no longer writing effort but a machine-checkable
68+
definition of "complete" and "too much".
69+
70+
## 2. Goals & Non-goals
71+
72+
### Goals
73+
74+
- A spec'd `docs` manifest element: collection rules, structure, syntax
75+
boundary, minimal set per package type.
76+
- Repo layout convention separating the four documentation lifecycles.
77+
- Platform rendering surfaces (registry page, console help center,
78+
contextual help, AI-assistant grounding) and their phasing.
79+
- Quality gates that make AI authorship safe: coverage floor, bloat
80+
ceiling, fact-checking, usefulness evals.
81+
- i18n model compatible with AI-derived translations.
82+
83+
### Non-goals
84+
85+
- **Delivery documents** (requirement traceability, solution confirmation,
86+
acceptance records). They bind to one contract, freeze at milestones,
87+
and must *not* auto-update with code. Convention: `delivery/`, outside
88+
the package. Their confirmation workflow is a cloud/process concern.
89+
- **Standalone product documentation sites** (e.g. docs.objectstack.ai,
90+
docs.objectos.app). Those are websites; `content/docs/` remains their
91+
convention. Package docs are package source, not a website.
92+
- **Internal engineering docs** (`internal/`, formerly `docs/` — renaming
93+
removes the perpetual "docs vs content/docs" ambiguity; the word *docs*
94+
appears in exactly one shippable path).
95+
- Rich media. v1 is text-only by design (§5).
96+
97+
## 3. Design
98+
99+
### 3.1 Collection (one rule)
100+
101+
`src/docs/**/*.md` plus per-directory `meta.json` compile into
102+
`manifest.docs`. Root `CHANGELOG.md` is collected at publish into the
103+
version record. `src/` already means "everything here ships"; docs simply
104+
join objects, views, and dashboards under that single boundary. The
105+
kernel/plugin-loader **skips or lazy-loads** the `docs` section — manuals
106+
must not occupy runtime memory.
107+
108+
### 3.2 Structure (closed set + explicit order)
109+
110+
- **First-level directory = docSet**, a *closed* enum: `index.md`
111+
(overview), `user/`, `admin/`. Closed means renderers know canonical
112+
order ("what it is → how to use → how to run") and display names without
113+
configuration, and AI cannot invent new trees.
114+
- **`meta.json`** (subset of the Fumadocs convention — exactly two fields,
115+
`title` and `pages`) fixes ordering and directory titles. Absent →
116+
alphabetical. Page title = first `#` heading; optional frontmatter may
117+
override.
118+
- Ordering deliberately lives in a dedicated, schema-validatable file. The
119+
alternatives each failed a long-term test: single-file-per-docSet mixes
120+
chapters and destroys diff locality; numeric filename prefixes break
121+
links on reorder; deriving order from `index.md` link order welds a
122+
machine contract onto human prose, so every copy-edit becomes a
123+
structure edit. **Structure and content stay physically separate** for
124+
the same reason `delivery/` is a directory and not a flag: separation
125+
beats convention, and a 3-line JSON the AI edits to reorder is a smaller
126+
blast radius than a prose file it must not accidentally reflow.
127+
128+
### 3.3 Syntax boundary (the two day-one prohibitions)
129+
130+
1. **Pure Markdown** (CommonMark + GFM; a small directive whitelist such
131+
as admonitions/mermaid may be added later). **MDX is forbidden**: MDX
132+
is code, and rendering publisher-supplied code inside the platform
133+
origin crosses the ADR-0025 trust boundary. Markdown is data; a
134+
sanitizing pipeline renders third-party packages safely.
135+
2. **No image references in v1** (publish lint rejects `![](…)`).
136+
Binaries in the artifact bloat installs; arbitrary external URLs break
137+
version immutability (a confirmed v1.3 whose screenshots silently
138+
change is not confirmed) and leak customer UI data to unmanaged hosts.
139+
v2 introduces a platform **asset service**: content-addressed,
140+
immutable, unguessable URLs (`…/blob/sha256-…`); the CLI uploads local
141+
images at publish and rewrites references; packages keep containing
142+
text only. Enforcing the ban from day one means zero legacy cleanup
143+
when assets arrive.
144+
145+
Everything else — docSet declarations beyond the closed set, audience
146+
config, binds — is **additive** and deliberately absent from v1. Only
147+
rules that are expensive to retrofit (syntax, images) are mandatory now.
148+
149+
### 3.4 Derived content is rendered, never written
150+
151+
Anything reconstructible from the manifest — field dictionaries, option
152+
lists, validation conditions, state-machine transitions, permission
153+
matrices — is a **metadata view**: the console renders it live
154+
(customer-readable schema browser, permission matrix view); the registry
155+
renders what package pages need. It must not exist as hand-written prose
156+
*or* as generated markdown committed to the repo (a cache in git, with a
157+
cache's lifecycle). Handwritten docs reference metadata via semantic links
158+
(`object://mtc_lead`) that renderers resolve; publish lint heuristically
159+
flags field-table blocks in prose.
160+
161+
**The frozen-export exception**: at confirmation milestones a tool may
162+
render metadata + docs into a versioned artifact (PDF) for signature.
163+
Freezing is the requirement there; the export is produced once and never
164+
maintained, so it cannot rot.
165+
166+
### 3.5 Minimal set per package type ("intent four-piece")
167+
168+
| Package type | Required | Rationale |
169+
|:--|:--|:--|
170+
| App (consumer-facing, ADR-0019) | `index.md`, `user/`, `admin/`, `CHANGELOG.md` | what it is / how to use / how to run / what changed |
171+
| plugin / driver / connector | `index.md`, `CHANGELOG.md` | the "user" is a developer; `configuration` schema is already metadata and is rendered, not documented |
172+
173+
Enforced by `os package publish` lint once available (warn → block). Until
174+
then it is the documented floor.
175+
176+
### 3.6 Distribution & rendering
177+
178+
- **v1 needs no registry change**: docs ride `manifest_json`; package
179+
visibility and `sys_package_grant` authorization apply to docs
180+
automatically — granting a package grants its documentation.
181+
- Rendering surfaces, in expected delivery order:
182+
1. **Registry package page** renders `index.md` (the npm-README model)
183+
and CHANGELOG per version.
184+
2. **Console help center**: sidebar tree from directories + `meta.json`,
185+
markdown body, search. docSet is *not* a UI concept — users see
186+
"Help", one tree; the closed set only fixes order and (later)
187+
default visibility (`admin/` → org admins).
188+
3. **Contextual help (v2)**: pages may declare `binds:
189+
[object/view/flow]`; object pages get a "?" opening the bound
190+
chapter. `binds` also powers coverage lint and staleness nudges
191+
("`mtc_lead` changed; its bound page didn't").
192+
4. **AI assistant grounding**: in-app agents answer "how do I…" from the
193+
package's own manual — same JSON the kernel loads. No UI required;
194+
likely the highest-frequency consumer of docs and a direct payoff of
195+
docs-in-manifest.
196+
197+
### 3.7 i18n: translations are derived-but-reviewed
198+
199+
- Single canonical source language per package. Translations are sibling
200+
files `<page>.<locale>.md`; one tree, one `meta.json`; renderers fall
201+
back to the source language.
202+
- In the AI era translations are *derived content with a review gate*
203+
(lockfile model): generated by AI when the source changes, committed via
204+
reviewed PR. Each translation records the source content hash;
205+
`os docs lint` reports stale translations. Locale-directory mirrors are
206+
rejected — two trees and N copies of `meta.json` drift by construction.
207+
208+
### 3.8 Quality gates for AI authorship
209+
210+
The floor and ceiling are computable *because the software is metadata*:
211+
212+
| Gate | Mechanism | Tool |
213+
|:--|:--|:--|
214+
| Completeness (floor) | minimal-set check; `binds` coverage (every object/flow bound by ≥1 page); manifest **version-diff → docs impact checklist** the author must address item-by-item | publish lint / `os docs lint` |
215+
| Bloat (ceiling) | derived-content ban (§3.4); **token budget** per package tracked like bundle size — docs share the agent context window, so size is a functional constraint, not style | `os docs lint` |
216+
| Correctness | fact-check agent: extract verifiable claims ("approver is the sales director", "status X can only reach Y") and check them against the manifest; contradictions fail CI | `os docs verify` |
217+
| Usefulness | Q&A eval: an agent that can see *only the docs* answers real user questions; failures are documentation gaps | `os docs verify` |
218+
219+
Humans review one thing only: **intent** — is the business context right.
220+
Everything mechanical is gated by machines, which is the only arrangement
221+
that scales when AI is the author (ADR-0033).
222+
223+
## 4. Alternatives considered
224+
225+
| Alternative | Verdict |
226+
|:--|:--|
227+
| Static doc sites per project (Pages/R2 + auth) | Right answer *without* a platform; duplicates registry versioning, identity, and grants when you have one. Acceptable only as a pre-§3.6 transition channel, retired afterwards. |
228+
| Docs as separate artifact beside the code artifact | Justified only by size; text-only packages make it moot. Revisit with the asset service if ever needed (ADR-0025 already provides the multi-artifact path). |
229+
| Generated reference markdown in-repo | A cache in git; rots by construction. Replaced by §3.4. Validated empirically on the pilot. |
230+
| MDX / React components in docs | Code crosses the trust boundary; interactivity comes from renderer-side directives and metadata views instead. |
231+
| `content/docs/` as the package-docs home | `content/` is the standalone-website convention (framework/cloud doc sites) and is role-empty for packages; package docs are package source → `src/docs/`. |
232+
| Ordering via filename prefixes / single file / index-link order | See §3.2 — each fails reorder cost, diff locality, or prose/contract separation. |
233+
234+
## 5. Phasing
235+
236+
| Phase | Scope | Depends on |
237+
|:--|:--|:--|
238+
| **P0** (no platform change) | Repo conventions (`src/docs`, `delivery/`, `internal/`, CLAUDE.md authoring checklists) on pilot projects; optional transition static site | this ADR |
239+
| **P1** (framework) | spec `docs` schema; `os build` collection; publish lint (minimal set, MDX/image ban); kernel skip | P0 |
240+
| **P2** (cloud/console) | registry page rendering → help center → customer-readable metadata views (dictionary, permission matrix) → contextual help via `binds` | P1 |
241+
| **P3** (quality) | coverage/budget lint, version-diff checklist, fact-check + Q&A eval (`os docs verify`) | P1, AI service |
242+
| **P4** (enrichment) | asset service + images, audience filtering, frozen PDF export, i18n staleness tooling | P2 |
243+
244+
Project-side conventions (P0) are isomorphic to the P1 schema by
245+
construction: when the compiler lands, pilot content migrates with zero
246+
edits.

0 commit comments

Comments
 (0)