JSON reports: ascribe diff and ascribe drift
The JSON Schemas of what ascribe diff and ascribe drift write with --format json.
For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.
Part of the JSON report contract, whose rules every document here follows.
ascribe diff
{
"$defs": {
"Anchor": {
"description": "Where a block's text is written: the README's anchor grammar, the same\nstring the rendered page carries in `data-ascribe-source` and\n`data-ascribe-via`.",
"properties": {
"source": {
"description": "`<path>:<first>-<last>`: the file's content path, percent-encoded by\nsegment, and the block's first and last lines, from 1.",
"type": "string"
},
"via": {
"description": "The includes the block came through, outermost first, each\n`<path>:<line>`. Empty for a block written in the page itself.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"source",
"via"
],
"type": "object"
},
"BaseInfo": {
"description": "The base of a comparison, in the report.",
"properties": {
"commit": {
"description": "The commit it names.",
"type": "string"
},
"merge_base": {
"description": "The merge base of that commit and `HEAD`, which the comparison reads;\n`null` with `--base-exact`, which reads `commit`.",
"type": [
"string",
"null"
]
},
"requested": {
"description": "The revision asked for, or the default branch used.",
"type": "string"
}
},
"required": [
"requested",
"commit",
"merge_base"
],
"type": "object"
},
"BuildDiff": {
"description": "What changed in one build.",
"properties": {
"build": {
"description": "The build's name.",
"type": "string"
},
"pages": {
"description": "The pages that changed, in path order.",
"items": {
"$ref": "#/$defs/PageDiff"
},
"type": "array"
}
},
"required": [
"build",
"pages"
],
"type": "object"
},
"Change": {
"description": "One block's change.",
"properties": {
"after": {
"anyOf": [
{
"$ref": "#/$defs/Anchor"
},
{
"type": "null"
}
],
"description": "For a removed block, and a moved block's old place: the block of the\nnew version it came after, among its siblings. Absent when it was\nfirst."
},
"kind": {
"$ref": "#/$defs/ChangeKind",
"description": "What happened to it."
},
"now": {
"anyOf": [
{
"$ref": "#/$defs/Anchor"
},
{
"type": "null"
}
],
"description": "Where it's written now: every kind but `removed`."
},
"parent": {
"anyOf": [
{
"$ref": "#/$defs/Anchor"
},
{
"type": "null"
}
],
"description": "For a removed block, and a moved block's old place: the block of the\nnew version it was inside. Absent at the top of the page."
},
"text": {
"description": "For a removed block, its text, whitespace collapsed, so it can be\nshown where it was.",
"type": [
"string",
"null"
]
},
"was": {
"anyOf": [
{
"$ref": "#/$defs/Anchor"
},
{
"type": "null"
}
],
"description": "Where it was written: every kind but `added`."
},
"words": {
"anyOf": [
{
"$ref": "#/$defs/Words"
},
{
"type": "null"
}
],
"description": "For changed prose, the words that differ."
}
},
"required": [
"kind"
],
"type": "object"
},
"ChangeKind": {
"description": "The kinds of block change.",
"oneOf": [
{
"const": "changed",
"description": "The block's content changed.",
"type": "string"
},
{
"const": "added",
"description": "The block is new.",
"type": "string"
},
{
"const": "removed",
"description": "The block is gone.",
"type": "string"
},
{
"const": "moved",
"description": "The same block is somewhere else.",
"type": "string"
}
]
},
"Counts": {
"description": "How many changes of each kind a page has.",
"properties": {
"added": {
"description": "Blocks only in the new version.",
"minimum": 0,
"type": "integer"
},
"changed": {
"description": "Blocks whose content changed.",
"minimum": 0,
"type": "integer"
},
"moved": {
"description": "Blocks in both, somewhere else.",
"minimum": 0,
"type": "integer"
},
"removed": {
"description": "Blocks only in the old version.",
"minimum": 0,
"type": "integer"
}
},
"required": [
"changed",
"added",
"removed",
"moved"
],
"type": "object"
},
"MovedPage": {
"description": "A page that moved: where it was, and where it is, by content path.",
"properties": {
"from": {
"description": "Where it was, in the base.",
"type": "string"
},
"to": {
"description": "Where it is now.",
"type": "string"
}
},
"required": [
"from",
"to"
],
"type": "object"
},
"PageDiff": {
"description": "What changed on one page of a build.",
"properties": {
"because": {
"description": "The other changed files the page's change can come from: fragments it\nincludes and pages its links take a title or a heading from, in path\norder; then the snippets whose code changed, by address\n(`code:app.py#main`); then the operations of a declared API whose spec\nchanged what the page writes for them, an `@operation`'s method, path,\nsummary, or deprecation, or a link's address, each by its spec's path\nfrom the project root, `#`, and the fragment the page names it by\n(`api/openapi.yaml#createWidget`); and `ascribe.toml` (last) when the\ncontent model is a cause. The kinds are told apart by their form: a\ncontent file's path ends in `.md`, a snippet's address has its\nsource's name and `:` before its path, and an operation's has a `#`\nand neither.",
"items": {
"type": "string"
},
"type": "array"
},
"changes": {
"description": "The block-level changes, in the page's order, a removed block where\nit was. Empty for an added or removed page, and for every page when\nthe report's `blocks_omitted` is `true`.",
"items": {
"$ref": "#/$defs/Change"
},
"type": "array"
},
"counts": {
"$ref": "#/$defs/Counts",
"description": "How many changes of each kind."
},
"moved_from": {
"description": "For a page that moved, the content path it was at: git sees its\nfile renamed, or `ascribe.addresses` records the move. The page is\nlisted once, at `path`, compared with the page as it was at\n`moved_from`; `status` is `changed`, and its `counts` and `changes`\nare what differs besides the move, none for a page that only moved.\nAbsent for a page that didn't move.",
"type": [
"string",
"null"
]
},
"moved_links": {
"description": "The pages among `because` whose only change is that they moved, each\nby where it was and where it is: a link on this page leads to its new\naddress. Absent when there's none.",
"items": {
"$ref": "#/$defs/MovedPage"
},
"type": "array"
},
"own_file_changed": {
"description": "Whether the page's own file changed (or exists on one side only).",
"type": "boolean"
},
"page_changed": {
"description": "What changed about the page itself besides its blocks, in this\norder: `title`, `frontmatter`, `availability` (the page-level one),\nand `route`. Empty for an added or removed page. A moved page's\nroute moves with it, so it isn't listed for one.",
"items": {
"type": "string"
},
"type": "array"
},
"path": {
"description": "The page's content path.",
"type": "string"
},
"route": {
"description": "Its route: the new one, or for a removed page the old one.",
"type": "string"
},
"status": {
"$ref": "#/$defs/PageStatus",
"description": "Whether the build publishes it only now, only before, or in both\nwith a difference."
}
},
"required": [
"path",
"route",
"status",
"own_file_changed",
"because",
"page_changed",
"counts",
"changes"
],
"type": "object"
},
"PageStatus": {
"description": "Whether a page is new, gone, or different.",
"oneOf": [
{
"const": "added",
"description": "The build publishes it now, and didn't before.",
"type": "string"
},
{
"const": "removed",
"description": "The build published it before, and doesn't now.",
"type": "string"
},
{
"const": "changed",
"description": "The build publishes it in both, and it differs.",
"type": "string"
}
]
},
"RepositoryInfo": {
"description": "The repository, in the report.",
"properties": {
"project_prefix": {
"description": "The project's folder inside it, with a trailing `/`, or empty when\nthe project is at the repository's root.",
"type": "string"
},
"root": {
"description": "The repository's top-level directory, as git prints it.",
"type": "string"
}
},
"required": [
"root",
"project_prefix"
],
"type": "object"
},
"Words": {
"description": "The words that differ inside a changed block of prose. Ranges are\n`[start, end)` in characters (Unicode scalar values) of each side's\n`text`, which is the block's text with whitespace collapsed.",
"properties": {
"now": {
"description": "Ranges in `now_text`: words added or replacing others.",
"items": {
"items": {
"minimum": 0,
"type": "integer"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
},
"type": "array"
},
"now_text": {
"description": "The block's text now.",
"type": "string"
},
"was": {
"description": "Ranges in `was_text`: words removed or replaced.",
"items": {
"items": {
"minimum": 0,
"type": "integer"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
},
"type": "array"
},
"was_text": {
"description": "The block's text before.",
"type": "string"
}
},
"required": [
"now",
"was",
"now_text",
"was_text"
],
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "The whole report, as `ascribe diff --format json` writes it.",
"properties": {
"ascribe_version": {
"description": "The version of Ascribe that wrote it.",
"type": "string"
},
"base": {
"$ref": "#/$defs/BaseInfo",
"description": "What was compared with."
},
"blocks_omitted": {
"description": "Whether every page's `changes` was left empty, as `ascribe diff\n--pages-only` leaves them: its `counts` still count them. Only there\nwhen it's `true`.",
"type": "boolean"
},
"builds": {
"description": "What changed, per build, in the order asked for.",
"items": {
"$ref": "#/$defs/BuildDiff"
},
"type": "array"
},
"repository": {
"$ref": "#/$defs/RepositoryInfo",
"description": "Where the project is."
},
"schema_version": {
"description": "`SCHEMA_VERSION`.",
"minimum": 0,
"type": "integer"
},
"working_tree_errors": {
"description": "How many errors `ascribe check` finds in the working tree, for the\nbuilds compared. The comparison runs regardless, but a page with an\nerror may not render as it will once it's fixed, so a reviewer should\nknow. `diff_project` sets it; `Report::new` leaves it zero.",
"minimum": 0,
"type": "integer"
}
},
"required": [
"schema_version",
"ascribe_version",
"base",
"repository",
"working_tree_errors",
"builds"
],
"title": "DiffReport",
"type": "object"
}
ascribe drift
{
"$defs": {
"BaseInfo": {
"description": "The base of a comparison, in the report.",
"properties": {
"commit": {
"description": "The commit it names.",
"type": "string"
},
"merge_base": {
"description": "The merge base of that commit and `HEAD`, which the comparison reads;\n`null` with `--base-exact`, which reads `commit`.",
"type": [
"string",
"null"
]
},
"requested": {
"description": "The revision asked for, or the default branch used.",
"type": "string"
}
},
"required": [
"requested",
"commit",
"merge_base"
],
"type": "object"
},
"BrokenExample": {
"description": "A snippet that resolved at the base and doesn't in the working tree: its\nregion, file, or source is gone.",
"properties": {
"address": {
"description": "Its address, as the page writes it.",
"type": "string"
},
"problem": {
"description": "The slug of the diagnostic `ascribe check` reports for it\n(`snippet-region-missing`).",
"type": "string"
},
"reason": {
"description": "Why it doesn't resolve, in a few words.",
"type": "string"
},
"source": {
"description": "The source the address names.",
"type": "string"
}
},
"required": [
"address",
"source",
"problem",
"reason"
],
"type": "object"
},
"ChangedExample": {
"description": "A snippet whose code changed.",
"properties": {
"added": {
"description": "Lines only in the code now.",
"minimum": 0,
"type": "integer"
},
"address": {
"description": "Its address, as the page writes it.",
"type": "string"
},
"file": {
"description": "The code file now, from the repository's root.",
"type": "string"
},
"removed": {
"description": "Lines only in the code at the base.",
"minimum": 0,
"type": "integer"
},
"source": {
"description": "The source the address names.",
"type": "string"
},
"was_file": {
"description": "The code file at the base, when it was renamed since.",
"type": [
"string",
"null"
]
}
},
"required": [
"address",
"source",
"file",
"was_file",
"added",
"removed"
],
"type": "object"
},
"DriftPage": {
"description": "A page with an example that changed or broke.",
"properties": {
"broken": {
"description": "The examples that resolved at the base and don't now, by address.",
"items": {
"$ref": "#/$defs/BrokenExample"
},
"type": "array"
},
"builds": {
"description": "The builds that show it with a changed example, in the order asked\nfor.",
"items": {
"type": "string"
},
"type": "array"
},
"examples": {
"description": "The examples that changed, by address. Empty when only `broken`\nisn't.",
"items": {
"$ref": "#/$defs/ChangedExample"
},
"type": "array"
},
"page_changed": {
"description": "Whether the page changed apart from its examples: its own file, or\nits resolved content through anything else it uses (a fragment, the\ncontent model), in any of those builds. When it didn't, the example\nchanged and the words around it didn't.",
"type": "boolean"
},
"path": {
"description": "The page's content path.",
"type": "string"
},
"route": {
"description": "Its route, in the first of the builds it's in.",
"type": "string"
}
},
"required": [
"path",
"route",
"builds",
"page_changed",
"examples",
"broken"
],
"type": "object"
},
"RepositoryInfo": {
"description": "The repository, in the report.",
"properties": {
"project_prefix": {
"description": "The project's folder inside it, with a trailing `/`, or empty when\nthe project is at the repository's root.",
"type": "string"
},
"root": {
"description": "The repository's top-level directory, as git prints it.",
"type": "string"
}
},
"required": [
"root",
"project_prefix"
],
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "The report, as `ascribe drift --format json` writes it.",
"properties": {
"ascribe_version": {
"description": "The version of Ascribe that wrote it.",
"type": "string"
},
"base": {
"$ref": "#/$defs/BaseInfo",
"description": "What was compared with."
},
"pages": {
"description": "Every page with an example that changed or broke, in path order.",
"items": {
"$ref": "#/$defs/DriftPage"
},
"type": "array"
},
"repository": {
"$ref": "#/$defs/RepositoryInfo",
"description": "Where the project is."
},
"schema_version": {
"description": "`DRIFT_SCHEMA_VERSION`.",
"minimum": 0,
"type": "integer"
}
},
"required": [
"schema_version",
"ascribe_version",
"base",
"repository",
"pages"
],
"title": "DriftReport",
"type": "object"
}