@@ -107,6 +107,8 @@ export const DocSchema = z.object({
107107 .describe (' Unique doc name; MUST carry the package namespace prefix (enforced by build/publish lint)' ),
108108 label: z .string ().optional ()
109109 .describe (' Display title; defaults to the first `#` heading, then the name' ),
110+ description: z .string ().optional ()
111+ .describe (' One-line summary for listings (frontmatter `description:`); travels in the list response' ),
110112 content: z .string ().describe (' Raw Markdown (CommonMark + GFM)' ),
111113});
112114```
@@ -144,6 +146,8 @@ unbounded, and manifest-size pressure lands here first.
144146 legacy, so day-one lint costs nothing — the same reasoning as the
145147 image ban.
146148- ` label ` = frontmatter ` title: ` if present, else first ` # ` heading.
149+ - ` description ` = frontmatter ` description: ` if present (optional one-line
150+ summary; the docs portal renders it under the title).
147151- ` content ` = the file body (frontmatter stripped).
148152- ** Subdirectories under ` src/docs/ ` are a build error** , not silently
149153 flattened — flatness is the contract that keeps references stable.
@@ -240,3 +244,30 @@ maintained, so it cannot rot.
240244
241245P0 conventions are isomorphic to the P1 schema by construction: when the
242246compiler lands, pilot content migrates with zero edits.
247+
248+ ### P3 design note — tags / categorization (deferred, not bolted on early)
249+
250+ Tags, categories, and ordering are ** navigation-model** concerns and stay
251+ in P3 by design — adding them before there is enough doc volume to need
252+ filtering buys an i18n-bearing protocol field with no payoff. When they
253+ land, design the discovery surface (tags + category + search + cross-package
254+ aggregation) as one thing, not field-by-field. The agreed shape for tags:
255+
256+ - ** A tag is a stable key, never a display string.** Display always resolves
257+ through the platform's existing label-key → i18n mechanism, exactly like
258+ every other label. Free-form display strings fail twice: cross-package
259+ fragmentation (` setup ` vs ` getting-started ` vs ` quickstart ` ) and no i18n
260+ owner. Keying fixes both.
261+ - ** Layered vocabulary, not closed-vs-open.** The protocol blesses a * small
262+ core* of cross-cutting "purpose" tags (` getting-started ` , ` guide ` ,
263+ ` reference ` , ` tutorial ` , ` api ` , ` migration ` , ` troubleshooting ` ) — central
264+ i18n, eligible for dedicated UI. Packages extend with ** namespace-prefixed**
265+ tags (` crm_* ` , same rule as doc names) for domain topics, shipping their
266+ translations in the package i18n bundle (same path as object/field labels).
267+ - Pure closed enum is too rigid (packages can't express domain topics); pure
268+ open free-form is too fragmented and has no i18n home. The layered
269+ key-based model is the resolution.
270+
271+ The current addition — ` description ` — deliberately stops short of this: it is
272+ a per-doc summary, not a taxonomy, so it carries no i18n-keying or
273+ cross-package-coherence burden.
0 commit comments