{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openwop.dev/spec/v2/declaration.schema.json",
  "title": "OpenWOP v2 capability declaration file (RFC 0169 \u00a7B)",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "$schema",
    "version",
    "updated",
    "witnessClasses",
    "extensionsKeyPattern",
    "reservedOrgs",
    "extensions",
    "metadata",
    "families",
    "profiles"
  ],
  "properties": {
    "$schema": {
      "type": "string"
    },
    "$comment": {
      "type": "string"
    },
    "version": {
      "type": "string",
      "pattern": "^2\\.[0-9]+$"
    },
    "updated": {
      "type": "string",
      "format": "date"
    },
    "witnessClasses": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "witnessable-unaided",
          "witnessable-gated",
          "seam-gated",
          "claims-check",
          "negative-existence"
        ]
      }
    },
    "extensionsKeyPattern": {
      "type": "string"
    },
    "reservedOrgs": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "extensions": {
      "type": "object",
      "description": "The vendor-org REGISTRY (events.md \u00a7Rules, RFC 0171 \u00a7A.1): a vendor event type's first segment MUST be a key here, and an unregistered org fails validation. Distinct from the `extensions` metadata key in the discovery payload (metadata[] row `extensions`, schemas/v2/capabilities.schema.json), which is a HOST's own extension map. An org in reservedOrgs is forbidden, never registered. An empty object is a well-formed registry that admits no vendor type \u2014 it is not a licence to skip the check.",
      "propertyNames": {
        "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$"
      },
      "additionalProperties": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "registered"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "registered": {
            "type": "string",
            "format": "date"
          },
          "reserved": {
            "type": "boolean",
            "description": "true when the org is held by the protocol and never assignable to a vendor."
          },
          "note": {
            "type": "string"
          }
        }
      }
    },
    "metadata": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key",
          "kind",
          "schema",
          "disposition"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "kind": {
            "const": "metadata"
          },
          "schema": {
            "type": "string"
          },
          "disposition": {
            "enum": [
              "kept",
              "deleted"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    },
    "families": {
      "type": "array",
      "items": {
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "key",
              "anchor",
              "reason"
            ],
            "properties": {
              "key": {
                "type": "string"
              },
              "anchor": {
                "const": "deleted"
              },
              "reason": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "key",
              "kind",
              "anchor",
              "section",
              "peerDependencyId",
              "witness",
              "maturity",
              "facets",
              "floorScenarios",
              "requirementIds"
            ],
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[a-z][A-Za-z0-9]*$"
              },
              "kind": {
                "const": "family"
              },
              "anchor": {
                "enum": [
                  "core",
                  "ext"
                ]
              },
              "section": {
                "type": "string"
              },
              "peerDependencyId": {
                "type": "string"
              },
              "extensionName": {
                "type": "string",
                "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
                "description": "RFC 0220 \u00a7A \u2014 ext families only (scripts/check-declaration.mjs requires it on every anchor: ext row and forbids it on core rows). The <name> half of the extensions key the family is advertised under, extensions[\"<org>.<extensionName>\"] (core/capabilities.md \u00a73.2). It is the kebab-case form of `key`: the family key is camelCase and cannot match extensionsKeyPattern, so a README that said `extensions.<org>.<key>` named a key no host could emit."
              },
              "witness": {
                "enum": [
                  "witnessable-unaided",
                  "witnessable-gated",
                  "seam-gated",
                  "claims-check",
                  "negative-existence"
                ]
              },
              "maturity": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "technical",
                  "adoption"
                ],
                "properties": {
                  "technical": {
                    "enum": [
                      "experimental",
                      "stable",
                      "deprecated"
                    ]
                  },
                  "adoption": {
                    "enum": [
                      "none",
                      "single-witness",
                      "multi-witness",
                      "independent"
                    ]
                  },
                  "until": {
                    "type": "string",
                    "pattern": "^2\\.[0-9]+$",
                    "description": "RFC 0197 \u00a7C.8 \u2014 the CORPUS shape-hold: no surface of this family is retired before this 2.x minor. scripts/check-v2-retirement.mjs R5 refuses a removal whose `removeIn` is not later than it. This is NOT the wire record\u2019s `until` (spec/v2/core/capabilities.md \u00a72), which is the host\u2019s own timeline for its own offer \u2014 two fields with one name and different owners, which is why the distinction is written here. No declaration row sets it today."
                  }
                }
              },
              "facets": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "floorScenarios": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "requirementIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "owningRfc": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "reason": {
                "type": "string"
              },
              "note": {
                "type": "string"
              },
              "disposition": {
                "enum": [
                  "externally-gated"
                ]
              },
              "facetsFrom": {
                "type": "string"
              },
              "normativeText": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "string",
                  "minLength": 1
                },
                "description": "Repo-relative path(s) to the document(s) where this family's normative text is actually written. The `section` pointer names the DECLARATION site (core/capabilities.md), which for most families is a stub naming an owning RFC; this names where a reader finds the behaviour. A path under `spec/v1/` means the family is orphaned at v1 end-of-support \u2014 counted and ratcheted by scripts/check-v2-normative-home.mjs."
              }
            }
          }
        ]
      }
    },
    "profiles": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "predicate"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "summary": {
            "type": "string",
            "minLength": 1,
            "description": "One plain-English line: what claiming this profile means to an adopter. Carried into spec/v2/profiles.json."
          },
          "predicate": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "families": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "metadata": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "floorScenarios": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "requirementIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "note": {
            "type": "string"
          }
        }
      }
    },
    "aliases": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "family"
        ],
        "properties": {
          "family": {
            "type": "string"
          },
          "facets": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "description": "Hand-decided alias rows for registry peer-dependency keys the mechanical rules (host.<family>, openwop.<family>.<facet>, <family>.<facet>) cannot explain (RFC 0177 \u00a7B.2)."
    }
  }
}
