115 lines
5.6 KiB
JSON
115 lines
5.6 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://soleprint.local/atlas2/docgen/ir/schema.json",
|
|
"title": "docgen graph IR",
|
|
"description": "What a graph is. Structure only: no colour, no shape, no position. If a field would change between light and dark theme, it does not belong here. Extractors write this; emitters read it; neither knows the other exists.",
|
|
"type": "object",
|
|
"required": ["meta", "nodes", "edges"],
|
|
"additionalProperties": false,
|
|
|
|
"properties": {
|
|
"meta": {
|
|
"type": "object",
|
|
"required": ["source", "root", "schema_version"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"source": {
|
|
"type": "string",
|
|
"description": "Which extractor produced this: python, db, ..."
|
|
},
|
|
"root": {
|
|
"type": "string",
|
|
"description": "What it was pointed at. Relative where possible, so the IR is not machine-specific."
|
|
},
|
|
"schema_version": {
|
|
"type": "string",
|
|
"description": "This document's version. Bumped when the shape changes, so a consumer can refuse what it cannot read."
|
|
},
|
|
"generated_at": {
|
|
"type": ["string", "null"],
|
|
"description": "ISO-8601, or null. Null is the default and the honest one: a timestamp makes two extractions of the same tree differ, which breaks diffing. Set it only when the run time is itself the fact being recorded."
|
|
},
|
|
"larder": {
|
|
"type": ["object", "null"],
|
|
"description": "What was actually read: the first step of a book, and the only measure of the input this document carries. Absent means not measured, which a reader can act on; a zero would be a claim. Provenance lives here and never in nodes or attrs, the same split that keeps colour out of the IR.",
|
|
"required": ["kind", "identity", "unit", "seen", "read", "failed"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"kind": { "type": "string", "description": "Which extractor stocked it: python, code, db, openapi, usage." },
|
|
"identity": { "type": "string", "description": "The path, or a connection string with its credentials masked. Scrubbed at construction in book/larder.py and swept for in validate.py — the one field in the IR that could carry a secret." },
|
|
"unit": { "type": "string", "description": "What `seen` counts: file, table, path, entry, document. Closed vocabulary, so two larders can be compared." },
|
|
"seen": { "type": "integer", "description": "How many units the larder offered." },
|
|
"read": { "type": "integer", "description": "How many were consumed. Always seen - len(failed); derived on the dataclass so the two cannot disagree." },
|
|
"failed": {
|
|
"type": "array",
|
|
"description": "What could not be consumed, by name and reason. Named rather than counted: a count says a book is incomplete, a name says which part of it to distrust.",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["name", "error"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"error": { "type": "string" }
|
|
}
|
|
}
|
|
},
|
|
"extra": { "type": "object", "description": "Per-source facts that do not generalise: packages for a tree, dialect for a database, hosts for a capture." }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
"nodes": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["id", "kind", "label"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"description": "Fully qualified and stable across runs. Stability is what makes two graphs from two commits diffable."
|
|
},
|
|
"kind": {
|
|
"type": "string",
|
|
"description": "The hinge of the whole system, and the ONLY field style and layout may key on. Small closed vocabulary per domain: module/class/function, table/column/view. `external` means a name that could not be resolved."
|
|
},
|
|
"label": {
|
|
"type": "string",
|
|
"description": "Human-facing short name. Distinct from id on purpose."
|
|
},
|
|
"parent": {
|
|
"type": ["string", "null"],
|
|
"description": "Containment only — a module contains a class. Not a relationship; relationships are edges. Must name another node or be null."
|
|
},
|
|
"attrs": {
|
|
"type": "object",
|
|
"description": "Open bag for domain data. file/line live here and are what let a UI link a box to a line. Anything not needed by every consumer goes here.",
|
|
"default": {}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
"edges": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["source", "target", "kind"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"source": { "type": "string", "description": "A node id." },
|
|
"target": { "type": "string", "description": "A node id." },
|
|
"kind": {
|
|
"type": "string",
|
|
"description": "Small closed vocabulary per domain: imports, inherits, calls / foreign_key, references."
|
|
},
|
|
"attrs": { "type": "object", "default": {} }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
"$comment": "Field names are shaped toward Cytoscape's {data: {id, source, target}} so an interactive canvas is nearly free. The banned-field list lives in validate.py, not here, because JSON Schema can say what is allowed but not why."
|
|
}
|