JSON report contract

The JSON Schemas of what the ascribe commands write with --format json.

For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.

The commands that take --format json each write one JSON document, and this contract gives each document’s schema. The schemas are JSON Schema (draft 2020-12), generated from the code that writes the documents, so the two can’t disagree. They’re in the repository’s schemas/ folder. The command reference says what each command does, with an example of its document.

Every document follows these rules:

  • schema_version changes only when a field is removed or changes meaning. Fields can be added without a new version, so ignore fields you don’t know. The schemas allow fields they don’t list.
  • A field whose values are a fixed set of names, such as a diagnostic’s severity or next, may gain a value without a new version. A reader treats a value it doesn’t know as the field says: an unknown severity as advice, which never fails a check.
  • A field the schema doesn’t list as required is left out when it has no value. A required field that can be null is always written.
  • Keys are snake_case.

The pages are grouped as the ascribe.toml reference is, by what the commands are for:

ascribe check and ascribe build

schemas/check.schema.json:

{
  "$defs": {
    "AcknowledgedEntry": {
      "description": "A problem acknowledged as intended.",
      "properties": {
        "at": {
          "$ref": "#/$defs/Place",
          "description": "Where the acknowledgement is written."
        },
        "builds": {
          "description": "The builds it appears in, as a diagnostic's `builds` are.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "code": {
          "description": "The code of the check that found it, such as `ASC036`.",
          "type": "string"
        },
        "file": {
          "description": "The file, as a diagnostic's `file` is.",
          "type": "string"
        },
        "message": {
          "description": "What the check found.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "Where in the file."
        },
        "reason": {
          "description": "Why it's intended: the acknowledgement's reason.",
          "type": "string"
        },
        "slug": {
          "description": "The check's name.",
          "type": "string"
        }
      },
      "required": [
        "code",
        "slug",
        "message",
        "file",
        "range",
        "builds",
        "reason",
        "at"
      ],
      "type": "object"
    },
    "CodeCount": {
      "description": "How many diagnostics have one code.",
      "properties": {
        "code": {
          "description": "The code, such as `ASC036`.",
          "type": "string"
        },
        "count": {
          "description": "How many.",
          "minimum": 0,
          "type": "integer"
        },
        "severity": {
          "description": "`error`, `warning`, or `advice`.",
          "type": "string"
        },
        "slug": {
          "description": "The diagnostic's name, such as `link-target-missing`.",
          "type": "string"
        }
      },
      "required": [
        "code",
        "slug",
        "severity",
        "count"
      ],
      "type": "object"
    },
    "Edit": {
      "description": "One edit of a fix.",
      "properties": {
        "new_text": {
          "description": "The text that replaces it.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "The text it replaces."
        }
      },
      "required": [
        "range",
        "new_text"
      ],
      "type": "object"
    },
    "Entry": {
      "description": "A diagnostic.",
      "properties": {
        "builds": {
          "description": "The builds a page-level diagnostic appears in, in `ascribe.toml`'s\norder. Empty for a file-level diagnostic, and for one in content no\nbuild publishes. With `--build`, only that build.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "code": {
          "description": "The code, such as `ASC036`.",
          "type": "string"
        },
        "docs": {
          "description": "The address of its entry in the diagnostics reference: the docs'\npermalink for its code, such as `https://ascribed-dev.com/go/ASC036/`,\nwhich forwards to the entry wherever it is.",
          "type": "string"
        },
        "file": {
          "description": "The file, relative to the project root (the directory of\n`ascribe.toml`), with `/` separators. `ascribe.toml` for a\ncontent-model problem.",
          "type": "string"
        },
        "fixes": {
          "description": "Edits that would fix it.",
          "items": {
            "$ref": "#/$defs/Fix"
          },
          "type": "array"
        },
        "help": {
          "description": "How to fix it, in general: the diagnostics reference's advice for its\ncode, in Markdown, whose links are the docs' permalinks, such as\n`https://ascribed-dev.com/go/22-checks/`.",
          "type": "string"
        },
        "message": {
          "description": "What's wrong, and what to do about it.",
          "type": "string"
        },
        "next": {
          "description": "The kind of next step: `fix` when Ascribe can make the edit,\n`choose` when the author picks among things Ascribe can list,\n`write` when it needs writing or judgment, `outside` when nothing in\nthe source can fix it, and `review` when it may be fine as it is.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "Where in the file."
        },
        "related": {
          "description": "Other places that explain it.",
          "items": {
            "$ref": "#/$defs/Related"
          },
          "type": "array"
        },
        "repeats": {
          "description": "For a problem in included content reported because one of its related\nplaces is in a path named: at how many other includes it's reported\ntoo, collapsed into this one. `0` otherwise.",
          "minimum": 0,
          "type": "integer"
        },
        "rule": {
          "description": "The rule of the program that found it, such as `Ascribe.Repeated`\nfrom Vale, for a `prose` diagnostic. Absent for Ascribe's own.",
          "type": [
            "string",
            "null"
          ]
        },
        "severity": {
          "description": "`error`, `warning`, or `advice`. Advice never fails the command.\nMore severities may be added; a reader treats one it doesn't know as\nit treats advice.",
          "type": "string"
        },
        "slug": {
          "description": "The diagnostic's name, such as `link-target-missing`.",
          "type": "string"
        },
        "unpublished": {
          "description": "Whether it's in content that no build publishes.",
          "type": "boolean"
        }
      },
      "required": [
        "code",
        "slug",
        "severity",
        "next",
        "message",
        "file",
        "range",
        "related",
        "fixes",
        "builds",
        "unpublished",
        "help",
        "docs",
        "repeats"
      ],
      "type": "object"
    },
    "FileCount": {
      "description": "How many diagnostics are in one file.",
      "properties": {
        "advice": {
          "description": "How many advice.",
          "minimum": 0,
          "type": "integer"
        },
        "errors": {
          "description": "How many errors.",
          "minimum": 0,
          "type": "integer"
        },
        "file": {
          "description": "The file, as a diagnostic's `file` is.",
          "type": "string"
        },
        "warnings": {
          "description": "How many warnings.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "file",
        "errors",
        "warnings",
        "advice"
      ],
      "type": "object"
    },
    "Fix": {
      "description": "Edits that would fix a diagnostic.",
      "properties": {
        "applicability": {
          "description": "`safe` when applying the edits as they are can't change what the page\nsays and leaves nothing to decide; `unsafe` otherwise.",
          "type": "string"
        },
        "edits": {
          "description": "The edits, each replacing the text of its range.",
          "items": {
            "$ref": "#/$defs/Edit"
          },
          "type": "array"
        },
        "file": {
          "description": "The file the edits are in, as a diagnostic's `file` is.",
          "type": "string"
        },
        "title": {
          "description": "What the fix does.",
          "type": "string"
        }
      },
      "required": [
        "title",
        "file",
        "edits",
        "applicability"
      ],
      "type": "object"
    },
    "FixedEntry": {
      "description": "A safe fix `--fix` applied.",
      "properties": {
        "code": {
          "description": "The code of the diagnostic it fixed, such as `ASC036`.",
          "type": "string"
        },
        "file": {
          "description": "The file it changed, as a diagnostic's `file` is.",
          "type": "string"
        },
        "slug": {
          "description": "The diagnostic's name.",
          "type": "string"
        },
        "title": {
          "description": "What the fix did.",
          "type": "string"
        }
      },
      "required": [
        "code",
        "slug",
        "title",
        "file"
      ],
      "type": "object"
    },
    "Place": {
      "description": "A place in a file.",
      "properties": {
        "file": {
          "description": "The file, as a diagnostic's `file` is.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "Where in the file."
        }
      },
      "required": [
        "file",
        "range"
      ],
      "type": "object"
    },
    "Pos": {
      "description": "A position in a file.",
      "properties": {
        "column": {
          "description": "The column, from 1, in Unicode characters (not bytes or UTF-16\nunits).",
          "minimum": 0,
          "type": "integer"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "offset": {
          "description": "The byte offset from the start of the file.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "line",
        "column",
        "offset"
      ],
      "type": "object"
    },
    "Range": {
      "description": "A span of a file. `end` is just past its last character; an empty range\n(an insertion) has equal positions.",
      "properties": {
        "end": {
          "$ref": "#/$defs/Pos",
          "description": "Just past its last character."
        },
        "start": {
          "$ref": "#/$defs/Pos",
          "description": "Its first character."
        }
      },
      "required": [
        "start",
        "end"
      ],
      "type": "object"
    },
    "Related": {
      "description": "Another place that explains a diagnostic.",
      "properties": {
        "file": {
          "description": "The file, as a diagnostic's `file` is.",
          "type": "string"
        },
        "message": {
          "description": "What it has to do with the diagnostic.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "Where in the file."
        }
      },
      "required": [
        "file",
        "range",
        "message"
      ],
      "type": "object"
    },
    "Summary": {
      "description": "How many diagnostics of each severity.",
      "properties": {
        "acknowledged": {
          "description": "How many problems are acknowledged as intended. Left out when there\nare none.",
          "minimum": 0,
          "type": "integer"
        },
        "advice": {
          "description": "How many advice.",
          "minimum": 0,
          "type": "integer"
        },
        "baselined": {
          "description": "How many problems `ascribe.baseline` holds back, which `diagnostics`\nleaves out and which are advice. Left out when there are none, and\nwith `--no-baseline`.",
          "minimum": 0,
          "type": "integer"
        },
        "by_code": {
          "description": "With `--summary`: how many diagnostics have each code, most first.",
          "items": {
            "$ref": "#/$defs/CodeCount"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "by_file": {
          "description": "With `--summary`: how many diagnostics are in each file, most first.",
          "items": {
            "$ref": "#/$defs/FileCount"
          },
          "type": [
            "array",
            "null"
          ]
        },
        "errors": {
          "description": "How many errors.",
          "minimum": 0,
          "type": "integer"
        },
        "warnings": {
          "description": "How many warnings.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "errors",
        "warnings",
        "advice"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe check --format json` and `ascribe build --format json`\nwrite: one document, whatever the outcome. Fields can be added without a\nnew `schema_version`, so a reader ignores fields it doesn't know.",
  "properties": {
    "acknowledged": {
      "description": "The problems acknowledged as intended, which `diagnostics` leaves\nout and which don't fail the command, in file order. Left out when\nthere are none, and with `--summary`.",
      "items": {
        "$ref": "#/$defs/AcknowledgedEntry"
      },
      "type": "array"
    },
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "builds_checked": {
      "description": "The builds whose page-level checks ran, in `ascribe.toml`'s order:\nevery build, the ones named with `--build`, or the editor's with\n`--editor-build`. Empty when the project couldn't be checked.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "diagnostics": {
      "description": "Every diagnostic, in file order, and in source order within a file;\nadvice after the errors and warnings, in the same order.\nWith paths, only those that count for them. With `--summary`, none:\nsee `truncated`.",
      "items": {
        "$ref": "#/$defs/Entry"
      },
      "type": "array"
    },
    "error": {
      "description": "Why the project couldn't be checked (exit code 2), or `null`. When it\nisn't `null`, `diagnostics` holds what was found first: the content\nmodel's problems.",
      "type": [
        "string",
        "null"
      ]
    },
    "files_checked": {
      "description": "How many source files were checked: the project's.",
      "minimum": 0,
      "type": "integer"
    },
    "files_reported": {
      "description": "How many of them the report covers: the source files in the paths\nnamed, or every one when no path was.",
      "minimum": 0,
      "type": "integer"
    },
    "fixed": {
      "description": "With `--fix`, the safe fixes it applied, in file order and, within a\nfile, in the order of their diagnostics; the rest of the report is\nthe check after them. With `--dry-run`, the fixes it would apply,\nnone of which was, and the check is of the files as they are. Left\nout when there's none.",
      "items": {
        "$ref": "#/$defs/FixedEntry"
      },
      "type": "array"
    },
    "next_command": {
      "description": "The command that lists the ones left out, when `truncated`; `null`\notherwise.",
      "type": [
        "string",
        "null"
      ]
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "shown": {
      "description": "How many diagnostics `diagnostics` lists.",
      "minimum": 0,
      "type": "integer"
    },
    "summary": {
      "$ref": "#/$defs/Summary",
      "description": "How many errors, warnings, and advice."
    },
    "total": {
      "description": "How many diagnostics there are.",
      "minimum": 0,
      "type": "integer"
    },
    "truncated": {
      "description": "Whether `diagnostics` leaves some out. It does with `--summary`,\nwhich lists none.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "error",
    "files_checked",
    "files_reported",
    "builds_checked",
    "diagnostics",
    "truncated",
    "shown",
    "total",
    "next_command",
    "summary"
  ],
  "title": "CheckReport",
  "type": "object"
}

ascribe fmt

schemas/fmt.schema.json:

{
  "$defs": {
    "Edit": {
      "description": "One edit of a fix.",
      "properties": {
        "new_text": {
          "description": "The text that replaces it.",
          "type": "string"
        },
        "range": {
          "$ref": "#/$defs/Range",
          "description": "The text it replaces."
        }
      },
      "required": [
        "range",
        "new_text"
      ],
      "type": "object"
    },
    "FmtFile": {
      "description": "A file and the edits that format it.",
      "properties": {
        "edits": {
          "description": "The edits, each replacing the text of its range in the file as it\nwas before formatting. They don't overlap, and are in file order.",
          "items": {
            "$ref": "#/$defs/Edit"
          },
          "type": "array"
        },
        "file": {
          "description": "The file, relative to the project root (the directory of\n`ascribe.toml`), with `/` separators; a file outside it as it was\nfound.",
          "type": "string"
        }
      },
      "required": [
        "file",
        "edits"
      ],
      "type": "object"
    },
    "Pos": {
      "description": "A position in a file.",
      "properties": {
        "column": {
          "description": "The column, from 1, in Unicode characters (not bytes or UTF-16\nunits).",
          "minimum": 0,
          "type": "integer"
        },
        "line": {
          "description": "The line, from 1.",
          "minimum": 0,
          "type": "integer"
        },
        "offset": {
          "description": "The byte offset from the start of the file.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "line",
        "column",
        "offset"
      ],
      "type": "object"
    },
    "Range": {
      "description": "A span of a file. `end` is just past its last character; an empty range\n(an insertion) has equal positions.",
      "properties": {
        "end": {
          "$ref": "#/$defs/Pos",
          "description": "Just past its last character."
        },
        "start": {
          "$ref": "#/$defs/Pos",
          "description": "Its first character."
        }
      },
      "required": [
        "start",
        "end"
      ],
      "type": "object"
    },
    "RefusedFile": {
      "description": "A file left alone.",
      "properties": {
        "file": {
          "description": "The file, as a formatted file's `file` is.",
          "type": "string"
        },
        "reason": {
          "description": "Why, as `ascribe check` words it.",
          "type": "string"
        }
      },
      "required": [
        "file",
        "reason"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe fmt --format json` writes: one document. Fields can be\nadded without a new `schema_version`, so a reader ignores fields it\ndoesn't know.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "files": {
      "description": "Each file that changed, or with `--check` or `--dry-run` would change, in path\norder, with the edits that format it.",
      "items": {
        "$ref": "#/$defs/FmtFile"
      },
      "type": "array"
    },
    "refused": {
      "description": "The files left alone because a symbolic link on the way to them\nleads to a file that isn't a source file of the content root.",
      "items": {
        "$ref": "#/$defs/RefusedFile"
      },
      "type": "array"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "written": {
      "description": "Whether the files were rewritten: `false` with `--check` or\n`--dry-run`, which write nothing.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "written",
    "files",
    "refused"
  ],
  "title": "FmtReport",
  "type": "object"
}

ascribe baseline write

schemas/baseline-write.schema.json:

{
  "$defs": {
    "Skipped": {
      "description": "The errors and warnings `ascribe baseline write` left out.",
      "properties": {
        "errors": {
          "description": "How many errors.",
          "minimum": 0,
          "type": "integer"
        },
        "warnings": {
          "description": "How many warnings.",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "errors",
        "warnings"
      ],
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe baseline write --format json` writes.",
  "properties": {
    "added": {
      "description": "How many of them this added.",
      "minimum": 0,
      "type": "integer"
    },
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "changed": {
      "description": "Whether the file changed: it's new, or its text differs. With\n`--dry-run`, whether it would.",
      "type": "boolean"
    },
    "created": {
      "description": "Whether the project had no baseline, so this made one.",
      "type": "boolean"
    },
    "entries": {
      "description": "How many problems the baseline holds, after this.",
      "minimum": 0,
      "type": "integer"
    },
    "file": {
      "description": "The file written, from the project's folder: `ascribe.baseline`.",
      "type": "string"
    },
    "files": {
      "description": "How many files they're in.",
      "minimum": 0,
      "type": "integer"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "skipped": {
      "$ref": "#/$defs/Skipped",
      "description": "How many errors and warnings it left out, which a baseline never\nholds."
    },
    "written": {
      "description": "Whether the file was written: it changed, without `--dry-run`.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "file",
    "created",
    "changed",
    "written",
    "entries",
    "files",
    "added",
    "skipped"
  ],
  "title": "BaselineWriteReport",
  "type": "object"
}

ascribe baseline prune

schemas/baseline-prune.schema.json:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe baseline prune --format json` writes.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "changed": {
      "description": "Whether the file changed: some entry was removed. With `--dry-run`,\nwhether it would.",
      "type": "boolean"
    },
    "entries": {
      "description": "How many problems the baseline holds, after this.",
      "minimum": 0,
      "type": "integer"
    },
    "file": {
      "description": "The file pruned, from the project's folder: `ascribe.baseline`.",
      "type": "string"
    },
    "files": {
      "description": "How many files they're in.",
      "minimum": 0,
      "type": "integer"
    },
    "found": {
      "description": "Whether the project has a baseline. Without one, there's nothing to\nprune, and nothing is written.",
      "type": "boolean"
    },
    "removed": {
      "description": "How many entries it removed.",
      "minimum": 0,
      "type": "integer"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "written": {
      "description": "Whether the file was written: it changed, without `--dry-run`.",
      "type": "boolean"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "file",
    "found",
    "changed",
    "written",
    "entries",
    "files",
    "removed"
  ],
  "title": "BaselinePruneReport",
  "type": "object"
}

ascribe vale eject

Ascribe (in main, not yet released, 0.4.5)

schemas/vale-eject.schema.json:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "What `ascribe vale eject --format json` writes: the preset written out,\nand the files written.",
  "properties": {
    "ascribe_version": {
      "description": "The version of Ascribe that wrote it.",
      "type": "string"
    },
    "dry_run": {
      "description": "Whether it ran with `--dry-run`, and wrote nothing.",
      "type": "boolean"
    },
    "preset": {
      "description": "The preset written out.",
      "type": "string"
    },
    "schema_version": {
      "description": "The version of this schema. It changes only when a field is removed\nor changes meaning.",
      "minimum": 0,
      "type": "integer"
    },
    "written": {
      "description": "The files written, from the project's folder, `/`-separated, in the\norder they were written: `ascribe.toml` last. With `--dry-run`, the\nfiles it would write.",
      "items": {
        "type": "string"
      },
      "type": "array"
    }
  },
  "required": [
    "schema_version",
    "ascribe_version",
    "preset",
    "written",
    "dry_run"
  ],
  "title": "ValeEjectReport",
  "type": "object"
}