Introduce a machine-readable layer on top of the markdown corpus so AI/scripts can query a topic's facts without re-reading whole sources (anti-RAG stays for synthesis/quotes). - md/demonology/demons.json: fact-cache, 33 entities attested in 2+ sources, each with rank/domain/signs/origins + provenance (sources, citations). - md/demonology/demons.schema.json: JSON Schema for the dataset. - md/demonology/INDEX.md: topic front-door (query JSON -> synthesis -> source). - validate.py: generic schema + house-rule validator (source_count, cross_refs, unique ids); discovers <name>.schema.json/<name>.json pairs across all topics. - docs/data-convention.md: the reusable, topic-agnostic pattern + how to add it to a new topic. - CLAUDE.md: pointer so the convention is picked up every session. - requirements.txt: add jsonschema (used by validate.py). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
139 lines
5.2 KiB
JSON
139 lines
5.2 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://demonology.local/demons.schema.json",
|
|
"title": "Demonology Entity Dataset",
|
|
"description": "Machine-readable roster of demonic entities cross-referenced across the source documents in md/demonology/. Each record is a fact-cache derived from whole-file reads of the sources, so AI agents can query attributes without re-reading the corpus. Scope: entities attested in 2 or more sources.",
|
|
"type": "object",
|
|
"required": ["meta", "sources", "demons"],
|
|
"properties": {
|
|
"meta": {
|
|
"type": "object",
|
|
"required": ["topic", "scope", "rank_ladder", "generated_from"],
|
|
"properties": {
|
|
"topic": { "type": "string" },
|
|
"scope": {
|
|
"type": "string",
|
|
"description": "Inclusion rule for this dataset, e.g. 'entities attested in 2+ sources'."
|
|
},
|
|
"rank_ladder": {
|
|
"type": "array",
|
|
"description": "Classical grimoire ranks, highest authority first.",
|
|
"items": { "type": "string" }
|
|
},
|
|
"generated_from": {
|
|
"type": "array",
|
|
"description": "Sibling files this data was derived from / kept in sync with.",
|
|
"items": { "type": "string" }
|
|
},
|
|
"caveat": { "type": "string" }
|
|
}
|
|
},
|
|
"sources": {
|
|
"type": "object",
|
|
"description": "Map of source code -> human-readable source description. Codes are used in each demon's 'sources' array.",
|
|
"patternProperties": {
|
|
"^[A-Z]{2}$": { "type": "string" }
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"demons": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/demon" }
|
|
}
|
|
},
|
|
"$defs": {
|
|
"sourceCode": {
|
|
"type": "string",
|
|
"enum": ["FG", "DM", "PU", "SP", "DJ", "EN"]
|
|
},
|
|
"demon": {
|
|
"type": "object",
|
|
"required": ["id", "name", "type", "tier", "domain", "signs", "origins", "sources", "source_count", "citations"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"pattern": "^[a-z0-9-]+$",
|
|
"description": "Stable kebab-case identifier; used as the target of cross_refs."
|
|
},
|
|
"name": { "type": "string", "description": "Primary / most common name." },
|
|
"aliases": {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"enum": ["individual", "collective", "class", "title"],
|
|
"description": "individual = a single named entity; collective = a named group acting as one (e.g. Legion); class = a category of spirits (e.g. Shedim); title = an honorific/role applied to others."
|
|
},
|
|
"tier": {
|
|
"type": "string",
|
|
"enum": ["supreme-prince", "directional-king", "goetia-officer", "watcher", "other-recurring"],
|
|
"description": "Which sub-hierarchy this entity belongs to in the synthesis."
|
|
},
|
|
"rank": {
|
|
"type": ["string", "null"],
|
|
"description": "Grimoire rank as stated (King, Duke, Prince, Marquis, Earl, President, Knight, Emperor, etc.). Null if none stated."
|
|
},
|
|
"legions": {
|
|
"type": ["integer", "null"],
|
|
"description": "Number of legions commanded, if stated."
|
|
},
|
|
"goetia_number": {
|
|
"type": ["integer", "null"],
|
|
"minimum": 1,
|
|
"maximum": 72,
|
|
"description": "Position among the 72 Spirits of the Ars Goetia, if applicable."
|
|
},
|
|
"deadly_sin": {
|
|
"type": ["string", "null"],
|
|
"description": "Associated deadly sin, for the seven Princes of Hell scheme."
|
|
},
|
|
"domain": {
|
|
"type": "array",
|
|
"description": "Spheres of influence / what it governs.",
|
|
"items": { "type": "string" }
|
|
},
|
|
"signs": {
|
|
"type": "array",
|
|
"description": "Identifying marks: apparition form, associations, sigils, manner of manifesting.",
|
|
"items": { "type": "string" }
|
|
},
|
|
"origins": {
|
|
"type": "string",
|
|
"description": "Etymology, backstory, derivation from earlier deities/traditions."
|
|
},
|
|
"sources": {
|
|
"type": "array",
|
|
"minItems": 2,
|
|
"uniqueItems": true,
|
|
"items": { "$ref": "#/$defs/sourceCode" },
|
|
"description": "Source codes that name this entity. Scope rule requires length >= 2."
|
|
},
|
|
"source_count": {
|
|
"type": "integer",
|
|
"minimum": 2,
|
|
"description": "Number of distinct sources; must equal sources.length."
|
|
},
|
|
"citations": {
|
|
"type": "object",
|
|
"description": "Map of source code -> location string within that source file.",
|
|
"patternProperties": {
|
|
"^[A-Z]{2}$": { "type": "string" }
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"cross_refs": {
|
|
"type": "array",
|
|
"description": "ids of entities this one is identified/conflated with, or commands/serves.",
|
|
"items": { "type": "string", "pattern": "^[a-z0-9-]+$" }
|
|
},
|
|
"notes": {
|
|
"type": ["string", "null"],
|
|
"description": "Uncertainty flags, conflation warnings, or scope caveats."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|