{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "manni:graph:1.0.0",
  "title": "manni graph vocabulary v1.0.0",
  "description": "SKOS concept fields, PROV-O provenance, and iiRDS Core/Software typing under the top-level `graph` key — a manni common vocabulary for knowledge-graph frontmatter that any graph builder or retrieval tool can implement and compose on. It adopts the house conventions: kebab-case keys, plain-language names, and single-value-or-list forgiveness on label fields. The `graph` envelope survives the house flattening rule deliberately: it is what lets a closed, typo-catching block coexist with an open page. Where the block and a page-level field speak to the same fact (`type`, `concepts`, `applies-to`, `revision-of`/`supersedes`), the deeper declaration wins and the page-level field is the harvest fallback. The root fields of manni:terminology:1.0.0 are the fallback for seven more: `label`, `alt-labels`, `broader`, `narrower`, `related-concepts` (from `related-terms`), `definition` and `abstract`. The RDF term each field becomes is named in its description — iiRDS itself spells its properties kebab (has-topic-type, relates-to-product-variant), so the mapping is stated, not mirrored in key names. Files without a `graph` key pass. AI provenance lives outside this block, in manni:ai-context:1.0.0. The machines that wrote the page come from the page-level `provenance`, as the distinct `generated-by` values across its entries, and that is where a graph builder's harvest reads them. Attribution for machine-proposed graph fields is in the page-level `meta-provenance`, with pointers under `/graph/` (`/graph/label`). A pointer can name any graph field, including the hand-curated `sections`, `revision-of` and `derived-from`. Keeping those out of machine attribution belongs to the graph builder's harvest, not to this schema.",
  "type": "object",
  "additionalProperties": true,
  "properties": {
    "graph": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "label": {
          "type": "string",
          "minLength": 1,
          "description": "The one canonical name of the concept this document is primarily about (skos:prefLabel). `alt-labels` carry every other name the concept goes by. On a page without a `graph` block, the root `label` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "alt-labels": {
          "$ref": "#/$defs/labelList",
          "description": "Alternative names — synonyms, abbreviations, spelling variants — for the primary concept (skos:altLabel). What search and retrieval match; `label` is what the graph displays. On a page without a `graph` block, the root `alt-labels` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "definition": {
          "type": "string",
          "minLength": 1,
          "description": "The definition of the concept this document is primarily about (skos:definition). It needs `label`, the name it defines. On a page without a `graph` block, the root `definition` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "abstract": {
          "type": "string",
          "minLength": 1,
          "description": "The one-sentence form of `definition`, for a hover card or a tooltip. It narrows skos:definition and needs the `definition` it shortens. On a page without a `graph` block, the root `abstract` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "broader": {
          "$ref": "#/$defs/labelList",
          "description": "Labels of broader (parent) concepts (skos:broader). On a page without a `graph` block, the root `broader` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "narrower": {
          "$ref": "#/$defs/labelList",
          "description": "Labels of narrower (child) concepts (skos:narrower). On a page without a `graph` block, the root `narrower` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "related-concepts": {
          "$ref": "#/$defs/labelList",
          "description": "Labels of associatively related concepts (skos:related) — a claim about the concept graph, independent of what this page covers. Named in step with the page-level `related-pages`: each says what it points at. On a page without a `graph` block, the root `related-terms` of manni:terminology:1.0.0 is the harvest fallback."
        },
        "concepts": {
          "$ref": "#/$defs/labelList",
          "description": "Concept labels this document is about (dcterms:subject, minting a skos:Concept per value). The deeper twin of the page-level `concepts`, same shape; when both are present this one wins."
        },
        "type": {
          "$ref": "#/$defs/topicType",
          "description": "iiRDS Core topic type (iirds:has-topic-type). The deeper twin of the page-level `type`: same name, and this closed iiRDS enum wins when present — the authority the no-invented-enums rule asks for. When absent, the deriver may default it from the page's type (how-to → task, tutorial → learning, explanation → concept)."
        },
        "applies-to": {
          "$ref": "#/$defs/labelList",
          "description": "Product/variant labels this document applies to (iirds:relates-to-product-variant, minting iirds:ProductVariant nodes). The deeper twin of the page-level `applies-to`, same flat shape; when both are present, this one wins and the page-level field is the harvest fallback."
        },
        "about-product-lifecycle": {
          "$ref": "#/$defs/lifecyclePhaseList",
          "description": "Which phases of the PRODUCT's lifecycle the content covers (iirds:relates-to-product-lifecycle-phase): a page about deploying or uninstalling the product. The `about-` prefix makes the about-ness structural — this says what the page is about, never anything about the page's own `lifecycle`."
        },
        "about-product-aspect": {
          "$ref": "#/$defs/productAspectList",
          "description": "Which aspects of the product the content is about (iirds:has-subject, Software domain): its architecture, its interfaces, or its system requirements. Pairs with `about-product-lifecycle`."
        },
        "not-applicable-to": {
          "$ref": "#/$defs/labelList",
          "description": "Product/variant labels this document explicitly does NOT apply to. The deeper twin of the page-level `not-applicable-to` (manni:structure:1.0.0), same flat shape; when both are present this one wins and the page-level field is the harvest fallback — the same rule `applies-to` follows, so the positive and its negative behave alike at both altitudes. Disjointness from `applies-to` is checked at the graph layer (SHACL), not here."
        },
        "not-about-product-aspect": {
          "$ref": "#/$defs/productAspectList",
          "description": "Product aspects this document is explicitly NOT about. Disjoint from `about-product-aspect`, checked at the graph layer."
        },
        "sections": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/sectionMetadata"
          },
          "description": "Per-section iiRDS typing, keyed by GitHub-style heading slug. Keys are deliberately unconstrained here: a key matching no heading is flagged at graph build time, which is the right layer for that check."
        },
        "revision-of": {
          "$ref": "#/$defs/labelList",
          "description": "Repo-relative paths or URLs of earlier documents this one revises (prov:wasRevisionOf). The deeper counterpart of the page-level `supersedes`, which emits the same predicate as the harvest fallback."
        },
        "derived-from": {
          "$ref": "#/$defs/labelList",
          "description": "Repo-relative paths or URLs this document was derived from (prov:wasDerivedFrom). Lineage between documents — distinct from the page-level `source-of-truth`, which anchors the page to external reality."
        }
      },
      "dependentRequired": {
        "alt-labels": [
          "label"
        ],
        "broader": [
          "label"
        ],
        "narrower": [
          "label"
        ],
        "related-concepts": [
          "label"
        ],
        "definition": [
          "label"
        ],
        "abstract": [
          "definition"
        ]
      },
      "x-manni-location": "page"
    }
  },
  "$defs": {
    "labelList": {
      "if": {
        "type": "array"
      },
      "then": {
        "type": "array",
        "minItems": 1,
        "items": {
          "type": "string",
          "minLength": 1
        },
        "uniqueItems": true
      },
      "else": {
        "type": "string",
        "minLength": 1
      },
      "description": "One non-empty label, or a non-empty unique list of them."
    },
    "topicType": {
      "enum": [
        "task",
        "concept",
        "reference",
        "learning",
        "troubleshooting",
        "form"
      ]
    },
    "lifecyclePhaseList": {
      "if": {
        "type": "array"
      },
      "then": {
        "type": "array",
        "minItems": 1,
        "items": {
          "enum": [
            "administration",
            "customization",
            "update",
            "deployment",
            "integration",
            "deinstallation"
          ]
        },
        "uniqueItems": true
      },
      "else": {
        "enum": [
          "administration",
          "customization",
          "update",
          "deployment",
          "integration",
          "deinstallation"
        ]
      },
      "description": "One iiRDS Software-domain product lifecycle phase, or a unique list of them."
    },
    "productAspectList": {
      "if": {
        "type": "array"
      },
      "then": {
        "type": "array",
        "minItems": 1,
        "items": {
          "enum": [
            "architecture",
            "interface",
            "system-requirement"
          ]
        },
        "uniqueItems": true
      },
      "else": {
        "enum": [
          "architecture",
          "interface",
          "system-requirement"
        ]
      },
      "description": "One iiRDS Software-domain aspect, or a unique list of them."
    },
    "sectionMetadata": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "description": "iiRDS typing for one heading section. Fields mirror the document-level graph fields; nothing is inherited from the document, and the SKOS hierarchy fields are deliberately not available per section.",
      "properties": {
        "type": {
          "$ref": "#/$defs/topicType",
          "description": "iiRDS Core topic type for this section (iirds:has-topic-type)."
        },
        "applies-to": {
          "$ref": "#/$defs/labelList",
          "description": "Product/variant labels this section applies to (iirds:relates-to-product-variant)."
        },
        "about-product-lifecycle": {
          "$ref": "#/$defs/lifecyclePhaseList",
          "description": "Product lifecycle phases this section covers."
        },
        "about-product-aspect": {
          "$ref": "#/$defs/productAspectList",
          "description": "Product aspects this section is about."
        },
        "not-applicable-to": {
          "$ref": "#/$defs/labelList",
          "description": "Product/variant labels this section explicitly does NOT apply to."
        },
        "not-about-product-aspect": {
          "$ref": "#/$defs/productAspectList",
          "description": "Product aspects this section is explicitly NOT about."
        },
        "concepts": {
          "$ref": "#/$defs/labelList",
          "description": "Concept labels for this section (dcterms:subject)."
        }
      }
    }
  }
}
