SummaryOne Schema, All Documents, No Guessing
The canon has over 400 documents with no authoritative frontmatter schema. Fields, types, and quoting rules have been copied ad-hoc from whatever document an agent finds first. This has produced: unquoted values that parse as wrong types (dates as date objects, tiers as integers, booleans as booleans), inconsistent field presence across documents of the same audience, and silent rendering failures when the site's build system encounters unexpected types.
This document fixes the problem by defining the schema once. Templates (canon/meta/TEMPLATE.md, docs/TEMPLATE.md) define document structure. This document defines field requirements. When they disagree, this document wins.
The Universal RuleQuote Only What Needs Quoting
YAML frontmatter values follow a simple pattern derived from the working corpus: quote strings that contain special characters, leave simple identifiers and native types unquoted.
# Simple identifiers — unquoted
audience: canon
exposure: nav
voice: neutral
stability: semi_stable
type: essay
author: Klappy
slug: my-article-slug
epoch: E0007
status: active
# Integers — unquoted
tier: 2
# Booleans — unquoted
public: true
archived: false
# Dates — unquoted
date: 2026-04-04
# URIs — unquoted
uri: klappy://canon/principles/example
# Strings with special characters — quoted
title: "My Title — With Dashes and Colons: Like This"
subtitle: "Why it matters"
description: "A sentence with punctuation, dashes — and other characters."
hook: "The one-liner that grabs attention."
derives_from: "canon/values/axioms.md, canon/principles/other.md"
complements: "docs/path/to/sibling.md"
governs: "All documents in this scope"
# Arrays — inline with quoted strings
tags: ["canon", "principle", "example"]
The test is simple: if the value is a single word or simple identifier (no spaces, no special characters), leave it unquoted. If it contains spaces, punctuation, or special characters, quote it. Booleans, integers, and dates are always unquoted — the renderer expects native YAML types, not strings.
Universal Required Fields — Every Document, Every Audience
These eight fields are required on every document in the knowledge base, regardless of audience:
| Field | Format | Description |
|---|---|---|
uri | "klappy://path/to/slug" | Canonical URI. Must match the file's logical location. |
title | "Full Title — With Stance" | Document title. Must name concept and signal position (Writing Canon Tier 1). |
audience | "canon" "docs" "public" "odd" "operators" "apocrypha" | Which audience this document serves. Determines required fields below. |
exposure | "nav" "public" "draft" "hidden" "internal" | Visibility level. nav = navigable but not promoted. public = published on site. draft = work in progress. hidden = exists but not listed. internal = team-only. |
tier | "1" "2" "3" "4" | Epistemic obligation level. 1 = foundational. 2 = governance. 3 = operational. 4 = ephemeral. See canon/definitions/epistemic-obligation-and-document-tiers.md. |
voice | "first_person" "neutral" "direct" "narrative" "conversational" "authoritative" | Writing voice. first_person = Klappy speaks. neutral = system documentation. |
stability | "stable" "semi_stable" "evolving" "draft" "experimental" | How likely this document is to change. Governs how confidently agents should rely on it. |
tags | ["tag1", "tag2"] | Inline array of quoted strings. Used for search and categorization. Must not be empty — at minimum include the audience and one topic tag. |
Audience-Specific Fields
audience: "canon"
Canon documents define program-level constraints.
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | recommended | "E0007" | Which epoch introduced this document |
date | recommended | "2026-04-04" | Date created or last substantively revised |
derives_from | recommended | "path/to/source.md, path/to/other.md" | What this document is grounded in. Full file paths, not floating names. |
complements | optional | "path/to/sibling.md" | Related documents that work alongside this one |
governs | optional | "description of what this constrains" | What behavior or documents this constrains |
status | optional | "active" "proposed" "final" "superseded" | Lifecycle status |
supersedes | optional | "path/to/old.md" | What this replaces |
superseded_by | optional | "path/to/replacement.md" | The replacement document (back-pointer; pair with status: superseded) |
audience: "docs"
Docs documents define implementation details, planning, and operational guides.
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | recommended | "E0007" | Which epoch this belongs to |
date | recommended | "2026-04-04" | Date created |
derives_from | optional | "path/to/source.md" | What this is grounded in |
complements | optional | "path/to/sibling.md" | Related documents |
governs | optional | "description" | What this constrains |
status | optional | "active" "proposed" "superseded" "archived" | Lifecycle status |
supersedes | optional | "path or description" | What this replaces |
superseded_by | optional | "path/to/replacement.md" | The replacement document (back-pointer; pair with status: superseded) |
forcing_fault | epoch docs only | "description" | What friction triggered this epoch |
new_invariant | epoch docs only | "statement" | What's now true that wasn't before |
core_shift | epoch docs only | "old → new" | What changed |
documents_introduced | epoch docs only | ["path1.md", "path2.md"] | Files added in this epoch |
extends | sub-epoch docs | "Epoch N (description)" | Parent epoch this extends |
audience: "public"
Public documents include essays, articles, about pages, and website navigation pages. Essays and articles have additional required fields for site rendering.
All public documents:
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | recommended | "E0007" | Which epoch |
date | recommended | "2026-04-04" | Publication or creation date |
derives_from | recommended | "path/to/source.md" | Grounding |
complements | optional | "path/to/sibling.md" | Related content |
Additional fields required when type is "essay" or "article":
| Field | Required? | Format | Notes |
|---|---|---|---|
slug | required | "kebab-case-url-slug" | URL path. Used by the site renderer to generate page URLs. Without this, pages render blank. |
author | required | "Klappy" | Author name. Always "Klappy" for this project. |
type | required | "essay" or "article" | Content type. Used by the renderer to select template. |
public | required | "true" or "false" | Whether this is published on the site. "false" = draft, not visible. |
description | required | "1-2 sentence description" | Used for SEO meta description and social sharing. |
hook | required | "One compelling sentence" | Used for teaser text in listings and cards. |
subtitle | recommended | "Explanatory subtitle" | Appears below title on the page |
og_title | recommended | "Title for social sharing" | OpenGraph title. Defaults to title if omitted. |
og_description | recommended | "Description for social sharing" | OpenGraph description. Defaults to description if omitted. |
og_type | optional | "article" | OpenGraph type |
og_image | optional | "/images/slug-og.png" | OpenGraph image path |
twitter_card | optional | "summary_large_image" | Twitter card type |
twitter_title | optional | "Title for Twitter" | Twitter title |
twitter_description | optional | "Description for Twitter" | Twitter description |
twitter_image | optional | "/images/slug-og.png" | Twitter image path |
related | optional | [{uri, label, relationship}] | Related content links (YAML object array) |
provenance | optional | {trigger, author_interventions, ...} | Detailed authorship record (YAML object) |
book_part | book chapters only | "Part N — Title" | Book section |
book_chapter | book chapters only | "5" | Chapter number (quoted) |
About pages, indexes, and navigation pages (about/, READMEs, positioning docs) do not need slug, author, type, or social metadata. They render through structural routes, not content templates.
audience: "operators"
Operator documents are tool references and MCP server documentation.
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | optional | "E0007" | Which epoch |
date | optional | "2026-04-04" | Date |
derives_from | optional | "path/to/source.md" | Grounding |
complements | optional | "path/to/sibling.md" | Related docs |
governs | optional | "description" | What this constrains |
audience: "apocrypha"
Apocrypha documents are system-voice fragments, predocumentaries, and reconstructions.
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | recommended | "E0005" | Origin epoch |
type | recommended | "fragment" "predocumentary" "reconstruction" | Apocrypha subtype |
depends_on | optional | ["path1.md", "path2.md"] | Source dependencies |
confidence | optional | "experiential" "unknown" | Interpretive confidence |
provenance | optional | "cinematic" "disputed" "uncertain" | Origin classification |
classification | optional | "post-stability record" | Temporal classification |
audience: "odd"
ODD documents define universal philosophy and methodology.
| Field | Required? | Format | Notes |
|---|---|---|---|
epoch | optional | "E0005" | Which epoch |
date | optional | "2026-04-04" | Date |
derives_from | optional | "path/to/source.md" | Grounding |
complements | optional | "path/to/sibling.md" | Related documents |
governs | optional | "description of what this handoff/ledger covers" | What scope this document governs. Common on handoffs and ledgers. |
status | optional | "active" "draft" "superseded" "archived" | Lifecycle status |
session_span | optional | "2026-04-19 closed" or "2026-04-19 to 2026-04-20" | For session ledgers and handoffs: which session(s) the document records |
version | optional | "1.0" (quoted) | Document version |
slug | if site-rendered | "kebab-case" | URL slug for public-facing ODD docs |
Archived Documents
Any document can be archived by adding:
archived: "true"
archived_reason: "E0005.1 — superseded by description"
Archived documents remain in the knowledge base for history but are excluded from active navigation and search prioritization.
Repo Routingtarget_repo
target_repo is an optional, cross-cutting field (it applies to documents of any audience) that declares which repository a file belongs to once the klappy.dev knowledge base bifurcates. It is the only routing signal extraction relies on: a later move is git grep target_repo plus copy, never a re-organization.
| Field | Required? | Format | Notes |
|---|---|---|---|
target_repo | optional | "outcomes-driven-development" "oddkit" "undecided" | Destination repo for this file. Quoted string, one of the three enum values. Absent = stays in klappy.dev — the default home is the absence of a tag, so routing never depends on a file's path. |
Values:
"outcomes-driven-development"— the portable core: universal governance and canon an outside adopter needs."oddkit"— the engine's own documentation and engine/release/telemetry governance."undecided"— contested; resolved deliberately before the file moves. Used for cross-cutting files and genuine judgment calls.- Absent — stays in klappy.dev (the personal overlay, the default home).
Only files that leave klappy.dev carry the tag; the overlay's own files stay untagged. Non-markdown files cannot carry frontmatter, so their routing is recorded in canon/meta/scope-map.json instead. A malformed value is a merge-gate failure (the validator enum-checks this field), so a bad route never reaches the renderer. Governing document: klappy://docs/repo-bifurcation-and-target-repo-routing.
Smell TestHow to Detect a Frontmatter Violation
- Type mismatches. If a boolean field like
publicis quoted as"false"(a truthy string) instead offalse(a boolean), the renderer behaves incorrectly. If an integer field liketieris quoted as"2"instead of2, type comparisons fail. - Blank pages in PR previews. The most common symptom of frontmatter errors. Usually caused by quoted booleans/integers that should be native types, missing
slugon public essays, or missingtypeon essays. - Copying frontmatter from another document. The smell itself. If the source of truth for frontmatter is "whatever document I found first," the schema is not being consulted. Consult this document instead.
- Fields that don't appear in this schema. If you're adding a field that doesn't appear here, either this schema needs updating (propose the addition) or the field is ad-hoc and shouldn't be added.
Required Response When a Violation Is Detected
- Match the type the renderer expects. Booleans unquoted (
true/false), integers unquoted (2), dates unquoted (2026-04-04), simple identifiers unquoted (canon,nav), complex strings quoted ("Title — With Special Characters"). - Check required fields. Consult the audience-specific table above. If required fields are missing, add them.
- Consult this document, not other documents. Do not copy frontmatter from another file to fix the problem. This schema is the source of truth.
- If this schema is incomplete, update it. If a legitimate field is missing from this schema, add it here first, then use it. The schema must stay current with actual usage.
Enforcement
This schema is a preflight and validate checkpoint. When the deliverable is a document, frontmatter compliance is a Definition of Done requirement alongside the Writing Canon checklist.
The schema was derived from corpus analysis of 389 documents on April 4, 2026, and should be maintained as the canon evolves. Fields that appear in fewer than 5% of documents within an audience are not listed unless they serve a specific structural purpose.
See Also
- Writing Canon — progressive disclosure and structural requirements for document bodies
- Canon Article Template — structural template for canon documents (consult this schema for field requirements)
- Epistemic Obligation and Document Tiers — what tier values mean
- Definition of Done — completion requirements including frontmatter compliance