{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://castles.bussetech.com/schema/registers.schema.json",
  "title": "Enumerable registers",
  "description": "The official inventories against which a band's count can be closed. A register is what makes an integer honest: it is a finite, citable list maintained by a named body, so 'complete' becomes a checkable claim rather than a hope. Registers are SUBJECTS OF STUDY as much as sources — Wikidata and OSM appear here precisely because other castle datasets treat them as ground truth and this one does not.",
  "type": "object",
  "required": ["version", "registers"],
  "additionalProperties": false,
  "properties": {
    "version": { "const": 1 },
    "registers": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/register" }
    }
  },
  "$defs": {
    "register": {
      "type": "object",
      "required": ["id", "name", "operator", "url", "jurisdiction", "enumerable", "inclusion_criterion", "accessed"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$" },
        "name": { "type": "string", "minLength": 1 },
        "operator": { "type": "string", "minLength": 1, "description": "The body that maintains it." },
        "url": { "type": "string", "pattern": "^https?://" },
        "jurisdiction": {
          "type": "string",
          "description": "ISO 3166-1 alpha-2, optionally with a subdivision (GB-ENG, GB-SCT, DE-SN, IE, FR, ES, JP). `WORLD` for global datasets."
        },
        "enumerable": {
          "type": "boolean",
          "description": "True when the full list can actually be obtained and counted (bulk download, API, or a published total). False for registers that exist but are not publicly enumerable — those still bound a band in principle while blocking closure in practice, which is itself worth publishing."
        },
        "entries_total": {
          "type": ["integer", "null"],
          "description": "Total entries in the register, ALL types — not the castle-relevant subset. Null when not published. Never estimate this field; an estimate belongs in a count claim with its basis, not here."
        },
        "entries_matching": {
          "type": ["integer", "null"],
          "description": "Entries matching this project's query for castle-relevant structures. Null until the query has actually been run and recorded in `query`."
        },
        "query": {
          "type": "string",
          "description": "The exact query, filter, or designation class used to derive entries_matching. Without this the number is not reproducible and must not be published."
        },
        "licence": { "type": "string", "description": "SPDX id or the licence's own name (e.g. OGL-UK-3.0, CC0-1.0, ODbL-1.0)." },
        "access": {
          "enum": ["bulk-download", "api", "search-only", "published-total", "offline"],
          "description": "How the list can be obtained. `search-only` means the register is browsable but not extractable — a real blocker on closure and reported as such."
        },
        "inclusion_criterion": {
          "type": "string",
          "minLength": 1,
          "description": "What the register itself counts and why — in ITS terms, not ours. The gap between this and a band's criterion is the whole reason coverage cells stay partial."
        },
        "accessed": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
        "note": { "type": "string" }
      }
    }
  }
}
