{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://glvtl.com/records-sources/schema.json",
  "title": "Records source entry",
  "description": "One public property-records source a CRE broker can open. This is the authoritative shape of a single object in data/harvest/<market>.json and in data/sources.json. See SPEC-RECORDS-SOURCE-INDEX.md section 4.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "market",
    "jurisdiction",
    "category",
    "agency",
    "url",
    "answers",
    "level",
    "checked"
  ],
  "properties": {
    "market": {
      "type": "string",
      "description": "Market slug. Must be one of the ten in data/markets.json.",
      "enum": [
        "sarasota-bradenton",
        "nashville",
        "charlotte",
        "austin",
        "huntsville",
        "atlanta",
        "charleston",
        "dallas-fort-worth",
        "miami",
        "houston"
      ]
    },
    "jurisdiction": {
      "type": "string",
      "minLength": 3,
      "description": "The county or city that owns the record, spelled as the agency spells it. Include the state for counties whose name repeats across markets, e.g. 'Williamson County, TN'.",
      "examples": [
        "Sarasota County",
        "City of Sarasota",
        "Williamson County, TN"
      ]
    },
    "category": {
      "type": "string",
      "description": "Which broker question this source answers. Rendering order on the page is fixed: parcel, zoning, future_land_use, flood, tax, permits, recorded_documents.",
      "enum": [
        "parcel",
        "zoning",
        "future_land_use",
        "flood",
        "tax",
        "recorded_documents",
        "permits"
      ]
    },
    "agency": {
      "type": "string",
      "minLength": 3,
      "description": "The office as it names itself. Not a description; the official name.",
      "examples": [
        "Sarasota County Tax Collector"
      ]
    },
    "url": {
      "type": "string",
      "format": "uri",
      "pattern": "^https?://",
      "description": "The page a human clicks. Not a machine endpoint. Deep links are fine if they survive; prefer the stable entry page over a session URL."
    },
    "answers": {
      "type": "string",
      "minLength": 8,
      "description": "The question this source answers, phrased as a broker would ask it. One sentence.",
      "examples": [
        "Is the tax paid, and what does the bill history show?"
      ]
    },
    "level": {
      "type": "string",
      "description": "Verification level, inherited verbatim from /cre-property-records/. A level must describe what the check actually did.",
      "enum": [
        "page_opened",
        "form_reached",
        "locate_only",
        "lookup_completed",
        "blocked"
      ]
    },
    "checked": {
      "type": "string",
      "format": "date",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
      "description": "UTC date a HUMAN opened this URL and confirmed the level. Never written by a script. check-sources.mjs proposes changes; a person verifies and re-dates."
    },
    "route": {
      "type": "string",
      "description": "Optional. The clicks between the entry page and the record.",
      "examples": [
        "Follow View/Pay Taxes, wait for the hub tiles, choose Property Tax."
      ]
    },
    "identifier": {
      "type": "string",
      "description": "Optional. What you need in hand to search: account number, PIN, parcel ID, address.",
      "examples": [
        "Real-estate account number"
      ]
    },
    "gotcha": {
      "type": "string",
      "description": "Optional but the highest-value field on the page. The thing that wastes ten minutes if you do not know it. Rendered with prominence, not as a footnote.",
      "examples": [
        "Check REAL PROPERTY in Manatee; a tangible-personal-property account is not the land record."
      ]
    },
    "glove": {
      "type": "string",
      "description": "DERIVED, not hand-set (spec section 4). scripts/lib/glove.mjs computes this from the promoted maps in ~/glove/maps. A value supplied by a harvester is preserved in `glove_claimed` for comparison and then overwritten. Default when nothing can be derived: manual.",
      "enum": [
        "covered",
        "partial",
        "manual"
      ]
    },
    "note": {
      "type": "string",
      "description": "Optional. Anything the harvester wants the maintainer to know that is not for publication."
    },
    "glove_claimed": {
      "type": "string",
      "enum": [
        "covered",
        "partial",
        "manual"
      ],
      "description": "Set by the build only: the value a harvester hand-set for `glove`, kept for comparison against the derived value. Harvesters should not write this field."
    }
  }
}
