{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "officecli/help-schema/v1",
  "title": "OfficeCLI Help Schema",
  "description": "Capability schema for one (format, element) pair. Consumed by `officecli <format> <op> <element> --help --json`, contract tests, and release-time wiki generation. This is the agent-facing source of truth for what officecli can do; it is updated in the same PR as the implementation.",
  "type": "object",
  "required": ["format", "element", "operations", "properties"],
  "properties": {
    "$schema": { "type": "string", "description": "pointer to this meta file for IDE tooling; ignored at runtime" },
    "format": {
      "type": "string",
      "enum": ["docx", "xlsx", "pptx"]
    },
    "element": {
      "type": "string",
      "description": "element name as used in CLI, e.g. shape, paragraph, cell, chart-series"
    },
    "parent": {
      "description": "if this element only exists as a child of another, name the parent(s). Omit for top-level elements.",
      "oneOf": [
        { "type": "string" },
        { "type": "array", "items": { "type": "string" } }
      ]
    },
    "note": {
      "type": "string",
      "description": "free-form clarification that does not fit into structured fields (e.g. schema source-of-truth pointer, invariants, caveats)"
    },
    "container": {
      "type": "boolean",
      "description": "set to true for read-only root/container entities (presentation, workbook, document, theme, slidemaster, etc.) that are navigated through but never created or mutated. Contract tests skip Add/Set assertions for containers."
    },
    "operations": {
      "type": "object",
      "description": "which top-level operations accept this element",
      "properties": {
        "add":    { "type": "boolean" },
        "set":    { "type": "boolean" },
        "get":    { "type": "boolean" },
        "query":  { "type": "boolean" },
        "remove": { "type": "boolean" }
      },
      "additionalProperties": false
    },
    "addParent": {
      "description": "concrete CLI parent path(s) accepted by Add. Used by help renderer to print accurate `officecli add <file> <parent> --type <element>` usage. Required when the element's positional/stable paths describe the element's own addressable location (e.g. /comments/comment[N], /footnotes/footnote[N]) rather than its Add-time parent. If omitted, the renderer derives parent by dropping the last segment of paths.positional[0].",
      "oneOf": [
        { "type": "string" },
        { "type": "array", "items": { "type": "string" } }
      ]
    },
    "paths": {
      "type": "object",
      "description": "path forms accepted when this element is addressed by index or @id",
      "properties": {
        "stable":     { "type": "array", "items": { "type": "string" } },
        "positional": { "type": "array", "items": { "type": "string" } }
      },
      "additionalProperties": false
    },
    "addressing": {
      "type": "object",
      "description": "used when children of this element are addressed by a key attribute (e.g. axis[@role=value]) rather than [N]. Mutually exclusive with plain positional paths.",
      "required": ["key", "pathForm"],
      "properties": {
        "key":       { "type": "string", "description": "name of the keying attribute, e.g. 'role'" },
        "pathForm":  { "type": "string", "description": "concrete path template, e.g. '/slide[N]/chart[N]/axis[@role=ROLE]'" },
        "keyValues": { "type": "array", "items": { "type": "string" }, "description": "permitted values for the key attribute" }
      },
      "additionalProperties": false
    },
    "properties": {
      "type": "object",
      "description": "properties supported on this element. Key = canonical property name.",
      "additionalProperties": { "$ref": "#/$defs/property" }
    },
    "children": {
      "type": "array",
      "description": "declared child element types addressable under this element in CLI paths",
      "items":       { "$ref": "#/$defs/childRef" }
    },
    "parts": {
      "type": "array",
      "description": "for the synthetic `raw` element only: enumerates the raw OOXML parts addressable via the `raw` and `raw-set` commands (e.g. /workbook, /Sheet1, /styles). Rendered as a 'Parts' section by the help renderer. Not used by other elements.",
      "items": {
        "type": "object",
        "required": ["name", "desc"],
        "properties": {
          "name": { "type": "string", "description": "part path as accepted by `officecli raw <file> <part>` (e.g. /workbook, /Sheet1)" },
          "desc": { "type": "string", "description": "one-line description of what this part contains" }
        },
        "additionalProperties": false
      }
    },
    "examples": {
      "type": "array",
      "description": "element-level command examples (in addition to per-property examples). Used primarily by the synthetic `raw` element.",
      "items": { "type": "string" }
    },
    "description": {
      "type": "string",
      "description": "element-level description (in addition to per-property descriptions). Used primarily by the synthetic `raw` element."
    },
    "elementAliases": {
      "type": "array",
      "description": "alternate element names that should resolve to this schema (e.g. `paragraph.json` declares ['p'] so that `help docx p` works the same as `help docx paragraph`). Lets path-form abbreviations used in /body/p[N], /Sheet1/col[B], etc. line up with the help index.",
      "items": { "type": "string" }
    }
  },
  "additionalProperties": false,

  "$defs": {
    "appliesWhen": {
      "type": "object",
      "description": "conditional applicability. Keys are dotted paths into sibling/ancestor state (e.g. 'chartType', 'role', 'parent.chartType'). Values are arrays of admissible values. ALL keys must match (AND).",
      "additionalProperties": {
        "type": "array",
        "items": { "type": "string" }
      }
    },
    "aliases": {
      "description": "legacy/lenient names accepted on input, normalized to the canonical key on output. Array form = plain alias list. Object form = alias → canonical mapping (useful when one canonical has multiple aliases pointing to distinct canonical values, e.g. chartType).",
      "oneOf": [
        { "type": "array",  "items": { "type": "string" } },
        { "type": "object", "additionalProperties": { "type": "string" } }
      ]
    },
    "property": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": {
          "type": "string",
          "enum": ["string", "bool", "number", "color", "length", "font-size", "enum"]
        },
        "description": { "type": "string" },
        "aliases":     { "$ref": "#/$defs/aliases" },
        "values": {
          "type": "array",
          "items": { "type": "string" },
          "description": "for type=enum, the allowed canonical values"
        },
        "modifiers": {
          "type": "object",
          "description": "for enum-like properties with orthogonal modifiers (e.g. chartType + 3d/stacked). Each modifier declares how it composes into the final value and which base values it applies to.",
          "additionalProperties": {
            "type": "object",
            "properties": {
              "prefix":      { "type": "string", "description": "modifier composed as '<prefix><Base>', e.g. stackedBar" },
              "suffix":      { "type": "string", "description": "modifier composed as '<base><suffix>', e.g. column3d" },
              "example":     { "type": "string" },
              "appliesWhen": { "$ref": "#/$defs/appliesWhen" }
            },
            "additionalProperties": false
          }
        },
        "appliesWhen": { "$ref": "#/$defs/appliesWhen" },
        "requires": {
          "type": "array",
          "items": { "type": "string" },
          "description": "other properties on the same element that must be set together with this one for the OOXML result to be well-formed (e.g. opacity requires fill to attach alpha to). Contract tests automatically bundle `requires` entries."
        },
        "add":         { "type": "boolean" },
        "set":         { "type": "boolean" },
        "get":         { "type": "boolean" },
        "examples":    { "type": "array", "items": { "type": "string" } },
        "readback": {
          "type": "string",
          "description": "expected format of the Get readback value"
        },
        "enforcement": {
          "type": "string",
          "enum": ["strict", "report"],
          "description": "strict = contract test failure breaks CI; report = drift only logged. New properties default to strict; historical debt may start as report and migrate."
        }
      },
      "additionalProperties": false
    },
    "childRef": {
      "type": "object",
      "required": ["element", "pathSegment", "cardinality"],
      "properties": {
        "element":     { "type": "string", "description": "name of the child element's schema file (without .json)" },
        "pathSegment": { "type": "string", "description": "CLI path segment for this child, e.g. 'series', 'title', 'axis'" },
        "cardinality": {
          "type": "string",
          "enum": ["0..1", "1", "0..n", "1..n"],
          "description": "0..1 = singleton optional (no [N]); 1 = singleton required; 0..n / 1..n = indexed or keyed"
        },
        "key": {
          "type": "string",
          "description": "if children are addressed by attribute instead of [N], name of that attribute (e.g. 'role' for axis[@role=value])"
        },
        "keyValues":   { "type": "array", "items": { "type": "string" }, "description": "permitted values for the key attribute" },
        "appliesWhen": { "$ref": "#/$defs/appliesWhen" }
      },
      "additionalProperties": false
    }
  }
}
