{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "manni:core:1.0.0",
  "title": "manni core vocabulary v1.0.0",
  "description": "What the page is: the descriptive floor of the manni house vocabulary, and the only vocabulary in the family that requires anything. Requires `title` and `description`, and holds every string it claims non-empty — an empty value is the habit a default exists to prevent, and an empty `type` reaches the tooling that switches on it (the graph type derivation, template selection) as a falsy key instead of a loud failure. Empty strings that other claimants permit are override cases, and so are Dublin Core's repeated-element arrays for type and language — the same recorded exception family as the required pair: core is deliberately stricter than the loosest claimant wherever looseness would teach a bad habit. Every other key here is a deliberately loose claim on a name other schemas share, made only where the fact is about the content and never where it is about the rendering, which stays with the generator. Facts about people are not here either: `authors` sits with `owner` and `reviewed-by` in `manni:stewardship:1.0.0`, because who made a page is a fact about its care rather than about what it is. One shape rule across the whole family: a single-valued fact is a plain string; a list-valued fact is a string or a list.",
  "type": "object",
  "required": [
    "title",
    "description"
  ],
  "additionalProperties": true,
  "properties": {
    "title": {
      "type": "string",
      "minLength": 1,
      "description": "What the page is called, as one non-empty string. Deliberately stricter than the other schemas that claim this key: Dublin Core's repeated-title array and Docusaurus's empty title are override cases here, because a default whose floor accepts an empty title teaches the habit it exists to prevent.",
      "x-manni-location": "page"
    },
    "description": {
      "type": "string",
      "minLength": 1,
      "description": "One full non-empty sentence saying what the page is for. Doubles as the search snippet, the link-card subtitle, and the first thing a retrieval system reads. Held to a single non-empty string for the same reason as `title`.",
      "x-manni-location": "page"
    },
    "id": {
      "type": "string",
      "description": "Stable identifier for this page, independent of its path. References from other pages (`related-pages`, `prerequisites`, `replaced-by`) stay valid across moves when they point at ids.",
      "minLength": 1,
      "x-manni-location": "page"
    },
    "type": {
      "type": "string",
      "description": "The kind of content this page is, as one string. Pair with a taxonomy schema (`diataxis:diataxis:1.0`, `tgdp:templates:1.0`) to constrain it to a published vocabulary; `graph.type` narrows it to the iiRDS ladder, and the seven-action `action` names what the reader is doing rather than what the page is.",
      "minLength": 1,
      "x-manni-location": "page"
    },
    "keywords": {
      "type": [
        "string",
        "array"
      ],
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Search keywords. A list of strings, or a single comma-separated string — the AsciiDoc form Antora reads. Deliberately distinct from two neighbors: `tags` (the browsable site taxonomy) is left entirely to your generator's schema, and `concepts` (manni:structure:1.0.0) holds controlled glossary terms rather than free words.",
      "minLength": 1,
      "x-manni-location": "page"
    },
    "language": {
      "type": "string",
      "description": "The natural language the page is written in, as one string. A BCP 47 language tag is the recommendation, not enforced. The tag carries region and script where they matter, as in `en-GB`, `pt-BR` or `zh-Hant`. The W3C's Language Tags and Locale Identifiers spec (LTLI) calls this a language tag, an identifier for the language of the content. It is the same fact that `lang`, `xml:lang`, `hreflang` and `dc:language` carry. Two neighbors are deliberately not this field. Open Graph's `og:locale` is that protocol's own key and takes the underscore form `en_US`. A programming language belongs to the code sample or to a key such as Microsoft Learn's `ms.devlang`, never here. Held to one string even against Dublin Core's repeatable `language` element, a recorded exception.",
      "minLength": 1,
      "x-manni-location": "page"
    }
  }
}
