Page types & schemas
Every page in the mesh has a page type — a typed contract that says both how to write it and what shape it must take. A page type is always a pair: a template and a schema.
What it is
Each entry under page_types in gtmesh.config.yaml names a template and a schema:
- The template (
templates/<type>.md) is the prose theory — the editorial guidance the article-writer follows when filling the body. - The schema (
schemas/<type>.schema.yaml) is the structure — a JSON Schema thatapplyscaffolds an empty skeleton from, and thatvalidate/sealenforce with ajv. The SSG renders against the same contract.
Two schema systems, never conflated: zod validates the registry; JSON Schema / ajv validates page YAML structure. Page types live entirely in the second world.
Why it matters
Typed contracts are what keep programmatic content consistent at scale. The schema guarantees a review page always carries a rubric and a verdict block, a comparison always carries its entity pair, and so on — so a thousand generated pages share one shape the SSG can render and AI answer engines can parse. The template guarantees they share one voice. Because the schema is checked at seal, a page that doesn’t conform simply cannot ship.
How it works
Selection is deterministic. Each page type carries a when predicate (a section, and/or a role, and/or the page’s resolved entity_kind) and plan picks the type whose predicate matches — first match wins, so specific rules sit above catch-alls. A kind: commercial_* project selects the money-layer types (product, tool, directory, trust); a content site selects entity-hub, educational, guide, and friends.
Typing hubs by entity class. A demand-minted hub (entity-aware classification) carries no section, so when: { role: hub } would collapse every hub onto one type. Add when: { entity_kind: … } (the entity node’s class label) to give each its own type — manufacturer-hub, industry-hub — placed above the generic entity-hub catch-all. Beyond the template/schema, this aligns the minted hub’s identity with a hand-authored typed seed of the same class, so the demand folds into the seed rather than minting a duplicate page. See Tuning §5 for the full recipe and Folding demand into a hub for why the alignment matters.
The key is entity_kind, not class. page_types.when isn’t validated for unknown keys, so when: { class: … } is silently ignored — the hub falls through to the generic entity-hub, its type no longer matches the typed seed, and you get a stray page with no error. If a typed hub won’t fold, check this first.
Catalog types. A page type can carry catalog: true to declare that it embodies an entity — a product, a category/manufacturer hub. Only catalog types emit the HAS_<axis> domain edges that file a page into the money layer; content types (guide, educational, comparison, …) merely mention entities, so they stay uncatalogued and emit only the topical MEMBER_OF membership that routes them up. The flag is opt-in and additive — see the HAS_<axis> gate for why it matters. The scaffold ships catalog: true on product and entity-hub.
Every page validates as the {type, meta, content} envelope (additionalProperties: false, with type as a const discriminator). meta is the render projection — slug is apply-owned, the editorial fields (metaTitle/title/navTitle/metaDescription) are writer-authored. content is the typed body.
A type can also carry schema variants — schema_variants plus variant_when lets one type resolve to a different schema by context. The scaffold’s educational type, for example, renders thin when a sibling hub exists in the same product cluster and full otherwise; plan resolves which one applies.
Key files & flags
| Thing | What it is |
|---|---|
gtmesh.config.yaml page_types | The list of types: each id → template + schema + when |
page_types[].catalog | true marks a type that embodies an entity → emits HAS_<axis> domain edges |
templates/<type>.md | Prose theory for the writer |
schemas/<type>.schema.yaml | The structural contract (JSON Schema) |
schemas/common.schema.yaml | Shared types, merged then locally $ref-ed |
schema_variants / variant_when | Resolve one type to a variant schema by context |
gtmesh page validate <slug> | Check a page against its schema (ajv) + editorial lints |
Related
- Writer & quality gate — who fills the body against these contracts
- Tuning — changing prose theory and structure per type