{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://yarramate.org/schema/projection/v1",
  "title": "YarraMate semantic projection",
  "type": "object",
  "additionalProperties": false,
  "required": ["format", "id", "version", "query"],
  "properties": {
    "format": {
      "const": "yarramate/projection/v1"
    },
    "id": {
      "$ref": "#/$defs/id"
    },
    "version": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+$"
    },
    "query": {
      "$ref": "#/$defs/query"
    },
    "presentation": {
      "$ref": "#/$defs/presentation"
    }
  },
  "$defs": {
    "query": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "subjects": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "instances": {
          "description": "Pattern instances whose CONTENTS this query selects (#473, ADR 0144). Each id names an instance, and the facet selects that instance together with everything the fold tree would draw inside it: the same closure `presentation.fold: \"instances\"` collapses into one box, read through this view's own `nesting`. This is the one facet that ADDS rather than narrows. `subjects` and `instances` are a single identity facet spelled two ways and their values combine with OR, while every other field still ANDs over the union. Naming the instance rather than hand-listing its members is what keeps a view from going stale the next time the pattern binds a slot. An id that names nothing is refused (YM921); one that names a subject which is not a pattern instance is refused separately (YM922), because a typo and a category error send an author to different places.",
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "exclude": {
          "description": "Subjects this query would otherwise select and the author has taken out: the exception a rule cannot state (#267, ADR 0122). Applied after every other facet and after relationship expansion, so an excluded subject is out whichever way it would have come back in. Naming a subject no facet selects is allowed and inert until the model grows into the rule.",
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "documents": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "kinds": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/qualifiedKind"
          }
        },
        "layers": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/layer"
          }
        },
        "statuses": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": ["planned", "current", "retired"]
          }
        },
        "excludeStatuses": {
          "description": "Drop concepts carrying one of these lifecycle statuses while keeping concepts that declare no status at all - 'everything except retired' for viewpoint projections, where a bare statuses filter would wrongly drop unstatused actors and motivation elements.",
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": ["planned", "current", "retired"]
          }
        },
        "states": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "owners": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "constraints": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/subjectIdentity"
          }
        },
        "relationshipKinds": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/qualifiedKind"
          }
        },
        "kindMatching": {
          "enum": ["exact", "descendants"]
        },
        "relationships": {
          "enum": ["between", "connected", "none"]
        },
        "isolatedConcepts": {
          "enum": ["include", "exclude"]
        }
      }
    },
    "presentation": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "title": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "description": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "layout": {
          "description": "How the view arranges itself (ADR 0147), a ladder where each mode keeps everything below it: \"layered\" places the nodes and lets the canvas draw straight lines between them; \"routed\" also routes every edge around the nodes and reserves room for its label; \"served-by\" also layers serving, realization and specialization upward, so the served element sits above what serves it and the label reads down the page; \"bands\" also pins every element to its ArchiMate layer's band. A view that says nothing lays out \"served-by\".",
          "enum": ["layered", "routed", "served-by", "bands"]
        },
        "direction": {
          "enum": ["top-down", "left-right"]
        },
        "nesting": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": ["composition", "assignment"]
          }
        },
        "fold": {
          "description": "Whether this view draws pattern instances FOLDED by default (#473). \"instances\" collapses every instance to a single node carrying its members, with the edges into and out of them lifted onto the box; \"none\", the default, draws everything. A reader opens what they want, so this says where to START rather than what may be seen. Folding reads the same containment tree nesting does, so a view declaring fold without \"assignment\" in nesting collapses less than its author probably expects; the editor says so rather than the loader refusing it, because a diagnostic has no warning severity and this is not an error.",
          "enum": ["instances", "none"]
        },
        "showLifecycle": {
          "type": "boolean"
        },
        "showEvidence": {
          "type": "boolean"
        },
        "showOwnership": {
          "type": "boolean"
        },
        "showResponsibility": {
          "description": "Whether responsibility edges (responsible, consulted, informed; ADR 0159) draw on the canvas. Off when absent: a role responsible for eight applications is eight lines out of one box, and the subject's properties read the letters either way.",
          "type": "boolean"
        },
        "showKindLabels": {
          "description": "Whether an unnamed relationship is labelled with its reading (\"serves\", \"served by\", \"realizes\"). Off, the line style and arrowhead alone say the kind; a named relationship keeps its name either way. On when absent.",
          "type": "boolean"
        },
        "notation": {
          "enum": ["archimate"]
        },
        "folder": {
          "type": "string",
          "description": "The folder this view files itself under in an editor's rail. A LABEL the author declares, never the directory the projection sits in: nest with '/' separators, and the filesystem is never consulted (ADR 0104).",
          "pattern": "^[^/]+(?:/[^/]+)*$",
          "minLength": 1
        }
      }
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
    },
    "qualifiedKind": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]*(?:/[a-z][a-z0-9-]*)+@[0-9]+\\.[0-9]+#[a-z][A-Za-z0-9-]*$"
    },
    "subjectIdentity": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
    },
    "nonEmptyText": {
      "type": "string",
      "minLength": 1
    },
    "layer": {
      "enum": [
        "motivation",
        "strategy",
        "business",
        "application",
        "technology",
        "physical",
        "implementation",
        "composite"
      ]
  }
  }
}
